#include "network.h"
#include "packet.h"
+#include "main.h" /* for optarg_verbose */
/* Config: */
#define NETWORK_PATHNAME_PID "/var/run/udpgate.pid"
#define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN) /* |G_IO_PRI */
#define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
-#define SERVER_INADDR 0x7F000001
-#undef SERVER_INADDR
-#define SERVER_INADDR 0x511F02EA /* paulina.vellum.cz = 81.31.2.234; host order */
+#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 (5)
+#define PROBE_TIMEOUT_SEC_BASE 5
+#define PROBE_TIMEOUT_SEC_MAX 3500
void (*network_notify_hostip)(guint32 hostip_guint32);
static struct client *master; /* 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);
return TRUE;
}
if (!(f=fopen(NETWORK_PATHNAME_PID,"w"))) {
- g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
+static gboolean once=TRUE;
+
+ if (once) {
+ once=FALSE;
+ g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
+ }
return FALSE;
}
fprintf(f,"%d\n",(int)pid); /* errors ignored */
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)
+static void client_touch(struct client *client,guint timeout_sec)
{
g_return_if_fail(client!=NULL);
g_return_if_fail(client!=master);
+ g_return_if_fail(timeout_sec>0);
client_timeout_remove(client);
client->timeout_id=g_timeout_add(
- (client==probe ? PROBE_TIMEOUT_SEC*1000 : CLIENT_TIMEOUT_SEC*1000), /* interval; msec */
+ 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,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_now<probe_timeout_sec_max) {
+ probe_timeout_sec_now*=PROBE_TIMEOUT_SEC_BASE;
+ g_assert(probe_timeout_sec_max==G_MAXUINT || probe_timeout_sec_max<PROBE_TIMEOUT_SEC_MAX);
+ probe_timeout_sec_now=MIN(probe_timeout_sec_now,probe_timeout_sec_max);
+ probe_timeout_sec_now=MIN(probe_timeout_sec_now,PROBE_TIMEOUT_SEC_MAX);
+ if (probe_send(probe,port_local)) {
+ client_touch(probe,probe_timeout_sec_now); /* timeout */
+ return FALSE; /* GSource should be removed */
+ }
+ /* failure FALLTHRU */
+ }
network_stop();
- if (network_notify_hostip)
- (*network_notify_hostip)(0);
+ /* Never destroy 'client' now - it has been destroyed by network_stop()! */
+ g_warning(_("No probe response from the server, stopping the daemon. Please check your public Internet connectivity."));
+ }
+ else {
+ client_destroy(client);
}
- client_destroy(client);
+ if (optarg_verbose)
+ g_message(_("Client timeout occurance finish"));
return FALSE; /* GSource should be removed */
}
static void handle_master(struct client *master)
{
+ g_return_if_fail(master!=NULL);
+
+ for (;;) {
+char packet[0x10000];
ssize_t gotlen;
struct sockaddr_in sockaddr_in_from;
-char packet[0x10000];
-struct client *client;
+struct client *client=NULL /* Prevent false positive: might be used uninitialized */;
struct sockaddr_in sockaddr_in_server;
socklen_t sockaddr_in_from_length;
-
- g_return_if_fail(master!=NULL);
-
- sockaddr_in_from_length=sizeof(sockaddr_in_from);
- while (-1!=(gotlen=recvfrom(
- master->gpollfd.fd, /* s */
- packet, /* buf */
- sizeof(packet), /* len */
- 0, /* flags */
- (struct sockaddr *)&sockaddr_in_from, /* from */
- &sockaddr_in_from_length))) { /* fromlen */
GList *clientl;
+ sockaddr_in_from_length=sizeof(sockaddr_in_from);
+ if (-1==(gotlen=recvfrom(
+ master->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;
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=client_new();
client->sockaddr_in_from=sockaddr_in_from;
}
- client_touch(client);
+ client_touch(client,CLIENT_TIMEOUT_SEC);
UDPGATE_MEMZERO(&sockaddr_in_server);
sockaddr_in_server.sin_family=AF_INET;
sockaddr_in_server.sin_port=htons(SERVER_PORT);
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;
- g_return_if_fail(probe!=NULL);
-
- sockaddr_in_from_length=sizeof(sockaddr_in_from);
- while (-1!=(gotlen=recvfrom(
- master->gpollfd.fd, /* s */
- packet, /* buf */
- sizeof(packet), /* len */
- 0, /* flags */
- (struct sockaddr *)&sockaddr_in_from, /* from */
- &sockaddr_in_from_length))) { /* fromlen */
+ sockaddr_in_from_length=sizeof(sockaddr_in_from);
+ if (-1==(gotlen=recvfrom(
+ master->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;
static void handle_client(struct client *client)
{
+ g_return_if_fail(client!=NULL);
+ g_return_if_fail(master!=NULL);
+
+ for (;;) {
ssize_t gotlen;
struct sockaddr_in sockaddr_in_from;
char packet [0x10000];
socklen_t sockaddr_in_from_length;
- g_return_if_fail(client!=NULL);
- g_return_if_fail(master!=NULL);
-
- while (-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 */
+ 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_touch(client,CLIENT_TIMEOUT_SEC);
/* FIXME: errors checking */
sendto(
master->gpollfd.fd, /* s */
client_destroy(sock_client_list->data);
g_assert(master==NULL);
+ g_assert(probe==NULL);
if (sock_gsource) {
g_source_destroy(sock_gsource);
sock_gsource=NULL;
}
- write_daemon_running((pid_t)-1); /* unlink */
+ write_daemon_running((pid_t)-1); /* unlink; errors ignored */
}
static gboolean sock_gsource_new(void)
if (!sock_gsource_new())
return FALSE;
- if (-1==(sock=socket(AF_INET,SOCK_DGRAM,0))) {
- g_warning("socket(AF_INET,SOCK_DGRAM): %m");
+ if (-1==(sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))) {
+ g_warning("socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP): %m");
return NULL;
}
if (ioctl(sock,FIONBIO,&oneul)) { /* non-blocking mode */
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;
}
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) {
master=NULL;
g_assert(client->timeout_id==0);
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 (optarg_verbose)
+ g_message(_("Client fd %d timeout id %d destroy finish"),client->gpollfd.fd,client->timeout_id);
+
g_free(client);
}
return TRUE;
}
-gboolean network_start(gint port)
+static gboolean master_start(gint port)
{
-pid_t daemon_pid;
struct sockaddr_in sockaddr_in;
+uint16_t port_use;
g_return_val_if_fail(port>=0,FALSE);
+ g_return_val_if_fail(master==NULL,FALSE);
- 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);
+ port_use=port;
+ if (port < 0 || port_use != port) {
+ g_warning(_("Port value %d is not valid for IPv4!"),(int)port);
return FALSE;
}
/* Setup 'master': */
- g_assert(master==NULL);
if (!(master=client_new()))
return FALSE;
UDPGATE_MEMZERO(&sockaddr_in);
sockaddr_in.sin_family=AF_INET;
- sockaddr_in.sin_port=htons(port);
+ sockaddr_in.sin_port=htons(port_use);
sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
- g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
-err_sock_gsource_destroy:
- sock_gsource_destroy();
+ g_warning("bind(sock,{AF_INET,INADDR_ANY:%u}): %m",(unsigned)port_use);
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()))
- goto err_sock_gsource_destroy;
- probe_send(probe,port);
- client_touch(probe); /* timeout */
+ 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);
+
+ 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);
- write_daemon_running(getpid());
+ 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;
}
}
if (daemon_pid==getpid()) {
sock_gsource_destroy();
- return TRUE;
+ 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;
+}