Cleanup getenv(3) by GLib compatible functions.
[udpgate.git] / src / network.c
1 /* $Id$
2  * UDP Gateway utility network layer
3  * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include <glib/gmessages.h>
23 #include <glib/gmain.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <glib/glist.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <glib/giochannel.h>
33 #include <sys/ioctl.h>
34 #include <string.h>
35 #include <glib/galloca.h>
36 #include <glib/gprintf.h>
37 #include <glib/grand.h>
38 #include <glib/gutils.h>
39
40 #include "network.h"
41 #include "packet.h"
42 #include "main.h"       /* for optarg_verbose */
43 #include "pathname.h"
44
45
46 /* Config: */
47 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN)      /* |G_IO_PRI */
48 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS)    /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
49 #define SERVER_INADDR 0xC37AD054        /* mms2.org = 195.122.208.84; host order */
50 #define SERVER_PORT   9201      /* host order */
51 #define PROBE_INADDR  SERVER_INADDR     /* host order */
52 #define PROBE_PORT    8201      /* host order */
53 #define CLIENT_TIMEOUT_SEC (5*60)
54 #define PROBE_TIMEOUT_SEC_BASE 5
55 #define PROBE_TIMEOUT_SEC_MAX 3500
56
57
58 void (*network_notify_hostip)(guint32 hostip_guint32);
59
60
61 static G_CONST_RETURN gchar *pid_pathname(void)
62 {
63 static const gchar *static_pathname;
64 static const char *user_val;
65
66         if (!static_pathname)
67                 user_val=g_get_user_name();
68         return pathname_find(&static_pathname,
69                         G_STRINGIFY(LOCALSTATEDIR) "/run",PACKAGE ".pid",
70                         (!user_val ? NULL : g_get_tmp_dir()),
71                                         (!user_val ? NULL : udpgate_printf_alloca(".%s-%s.pid",user_val,PACKAGE)),
72                         NULL);
73 }
74
75
76 struct client {
77         GPollFD gpollfd;
78         struct sockaddr_in sockaddr_in_from;
79         guint timeout_id;
80         };
81
82 static GSource *sock_gsource;
83
84 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
85 static struct client *master;   /* no 'timeout' permitted */
86 static struct client *probe;    /* 'timeout' permitted */
87 static guint32 probe_unique;
88 static guint probe_timeout_sec_now;
89 guint probe_timeout_sec_max=G_MAXUINT;
90 static gint port_local; /* for 'probe' resends */
91
92 static gboolean write_daemon_running(pid_t pid);
93
94 pid_t is_daemon_running(void)
95 {
96 FILE *f;
97 char buf[LINE_MAX],*got;
98 int pid_int;
99
100         if (sock_gsource)
101                 return getpid();
102
103         if (!(f=fopen(pid_pathname(),"r")))
104                 goto err;
105         got=fgets(buf,sizeof(buf),f);
106         fclose(f);      /* FIXME: error ignored */
107         if (got!=buf) {
108 err_unlink:
109                 write_daemon_running((pid_t)-1);        /* unlink */
110 err:
111                 return (pid_t)-1;
112                 }
113         pid_int=atoi(buf);
114         if (pid_int<=1)
115                 goto err_unlink;
116         if (kill((pid_t)pid_int,0)) {
117                 if (errno==ESRCH)
118                         goto err_unlink;
119                 goto err;
120                 }
121         return (pid_t)pid_int;
122 }
123
124 static gboolean write_daemon_running(pid_t pid)
125 {
126 FILE *f;
127
128         if (pid==(pid_t)-1) {
129                 if (unlink(pid_pathname())) {
130                         if (errno!=ENOENT)
131                                 g_warning(_("Error removing PID file \"%s\": %m"),pid_pathname());
132                         return FALSE;
133                         }
134                 return TRUE;
135                 }
136         if (!(f=fopen(pid_pathname(),"w"))) {
137 static gboolean once=TRUE;
138
139                 if (once) {
140                         once=FALSE;
141                         g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,pid_pathname());
142                         }
143                 return FALSE;
144                 }
145         fprintf(f,"%d\n",(int)pid);     /* errors ignored */
146         fclose(f);      /* FIXME: error ignored */
147         return TRUE;
148 }
149
150 static void client_timeout_remove(struct client *client)
151 {
152         g_return_if_fail(client!=NULL);
153         g_return_if_fail(client!=master);
154
155         if (client->timeout_id) {
156 gboolean errgboolean;
157
158                 if (optarg_verbose)
159                         g_message(_("Client fd %d removed timeout id %d"),client->gpollfd.fd,client->timeout_id);
160                 errgboolean=g_source_remove(client->timeout_id);
161                 g_assert(errgboolean==TRUE);
162                 client->timeout_id=0;
163                 }
164 }
165
166 static gboolean client_touch_timeout(struct client *client);
167
168 static void client_touch(struct client *client,guint timeout_sec)
169 {
170         g_return_if_fail(client!=NULL);
171         g_return_if_fail(client!=master);
172         g_return_if_fail(timeout_sec>0);
173
174         client_timeout_remove(client);
175         client->timeout_id=g_timeout_add(
176                         timeout_sec*1000,       /* interval; msec */
177                         (GSourceFunc)client_touch_timeout,      /* function */
178                         client);        /* data */
179         if (optarg_verbose)
180                 g_message(_("Client fd %d new timeout id %d"),client->gpollfd.fd,client->timeout_id);
181         g_assert(client->timeout_id!=0);
182 }
183
184 static void client_destroy(struct client *client);
185 static gboolean probe_send(struct client *probe,gint port_local);
186
187 static gboolean client_touch_timeout(struct client *client)
188 {
189         g_return_val_if_fail(client!=NULL,FALSE);       /* FALSE=>should be removed */
190         g_return_val_if_fail(client!=master,FALSE);     /* FALSE=>should be removed */
191
192         if (optarg_verbose)
193                 g_message(_("Client fd %d timeout id %d occured/entered"),client->gpollfd.fd,client->timeout_id);
194
195         /* Do not destroy the timeout in client_destroy().
196          * It would crash GLib - we remove it be returning FALSE from here.
197          */
198         g_assert(client->timeout_id!=0);
199         client->timeout_id=0;
200
201         if (client==probe) {
202                 if (probe_timeout_sec_now<probe_timeout_sec_max) {
203                         probe_timeout_sec_now*=PROBE_TIMEOUT_SEC_BASE;
204                         g_assert(probe_timeout_sec_max==G_MAXUINT || probe_timeout_sec_max<PROBE_TIMEOUT_SEC_MAX);
205                         probe_timeout_sec_now=MIN(probe_timeout_sec_now,probe_timeout_sec_max);
206                         probe_timeout_sec_now=MIN(probe_timeout_sec_now,PROBE_TIMEOUT_SEC_MAX);
207                         if (probe_send(probe,port_local)) {
208                                 client_touch(probe,probe_timeout_sec_now);      /* timeout */
209                                 return FALSE;   /* GSource should be removed */
210                                 }
211                         /* failure FALLTHRU */
212                         }
213                 network_stop();
214                 /* Never destroy 'client' now - it has been destroyed by network_stop()! */
215                 g_warning(_("No probe response from the server, stopping the daemon. Please check your public Internet connectivity."));
216                 }
217         else {
218                 client_destroy(client);
219                 }
220
221         if (optarg_verbose)
222                 g_message(_("Client timeout occurance finish"));
223
224         return FALSE;   /* GSource should be removed */
225 }
226
227 static void handle_master_probe(const void *packet,size_t gotlen,const struct sockaddr_in *sockaddr_in_from)
228 {
229 GHashTable *got_hash;
230 gpointer got_unique_gpointer;
231 gpointer hostip_gpointer;
232 guint32 hostip_guint32;
233
234         g_return_if_fail(packet!=NULL);
235         g_return_if_fail(sockaddr_in_from!=NULL);
236
237         if (!probe)
238                 return;
239
240         if (!(got_hash=packet_disassembly(packet,gotlen)))
241                 return;
242         if (!(g_hash_table_lookup_extended(
243                         got_hash,       /* hash_table */
244                         GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),        /* lookup_key */
245                         NULL,   /* orig_key */
246                         &got_unique_gpointer))) {
247 err_packet_disassembly_destroy_got_hash:
248                 packet_disassembly_destroy(got_hash);
249                 return;
250                 }
251         if (GPOINTER_TO_UINT(got_unique_gpointer)!=probe_unique)
252                 goto err_packet_disassembly_destroy_got_hash;
253         if (!(g_hash_table_lookup_extended(
254                         got_hash,       /* hash_table */
255                         GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_INADDR),       /* lookup_key */
256                         NULL,   /* orig_key */
257                         &hostip_gpointer)))
258                 goto err_packet_disassembly_destroy_got_hash;
259         hostip_guint32=GPOINTER_TO_UINT(hostip_gpointer);
260         packet_disassembly_destroy(got_hash);
261
262         client_destroy(probe);
263         if (network_notify_hostip)
264                 (*network_notify_hostip)(hostip_guint32);
265 }
266
267 static struct client *client_new(void);
268
269 static void handle_master(struct client *master)
270 {
271         g_return_if_fail(master!=NULL);
272
273         for (;;) {
274 char packet[0x10000];
275 ssize_t gotlen;
276 struct sockaddr_in sockaddr_in_from;
277 struct client *client=NULL      /* Prevent false positive: might be used uninitialized */;
278 struct sockaddr_in sockaddr_in_server;
279 socklen_t sockaddr_in_from_length;
280 GList *clientl;
281
282                 sockaddr_in_from_length=sizeof(sockaddr_in_from);
283                 if (-1==(gotlen=recvfrom(
284                                 master->gpollfd.fd,     /* s */
285                                 packet, /* buf */
286                                 sizeof(packet), /* len */
287                                 0,      /* flags */
288                                 (struct sockaddr *)&sockaddr_in_from,   /* from */
289                                 &sockaddr_in_from_length)))     /* fromlen */
290                         break;
291
292                 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from))  /* FIXME: errors reporting */
293                         continue;
294
295                 if (packet_recognized(packet,gotlen)) {
296                         handle_master_probe(packet,gotlen,&sockaddr_in_from);
297                         continue;
298                         }
299                 /* Not yet initialized by 'probe' reply - drop it. */
300                 if (probe) {
301                         if (optarg_verbose)
302                                 g_message(_("Data packet received from %s but no probe reply yet; dropping packet."),
303                                                 SOCKADDR_IN_TO_STRING(&sockaddr_in_from));
304                         continue;
305                         }
306
307                 /* FIXME: Performance: Ugly search... */
308                 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
309                         client=clientl->data;
310                         if (client==master)
311                                 continue;
312                         if (1
313                                         && client->sockaddr_in_from.sin_family     ==sockaddr_in_from.sin_family
314                                         && client->sockaddr_in_from.sin_port       ==sockaddr_in_from.sin_port
315                                         && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
316                                 break;
317                         }
318                 if (!clientl) {
319                         client=client_new();
320                         client->sockaddr_in_from=sockaddr_in_from;
321                         }
322                 client_touch(client,CLIENT_TIMEOUT_SEC);
323                 UDPGATE_MEMZERO(&sockaddr_in_server);
324                 sockaddr_in_server.sin_family=AF_INET;
325                 sockaddr_in_server.sin_port=htons(SERVER_PORT);
326                 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
327                 /* FIXME: errors checking */
328                 sendto(
329                                 client->gpollfd.fd,     /* s */
330                                 packet, /* msg */
331                                 gotlen, /* len */
332                                 0,      /* flags */
333                                 (struct sockaddr *)&sockaddr_in_server, /* to */
334                                 sizeof(sockaddr_in_server));    /* tolen */
335                 }
336 }
337
338 static void handle_probe(struct client *probe)
339 {
340         g_return_if_fail(probe!=NULL);
341
342         for (;;) {
343 ssize_t gotlen;
344 struct sockaddr_in sockaddr_in_from;
345 char packet[0x10000];
346 socklen_t sockaddr_in_from_length;
347
348                 sockaddr_in_from_length=sizeof(sockaddr_in_from);
349                 if (-1==(gotlen=recvfrom(
350                                 master->gpollfd.fd,     /* s */
351                                 packet, /* buf */
352                                 sizeof(packet), /* len */
353                                 0,      /* flags */
354                                 (struct sockaddr *)&sockaddr_in_from,   /* from */
355                                 &sockaddr_in_from_length)))     /* fromlen */
356                         break;
357                 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from))  /* FIXME: errors reporting */
358                         continue;
359
360                 /* Probe socket should have no response; maybe some ICMP errors - ignored. */
361                 }
362 }
363
364 static void handle_client(struct client *client)
365 {
366         g_return_if_fail(client!=NULL);
367         g_return_if_fail(master!=NULL);
368
369         for (;;) {
370 ssize_t gotlen;
371 struct sockaddr_in sockaddr_in_from;
372 char packet [0x10000];
373 socklen_t sockaddr_in_from_length;
374
375                 sockaddr_in_from_length=sizeof(sockaddr_in_from);
376                 if (-1==(gotlen=recvfrom(
377                                 client->gpollfd.fd,     /* s */
378                                 packet, /* buf */
379                                 sizeof(packet), /* len */
380                                 0,      /* flags */
381                                 (struct sockaddr *)&sockaddr_in_from,   /* from */
382                                 &sockaddr_in_from_length)))     /* fromlen */
383                         break;
384                 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from))  /* FIXME: errors reporting */
385                         continue;
386                 client_touch(client,CLIENT_TIMEOUT_SEC);
387                 /* FIXME: errors checking */
388                 sendto(
389                                 master->gpollfd.fd,     /* s */
390                                 packet, /* msg */
391                                 gotlen, /* len */
392                                 0,      /* flags */
393                                 (struct sockaddr *)&client->sockaddr_in_from,   /* to */
394                                 sizeof(client->sockaddr_in_from));      /* tolen */
395                 }
396 }
397
398 static gboolean sock_source_callback(gpointer data /* unused */)
399 {
400 GList *clientl;
401
402         for (clientl=sock_client_list;clientl;clientl=clientl->next) {
403 struct client *client=clientl->data;
404
405                 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
406                         /**/ if (client==master)
407                                 handle_master(client);
408                         else if (client==probe)
409                                 handle_probe(client);
410                         else
411                                 handle_client(client);
412                         }
413                 }
414
415         return TRUE; /* the source should be kept active */
416 }
417
418 static gboolean sock_source_prepare(GSource *source,gint *timeout)
419 {
420         *timeout=-1;
421
422         return FALSE;
423 }
424
425 static gboolean sock_source_check(GSource *source)
426 {
427 GList *clientl;
428
429         for (clientl=sock_client_list;clientl;clientl=clientl->next) {
430 struct client *client=clientl->data;
431
432                 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
433                         return TRUE;
434                 }
435         return FALSE;
436 }
437
438 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
439 {
440         g_assert(callback!=NULL);
441         return (*callback)(user_data);
442 }
443
444 static GSourceFuncs sock_source_watch_funcs={
445                 sock_source_prepare,
446                 sock_source_check,
447                 sock_source_dispatch,
448                 NULL, /* finalize */
449                 };
450
451 static void sock_gsource_destroy(void)
452 {
453         while (sock_client_list)
454                 client_destroy(sock_client_list->data);
455
456         g_assert(master==NULL);
457         g_assert(probe==NULL);
458
459         if (sock_gsource) {
460                 g_source_destroy(sock_gsource);
461                 sock_gsource=NULL;
462                 }
463
464         write_daemon_running((pid_t)-1);        /* unlink; errors ignored */
465 }
466
467 static gboolean sock_gsource_new(void)
468 {
469         if (sock_gsource)
470                 return TRUE;
471
472         g_assert(sock_client_list==NULL);
473
474         /* attach sock_source_callback() to watch for any abnormalities
475          * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
476          */
477         if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
478                 g_warning("g_source_new(): %m");
479                 return FALSE;
480                 }
481         g_source_set_callback(
482                         sock_gsource,  /* source */
483                         sock_source_callback,  /* func */
484                         NULL, /* data */
485                         NULL);  /* notify */
486         if (!g_source_attach(   /* returns 'guint' id */
487                         sock_gsource,  /* source */
488                         NULL)) {        /* context; NULL means 'default context' */
489                 g_warning("g_source_attach(gsource,NULL): %m");
490                 sock_gsource_destroy();
491                 return FALSE;
492                 }
493         return TRUE;
494 }
495
496 static struct client *client_new(void)
497 {
498 struct client *client;
499 static unsigned long oneul=1;
500 int sock;
501
502         if (!sock_gsource_new())
503                 return FALSE;
504
505         if (-1==(sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))) {
506                 g_warning("socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP): %m");
507                 return NULL;
508                 }
509         if (ioctl(sock,FIONBIO,&oneul)) {       /* non-blocking mode */
510                 g_warning("ioctl(sock,FIONBIO,&1): %m");
511                 close(sock);    /* errors ignored */
512                 return NULL;
513                 }
514
515         udpgate_new(client);
516         client->gpollfd.fd=sock;
517         client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
518         client->gpollfd.revents=0;
519         client->timeout_id=0;
520         sock_client_list=g_list_prepend(sock_client_list,client);
521         g_source_add_poll(sock_gsource,&client->gpollfd);
522
523         if (optarg_verbose)
524                 g_message(_("Client fd %d created"),client->gpollfd.fd);
525
526         return client;
527 }
528
529 static void client_destroy(struct client *client)
530 {
531         g_return_if_fail(client!=NULL);
532
533         if (!sock_gsource_new())
534                 return;
535
536         if (optarg_verbose)
537                 g_message(_("Client fd %d timeout id %d destroy enter"),client->gpollfd.fd,client->timeout_id);
538
539         if (client==master) {
540                 master=NULL;
541                 g_assert(client->timeout_id==0);
542                 }
543         else {
544                 if (client==probe)
545                         probe=NULL;
546                 client_timeout_remove(client);
547                 }
548
549         g_source_remove_poll(sock_gsource,&client->gpollfd);
550         sock_client_list=g_list_remove(sock_client_list,client);
551         close(client->gpollfd.fd);      /* errors ignored */
552
553         if (optarg_verbose)
554                 g_message(_("Client fd %d timeout id %d destroy finish"),client->gpollfd.fd,client->timeout_id);
555
556         g_free(client);
557 }
558
559 static gboolean probe_send(struct client *probe,gint port_local)
560 {
561 struct sockaddr_in sockaddr_in_server;
562 GHashTable *probe_hash;
563 gpointer packet;
564 size_t packet_length;
565
566         g_return_val_if_fail(probe!=NULL,FALSE);
567
568         probe_unique=g_random_int();
569
570         probe_hash=g_hash_table_new(
571                         g_direct_hash,  /* hash_func */
572                         g_direct_equal);        /* key_equal_func */
573         g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local));
574         g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique));
575         packet=packet_assembly(&packet_length,probe_hash);
576         g_hash_table_destroy(probe_hash);
577         if (!packet)
578                 return FALSE;
579
580         UDPGATE_MEMZERO(&sockaddr_in_server);
581         sockaddr_in_server.sin_family=AF_INET;
582         sockaddr_in_server.sin_port=htons(PROBE_PORT);
583         sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
584         /* FIXME: errors checking */
585         sendto(
586                         probe->gpollfd.fd,      /* s */
587                         packet, /* msg */
588                         packet_length,  /* len */
589                         0,      /* flags */
590                         (struct sockaddr *)&sockaddr_in_server, /* to */
591                         sizeof(sockaddr_in_server));    /* tolen */
592
593         return TRUE;
594 }
595
596 static gboolean master_start(gint port)
597 {
598 struct sockaddr_in sockaddr_in;
599 uint16_t port_use;
600
601         g_return_val_if_fail(port>=0,FALSE);
602         g_return_val_if_fail(master==NULL,FALSE);
603
604         port_use=port;
605         if (port < 0 || port_use != port) {
606                 g_warning(_("Port value %d is not valid for IPv4!"),(int)port);
607                 return FALSE;
608                 }
609
610         /* Setup 'master': */
611         if (!(master=client_new()))
612                 return FALSE;
613         UDPGATE_MEMZERO(&sockaddr_in);
614         sockaddr_in.sin_family=AF_INET;
615         sockaddr_in.sin_port=htons(port_use);
616         sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
617         if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
618                 g_warning("bind(sock,{AF_INET,INADDR_ANY:%u}): %m",(unsigned)port_use);
619                 return FALSE;
620                 }
621         return TRUE;
622 }
623
624 static gboolean probe_start(gint port)
625 {
626         g_return_val_if_fail(port>=0,FALSE);
627         g_return_val_if_fail(probe==NULL,FALSE);
628
629         /* Setup 'probe': */
630         if (!(probe=client_new()))
631                 return FALSE;
632         port_local=port;
633         if (!probe_send(probe,port)) {
634                 client_destroy(probe);
635                 return FALSE;
636                 }
637         probe_timeout_sec_now=PROBE_TIMEOUT_SEC_BASE;
638         client_touch(probe,probe_timeout_sec_now);      /* timeout */
639         return TRUE;
640 }
641
642 gboolean network_start(gint port)
643 {
644 pid_t daemon_pid;
645
646         g_return_val_if_fail(port>=0,FALSE);
647
648         if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
649                 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
650                 return FALSE;
651                 }
652         if (!master_start(port)) {
653                 sock_gsource_destroy();
654                 return FALSE;
655                 }
656         if (!probe_start(port)) {
657                 sock_gsource_destroy();
658                 return FALSE;
659                 }
660         write_daemon_running(getpid()); /* errors ignored */
661         if (network_notify_hostip)
662                 (*network_notify_hostip)(0);
663         return TRUE;
664 }
665
666 gboolean optarg_port_set_string(const gchar *port_string)
667 {
668 char *endp;
669 long port_long;
670
671         g_return_val_if_fail(port_string!=NULL,FALSE);
672
673         port_long=strtol(port_string,&endp,0);
674         if (endp && *endp) {
675                 g_warning(_("Invalid port specification, offending string: %s"),endp);
676                 return FALSE;
677                 }
678         if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) {
679                 g_warning(_("Invalid port integer number specification (%ld)"),port_long);
680                 return FALSE;
681                 }
682         optarg_port=port_long;
683         return TRUE;
684 }
685
686 gboolean network_stop(void)
687 {
688 pid_t daemon_pid;
689 int errno_save;
690
691         if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
692                 g_warning(_("Cannot stop network daemon: Daemon is not running"));
693                 return FALSE;
694                 }
695         if (daemon_pid==getpid()) {
696                 sock_gsource_destroy();
697                 goto ok;
698                 }
699         errno=0;
700         kill(daemon_pid,SIGKILL);
701         errno_save=errno;
702         write_daemon_running((pid_t)-1);        /* unlink; errors ignored */
703         if (errno_save)  {
704                 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
705                                 (int)daemon_pid,strerror(errno_save)));
706                 return FALSE;
707                 }
708 ok:
709         if (network_notify_hostip)
710                 (*network_notify_hostip)(0);
711         return TRUE;
712 }
713
714 static GMainLoop *gmainloop;
715 static void network_detach_network_notify_hostip(guint32 hostip_guint32)
716 {
717         if (!hostip_guint32)
718                 g_main_loop_quit(gmainloop);
719 }
720
721 gboolean network_detach(void)
722 {
723 pid_t daemon_pid,forked_pid;
724
725         if ((pid_t)-1==(daemon_pid=is_daemon_running()))
726                 return TRUE;
727         if (getpid()!=daemon_pid)
728                 return TRUE;
729         if (!optarg_no_fork) {
730                 if ((pid_t)-1==(forked_pid=fork())) {
731                         g_warning("fork(2): %m");
732                         return FALSE;
733                         }
734                 if (forked_pid) {
735                         /* parent */
736                         return TRUE;
737                         }
738                 write_daemon_running(getpid()); /* errors ignored */
739                 optarg_verbose=0;
740                 close(STDIN_FILENO);
741                 close(STDOUT_FILENO);
742                 close(STDERR_FILENO);
743                 setpgrp();
744                 setsid();
745                 }
746
747         network_notify_hostip=network_detach_network_notify_hostip;
748         gmainloop=g_main_loop_new(
749                         NULL,   /* context */
750                         TRUE);  /* is_running; ignored */
751         g_main_loop_run(gmainloop);     /* loop */
752         /* Unable to contact the server, aborting. */
753         return FALSE;
754 }