2 * UDP Gateway utility network layer
3 * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
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
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.
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
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>
29 #include <sys/types.h>
32 #include <glib/giochannel.h>
33 #include <sys/ioctl.h>
35 #include <glib/galloca.h>
36 #include <glib/gprintf.h>
42 #define NETWORK_PATHNAME_PID "/var/run/udpgate.pid"
43 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN) /* |G_IO_PRI */
44 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
45 #define SERVER_INADDR 0x511F02EA /* paulina.vellum.cz = 81.31.2.234; host order */
46 #define SERVER_PORT 9201 /* host order */
47 #define PROBE_INADDR SERVER_INADDR /* host order */
48 #define PROBE_PORT 8201 /* host order */
49 #define CLIENT_TIMEOUT_SEC (5*60)
50 #define PROBE_TIMEOUT_SEC (5)
55 struct sockaddr_in sockaddr_in_from;
59 static GSource *sock_gsource;
61 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
62 struct client *master; /* no 'timeout' permitted */
63 struct client *probe; /* 'timeout' permitted */
65 static gboolean write_daemon_running(pid_t pid);
67 pid_t is_daemon_running(void)
70 char buf[LINE_MAX],*got;
76 if (!(f=fopen(NETWORK_PATHNAME_PID,"r")))
78 got=fgets(buf,sizeof(buf),f);
79 fclose(f); /* FIXME: error ignored */
82 write_daemon_running((pid_t)-1); /* unlink */
89 if (kill((pid_t)pid_int,0)) {
94 return (pid_t)pid_int;
97 static gboolean write_daemon_running(pid_t pid)
101 if (pid==(pid_t)-1) {
102 if (unlink(NETWORK_PATHNAME_PID)) {
104 g_warning(_("Error removing PID file \"%s\": %m"),NETWORK_PATHNAME_PID);
109 if (!(f=fopen(NETWORK_PATHNAME_PID,"w"))) {
110 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
113 fprintf(f,"%d\n",(int)pid); /* errors ignored */
114 fclose(f); /* FIXME: error ignored */
118 static void client_timeout_remove(struct client *client)
120 g_return_if_fail(client!=NULL);
121 g_return_if_fail(client!=master);
123 if (client->timeout_id) {
124 gboolean errgboolean;
126 errgboolean=g_source_remove(client->timeout_id);
127 g_assert(errgboolean==TRUE);
128 client->timeout_id=0;
132 static gboolean client_touch_timeout(struct client *client);
134 static void client_touch(struct client *client)
136 g_return_if_fail(client!=NULL);
137 g_return_if_fail(client!=master);
139 client_timeout_remove(client);
140 client->timeout_id=g_timeout_add(
141 (client==probe ? PROBE_TIMEOUT_SEC*1000 : CLIENT_TIMEOUT_SEC*1000), /* interval; msec */
142 (GSourceFunc)client_touch_timeout, /* function */
144 g_assert(client->timeout_id!=0);
147 static void client_destroy(struct client *client);
149 static gboolean client_touch_timeout(struct client *client)
151 g_return_val_if_fail(client!=NULL,FALSE); /* FALSE=>should be removed */
152 g_return_val_if_fail(client!=master,FALSE); /* FALSE=>should be removed */
154 client_destroy(client);
156 return FALSE; /* GSource should be removed */
159 static struct client *client_new(void);
161 static void handle_master(struct client *master)
164 struct sockaddr_in sockaddr_in_from;
165 char packet[0x10000];
166 struct client *client;
167 struct sockaddr_in sockaddr_in_server;
168 socklen_t sockaddr_in_from_len;
170 g_return_if_fail(master!=NULL);
172 sockaddr_in_from_len=sizeof(sockaddr_in_from);
173 while (-1!=(gotlen=recvfrom(
174 master->gpollfd.fd, /* s */
176 sizeof(packet), /* len */
178 (struct sockaddr *)&sockaddr_in_from, /* from */
179 &sockaddr_in_from_len))) { /* fromlen */
182 if (sockaddr_in_from_len!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
184 /* FIXME: Performance: Ugly search... */
185 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
186 client=clientl->data;
190 && client->sockaddr_in_from.sin_family ==sockaddr_in_from.sin_family
191 && client->sockaddr_in_from.sin_port ==sockaddr_in_from.sin_port
192 && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
197 client->sockaddr_in_from=sockaddr_in_from;
199 client_touch(client);
200 UDPGATE_MEMZERO(&sockaddr_in_server);
201 sockaddr_in_server.sin_family=AF_INET;
202 sockaddr_in_server.sin_port=htons(SERVER_PORT);
203 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
204 /* FIXME: errors checking */
206 client->gpollfd.fd, /* s */
210 (struct sockaddr *)&sockaddr_in_server, /* to */
211 sizeof(sockaddr_in_server)); /* tolen */
215 static void handle_probe(struct client *probe)
218 struct sockaddr_in sockaddr_in_from;
219 char packet[0x10000];
220 socklen_t sockaddr_in_from_len;
222 g_return_if_fail(probe!=NULL);
224 sockaddr_in_from_len=sizeof(sockaddr_in_from);
225 while (-1!=(gotlen=recvfrom(
226 master->gpollfd.fd, /* s */
228 sizeof(packet), /* len */
230 (struct sockaddr *)&sockaddr_in_from, /* from */
231 &sockaddr_in_from_len))) { /* fromlen */
233 if (sockaddr_in_from_len!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
238 static void handle_client(struct client *client)
241 struct sockaddr_in sockaddr_in_from;
242 char packet [0x10000];
243 socklen_t sockaddr_in_from_len;
245 g_return_if_fail(client!=NULL);
246 g_return_if_fail(master!=NULL);
248 while (-1!=(gotlen=recvfrom(
249 client->gpollfd.fd, /* s */
251 sizeof(packet), /* len */
253 (struct sockaddr *)&sockaddr_in_from, /* from */
254 &sockaddr_in_from_len))) { /* fromlen */
255 if (sockaddr_in_from_len!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
257 client_touch(client);
258 /* FIXME: errors checking */
260 master->gpollfd.fd, /* s */
264 (struct sockaddr *)&client->sockaddr_in_from, /* to */
265 sizeof(client->sockaddr_in_from)); /* tolen */
269 static gboolean sock_source_callback(gpointer data /* unused */)
273 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
274 struct client *client=clientl->data;
276 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
277 /**/ if (client==master)
278 handle_master(client);
279 else if (client==probe)
280 handle_probe(client);
282 handle_client(client);
286 return TRUE; /* the source should be kept active */
289 static gboolean sock_source_prepare(GSource *source,gint *timeout)
296 static gboolean sock_source_check(GSource *source)
300 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
301 struct client *client=clientl->data;
303 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
309 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
311 g_assert(callback!=NULL);
312 return (*callback)(user_data);
315 static GSourceFuncs sock_source_watch_funcs={
318 sock_source_dispatch,
322 static void sock_gsource_destroy(void)
324 while (sock_client_list)
325 client_destroy(sock_client_list->data);
327 g_assert(master==NULL);
330 g_source_destroy(sock_gsource);
334 write_daemon_running((pid_t)-1); /* unlink */
337 static gboolean sock_gsource_new(void)
342 g_assert(sock_client_list==NULL);
344 /* attach sock_source_callback() to watch for any abnormalities
345 * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
347 if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
348 g_warning("g_source_new(): %m");
351 g_source_set_callback(
352 sock_gsource, /* source */
353 sock_source_callback, /* func */
356 if (!g_source_attach( /* returns 'guint' id */
357 sock_gsource, /* source */
358 NULL)) { /* context; NULL means 'default context' */
359 g_warning("g_source_attach(gsource,NULL): %m");
360 sock_gsource_destroy();
366 static struct client *client_new(void)
368 struct client *client;
369 static unsigned long oneul=1;
372 if (!sock_gsource_new())
375 if (-1==(sock=socket(AF_INET,SOCK_DGRAM,0))) {
376 g_warning("socket(AF_INET,SOCK_DGRAM): %m");
379 if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */
380 g_warning("ioctl(sock,FIONBIO,&1): %m");
381 close(sock); /* errors ignored */
386 client->gpollfd.fd=sock;
387 client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
388 client->gpollfd.revents=0;
389 client->timeout_id=0;
390 sock_client_list=g_list_prepend(sock_client_list,client);
391 g_source_add_poll(sock_gsource,&client->gpollfd);
396 static void client_destroy(struct client *client)
398 g_return_if_fail(client!=NULL);
400 if (!sock_gsource_new())
403 if (client==master) {
405 g_assert(client->timeout_id==0);
408 client_timeout_remove(client);
410 g_source_remove_poll(sock_gsource,&client->gpollfd);
411 sock_client_list=g_list_remove(sock_client_list,client);
412 close(client->gpollfd.fd); /* errors ignored */
416 static gboolean probe_send(struct client *probe)
418 struct sockaddr_in sockaddr_in_server;
420 g_return_val_if_fail(probe!=NULL,FALSE);
422 UDPGATE_MEMZERO(&sockaddr_in_server);
423 sockaddr_in_server.sin_family=AF_INET;
424 sockaddr_in_server.sin_port=htons(PROBE_PORT);
425 sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
426 /* FIXME: errors checking */
429 probe->gpollfd.fd, /* s */
433 (struct sockaddr *)&sockaddr_in_server, /* to */
434 sizeof(sockaddr_in_server)); /* tolen */
441 gboolean network_start(gint port)
444 struct sockaddr_in sockaddr_in;
446 g_return_val_if_fail(port>=0,FALSE);
448 if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
449 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
453 /* Setup 'master': */
454 g_assert(master==NULL);
455 if (!(master=client_new()))
457 UDPGATE_MEMZERO(&sockaddr_in);
458 sockaddr_in.sin_family=AF_INET;
459 sockaddr_in.sin_port=htons(port);
460 sockaddr_in.sin_addr.s_addr=INADDR_ANY;
461 if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
462 g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
463 err_sock_gsource_destroy:
464 sock_gsource_destroy();
469 if (!(probe=client_new()))
470 goto err_sock_gsource_destroy;
472 client_touch(probe); /* timeout */
474 write_daemon_running(getpid());
478 gboolean network_stop(void)
483 if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
484 g_warning(_("Cannot stop network daemon: Daemon is not running"));
487 if (daemon_pid==getpid()) {
488 sock_gsource_destroy();
492 kill(daemon_pid,SIGKILL);
495 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
496 (int)daemon_pid,strerror(errno_save)));