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