diff --git a/fanout.c b/fanout.c index 8f14a52afa568773071388e11a6ee5c98e0aabaf..114fbb72adabdfbd70829c7ddc4ac92bdb3974f8 100644 --- a/fanout.c +++ b/fanout.c @@ -9,8 +9,11 @@ #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/stat.h> #include <netinet/in.h> #include <time.h> +#include <getopt.h> +#include <stdarg.h> struct client @@ -44,7 +47,7 @@ char *substr (const char *s, int start, int stop); void str_swap_free (char **target, char *source); char *str_append (char *target, const char *data); void fanout_error(const char *msg); -void fanout_debug (const char *msg); +void fanout_debug (int level, const char *format, ...); int channel_exists (const char *channel_name); int channel_has_subscription (struct channel *c); @@ -75,17 +78,28 @@ void unsubscribe (struct client *c, const char *channel_name); // GLOBAL VARS fd_set readset, tempset; int max; +static int daemonize = 0; + +FILE *logfile; + +// 0 = ERROR +// 1 = WARNING +// 2 = INFO +// 3 = DEBUG +int debug_level = 1; struct client *client_head = NULL; struct subscription *subscription_head = NULL; struct channel *channel_head = NULL; -int main(int argc, char *argv[]) +int main (int argc, char *argv[]) { - int srvsock, portno, res; + int srvsock, res; + int portno = 1986; u_int yes = 1; u_int listen_backlog = 25; + FILE *pidfile; char buffer[1025]; struct client *client_i = NULL; @@ -94,23 +108,74 @@ int main(int argc, char *argv[]) socklen_t clilen; struct sockaddr_in serv_addr, cli_addr; - if (argc < 2) - fanout_error ("ERROR, no port provided"); - - srvsock = socket(AF_INET, SOCK_STREAM, 0); - + static struct option long_options[] = { + {"port", 1, 0, 0}, + {"daemon", 0, &daemonize, 1}, + {"logfile", 1, 0, 0}, + {"pidfile", 1, 0, 0}, + {"debug-level", 1, 0, 0}, + {NULL, 0, NULL, 0} + }; + + int c; + int option_index = 0; + while ((c = getopt_long (argc, argv, "", + long_options, &option_index)) != -1) { + switch (c) { + case 0: + switch (option_index) { + // port + case 0: + portno = atoi (optarg); + break; + // logfile + case 2: + if ((logfile = fopen (optarg, "a")) == NULL) { + fanout_error ("ERROR cannot open logfile"); + } + break; + // pidfile + case 3: + if ((pidfile = fopen (optarg, "w+")) == NULL) { + fanout_error ("ERROR cannot open pidfile"); + } + break; + //daemon + case 4: + debug_level = atoi (optarg); + break; + } + break; + default: + exit (EXIT_FAILURE); + } + } + + if (optind < argc) { + fanout_debug (0, "ERROR invalid args: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + exit (EXIT_FAILURE); + } + + + if ( ! portno > 0) { + fanout_debug (0, "ERROR invalid port\n"); + exit (EXIT_FAILURE); + } + + srvsock = socket (AF_INET, SOCK_STREAM, 0); if (srvsock < 0) - fanout_error("ERROR opening socket"); + fanout_error ("ERROR opening socket"); - bzero((char *) &serv_addr, sizeof(serv_addr)); - portno = atoi(argv[1]); + bzero((char *) &serv_addr, sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(portno); + serv_addr.sin_port = htons (portno); - setsockopt (srvsock,SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (yes)); - if (bind(srvsock, (struct sockaddr *) &serv_addr, - sizeof(serv_addr)) < 0) + setsockopt (srvsock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (yes)); + if (bind (srvsock, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) { fanout_error ("ERROR on binding"); } @@ -122,27 +187,68 @@ int main(int argc, char *argv[]) FD_SET (srvsock, &readset); + if (daemonize) { + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork (); + if (pid < 0) { + exit (EXIT_FAILURE); + } + /* If we got a good PID, then + we can exit the parent process. */ + if (pid > 0) { + /* Write pid to file */ + if (pidfile != NULL) { + fprintf (pidfile, "%d\n", (int) pid); + fclose (pidfile); + } + exit (EXIT_SUCCESS); + } + + /* Change the file mode mask */ + umask (0); + + /* Create a new SID for the child process */ + sid = setsid (); + if (sid < 0) { + /* Log any failure */ + exit (EXIT_FAILURE); + } + + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + /* Change the current working directory */ + if ((chdir ("/")) < 0) { + /* Log the failure */ + exit (EXIT_FAILURE); + } + while (1) { - fanout_debug ("server waiting for new activity"); + fanout_debug (3, "server waiting for new activity\n"); tempset = readset; // Wait indefinitely for read events res = select (max+1, &tempset, NULL, NULL, NULL); if (res < 0) { - printf ("something bad happened\n"); + fanout_debug (1, "something bad happened\n"); continue; } if (res == 0) { - printf ("timeout is met, in our case never\n"); + fanout_debug (1, "timeout is met, in our case never\n"); continue; } // Process new connections first if (FD_ISSET (srvsock, &tempset)) { if ((client_i = calloc (1, sizeof (struct client))) == NULL) { - fanout_debug ("memory error"); + fanout_debug (0, "memory error\n"); continue; } @@ -161,7 +267,7 @@ int main(int argc, char *argv[]) max = client_i->fd; } - fanout_debug ("client socket connected"); + fanout_debug (2, "client socket connected\n"); client_write (client_i, "debug!connected...\n"); subscribe (client_i, "all"); } @@ -171,11 +277,11 @@ int main(int argc, char *argv[]) while (client_i != NULL) { if (FD_ISSET (client_i->fd, &tempset)) { // Process data from socket i - printf ("processing client %d\n", client_i->fd); + fanout_debug (3, "processing client %d\n", client_i->fd); memset (buffer, 0, sizeof (buffer)); res = recv (client_i->fd, buffer, 1024, 0); if (res <= 0) { - fanout_debug ("client socket disconnected"); + fanout_debug (2, "client socket disconnected\n"); client_tmp = client_i; client_i = client_i->next; @@ -184,7 +290,8 @@ int main(int argc, char *argv[]) continue; } else { // Process data in buffer - printf ("%d bytes read: [%.*s]\n", res, (res - 1), buffer); + fanout_debug (3, "%d bytes read: [%.*s]\n", res, (res - 1), + buffer); client_i->input_buffer = str_append ( client_i->input_buffer, buffer); client_process_input_buffer (client_i); @@ -250,9 +357,46 @@ void fanout_error(const char *msg) } -void fanout_debug (const char *msg) +void fanout_debug (int level, const char *format, ...) { - printf("[%d] DEBUG: %s\n", (u_int) time(NULL), msg); + char *s_level; + + switch (level) { + case 0: + s_level = "ERROR"; + break; + case 1: + s_level = "WARNING"; + break; + case 2: + s_level = "INFO"; + break; + default: + s_level = "DEBUG"; + break; + } + + char *message; + asprintf(&message, "[%d] %s: ", (u_int) time(NULL), s_level); + char *data; + va_list args; + va_start(args, format); + vasprintf (&data, format, args); + va_end(args); + + message = str_append (message, data); + + if (debug_level >= level) { + if (daemonize && logfile != NULL) { + fprintf (logfile, "%s", message); + fflush (logfile); + } else { + printf ("%s", message); + } + } + + free (data); + free (message); } @@ -287,7 +431,7 @@ struct channel *get_channel (const char *channel_name) channel_i = channel_i->next; } - fanout_debug ("creating new channel"); + fanout_debug (2, "creating new channel\n"); if ((channel_i = calloc (1, sizeof (struct channel))) == NULL) { fanout_error ("memory error"); } @@ -303,7 +447,7 @@ struct channel *get_channel (const char *channel_name) void remove_channel (struct channel *c) { - printf ("removing unused channel %s\n", c->name); + fanout_debug (3, "removing unused channel %s\n", c->name); if (c->next != NULL) { c->next->previous = c->previous; } @@ -339,7 +483,7 @@ struct client *get_client (int fd) void remove_client (struct client *c) { - printf ("removing client %d from service\n", c->fd); + fanout_debug (3, "removing client %d from service\n", c->fd); if (c->next != NULL) { c->next->previous = c->previous; } @@ -390,11 +534,11 @@ void client_write (struct client *c, const char *data) sent = send (c->fd, c->output_buffer, strlen (c->output_buffer), 0); if (sent == -1) break; - printf ("wrote %d bytes\n", sent); + fanout_debug (3, "wrote %d bytes\n", sent); str_swap_free (&c->output_buffer, substr (c->output_buffer, sent, strlen (c->output_buffer))); } - printf ("remaining output buffer is %d chars: %s\n", + fanout_debug (3, "remaining output buffer is %d chars: %s\n", (u_int) strlen (c->output_buffer), c->output_buffer); } @@ -408,11 +552,11 @@ void client_process_input_buffer (struct client *c) int i; - printf ("full buffer\n\n%s\n\n", c->input_buffer); + fanout_debug (3, "full buffer\n\n%s\n\n", c->input_buffer); while ((i = strcpos (c->input_buffer, '\n')) >= 0) { line = substr (c->input_buffer, 0, i -1); - printf ("buffer has a newline at char %d\n", i); - printf ("line is %d chars: %s\n", (u_int) strlen (line), line); + fanout_debug (3, "buffer has a newline at char %d\n", i); + fanout_debug (3, "line is %d chars: %s\n", (u_int) strlen (line), line); if ( ! strcmp (line, "ping")) { asprintf (&message, "%d\n", (u_int) time(NULL)); @@ -423,7 +567,7 @@ void client_process_input_buffer (struct client *c) action = strtok (line, " "); channel = strtok (NULL, " "); if (action == NULL) { - fanout_debug ("received garbage from client"); + fanout_debug (3, "received garbage from client\n"); } else { if ( ! strcmp (action, "announce")) { //perform announce @@ -442,7 +586,7 @@ void client_process_input_buffer (struct client *c) if (strcpos (channel, '!') == -1) unsubscribe (c, channel); } else { - fanout_debug ("invalid action attempted"); + fanout_debug (3, "invalid action attempted\n"); } } } @@ -452,7 +596,7 @@ void client_process_input_buffer (struct client *c) free (line); } - printf ("remaining input buffer is %d chars: %s\n", + fanout_debug (3, "remaining input buffer is %d chars: %s\n", (int) strlen (c->input_buffer), c->input_buffer); } @@ -471,8 +615,6 @@ struct subscription *get_subscription (struct client *c, void remove_subscription (struct subscription *s) { - printf ("unsubscribing client %d from channel %s\n", s->client->fd, - s->channel->name); if (s->next != NULL) { s->next->previous = s->previous; } @@ -493,21 +635,23 @@ void destroy_subscription (struct subscription *s) void announce (const char *channel, const char *message) { - printf ("attempting to announce message %s to channel %s\n", message, - channel); + fanout_debug (3, "attempting to announce message %s to channel %s\n", + message, channel); char *s = NULL; asprintf (&s, "%s!%s\n", channel, message); struct subscription *subscription_i = subscription_head; while (subscription_i != NULL) { - printf ("testing subscription for client %d on channel %s\n", - subscription_i->client->fd, subscription_i->channel->name); + fanout_debug (3, "testing subscription for client %d on channel %s\n", + subscription_i->client->fd, + subscription_i->channel->name); if ( ! strcmp (subscription_i->channel->name, channel)) { - printf ("announcing message %s to %d on channel %s\n", message, - subscription_i->client->fd, channel); + fanout_debug (3, "announcing message %s to %d on channel %s\n", + message, subscription_i->client->fd, channel); client_write (subscription_i->client, s); } subscription_i = subscription_i->next; } + fanout_debug (2, "announced messge %s\n", s); free (s); } @@ -515,16 +659,15 @@ void announce (const char *channel, const char *message) void subscribe (struct client *c, const char *channel_name) { if (get_subscription (c, get_channel (channel_name)) != NULL) { - printf ("client %d already subscribed to channel %s\n", c->fd, channel_name); + fanout_debug (3, "client %d already subscribed to channel %s\n", + c->fd, channel_name); return; } - printf ("subscribing client %d to channel %s\n", c->fd, channel_name); - struct subscription *subscription_i = NULL; if ((subscription_i = calloc (1, sizeof (struct subscription))) == NULL) { - fanout_debug ("memory error trying to create new subscription"); + fanout_debug (1, "memory error trying to create new subscription\n"); return; } @@ -532,7 +675,8 @@ void subscribe (struct client *c, const char *channel_name) subscription_i->channel = get_channel (channel_name); subscription_i->channel->subscription_count++; - printf ("subscribed to channel %s\n", subscription_i->channel->name); + fanout_debug (2, "subscribed client %d to channel %s\n", c->fd, + subscription_i->channel->name); subscription_i->next = subscription_head; if (subscription_head != NULL) @@ -553,6 +697,9 @@ void unsubscribe (struct client *c, const char *channel_name) remove_subscription (subscription_i); destroy_subscription (subscription_i); + fanout_debug (2, "unsubscribed client %d from channel %s\n", c->fd, + channel_name); + channel->subscription_count--; if ( ! channel_has_subscription (channel)) {