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