Main Page | Data Structures | Directories | File List | Data Fields

wifidog-1.1.3_beta2/src/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 935 2006-01-31 22:22:04 -0500 (Tue, 31 Jan 2006) benoitg $ */
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 void append_x_restartargv(void) {
00088         int i;
00089 
00090         for (i=0; restartargv[i]; i++);
00091 
00092         restartargv[i++] = safe_strdup("-x");
00093         safe_asprintf(&(restartargv[i++]), "%d", getpid());
00094 }
00095 
00096 /* @internal
00097  * @brief During gateway restart, connects to the parent process via the internal socket
00098  * Downloads from it the active client list
00099  */
00100 void get_clients_from_parent(void) {
00101         int sock;
00102         struct sockaddr_un      sa_un;
00103         s_config * config = NULL;
00104         char linebuffer[MAX_BUF];
00105         int len = 0;
00106         char *running1 = NULL;
00107         char *running2 = NULL;
00108         char *token1 = NULL;
00109         char *token2 = NULL;
00110         char onechar;
00111         char *command = NULL;
00112         char *key = NULL;
00113         char *value = NULL;
00114         t_client * client = NULL;
00115         t_client * lastclient = NULL;
00116 
00117         config = config_get_config();
00118         
00119         debug(LOG_INFO, "Connecting to parent to download clients");
00120 
00121         /* Connect to socket */
00122         sock = socket(AF_UNIX, SOCK_STREAM, 0);
00123         memset(&sa_un, 0, sizeof(sa_un));
00124         sa_un.sun_family = AF_UNIX;
00125         strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1));
00126 
00127         if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) {
00128                 debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno));
00129                 return;
00130         }
00131 
00132         debug(LOG_INFO, "Connected to parent.  Downloading clients");
00133 
00134         LOCK_CLIENT_LIST();
00135 
00136         command = NULL;
00137         memset(linebuffer, 0, sizeof(linebuffer));
00138         len = 0;
00139         client = NULL;
00140         /* Get line by line */
00141         while (read(sock, &onechar, 1) == 1) {
00142                 if (onechar == '\n') {
00143                         /* End of line */
00144                         onechar = '\0';
00145                 }
00146                 linebuffer[len++] = onechar;
00147 
00148                 if (!onechar) {
00149                         /* We have a complete entry in linebuffer - parse it */
00150                         debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer);
00151                         running1 = linebuffer;
00152                         while ((token1 = strsep(&running1, "|")) != NULL) {
00153                                 if (!command) {
00154                                         /* The first token is the command */
00155                                         command = token1;
00156                                 }
00157                                 else {
00158                                 /* Token1 has something like "foo=bar" */
00159                                         running2 = token1;
00160                                         key = value = NULL;
00161                                         while ((token2 = strsep(&running2, "=")) != NULL) {
00162                                                 if (!key) {
00163                                                         key = token2;
00164                                                 }
00165                                                 else if (!value) {
00166                                                         value = token2;
00167                                                 }
00168                                         }
00169                                 }
00170 
00171                                 if (strcmp(command, "CLIENT") == 0) {
00172                                         /* This line has info about a client in the client list */
00173                                         if (!client) {
00174                                                 /* Create a new client struct */
00175                                                 client = safe_malloc(sizeof(t_client));
00176                                                 memset(client, 0, sizeof(t_client));
00177                                         }
00178                                 }
00179 
00180                                 if (key && value) {
00181                                         if (strcmp(command, "CLIENT") == 0) {
00182                                                 /* Assign the key into the appropriate slot in the connection structure */
00183                                                 if (strcmp(key, "ip") == 0) {
00184                                                         client->ip = safe_strdup(value);
00185                                                 }
00186                                                 else if (strcmp(key, "mac") == 0) {
00187                                                         client->mac = safe_strdup(value);
00188                                                 }
00189                                                 else if (strcmp(key, "token") == 0) {
00190                                                         client->token = safe_strdup(value);
00191                                                 }
00192                                                 else if (strcmp(key, "fw_connection_state") == 0) {
00193                                                         client->fw_connection_state = atoi(value);
00194                                                 }
00195                                                 else if (strcmp(key, "fd") == 0) {
00196                                                         client->fd = atoi(value);
00197                                                 }
00198                                                 else if (strcmp(key, "counters_incoming") == 0) {
00199                                                         client->counters.incoming_history = atoll(value);
00200                                                         client->counters.incoming = client->counters.incoming_history;
00201                                                 }
00202                                                 else if (strcmp(key, "counters_outgoing") == 0) {
00203                                                         client->counters.outgoing_history = atoll(value);
00204                                                         client->counters.outgoing = client->counters.outgoing_history;
00205                                                 }
00206                                                 else if (strcmp(key, "counters_last_updated") == 0) {
00207                                                         client->counters.last_updated = atol(value);
00208                                                 }
00209                                                 else {
00210                                                         debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value);
00211                                                 }
00212                                         }
00213                                 }
00214                         }
00215 
00216                         /* End of parsing this command */
00217                         if (client) {
00218                                 /* Add this client to the client list */
00219                                 if (!firstclient) {
00220                                         firstclient = client;
00221                                         lastclient = firstclient;
00222                                 }
00223                                 else {
00224                                         lastclient->next = client;
00225                                         lastclient = client;
00226                                 }
00227                         }
00228 
00229                         /* Clean up */
00230                         command = NULL;
00231                         memset(linebuffer, 0, sizeof(linebuffer));
00232                         len = 0;
00233                         client = NULL;
00234                 }
00235         }
00236 
00237         UNLOCK_CLIENT_LIST();
00238         debug(LOG_INFO, "Client list downloaded successfully from parent");
00239 
00240         close(sock);
00241 }
00242 
00250 void
00251 sigchld_handler(int s)
00252 {
00253         int     status;
00254         pid_t rc;
00255         
00256         debug(LOG_DEBUG, "Handler for SIGCHLD called. Trying to reap a child");
00257 
00258         rc = waitpid(-1, &status, WNOHANG);
00259 
00260         debug(LOG_DEBUG, "Handler for SIGCHLD reaped child PID %d", rc);
00261 }
00262 
00265 void
00266 termination_handler(int s)
00267 {
00268         static  pthread_mutex_t sigterm_mutex = PTHREAD_MUTEX_INITIALIZER;
00269         s_config *config = config_get_config();
00270 
00271         debug(LOG_INFO, "Handler for termination caught signal %d", s);
00272 
00273         /* Makes sure we only call fw_destroy() once. */
00274         if (pthread_mutex_trylock(&sigterm_mutex)) {
00275                 debug(LOG_INFO, "Another thread already began global termination handler. I'm exiting");
00276                 pthread_exit(NULL);
00277         }
00278         else {
00279                 debug(LOG_INFO, "Cleaning up and exiting");
00280         }
00281 
00282         debug(LOG_INFO, "Flushing firewall rules...");
00283         fw_destroy();
00284 
00285         /* XXX Hack
00286          * Aparently pthread_cond_timedwait under openwrt prevents signals (and therefore termination handler) from happening
00287          * so we need to explicitly kill the threads that use that
00288          */
00289         if (tid_fw_counter) {
00290                 debug(LOG_INFO, "Explicitly killing the fw_counter thread");
00291                 pthread_kill(tid_fw_counter, SIGKILL);
00292         }
00293         if (tid_ping) {
00294                 debug(LOG_INFO, "Explicitly killing the ping thread");
00295                 pthread_kill(tid_ping, SIGKILL);
00296         }
00297 
00298         debug(LOG_NOTICE, "Exiting...");
00299         exit(s == 0 ? 1 : 0);
00300 }
00301 
00305 static void
00306 init_signals(void)
00307 {
00308         struct sigaction sa;
00309 
00310         debug(LOG_DEBUG, "Initializing signal handlers");
00311         
00312         sa.sa_handler = sigchld_handler;
00313         sigemptyset(&sa.sa_mask);
00314         sa.sa_flags = SA_RESTART;
00315         if (sigaction(SIGCHLD, &sa, NULL) == -1) {
00316                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00317                 exit(1);
00318         }
00319 
00320         /* Trap SIGPIPE */
00321         /* This is done so that when libhttpd does a socket operation on
00322          * a disconnected socket (i.e.: Broken Pipes) we catch the signal
00323          * and do nothing. The alternative is to exit. SIGPIPE are harmless
00324          * if not desirable.
00325          */
00326         sa.sa_handler = SIG_IGN;
00327         if (sigaction(SIGPIPE, &sa, NULL) == -1) {
00328                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00329                 exit(1);
00330         }
00331 
00332         sa.sa_handler = termination_handler;
00333         sigemptyset(&sa.sa_mask);
00334         sa.sa_flags = SA_RESTART;
00335 
00336         /* Trap SIGTERM */
00337         if (sigaction(SIGTERM, &sa, NULL) == -1) {
00338                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00339                 exit(1);
00340         }
00341 
00342         /* Trap SIGQUIT */
00343         if (sigaction(SIGQUIT, &sa, NULL) == -1) {
00344                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00345                 exit(1);
00346         }
00347 
00348         /* Trap SIGINT */
00349         if (sigaction(SIGINT, &sa, NULL) == -1) {
00350                 debug(LOG_ERR, "sigaction(): %s", strerror(errno));
00351                 exit(1);
00352         }
00353 }
00354 
00358 static void
00359 main_loop(void)
00360 {
00361         int result;
00362         pthread_t       tid;
00363         s_config *config = config_get_config();
00364         request *r;
00365         void **params;
00366     FILE *fh;
00367 
00368     /* Set the time when wifidog started */
00369         if (!started_time) {
00370                 debug(LOG_INFO, "Setting started_time");
00371                 started_time = time(NULL);
00372         }
00373         else if (started_time < MINIMUM_STARTED_TIME) {
00374                 debug(LOG_WARNING, "Detected possible clock skew - re-setting started_time");
00375                 started_time = time(NULL);
00376         }
00377 
00378         /* If we don't have the Gateway IP address, get it. Can't fail. */
00379         if (!config->gw_address) {
00380                 debug(LOG_DEBUG, "Finding IP address of %s", config->gw_interface);
00381                 if ((config->gw_address = get_iface_ip(config->gw_interface)) == NULL) {
00382                         debug(LOG_ERR, "Could not get IP address information of %s, exiting...", config->gw_interface);
00383                         exit(1);
00384                 }
00385                 debug(LOG_DEBUG, "%s = %s", config->gw_interface, config->gw_address);
00386         }
00387 
00388   /* If we don't have the Gateway ID, construct it from the internal MAC address. Can't fail. */
00389   if (!config->gw_id) {
00390     debug(LOG_DEBUG, "Finding MAC address of %s", config->gw_interface);
00391     if ((config->gw_id = get_iface_mac(config->gw_interface)) == NULL) {
00392       debug(LOG_ERR, "Could not get MAC address information of %s, exiting...", config->gw_interface);
00393       exit(1);
00394     }
00395     debug(LOG_DEBUG, "%s = %s", config->gw_interface, config->gw_id);
00396   }
00397 
00398         /* Initializes the web server */
00399         debug(LOG_NOTICE, "Creating web server on %s:%d", config->gw_address, config->gw_port);
00400         if ((webserver = httpdCreate(config->gw_address, config->gw_port)) == NULL) {
00401                 debug(LOG_ERR, "Could not create web server: %s", strerror(errno));
00402                 exit(1);
00403         }
00404 
00405         debug(LOG_DEBUG, "Assigning callbacks to web server");
00406         httpdAddCContent(webserver, "/", "wifidog", 0, NULL, http_callback_wifidog);
00407         httpdAddCContent(webserver, "/wifidog", "", 0, NULL, http_callback_wifidog);
00408         httpdAddCContent(webserver, "/wifidog", "about", 0, NULL, http_callback_about);
00409         httpdAddCContent(webserver, "/wifidog", "status", 0, NULL, http_callback_status);
00410         httpdAddCContent(webserver, "/wifidog", "auth", 0, NULL, http_callback_auth);
00411 
00412         httpdAddC404Content(webserver, http_callback_404);
00413 
00414         /* Reset the firewall (if WiFiDog crashed) */
00415         fw_destroy();
00416         /* Then initialize it */
00417         fw_init();
00418 
00419         /* Start clean up thread */
00420         result = pthread_create(&tid_fw_counter, NULL, (void *)thread_client_timeout_check, NULL);
00421         if (result != 0) {
00422             debug(LOG_ERR, "FATAL: Failed to create a new thread (fw_counter) - exiting");
00423             termination_handler(0);
00424         }
00425         pthread_detach(tid_fw_counter);
00426 
00427         /* Start control thread */
00428         result = pthread_create(&tid, NULL, (void *)thread_wdctl, (void *)safe_strdup(config->wdctl_sock));
00429         if (result != 0) {
00430                 debug(LOG_ERR, "FATAL: Failed to create a new thread (wdctl) - exiting");
00431                 termination_handler(0);
00432         }
00433         pthread_detach(tid);
00434         
00435         /* Start heartbeat thread */
00436         result = pthread_create(&tid_ping, NULL, (void *)thread_ping, NULL);
00437         if (result != 0) {
00438             debug(LOG_ERR, "FATAL: Failed to create a new thread (ping) - exiting");
00439                 termination_handler(0);
00440         }
00441         pthread_detach(tid_ping);
00442         
00443         debug(LOG_NOTICE, "Waiting for connections");
00444         while(1) {
00445                 r = httpdGetConnection(webserver, NULL);
00446 
00447                 /* We can't convert this to a switch because there might be
00448                  * values that are not -1, 0 or 1. */
00449                 if (webserver->lastError == -1) {
00450                         /* Interrupted system call */
00451                         continue; /* restart loop */
00452                 }
00453                 else if (webserver->lastError < -1) {
00454                         /*
00455                          * FIXME
00456                          * An error occurred - should we abort?
00457                          * reboot the device ?
00458                          */
00459                         debug(LOG_ERR, "FATAL: httpdGetConnection returned unexpected value %d, exiting.", webserver->lastError);
00460                         termination_handler(0);
00461                 }
00462                 else if (r != NULL) {
00463                         /*
00464                          * We got a connection
00465                          *
00466                          * We should create another thread
00467                          */
00468                         debug(LOG_INFO, "Received connection from %s, spawning worker thread", r->clientAddr);
00469                         /* The void**'s are a simulation of the normal C
00470                          * function calling sequence. */
00471                         params = safe_malloc(2 * sizeof(void *));
00472                         *params = webserver;
00473                         *(params + 1) = r;
00474 
00475                         result = pthread_create(&tid, NULL, (void *)thread_httpd, (void *)params);
00476                         if (result != 0) {
00477                                 debug(LOG_ERR, "FATAL: Failed to create a new thread (httpd) - exiting");
00478                                 termination_handler(0);
00479                         }
00480                         pthread_detach(tid);
00481                 }
00482                 else {
00483                         /* webserver->lastError should be 2 */
00484                         /* XXX We failed an ACL.... No handling because
00485                          * we don't set any... */
00486                 }
00487         }
00488 
00489         /* never reached */
00490 }
00491 
00493 int main(int argc, char **argv) {
00494 
00495         s_config *config = config_get_config();
00496         config_init();
00497 
00498         parse_commandline(argc, argv);
00499 
00500         /* Initialize the config */
00501         config_read(config->configfile);
00502         config_validate();
00503 
00504         /* Initializes the linked list of connected clients */
00505         client_list_init();
00506 
00507         /* Init the signals to catch chld/quit/etc */
00508         init_signals();
00509 
00510         if (restart_orig_pid) {
00511                 /*
00512                  * We were restarted and our parent is waiting for us to talk to it over the socket
00513                  */
00514                 get_clients_from_parent();
00515 
00516                 /*
00517                  * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue
00518                  */
00519                 while (kill(restart_orig_pid, 0) != -1) {
00520                         debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
00521                         sleep(1);
00522                 }
00523 
00524                 debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
00525         }
00526 
00527         if (config->daemon) {
00528 
00529                 debug(LOG_INFO, "Forking into background");
00530 
00531                 switch(safe_fork()) {
00532                         case 0: /* child */
00533                                 setsid();
00534                                 append_x_restartargv();
00535                                 main_loop();
00536                                 break;
00537 
00538                         default: /* parent */
00539                                 exit(0);
00540                                 break;
00541                 }
00542         }
00543         else {
00544                 append_x_restartargv();
00545                 main_loop();
00546         }
00547 
00548         return(0); /* never reached */
00549 }

Generated on Tue Jan 31 23:13:16 2006 for WifiDog by  doxygen 1.4.4