/* $Id$ * UDP Gateway utility network layer * Copyright (C) 2004 Jan Kratochvil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "network.h" #include "packet.h" #include "main.h" /* for optarg_verbose */ #include "pathname.h" /* Config: */ #define SOCK_SOURCE_CHECK_REVENTS (G_IO_IN|G_IO_OUT) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */ #define SERVER_INADDR 0xC37AD054 /* mms2.org = 195.122.208.84; host order */ #define SERVER_PORT 9201 /* host order */ #define PROBE_INADDR SERVER_INADDR /* host order */ #define PROBE_PORT 8201 /* host order */ #define CLIENT_TIMEOUT_SEC (5*60) #define PROBE_TIMEOUT_SEC_BASE 5 #define PROBE_TIMEOUT_SEC_MAX 3500 #define TCP_BACKLOG 5 #define GSTRING_TO_SEND_MAX 1500*6 /* maximum data buffered per TCP client */ void (*network_notify_hostip)(guint32 hostip_guint32); static G_CONST_RETURN gchar *pid_pathname(void) { static const gchar *static_pathname; static const char *user_val; if (!static_pathname) user_val=g_get_user_name(); return pathname_find(&static_pathname, G_STRINGIFY(LOCALSTATEDIR) "/run",PACKAGE ".pid", (!user_val ? NULL : g_get_tmp_dir()), (!user_val ? NULL : udpgate_printf_alloca(".%s-%s.pid",user_val,PACKAGE)), NULL); } struct client { GPollFD gpollfd; struct sockaddr_in sockaddr_in_from; guint timeout_id; struct client *bound; unsigned connect_pending:1; /* for TCP connection to the server; activated by: G_IO_OUT */ unsigned server_only:1; /* nonimportant connect - the server side */ unsigned done:1; /* temporary marker */ GString *gstring_to_send; /* for TCP connections; data to be sent to this socket */ }; static GSource *sock_gsource; static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */ static struct client *master_udp; /* no 'timeout' permitted */ static struct client *master_tcp; /* no 'timeout' permitted */ static struct client *probe; /* 'timeout' permitted */ static guint32 probe_unique; static guint probe_timeout_sec_now; guint probe_timeout_sec_max=G_MAXUINT; static gint port_local; /* for 'probe' resends */ static gboolean write_daemon_running(pid_t pid); pid_t is_daemon_running(void) { FILE *f; char buf[LINE_MAX],*got; int pid_int; if (sock_gsource) return getpid(); if (!(f=fopen(pid_pathname(),"r"))) goto err; got=fgets(buf,sizeof(buf),f); fclose(f); /* FIXME: error ignored */ if (got!=buf) { err_unlink: write_daemon_running((pid_t)-1); /* unlink */ err: return (pid_t)-1; } pid_int=atoi(buf); if (pid_int<=1) goto err_unlink; if (kill((pid_t)pid_int,0)) { if (errno==ESRCH) goto err_unlink; goto err; } return (pid_t)pid_int; } static gboolean write_daemon_running(pid_t pid) { FILE *f; if (pid==(pid_t)-1) { if (unlink(pid_pathname())) { if (errno!=ENOENT) g_warning(_("Error removing PID file \"%s\": %m"),pid_pathname()); return FALSE; } return TRUE; } if (!(f=fopen(pid_pathname(),"w"))) { static gboolean once=TRUE; if (once) { once=FALSE; g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,pid_pathname()); } return FALSE; } fprintf(f,"%d\n",(int)pid); /* errors ignored */ fclose(f); /* FIXME: error ignored */ return TRUE; } static void client_timeout_remove(struct client *client) { g_return_if_fail(client!=NULL); g_return_if_fail(client!=master_udp); g_return_if_fail(client!=master_tcp); if (client->timeout_id) { gboolean errgboolean; if (optarg_verbose) g_message(_("Client fd %d removed timeout id %d"),client->gpollfd.fd,client->timeout_id); errgboolean=g_source_remove(client->timeout_id); g_assert(errgboolean==TRUE); client->timeout_id=0; } } static gboolean client_touch_timeout(struct client *client); static void client_touch(struct client *client,guint timeout_sec) { g_return_if_fail(client!=NULL); g_return_if_fail(client!=master_udp); g_return_if_fail(client!=master_tcp); g_return_if_fail(timeout_sec>0); client_timeout_remove(client); client->timeout_id=g_timeout_add( timeout_sec*1000, /* interval; msec */ (GSourceFunc)client_touch_timeout, /* function */ client); /* data */ if (optarg_verbose) g_message(_("Client fd %d new timeout id %d"),client->gpollfd.fd,client->timeout_id); g_assert(client->timeout_id!=0); } static void client_destroy(struct client *client); static gboolean probe_send(struct client *probe,gint port_local); static gboolean client_touch_timeout(struct client *client) { g_return_val_if_fail(client!=NULL,FALSE); /* FALSE=>should be removed */ g_return_val_if_fail(client!=master_udp,FALSE); /* FALSE=>should be removed */ g_return_val_if_fail(client!=master_tcp,FALSE); /* FALSE=>should be removed */ if (optarg_verbose) g_message(_("Client fd %d timeout id %d occured/entered"),client->gpollfd.fd,client->timeout_id); /* Do not destroy the timeout in client_destroy(). * It would crash GLib - we remove it be returning FALSE from here. */ g_assert(client->timeout_id!=0); client->timeout_id=0; if (client==probe) { if (probe_timeout_sec_nowbound==NULL); g_return_if_fail(client0->gstring_to_send==NULL); g_return_if_fail(client1!=NULL); g_return_if_fail(client1->bound==NULL); g_return_if_fail(client1->gstring_to_send==NULL); g_return_if_fail(client0!=client1); client0->bound=client1; client1->bound=client0; client0->gstring_to_send=g_string_sized_new(GSTRING_TO_SEND_MAX); client1->gstring_to_send=g_string_sized_new(GSTRING_TO_SEND_MAX); } static void client_split(struct client *client) { g_return_if_fail(client!=NULL); g_return_if_fail(client->bound!=NULL); g_return_if_fail(client->bound!=client); g_return_if_fail(client->bound->bound==client); g_return_if_fail(client->gstring_to_send!=NULL); client->bound->bound=NULL; client->bound=NULL; } static struct sockaddr_in sockaddr_in_server; static void handle_master_udp(struct client *master_udp) { g_return_if_fail(master_udp!=NULL); for (;;) { char packet[0x10000]; ssize_t gotlen; struct sockaddr_in sockaddr_in_from; struct client *client=NULL /* Prevent false positive: might be used uninitialized */; socklen_t sockaddr_in_from_length; GList *clientl; sockaddr_in_from_length=sizeof(sockaddr_in_from); if (-1==(gotlen=recvfrom( master_udp->gpollfd.fd, /* s */ packet, /* buf */ sizeof(packet), /* len */ 0, /* flags */ (struct sockaddr *)&sockaddr_in_from, /* from */ &sockaddr_in_from_length))) /* fromlen */ break; if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */ continue; if (packet_recognized(packet,gotlen)) { handle_master_probe(packet,gotlen,&sockaddr_in_from); continue; } /* Not yet initialized by 'probe' reply - drop it. */ if (probe) { if (optarg_verbose) g_message(_("Data packet received from %s but no probe reply yet; dropping packet."), SOCKADDR_IN_TO_STRING(&sockaddr_in_from)); continue; } /* FIXME: Performance: Ugly search... */ for (clientl=sock_client_list;clientl;clientl=clientl->next) { client=clientl->data; if (client==master_udp) continue; if (1 && client->sockaddr_in_from.sin_family ==sockaddr_in_from.sin_family && client->sockaddr_in_from.sin_port ==sockaddr_in_from.sin_port && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr) break; } if (!clientl) { client=client_new(CLIENT_NEW_TYPE_UDP); client->sockaddr_in_from=sockaddr_in_from; } client_touch(client,CLIENT_TIMEOUT_SEC); /* FIXME: errors checking */ sendto( client->gpollfd.fd, /* s */ packet, /* msg */ gotlen, /* len */ 0, /* flags */ (struct sockaddr *)&sockaddr_in_server, /* to */ sizeof(sockaddr_in_server)); /* tolen */ } } static void handle_master_tcp(struct client *master_tcp) { g_return_if_fail(master_tcp!=NULL); for (;;) { int sock_new; struct sockaddr_in sockaddr_in_from; struct client *client=NULL /* Prevent false positive: might be used uninitialized */; socklen_t sockaddr_in_from_length; int connect_got; struct client *server; sockaddr_in_from_length=sizeof(sockaddr_in_from); if (-1==(sock_new=accept( master_tcp->gpollfd.fd, /* s */ (struct sockaddr *)&sockaddr_in_from, /* from */ &sockaddr_in_from_length))) /* fromlen */ break; if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */ continue; /* Not yet initialized by 'probe' reply - drop it. */ if (probe) { if (optarg_verbose) g_message(_("Incoming connection received from %s but no probe reply yet; dropping connection."), SOCKADDR_IN_TO_STRING(&sockaddr_in_from)); close(sock_new); /* FIXME: errors reporting */ continue; } if (!(client=client_new_from_sock(sock_new))) continue; client->sockaddr_in_from=sockaddr_in_from; if (!(server=client_new(CLIENT_NEW_TYPE_TCP))) { client_destroy(client); continue; } server->server_only=1; server->sockaddr_in_from=sockaddr_in_server; clients_bind(client,server); /* Currently only one of 'client' or 'server' needs to be destroyed. */ errno=0; connect_got=connect( server->gpollfd.fd, /* sockfd */ (struct sockaddr *)&sockaddr_in_server, /* to */ sizeof(sockaddr_in_server)); /* tolen */ if (connect_got!=-1 || errno!=EINPROGRESS) { g_warning(_("Error establishing server TCP connection: %m")); client_destroy(server); continue; } g_assert(!server->connect_pending); server->connect_pending=1; } } static void handle_probe(struct client *probe) { g_return_if_fail(probe!=NULL); for (;;) { ssize_t gotlen; struct sockaddr_in sockaddr_in_from; char packet[0x10000]; socklen_t sockaddr_in_from_length; sockaddr_in_from_length=sizeof(sockaddr_in_from); if (-1==(gotlen=recvfrom( master_udp->gpollfd.fd, /* s */ packet, /* buf */ sizeof(packet), /* len */ 0, /* flags */ (struct sockaddr *)&sockaddr_in_from, /* from */ &sockaddr_in_from_length))) /* fromlen */ break; if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */ continue; /* Probe socket should have no response; maybe some ICMP errors - ignored. */ } } static void handle_client_udp(struct client *client) { g_return_if_fail(client!=NULL); g_return_if_fail(master_udp!=NULL); for (;;) { ssize_t gotlen; struct sockaddr_in sockaddr_in_from; char packet[0x10000]; socklen_t sockaddr_in_from_length; sockaddr_in_from_length=sizeof(sockaddr_in_from); if (-1==(gotlen=recvfrom( client->gpollfd.fd, /* s */ packet, /* buf */ sizeof(packet), /* len */ 0, /* flags */ (struct sockaddr *)&sockaddr_in_from, /* from */ &sockaddr_in_from_length))) /* fromlen */ break; if (sockaddr_in_from_length!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */ continue; client_touch(client,CLIENT_TIMEOUT_SEC); /* FIXME: errors checking */ sendto( master_udp->gpollfd.fd, /* s */ packet, /* msg */ gotlen, /* len */ 0, /* flags */ (struct sockaddr *)&client->sockaddr_in_from, /* to */ sizeof(client->sockaddr_in_from)); /* tolen */ } } static void handle_client_tcp_connect(struct client *server) { g_return_if_fail(server!=NULL); g_return_if_fail(server->connect_pending); g_return_if_fail(master_tcp!=NULL); errno=0; if (connect( server->gpollfd.fd, /* sockfd */ (struct sockaddr *)&sockaddr_in_server, /* to */ sizeof(sockaddr_in_server))) { /* tolen */ g_warning(_("Error establishing server TCP connection: %m")); client_destroy(server); return; } g_assert(server->connect_pending); server->connect_pending=0; } /* Read from client->gpollfd.fd the data for client->bound->gstring_to_send. */ static void handle_client_tcp_read(struct client *client) { g_return_if_fail(client!=NULL); g_return_if_fail(client->bound!=NULL); g_return_if_fail(!client->connect_pending); g_return_if_fail(master_tcp!=NULL); for (;;) { ssize_t gotlen; size_t read_max; char buf[GSTRING_TO_SEND_MAX]; read_max=sizeof(buf)-client->bound->gstring_to_send->len; g_assert(read_max>=0); if (read_max<=0) /* In fact it initially should not happen, there would be no: G_IO_IN */ break; gotlen=read(client->gpollfd.fd,buf,read_max); if (gotlen==-1) { if (errno==EAGAIN) break; g_warning(_("Error reading data from %s"),SOCKADDR_IN_TO_STRING(&client->sockaddr_in_from)); client_destroy(client); return; } if (gotlen==0) { /* Successful close; leave the bound peer's 'gstring_to_send' to be written there. */ if (client->server_only && client->bound->gstring_to_send->len) client_split(client); client_destroy(client); return; } g_assert(gotlen>0); g_assert((size_t)gotlen<=read_max); g_string_append_len(client->bound->gstring_to_send,buf,gotlen); } } /* Write to client->gpollfd.fd the data from client->gstring_to_send. */ static void handle_client_tcp_write(struct client *client) { g_return_if_fail(client!=NULL); g_return_if_fail(!client->connect_pending); g_return_if_fail(master_tcp!=NULL); for (;;) { ssize_t gotlen; if (client->gstring_to_send->len<=0) /* In fact it initially should not happen, there would be no: G_IO_OUT */ break; gotlen=write(client->gpollfd.fd,client->gstring_to_send->str,client->gstring_to_send->len); if (gotlen==-1) { if (errno==EAGAIN) break; g_warning(_("Error writing data to %s"),SOCKADDR_IN_TO_STRING(&client->sockaddr_in_from)); client_destroy(client); return; } if (gotlen<=0) break; g_assert((size_t)gotlen<=client->gstring_to_send->len); g_string_erase(client->gstring_to_send,0,gotlen); } /* Nothing left to do for such client. */ if (!client->gstring_to_send->len && !client->bound) client_destroy(client); } static gboolean sock_source_callback(gpointer data /* unused */) { GList *clientl; /* FIXME: Quadratic 'done' marking to not to stick on stale entry! */ for (clientl=sock_client_list;clientl;clientl=clientl->next) { struct client *client=clientl->data; client->done=0; } retry_in: for (clientl=sock_client_list;clientl;clientl=clientl->next) { struct client *client=clientl->data; if (client->done) continue; client->done=1; if (client->gpollfd.revents&G_IO_IN) { /**/ if (client==master_udp) handle_master_udp(client); else if (client==master_tcp) handle_master_tcp(client); else if (client==probe) handle_probe(client); else if (client->connect_pending) g_assert_not_reached(); else if (client->bound) handle_client_tcp_read(client); else handle_client_udp(client); } goto retry_in; } for (clientl=sock_client_list;clientl;clientl=clientl->next) { struct client *client=clientl->data; client->done=0; } retry_out: for (clientl=sock_client_list;clientl;clientl=clientl->next) { struct client *client=clientl->data; if (client->done) continue; client->done=1; if (client->gpollfd.revents&G_IO_OUT) { /**/ if (client==master_udp) g_assert_not_reached(); else if (client==master_tcp) g_assert_not_reached(); else if (client==probe) g_assert_not_reached(); else if (client->connect_pending) handle_client_tcp_connect(client); else if (client->gstring_to_send) handle_client_tcp_write(client); else g_assert_not_reached(); } goto retry_out; } return TRUE; /* the source should be kept active */ } static gboolean sock_source_prepare(GSource *source,gint *timeout) { GList *clientl; *timeout=-1; for (clientl=sock_client_list;clientl;clientl=clientl->next) { struct client *client=clientl->data; client->gpollfd.events=G_IO_IN; if (!client->bound && client->gstring_to_send) client->gpollfd.events&=~G_IO_IN; if (client->bound && client->bound->gstring_to_send->len>=GSTRING_TO_SEND_MAX) client->gpollfd.events&=~G_IO_IN; if (client->connect_pending) client->gpollfd.events&=~G_IO_IN; if (client->bound && client->bound->connect_pending) client->gpollfd.events&=~G_IO_IN; if (client->connect_pending) client->gpollfd.events|=G_IO_OUT; if (client->gstring_to_send && client->gstring_to_send->len>0) client->gpollfd.events|=G_IO_OUT; g_assert(!(client->gpollfd.events&~SOCK_SOURCE_CHECK_REVENTS)); } return FALSE; } static gboolean sock_source_check(GSource *source) { GList *clientl; for (clientl=sock_client_list;clientl;clientl=clientl->next) { struct client *client=clientl->data; if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) return TRUE; } return FALSE; } static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data) { g_assert(callback!=NULL); return (*callback)(user_data); } static GSourceFuncs sock_source_watch_funcs={ sock_source_prepare, sock_source_check, sock_source_dispatch, NULL, /* finalize */ }; static void sock_gsource_destroy(void) { while (sock_client_list) client_destroy(sock_client_list->data); g_assert(master_udp==NULL); g_assert(master_tcp==NULL); g_assert(probe==NULL); if (sock_gsource) { g_source_destroy(sock_gsource); sock_gsource=NULL; } write_daemon_running((pid_t)-1); /* unlink; errors ignored */ } static gboolean sock_gsource_new(void) { if (sock_gsource) return TRUE; g_assert(sock_client_list==NULL); /* attach sock_source_callback() to watch for any abnormalities * on our open pipe 'parentheart_fds' and terminate the child if parent dies. */ if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) { g_warning("g_source_new(): %m"); return FALSE; } g_source_set_callback( sock_gsource, /* source */ sock_source_callback, /* func */ NULL, /* data */ NULL); /* notify */ if (!g_source_attach( /* returns 'guint' id */ sock_gsource, /* source */ NULL)) { /* context; NULL means 'default context' */ g_warning("g_source_attach(gsource,NULL): %m"); sock_gsource_destroy(); return FALSE; } return TRUE; } static struct client *client_new_from_sock(int sock) { struct client *client; static unsigned long oneul=1; g_return_val_if_fail(sock>=0,NULL); if (!sock_gsource_new()) return FALSE; if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */ g_warning("ioctl(sock,FIONBIO,&1): %m"); close(sock); /* errors ignored */ return NULL; } udpgate_new(client); client->gpollfd.fd=sock; client->gpollfd.events=0; /* Initialized later. */ client->gpollfd.revents=0; /* Probably does not need to be initialized. */ client->timeout_id=0; client->bound=NULL; client->connect_pending=0; client->server_only=0; client->gstring_to_send=0; sock_client_list=g_list_prepend(sock_client_list,client); g_source_add_poll(sock_gsource,&client->gpollfd); if (optarg_verbose) g_message(_("Client fd %d created"),client->gpollfd.fd); return client; } static struct client *client_new(enum client_new_type type) { int sock; switch (type) { case CLIENT_NEW_TYPE_UDP: if (-1==(sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP))) { g_warning("socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP): %m"); return NULL; } break; case CLIENT_NEW_TYPE_TCP: if (-1==(sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))) { g_warning("socket(PF_INET,SOCK_STREAM,IPPROTO_TCP): %m"); return NULL; } break; default: g_assert_not_reached(); } return client_new_from_sock(sock); } static void client_destroy(struct client *client) { struct client *bound; g_return_if_fail(client!=NULL); if (!sock_gsource_new()) return; if (optarg_verbose) g_message(_("Client fd %d timeout id %d destroy enter"),client->gpollfd.fd,client->timeout_id); if (client==master_udp) { master_udp=NULL; g_assert(client->timeout_id==0); } else if (client==master_tcp) { master_tcp=NULL; g_assert(client->timeout_id==0); } else { if (client==probe) probe=NULL; client_timeout_remove(client); } g_source_remove_poll(sock_gsource,&client->gpollfd); sock_client_list=g_list_remove(sock_client_list,client); close(client->gpollfd.fd); /* errors ignored */ if (client->gstring_to_send) g_string_free(client->gstring_to_send,TRUE); if (optarg_verbose) g_message(_("Client fd %d timeout id %d destroy finish"),client->gpollfd.fd,client->timeout_id); bound=client->bound; g_free(client); if (bound) { g_assert(bound!=client); g_assert(bound->bound==client); client->bound=NULL; bound->bound=NULL; client_destroy(bound); } } static gboolean probe_send(struct client *probe,gint port_local) { struct sockaddr_in sockaddr_in_probe; GHashTable *probe_hash; gpointer packet; size_t packet_length; g_return_val_if_fail(probe!=NULL,FALSE); probe_unique=g_random_int(); probe_hash=g_hash_table_new( g_direct_hash, /* hash_func */ g_direct_equal); /* key_equal_func */ g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local)); g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique)); packet=packet_assembly(&packet_length,probe_hash); g_hash_table_destroy(probe_hash); if (!packet) return FALSE; UDPGATE_MEMZERO(&sockaddr_in_probe); sockaddr_in_probe.sin_family=AF_INET; sockaddr_in_probe.sin_port=htons(PROBE_PORT); sockaddr_in_probe.sin_addr.s_addr=htonl(PROBE_INADDR); /* FIXME: errors checking */ sendto( probe->gpollfd.fd, /* s */ packet, /* msg */ packet_length, /* len */ 0, /* flags */ (struct sockaddr *)&sockaddr_in_probe, /* to */ sizeof(sockaddr_in_probe)); /* tolen */ return TRUE; } static gboolean master_udp_start(gint port,const struct sockaddr_in *sockaddr_in_pointer) { g_return_val_if_fail(master_udp==NULL,FALSE); if (!(master_udp=client_new(CLIENT_NEW_TYPE_UDP))) return FALSE; if (bind(master_udp->gpollfd.fd,(struct sockaddr *)sockaddr_in_pointer,sizeof(*sockaddr_in_pointer))) { g_warning("bind(sock_udp,{AF_INET,INADDR_ANY:%d}): %m",(int)port); return FALSE; } return TRUE; } static gboolean master_tcp_start(gint port,const struct sockaddr_in *sockaddr_in_pointer) { static int onei=1; g_return_val_if_fail(master_tcp==NULL,FALSE); if (!(master_tcp=client_new(CLIENT_NEW_TYPE_TCP))) return FALSE; if (setsockopt(master_tcp->gpollfd.fd,SOL_SOCKET,SO_REUSEADDR,&onei,sizeof(onei))) { g_warning("setsockopt(sock_tcp,SOL_SOCKET,SO_REUSEADDR,1): %m"); return FALSE; } if (bind(master_tcp->gpollfd.fd,(struct sockaddr *)sockaddr_in_pointer,sizeof(*sockaddr_in_pointer))) { g_warning("bind(sock_tcp,{AF_INET,INADDR_ANY:%d}): %m",(int)port); return FALSE; } if (listen(master_tcp->gpollfd.fd,TCP_BACKLOG)) { g_warning("listen(sock_tcp,%d): %m",TCP_BACKLOG); return FALSE; } return TRUE; } static gboolean master_start(gint port) { struct sockaddr_in sockaddr_in; uint16_t port_use; g_return_val_if_fail(port>=0,FALSE); g_return_val_if_fail(master_udp==NULL,FALSE); g_return_val_if_fail(master_tcp==NULL,FALSE); port_use=port; if (port < 0 || port_use != port) { g_warning(_("Port value %d is not valid for IPv4!"),(int)port); return FALSE; } UDPGATE_MEMZERO(&sockaddr_in); sockaddr_in.sin_family=AF_INET; sockaddr_in.sin_port=htons(port_use); sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY); if (!master_udp_start(port,&sockaddr_in)) return FALSE; if (!master_tcp_start(port,&sockaddr_in)) { client_destroy(master_udp); return FALSE; } return TRUE; } static gboolean probe_start(gint port) { g_return_val_if_fail(port>=0,FALSE); g_return_val_if_fail(probe==NULL,FALSE); /* Setup 'probe': */ if (!(probe=client_new(CLIENT_NEW_TYPE_UDP))) return FALSE; port_local=port; if (!probe_send(probe,port)) { client_destroy(probe); return FALSE; } probe_timeout_sec_now=PROBE_TIMEOUT_SEC_BASE; client_touch(probe,probe_timeout_sec_now); /* timeout */ return TRUE; } gboolean network_start(gint port) { pid_t daemon_pid; g_return_val_if_fail(port>=0,FALSE); UDPGATE_MEMZERO(&sockaddr_in_server); sockaddr_in_server.sin_family=AF_INET; sockaddr_in_server.sin_port=htons(SERVER_PORT); sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR); if ((pid_t)-1!=(daemon_pid=is_daemon_running())) { g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid); return FALSE; } if (!master_start(port)) { sock_gsource_destroy(); return FALSE; } if (!probe_start(port)) { sock_gsource_destroy(); return FALSE; } write_daemon_running(getpid()); /* errors ignored */ if (network_notify_hostip) (*network_notify_hostip)(0); return TRUE; } gboolean optarg_port_set_string(const gchar *port_string) { char *endp; long port_long; g_return_val_if_fail(port_string!=NULL,FALSE); port_long=strtol(port_string,&endp,0); if (endp && *endp) { g_warning(_("Invalid port specification, offending string: %s"),endp); return FALSE; } if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) { g_warning(_("Invalid port integer number specification (%ld)"),port_long); return FALSE; } optarg_port=port_long; return TRUE; } gboolean network_stop(void) { pid_t daemon_pid; int errno_save; if ((pid_t)-1==(daemon_pid=is_daemon_running())) { g_warning(_("Cannot stop network daemon: Daemon is not running")); return FALSE; } if (daemon_pid==getpid()) { sock_gsource_destroy(); goto ok; } errno=0; kill(daemon_pid,SIGKILL); errno_save=errno; write_daemon_running((pid_t)-1); /* unlink; errors ignored */ if (errno_save) { g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"), (int)daemon_pid,strerror(errno_save))); return FALSE; } ok: if (network_notify_hostip) (*network_notify_hostip)(0); return TRUE; } static GMainLoop *gmainloop; static void network_detach_network_notify_hostip(guint32 hostip_guint32) { if (!hostip_guint32) g_main_loop_quit(gmainloop); } gboolean network_detach(void) { pid_t daemon_pid,forked_pid; if ((pid_t)-1==(daemon_pid=is_daemon_running())) return TRUE; if (getpid()!=daemon_pid) return TRUE; if (!optarg_no_fork) { if ((pid_t)-1==(forked_pid=fork())) { g_warning("fork(2): %m"); return FALSE; } if (forked_pid) { /* parent */ return TRUE; } write_daemon_running(getpid()); /* errors ignored */ optarg_verbose=0; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); setpgrp(); setsid(); } network_notify_hostip=network_detach_network_notify_hostip; gmainloop=g_main_loop_new( NULL, /* context */ TRUE); /* is_running; ignored */ g_main_loop_run(gmainloop); /* loop */ /* Unable to contact the server, aborting. */ return FALSE; }