gateway.c

00001 /********************************************************************\
00002  * This program is free software; you can redistribute it and/or    *
00003  * modify it under the terms of the GNU General Public License as   *
00004  * published by the Free:Software Foundation; either version 2 of   *
00005  * the License, or (at your option) any later version.              *
00006  *                                                                  *
00007  * This program is distributed in the hope that it will be useful,  *
00008  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00009  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00010  * GNU General Public License for more details.                     *
00011  *                                                                  *
00012  * You should have received a copy of the GNU General Public License*
00013  * along with this program; if not, contact:                        *
00014  *                                                                  *
00015  * Free Software Foundation           Voice:  +1-617-542-5942       *
00016  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00017  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00018  *                                                                  *
00019  \********************************************************************/
00020 
00021 /* $Id: gateway.c 1104 2006-10-09 00:58:46Z acv $ */
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <syslog.h>
00032 #include <pthread.h>
00033 #include <signal.h>
00034 #include <errno.h>
00035 #include <time.h>
00036 
00037 /* for strerror() */
00038 #include <string.h>
00039 
00040 /* for wait() */
00041 #include <sys/wait.h>
00042 
00043 /* for unix socket communication*/
00044 #include <sys/socket.h>
00045 #include <sys/un.h>
00046 
00047 #include "common.h"
00048 #include "httpd.h"
00049 #include "safe.h"
00050 #include "debug.h"
00051 #include "conf.h"
00052 #include "gateway.h"
00053 #include "firewall.h"
00054 #include "commandline.h"
00055 #include "auth.h"
00056 #include "http.h"
00057 #include "client_list.h"
00058 #include "wdctl_thread.h"
00059 #include "ping_thread.h"
00060 #include "httpd_thread.h"
00061 #include "util.h"
00062 
00067 static pthread_t tid_fw_counter = 0;
00068 static pthread_t tid_ping = 0; 
00069 
00070 /* The internal web server */
00071 httpd * webserver = NULL;
00072 
00073 /* from commandline.c */
00074 extern char ** restartargv;
00075 extern pid_t restart_orig_pid;
00076 t_client *firstclient;
00077 
00078 /* from client_list.c */
00079 extern pthread_mutex_t client_list_mutex;
00080 
00081 /* Time when wifidog started  */
00082 time_t started_time = 0;
00083 
00084 /* Appends -x, the current PID, and NULL to restartargv
00085  * see parse_commandline in commandline.c for details
00086  *
00087  * Why is restartargv global? Shouldn't it be at most static to commandline.c
00088  * and this function static there? -Alex @ 8oct2006
00089  */
00090 void append_x_restartargv(void) {
00091         int i;
00092 
00093         for (i=0; restartargv[i]; i++);
00094 
00095         restartargv[i++] = safe_strdup("-x");
00096         safe_asprintf(&(restartargv[i++]), "%d", getpid());
00097 }
00098 
00099 /* @internal
00100  * @brief During gateway restart, connects to the parent process via the internal socket
00101  * Downloads from it the active client list
00102  */
00103 void get_clients_from_parent(void) {
00104         int sock;
00105         struct sockaddr_un      sa_un;
00106         s_config * config = NULL;
00107         char linebuffer[MAX_BUF];
00108         int len = 0;
00109         char *running1 = NULL;
00110         char *running2 = NULL;
00111         char *token1 = NULL;
00112         char *token2 = NULL;
00113         char onechar;
00114         char *command = NULL;
00115         char *key = NULL;
00116         char *value = NULL;
00117         t_client * client = NULL;
00118         t_client * lastclient = NULL;
00119 
00120         config = config_get_config();
00121         
00122         debug(LOG_INFO, "Connecting to parent to download clients");
00123 
00124         /* Connect to socket */
00125         sock = socket(AF_UNIX, SOCK_STREAM, 0);
00126         memset(&sa_un, 0, sizeof(sa_un));
00127         sa_un.sun_family = AF_UNIX;
00128         strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1));
00129 
00130         if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) {
00131                 debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno));
00132                 return;
00133         }
00134 
00135         debug(LOG_INFO, "Connected to parent.  Downloading clients");
00136 
00137         LOCK_CLIENT_LIST();
00138 
00139         command = NULL;
00140         memset(linebuffer, 0, sizeof(linebuffer));
00141         len = 0;
00142         client = NULL;
00143         /* Get line by line */
00144         while (read(sock, &onechar, 1) == 1) {
00145                 if (onechar == '\n') {
00146                         /* End of line */
00147                         onechar = '\0';
00148                 }
00149                 linebuffer[len++] = onechar;
00150 
00151                 if (!onechar) {
00152                         /* We have a complete entry in linebuffer - parse it */
00153                         debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer);
00154                         running1 = linebuffer;
00155                         while ((token1 = strsep(&running1, "|")) != NULL) {
00156                                 if (!command) {
00157                                         /* The first token is the command */
00158                                         command = token1;
00159                                 }
00160                                 else {
00161                                 /* Token1 has something like "foo=bar" */
00162                                         running2 = token1;
00163                                         key = value = NULL;
00164                                         while ((token2 = strsep(&running2, "=")) != NULL) {
00165                                                 if (!key) {
00166                                                         key = token2;
00167                                                 }
00168                                                 else if (!value) {
00169                                                         value = token2;
00170                                                 }
00171                                         }
00172                                 }
00173 
00174                                 if (strcmp(command, "CLIENT") == 0) {
00175                                         /* This line has info about a client in the client list */
00176                                         if (!client) {
00177                                                 /* Create a new client struct */
00178                                                 client = safe_malloc(sizeof(t_client));
00179                                                 memset(client, 0, sizeof(t_client));
00180                                         }
00181                                 }
00182 
00183                                 if (key && value) {
00184                                         if (strcmp(command, "CLIENT") == 0) {
00185                                                 /* Assign the key into the appropriate slot in the connection structure */
00186                                                 if (strcmp(key, "ip") == 0) {
00187                                                         client->ip = safe_strdup(value);
00188                                                 }
00189                                                 else if (strcmp(key, "mac") == 0) {
00190                                                         client->mac = safe_strdup(value);
00191                                                 }
00192                                                 else if (strcmp(key, "token") == 0) {
00193                                                         client->token = safe_strdup(value);
00194                                                 }
00195                                                 else if (strcmp(key, "fw_connection_state") == 0) {
00196                                                         client->fw_connection_state = atoi(value);
00197                                                 }
00198                                                 else if (strcmp(key, "fd") == 0) {
00199                                                         client->fd = atoi(value);
00200                                                 }
00201                                                 else if (strcmp(key, "counters_incoming") == 0) {
00202                                                         client->counters.incoming_history = atoll(value);
00203                                                         client->counters.incoming = client->counters.incoming_history;
00204                                                 }
00205                                                 else if (strcmp(key, "counters_outgoing") == 0) {
00206                                                         client->counters.outgoing_history = atoll(value);
00207                                                         client->counters.outgoing = client->counters.outgoing_history;
00208                                                 }
00209                                                 else if (strcmp(key, "counters_last_updated") == 0) {
00210                                                         client->counters.last_updated = atol(value);
00211                                                 }
00212                                                 else {
00213                                                         debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value);
00214                                                 }
00215                                         }
00216                                 }
00217                         }
00218 
00219                         /* End of parsing this command */
00220                         if (client) {
00221                                 /* Add this client to the client list */
00222                                 if (!firstclient) {
00223                                         firstclient = client;
00224                                         lastclient = firstclient;
00225                                 }
00226                                 else {
00227                                         lastclient->next = client;
00228                                         lastclient = client;
00229                                 }
00230                         }
00231 
00232                         /* Clean up */
00233                         command = NULL;
00234                         memset(linebuffer, 0, sizeof(linebuffer));
00235                         len = 0;
00236                         client = NULL;
00237                 }
00238         }
00239 
00240         UNLOCK_CLIENT_LIST();
00241         debug(LOG_INFO, "Client list downloaded successfully from parent");
00242 
00243         close(sock);
00244 }
00245 
00253 void
00254 sigchld_handler(int s)
00255 {
00256         int     status;
00257         pid_t rc;
00258         
00259         debug(LOG_DEBUG, "Handler for SIGCHLD called. Trying to reap a child");
00260 
00261         rc = waitpid(-1, &status, WNOHANG);
00262 
00263         debug(LOG_DEBUG, "Handler for SIGCHLD reaped child PID %d", rc);
00264 }
00265 
00268 void
00269 termination_handler(int s)
00270 {
00271         static  pthread_mutex_t sigterm_mutex = PTHREAD_MUTEX_INITIALIZER;
00272         s_config *config = config_get_config();
00273 
00274         debug(LOG_INFO, "Handler for termination caught signal %d", s);
00275 
00276         /* Makes sure we only call fw_destroy() once. */
00277         if (pthread_mutex_trylock(&sigterm_mutex)) {
00278                 debug(LOG_INFO, "Another thread already began global termination handler. I'm exiting");
00279                 pthread_exit(NULL);
00280         }
00281         else {
00282                 debug(LOG_INFO, "Cleaning up and exiting");
00283         }
00284 
00285         debug(LOG_INFO, "Flushing firewall rules...");
00286         fw_destroy();
00287 
00288         /* XXX Hack
00289          * Aparently pthread_cond_timedwait under openwrt prevents signals (and therefore
00290          * termination handler) from happening so we need to explicitly kill the threads 
00291          * that use that
00292          */
00293         if (tid_fw_counter) {
00294                 debug(LOG_INFO, "Explicitly killing the fw_counter thread");
00295                 pthread_kill(tid_fw_counter, SIGKILL);
00296         }
00297         if (tid_ping) {
00298                 debug(LOG_INFO, "Explicitly killing the ping thread");
00299                 pthread_kill(tid_ping, SIGKILL);
00300         }
00301 
00302         debug(LOG_NOTICE, "Exiting...");
00303         exit(s == 0 ? 1 : 0);
00304 }
00305 
00309 static void
00310 init_signals(void)
00311 {
00312         struct sigaction sa;
00313 
00314         debug(LOG_DEBUG, "Initializing signal handlers");
00315         
00316         sa.sa_handler = sigchld_handler;
00317         sigemptyset(&sa.sa_mask);
00318         sa.sa_flags = SA_RESTART;
00319         if (sigaction(SIGCHLD, &sa, NULL) == -1) {
00320                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00321                 exit(1);
00322         }
00323 
00324         /* Trap SIGPIPE */
00325         /* This is done so that when libhttpd does a socket operation on
00326          * a disconnected socket (i.e.: Broken Pipes) we catch the signal
00327          * and do nothing. The alternative is to exit. SIGPIPE are harmless
00328          * if not desirable.
00329          */
00330         sa.sa_handler = SIG_IGN;
00331         if (sigaction(SIGPIPE, &sa, NULL) == -1) {
00332                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00333                 exit(1);
00334         }
00335 
00336         sa.sa_handler = termination_handler;
00337         sigemptyset(&sa.sa_mask);
00338         sa.sa_flags = SA_RESTART;
00339 
00340         /* Trap SIGTERM */
00341         if (sigaction(SIGTERM, &sa, NULL) == -1) {
00342                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00343                 exit(1);
00344         }
00345 
00346         /* Trap SIGQUIT */
00347         if (sigaction(SIGQUIT, &sa, NULL) == -1) {
00348                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00349                 exit(1);
00350         }
00351 
00352         /* Trap SIGINT */
00353         if (sigaction(SIGINT, &sa, NULL) == -1) {
00354                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00355                 exit(1);
00356         }
00357 }
00358 
00362 static void
00363 main_loop(void)
00364 {
00365         int result;
00366         pthread_t       tid;
00367         s_config *config = config_get_config();
00368         request *r;
00369         void **params;
00370     FILE *fh;
00371 
00372     /* Set the time when wifidog started */
00373         if (!started_time) {
00374                 debug(LOG_INFO, "Setting started_time");
00375                 started_time = time(NULL);
00376         }
00377         else if (started_time < MINIMUM_STARTED_TIME) {
00378                 debug(LOG_WARNING, "Detected possible clock skew - re-setting started_time");
00379                 started_time = time(NULL);
00380         }
00381 
00382         /* If we don't have the Gateway IP address, get it. Can't fail. */
00383         if (!config->gw_address) {
00384                 debug(LOG_DEBUG, "Finding IP address of %s", config->gw_interface);
00385                 if ((config->gw_address = get_iface_ip(config->gw_interface)) == NULL) {
00386                         debug(LOG_ERR, "Could not get IP address information of %s, exiting...", config->gw_interface);
00387                         exit(1);
00388                 }
00389                 debug(LOG_DEBUG, "%s = %s", config->gw_interface, config->gw_address);
00390         }
00391 
00392         /* If we don't have the Gateway ID, construct it from the internal MAC address.
00393          * "Can't fail" so exit() if the impossible happens. */
00394         if (!config->gw_id) {
00395         debug(LOG_DEBUG, "Finding MAC address of %s", config->gw_interface);
00396         if ((config->gw_id = get_iface_mac(config->gw_interface)) == NULL) {
00397                         debug(LOG_ERR, "Could not get MAC address information of %s, exiting...", config->gw_interface);
00398                         exit(1);
00399                 }
00400                 debug(LOG_DEBUG, "%s = %s", config->gw_interface, config->gw_id);
00401         }
00402 
00403         /* Initializes the web server */
00404         debug(LOG_NOTICE, "Creating web server on %s:%d", config->gw_address, config->gw_port);
00405         if ((webserver = httpdCreate(config->gw_address, config->gw_port)) == NULL) {
00406                 debug(LOG_ERR, "Could not create web server: %s", strerror(errno));
00407                 exit(1);
00408         }
00409 
00410         debug(LOG_DEBUG, "Assigning callbacks to web server");
00411         httpdAddCContent(webserver, "/", "wifidog", 0, NULL, http_callback_wifidog);
00412         httpdAddCContent(webserver, "/wifidog", "", 0, NULL, http_callback_wifidog);
00413         httpdAddCContent(webserver, "/wifidog", "about", 0, NULL, http_callback_about);
00414         httpdAddCContent(webserver, "/wifidog", "status", 0, NULL, http_callback_status);
00415         httpdAddCContent(webserver, "/wifidog", "auth", 0, NULL, http_callback_auth);
00416 
00417         httpdAddC404Content(webserver, http_callback_404);
00418 
00419         /* Reset the firewall (if WiFiDog crashed) */
00420         fw_destroy();
00421         /* Then initialize it */
00422         fw_init();
00423 
00424         /* Start clean up thread */
00425         result = pthread_create(&tid_fw_counter, NULL, (void *)thread_client_timeout_check, NULL);
00426         if (result != 0) {
00427             debug(LOG_ERR, "FATAL: Failed to create a new thread (fw_counter) - exiting");
00428             termination_handler(0);
00429         }
00430         pthread_detach(tid_fw_counter);
00431 
00432         /* Start control thread */
00433         result = pthread_create(&tid, NULL, (void *)thread_wdctl, (void *)safe_strdup(config->wdctl_sock));
00434         if (result != 0) {
00435                 debug(LOG_ERR, "FATAL: Failed to create a new thread (wdctl) - exiting");
00436                 termination_handler(0);
00437         }
00438         pthread_detach(tid);
00439         
00440         /* Start heartbeat thread */
00441         result = pthread_create(&tid_ping, NULL, (void *)thread_ping, NULL);
00442         if (result != 0) {
00443             debug(LOG_ERR, "FATAL: Failed to create a new thread (ping) - exiting");
00444                 termination_handler(0);
00445         }
00446         pthread_detach(tid_ping);
00447         
00448         debug(LOG_NOTICE, "Waiting for connections");
00449         while(1) {
00450                 r = httpdGetConnection(webserver, NULL);
00451 
00452                 /* We can't convert this to a switch because there might be
00453                  * values that are not -1, 0 or 1. */
00454                 if (webserver->lastError == -1) {
00455                         /* Interrupted system call */
00456                         continue; /* restart loop */
00457                 }
00458                 else if (webserver->lastError < -1) {
00459                         /*
00460                          * FIXME
00461                          * An error occurred - should we abort?
00462                          * reboot the device ?
00463                          */
00464                         debug(LOG_ERR, "FATAL: httpdGetConnection returned unexpected value %d, exiting.", webserver->lastError);
00465                         termination_handler(0);
00466                 }
00467                 else if (r != NULL) {
00468                         /*
00469                          * We got a connection
00470                          *
00471                          * We should create another thread
00472                          */
00473                         debug(LOG_INFO, "Received connection from %s, spawning worker thread", r->clientAddr);
00474                         /* The void**'s are a simulation of the normal C
00475                          * function calling sequence. */
00476                         params = safe_malloc(2 * sizeof(void *));
00477                         *params = webserver;
00478                         *(params + 1) = r;
00479 
00480                         result = pthread_create(&tid, NULL, (void *)thread_httpd, (void *)params);
00481                         if (result != 0) {
00482                                 debug(LOG_ERR, "FATAL: Failed to create a new thread (httpd) - exiting");
00483                                 termination_handler(0);
00484                         }
00485                         pthread_detach(tid);
00486                 }
00487                 else {
00488                         /* webserver->lastError should be 2 */
00489                         /* XXX We failed an ACL.... No handling because
00490                          * we don't set any... */
00491                 }
00492         }
00493 
00494         /* never reached */
00495 }
00496 
00498 int main(int argc, char **argv) {
00499 
00500         s_config *config = config_get_config();
00501         config_init();
00502 
00503         parse_commandline(argc, argv);
00504 
00505         /* Initialize the config */
00506         config_read(config->configfile);
00507         config_validate();
00508 
00509         /* Initializes the linked list of connected clients */
00510         client_list_init();
00511 
00512         /* Init the signals to catch chld/quit/etc */
00513         init_signals();
00514 
00515         if (restart_orig_pid) {
00516                 /*
00517                  * We were restarted and our parent is waiting for us to talk to it over the socket
00518                  */
00519                 get_clients_from_parent();
00520 
00521                 /*
00522                  * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue
00523                  */
00524                 while (kill(restart_orig_pid, 0) != -1) {
00525                         debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
00526                         sleep(1);
00527                 }
00528 
00529                 debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
00530         }
00531 
00532         if (config->daemon) {
00533 
00534                 debug(LOG_INFO, "Forking into background");
00535 
00536                 switch(safe_fork()) {
00537                         case 0: /* child */
00538                                 setsid();
00539                                 append_x_restartargv();
00540                                 main_loop();
00541                                 break;
00542 
00543                         default: /* parent */
00544                                 exit(0);
00545                                 break;
00546                 }
00547         }
00548         else {
00549                 append_x_restartargv();
00550                 main_loop();
00551         }
00552 
00553         return(0); /* never reached */
00554 }

Generated on Sat Jan 6 18:51:44 2007 for WifiDog by  doxygen 1.5.1