wdctl_thread.c

Go to the documentation of this file.
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: wdctl_thread.c 1305 2007-11-01 20:04:20Z benoitg $ */
00027 #define _GNU_SOURCE
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <pthread.h>
00032 #include <string.h>
00033 #include <stdarg.h>
00034 #include <sys/types.h>
00035 #include <sys/socket.h>
00036 #include <sys/un.h>
00037 #include <unistd.h>
00038 #include <syslog.h>
00039 #include <signal.h>
00040 #include <errno.h>
00041 
00042 #include "common.h"
00043 #include "httpd.h"
00044 #include "util.h"
00045 #include "conf.h"
00046 #include "debug.h"
00047 #include "auth.h"
00048 #include "centralserver.h"
00049 #include "fw_iptables.h"
00050 #include "firewall.h"
00051 #include "client_list.h"
00052 #include "wdctl_thread.h"
00053 #include "gateway.h"
00054 #include "safe.h"
00055 
00056 /* Defined in clientlist.c */
00057 extern  pthread_mutex_t client_list_mutex;
00058 extern  pthread_mutex_t config_mutex;
00059 
00060 /* From commandline.c: */
00061 extern char ** restartargv;
00062 static void *thread_wdctl_handler(void *);
00063 static void wdctl_status(int);
00064 static void wdctl_stop(int);
00065 static void wdctl_reset(int, char *);
00066 static void wdctl_restart(int);
00067 
00072 void
00073 thread_wdctl(void *arg)
00074 {
00075         int     fd;
00076         char    *sock_name;
00077         struct  sockaddr_un     sa_un;
00078         int result;
00079         pthread_t       tid;
00080         socklen_t len;
00081 
00082         debug(LOG_DEBUG, "Starting wdctl.");
00083 
00084         memset(&sa_un, 0, sizeof(sa_un));
00085         sock_name = (char *)arg;
00086         debug(LOG_DEBUG, "Socket name: %s", sock_name);
00087 
00088         if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
00089                 /* TODO: Die handler with logging.... */
00090                 debug(LOG_ERR, "WDCTL socket name too long");
00091                 exit(1);
00092         }
00093         
00094 
00095         debug(LOG_DEBUG, "Creating socket");
00096         wdctl_socket_server = socket(PF_UNIX, SOCK_STREAM, 0);
00097 
00098         debug(LOG_DEBUG, "Got server socket %d", wdctl_socket_server);
00099 
00100         /* If it exists, delete... Not the cleanest way to deal. */
00101         unlink(sock_name);
00102 
00103         debug(LOG_DEBUG, "Filling sockaddr_un");
00104         strcpy(sa_un.sun_path, sock_name); /* XXX No size check because we
00105                                             * check a few lines before. */
00106         sa_un.sun_family = AF_UNIX;
00107         
00108         debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path,
00109                         strlen(sock_name));
00110         
00111         /* Which to use, AF_UNIX, PF_UNIX, AF_LOCAL, PF_LOCAL? */
00112         if (bind(wdctl_socket_server, (struct sockaddr *)&sa_un, strlen(sock_name) 
00113                                 + sizeof(sa_un.sun_family))) {
00114                 debug(LOG_ERR, "Could not bind control socket: %s",
00115                                 strerror(errno));
00116                 pthread_exit(NULL);
00117         }
00118 
00119         if (listen(wdctl_socket_server, 5)) {
00120                 debug(LOG_ERR, "Could not listen on control socket: %s",
00121                                 strerror(errno));
00122                 pthread_exit(NULL);
00123         }
00124 
00125         while (1) {
00126                 len = sizeof(sa_un); 
00127                 memset(&sa_un, 0, len);
00128                 if ((fd = accept(wdctl_socket_server, (struct sockaddr *)&sa_un, &len)) == -1){
00129                         debug(LOG_ERR, "Accept failed on control socket: %s",
00130                                         strerror(errno));
00131                 } else {
00132                         debug(LOG_DEBUG, "Accepted connection on wdctl socket %d (%s)", fd, sa_un.sun_path);
00133                         result = pthread_create(&tid, NULL, &thread_wdctl_handler, (void *)fd);
00134                         if (result != 0) {
00135                                 debug(LOG_ERR, "FATAL: Failed to create a new thread (wdctl handler) - exiting");
00136                                 termination_handler(0);
00137                         }
00138                         pthread_detach(tid);
00139                 }
00140         }
00141 }
00142 
00143 
00144 static void *
00145 thread_wdctl_handler(void *arg)
00146 {
00147         int     fd,
00148                 done,
00149                 i;
00150         char    request[MAX_BUF];
00151         ssize_t read_bytes,
00152                 len;
00153 
00154         debug(LOG_DEBUG, "Entering thread_wdctl_handler....");
00155 
00156         fd = (int)arg;
00157         
00158         debug(LOG_DEBUG, "Read bytes and stuff from %d", fd);
00159 
00160         /* Init variables */
00161         read_bytes = 0;
00162         done = 0;
00163         memset(request, 0, sizeof(request));
00164         
00165         /* Read.... */
00166         while (!done && read_bytes < (sizeof(request) - 1)) {
00167                 len = read(fd, request + read_bytes,
00168                                 sizeof(request) - read_bytes);
00169 
00170                 /* Have we gotten a command yet? */
00171                 for (i = read_bytes; i < (read_bytes + len); i++) {
00172                         if (request[i] == '\r' || request[i] == '\n') {
00173                                 request[i] = '\0';
00174                                 done = 1;
00175                         }
00176                 }
00177                 
00178                 /* Increment position */
00179                 read_bytes += len;
00180         }
00181 
00182         if (strncmp(request, "status", 6) == 0) {
00183                 wdctl_status(fd);
00184         } else if (strncmp(request, "stop", 4) == 0) {
00185                 wdctl_stop(fd);
00186         } else if (strncmp(request, "reset", 5) == 0) {
00187                 wdctl_reset(fd, (request + 6));
00188         } else if (strncmp(request, "restart", 7) == 0) {
00189                 wdctl_restart(fd);
00190         }
00191 
00192         if (!done) {
00193                 debug(LOG_ERR, "Invalid wdctl request.");
00194                 shutdown(fd, 2);
00195                 close(fd);
00196                 pthread_exit(NULL);
00197         }
00198 
00199         debug(LOG_DEBUG, "Request received: [%s]", request);
00200         
00201         shutdown(fd, 2);
00202         close(fd);
00203         debug(LOG_DEBUG, "Exiting thread_wdctl_handler....");
00204 
00205         return NULL;
00206 }
00207 
00208 static void
00209 wdctl_status(int fd)
00210 {
00211         char * status = NULL;
00212         int len = 0;
00213 
00214         status = get_status_text();
00215         len = strlen(status);
00216 
00217         write(fd, status, len);
00218 
00219         free(status);
00220 }
00221 
00223 static void
00224 wdctl_stop(int fd)
00225 {
00226         pid_t   pid;
00227 
00228         pid = getpid();
00229         kill(pid, SIGINT);
00230 }
00231 
00232 static void
00233 wdctl_restart(int afd)
00234 {
00235         int     sock,
00236                 fd;
00237         char    *sock_name;
00238         struct  sockaddr_un     sa_un;
00239         s_config * conf = NULL;
00240         t_client * client = NULL;
00241         char * tempstring = NULL;
00242         pid_t pid;
00243         ssize_t written;
00244         socklen_t len;
00245 
00246         conf = config_get_config();
00247 
00248         debug(LOG_NOTICE, "Will restart myself");
00249 
00250         /*
00251          * First, prepare the internal socket
00252          */
00253         memset(&sa_un, 0, sizeof(sa_un));
00254         sock_name = conf->internal_sock;
00255         debug(LOG_DEBUG, "Socket name: %s", sock_name);
00256 
00257         if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
00258                 /* TODO: Die handler with logging.... */
00259                 debug(LOG_ERR, "INTERNAL socket name too long");
00260                 return;
00261         }
00262 
00263         debug(LOG_DEBUG, "Creating socket");
00264         sock = socket(PF_UNIX, SOCK_STREAM, 0);
00265 
00266         debug(LOG_DEBUG, "Got internal socket %d", sock);
00267 
00268         /* If it exists, delete... Not the cleanest way to deal. */
00269         unlink(sock_name);
00270 
00271         debug(LOG_DEBUG, "Filling sockaddr_un");
00272         strcpy(sa_un.sun_path, sock_name); /* XXX No size check because we check a few lines before. */
00273         sa_un.sun_family = AF_UNIX;
00274         
00275         debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));
00276         
00277         /* Which to use, AF_UNIX, PF_UNIX, AF_LOCAL, PF_LOCAL? */
00278         if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {
00279                 debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));
00280                 return;
00281         }
00282 
00283         if (listen(sock, 5)) {
00284                 debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));
00285                 return;
00286         }
00287         
00288         /*
00289          * The internal socket is ready, fork and exec ourselves
00290          */
00291         debug(LOG_DEBUG, "Forking in preparation for exec()...");
00292         pid = safe_fork();
00293         if (pid > 0) {
00294                 /* Parent */
00295 
00296                 /* Wait for the child to connect to our socket :*/
00297                 debug(LOG_DEBUG, "Waiting for child to connect on internal socket");
00298                 len = sizeof(sa_un);
00299                 if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){
00300                         debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));
00301                         close(sock);
00302                         return;
00303                 }
00304 
00305                 close(sock);
00306 
00307                 debug(LOG_DEBUG, "Received connection from child.  Sending them all existing clients");
00308 
00309                 /* The child is connected. Send them over the socket the existing clients */
00310                 LOCK_CLIENT_LIST();
00311                 client = client_get_first_client();
00312                 while (client) {
00313                         /* Send this client */
00314                         safe_asprintf(&tempstring, "CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n", client->ip, client->mac, client->token, client->fw_connection_state, client->fd, client->counters.incoming, client->counters.outgoing, client->counters.last_updated);
00315                         debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);
00316                         len = 0;
00317                         while (len != strlen(tempstring)) {
00318                                 written = write(fd, (tempstring + len), strlen(tempstring) - len);
00319                                 if (written == -1) {
00320                                         debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));
00321                                         free(tempstring);
00322                                         break;
00323                                 }
00324                                 else {
00325                                         len += written;
00326                                 }
00327                         }
00328                         free(tempstring);
00329                         client = client->next;
00330                 }
00331                 UNLOCK_CLIENT_LIST();
00332 
00333                 close(fd);
00334 
00335                 debug(LOG_INFO, "Sent all existing clients to child.  Committing suicide!");
00336 
00337                 shutdown(afd, 2);
00338                 close(afd);
00339 
00340                 /* Our job in life is done. Commit suicide! */
00341                 wdctl_stop(afd);
00342         }
00343         else {
00344                 /* Child */
00345                 close(wdctl_socket_server);
00346                 close(icmp_fd);
00347                 close(sock);
00348                 shutdown(afd, 2);
00349                 close(afd);
00350                 debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);
00351                 setsid();
00352                 execvp(restartargv[0], restartargv);
00353                 /* If we've reached here the exec() failed - die quickly and silently */
00354                 debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
00355                 debug(LOG_ERR, "Exiting without cleanup");
00356                 exit(1);
00357         }
00358 
00359 }
00360 
00361 static void
00362 wdctl_reset(int fd, char *arg)
00363 {
00364         t_client        *node;
00365 
00366         debug(LOG_DEBUG, "Entering wdctl_reset...");
00367         
00368         LOCK_CLIENT_LIST();
00369         debug(LOG_DEBUG, "Argument: %s (@%x)", arg, arg);
00370         
00371         /* We get the node or return... */
00372         if ((node = client_list_find_by_ip(arg)) != NULL);
00373         else if ((node = client_list_find_by_mac(arg)) != NULL);
00374         else {
00375                 debug(LOG_DEBUG, "Client not found.");
00376                 UNLOCK_CLIENT_LIST();
00377                 write(fd, "No", 2);
00378                 return;
00379         }
00380 
00381         debug(LOG_DEBUG, "Got node %x.", node);
00382         
00383         /* deny.... */
00384         /* TODO: maybe just deleting the connection is not best... But this
00385          * is a manual command, I don't anticipate it'll be that useful. */
00386         fw_deny(node->ip, node->mac, node->fw_connection_state);
00387         client_list_delete(node);
00388         
00389         UNLOCK_CLIENT_LIST();
00390         
00391         write(fd, "Yes", 3);
00392         
00393         debug(LOG_DEBUG, "Exiting wdctl_reset...");
00394 }

Generated on Sun Apr 13 21:55:00 2008 for WifiDog by  doxygen 1.5.3