root/wifidog/wifidog-client/src/wdctl_thread.c

Revision 345, 10.5 kB (checked in by florida, 2 years ago)

corrections diverses

Line 
1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or    *
3  * modify it under the terms of the GNU General Public License as   *
4  * published by the Free Software Foundation; either version 2 of   *
5  * the License, or (at your option) any later version.              *
6  *                                                                  *
7  * This program is distributed in the hope that it will be useful,  *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
10  * GNU General Public License for more details.                     *
11  *                                                                  *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact:                        *
14  *                                                                  *
15  * Free Software Foundation           Voice:  +1-617-542-5942       *
16  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
17  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
18  *                                                                  *
19 \********************************************************************/
20
21 /* $Id: wdctl_thread.c 1241 2007-06-24 04:13:13Z benoitg $ */
22 /** @file wdctl_thread.c
23     @brief Monitoring and control of wifidog, server part
24     @author Copyright (C) 2004 Alexandre Carmel-Veilleux <acv@acv.ca>
25 */
26
27 #define _GNU_SOURCE
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <pthread.h>
32 #include <string.h>
33 #include <stdarg.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <unistd.h>
38 #include <syslog.h>
39 #include <signal.h>
40 #include <errno.h>
41
42 #include "common.h"
43 #include "httpd.h"
44 #include "util.h"
45 #include "conf.h"
46 #include "debug.h"
47 #include "auth.h"
48 #include "centralserver.h"
49 #include "fw_iptables.h"
50 #include "firewall.h"
51 #include "client_list.h"
52 #include "wdctl_thread.h"
53
54 /* Defined in clientlist.c */
55 extern  pthread_mutex_t client_list_mutex;
56 extern  pthread_mutex_t config_mutex;
57
58 /* From commandline.c: */
59 extern char ** restartargv;
60 static void *thread_wdctl_handler(void *);
61 static void wdctl_status(int);
62 static void wdctl_stop(int);
63 static void wdctl_reset(int, char *);
64 static void wdctl_restart(int);
65
66 /** Launches a thread that monitors the control socket for request
67 @param arg Must contain a pointer to a string containing the Unix domain socket to open
68 @todo This thread loops infinitely, need a watchdog to verify that it is still running?
69 */ 
70 void
71 thread_wdctl(void *arg)
72 {
73         int     fd;
74         char    *sock_name;
75         struct  sockaddr_un     sa_un;
76         int result;
77         pthread_t       tid;
78        socklen_t len;
79
80         debug(LOG_DEBUG, "Starting wdctl.");
81
82         memset(&sa_un, 0, sizeof(sa_un));
83         sock_name = (char *)arg;
84         debug(LOG_DEBUG, "Socket name: %s", sock_name);
85
86         if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
87                 /* TODO: Die handler with logging.... */
88                 debug(LOG_ERR, "WDCTL socket name too long");
89                 exit(1);
90         }
91        
92
93         debug(LOG_DEBUG, "Creating socket");
94         wdctl_socket_server = socket(PF_UNIX, SOCK_STREAM, 0);
95
96         debug(LOG_DEBUG, "Got server socket %d", wdctl_socket_server);
97
98         /* If it exists, delete... Not the cleanest way to deal. */
99         unlink(sock_name);
100
101         debug(LOG_DEBUG, "Filling sockaddr_un");
102         strcpy(sa_un.sun_path, sock_name); /* XXX No size check because we
103                                             * check a few lines before. */
104         sa_un.sun_family = AF_UNIX;
105        
106         debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path,
107                         strlen(sock_name));
108        
109         /* Which to use, AF_UNIX, PF_UNIX, AF_LOCAL, PF_LOCAL? */
110         if (bind(wdctl_socket_server, (struct sockaddr *)&sa_un, strlen(sock_name)
111                                 + sizeof(sa_un.sun_family))) {
112                 debug(LOG_ERR, "Could not bind control socket: %s",
113                                 strerror(errno));
114                 pthread_exit(NULL);
115         }
116
117         if (listen(wdctl_socket_server, 5)) {
118                 debug(LOG_ERR, "Could not listen on control socket: %s",
119                                 strerror(errno));
120                 pthread_exit(NULL);
121         }
122
123         while (1) {
124                 len = sizeof(sa_un);
125                 memset(&sa_un, 0, len);
126                 if ((fd = accept(wdctl_socket_server, (struct sockaddr *)&sa_un, &len)) == -1){
127                         debug(LOG_ERR, "Accept failed on control socket: %s",
128                                         strerror(errno));
129                 } else {
130                         debug(LOG_DEBUG, "Accepted connection on wdctl socket %d (%s)", fd, sa_un.sun_path);
131                         result = pthread_create(&tid, NULL, &thread_wdctl_handler, (void *)fd);
132                         if (result != 0) {
133                                 debug(LOG_ERR, "FATAL: Failed to create a new thread (wdctl handler) - exiting");
134                                 termination_handler(0);
135                         }
136                         pthread_detach(tid);
137                 }
138         }
139 }
140
141
142 static void *
143 thread_wdctl_handler(void *arg)
144 {
145         int     fd,
146                 done,
147                 i;
148         char    request[MAX_BUF];
149         ssize_t read_bytes,
150                 len;
151
152         debug(LOG_DEBUG, "Entering thread_wdctl_handler....");
153
154         fd = (int)arg;
155        
156         debug(LOG_DEBUG, "Read bytes and stuff from %d", fd);
157
158         /* Init variables */
159         read_bytes = 0;
160         done = 0;
161         memset(request, 0, sizeof(request));
162        
163         /* Read.... */
164         while (!done && read_bytes < (sizeof(request) - 1)) {
165                 len = read(fd, request + read_bytes,
166                                 sizeof(request) - read_bytes);
167
168                 /* Have we gotten a command yet? */
169                 for (i = read_bytes; i < (read_bytes + len); i++) {
170                         if (request[i] == '\r' || request[i] == '\n') {
171                                 request[i] = '\0';
172                                 done = 1;
173                         }
174                 }
175                
176                 /* Increment position */
177                 read_bytes += len;
178         }
179
180         if (strncmp(request, "status", 6) == 0) {
181                 wdctl_status(fd);
182         } else if (strncmp(request, "stop", 4) == 0) {
183                 wdctl_stop(fd);
184         } else if (strncmp(request, "reset", 5) == 0) {
185                 wdctl_reset(fd, (request + 6));
186         } else if (strncmp(request, "restart", 7) == 0) {
187                 wdctl_restart(fd);
188         }
189
190         if (!done) {
191                 debug(LOG_ERR, "Invalid wdctl request.");
192                 shutdown(fd, 2);
193                 close(fd);
194                 pthread_exit(NULL);
195         }
196
197         debug(LOG_DEBUG, "Request received: [%s]", request);
198        
199         shutdown(fd, 2);
200         close(fd);
201         debug(LOG_DEBUG, "Exiting thread_wdctl_handler....");
202
203         return NULL;
204 }
205
206 static void
207 wdctl_status(int fd)
208 {
209         char * status = NULL;
210         int len = 0;
211
212         status = get_status_text(1);
213         len = strlen(status);
214
215         write(fd, status, len);
216
217         free(status);
218 }
219
220 /** A bit of an hack, self kills.... */
221 static void
222 wdctl_stop(int fd)
223 {
224         pid_t   pid;
225
226         pid = getpid();
227         kill(pid, SIGINT);
228 }
229
230 static void
231 wdctl_restart(int afd)
232 {
233         int     sock,
234                 fd;
235         char    *sock_name;
236         struct  sockaddr_un     sa_un;
237         int result;
238         s_config * conf = NULL;
239         t_client * client = NULL;
240         char * tempstring = NULL;
241         pid_t pid;
242         ssize_t written;
243         socklen_t len;
244
245         conf = config_get_config();
246
247         debug(LOG_NOTICE, "Will restart myself");
248
249         /*
250          * First, prepare the internal socket
251          */
252         memset(&sa_un, 0, sizeof(sa_un));
253         sock_name = conf->internal_sock;
254         debug(LOG_DEBUG, "Socket name: %s", sock_name);
255
256         if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
257                 /* TODO: Die handler with logging.... */
258                 debug(LOG_ERR, "INTERNAL socket name too long");
259                 return;
260         }
261
262         debug(LOG_DEBUG, "Creating socket");
263         sock = socket(PF_UNIX, SOCK_STREAM, 0);
264
265         debug(LOG_DEBUG, "Got internal socket %d", sock);
266
267         /* If it exists, delete... Not the cleanest way to deal. */
268         unlink(sock_name);
269
270         debug(LOG_DEBUG, "Filling sockaddr_un");
271         strcpy(sa_un.sun_path, sock_name); /* XXX No size check because we check a few lines before. */
272         sa_un.sun_family = AF_UNIX;
273        
274         debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));
275        
276         /* Which to use, AF_UNIX, PF_UNIX, AF_LOCAL, PF_LOCAL? */
277         if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {
278                 debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));
279                 return;
280         }
281
282         if (listen(sock, 5)) {
283                 debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));
284                 return;
285         }
286        
287         /*
288          * The internal socket is ready, fork and exec ourselves
289          */
290         debug(LOG_DEBUG, "Forking in preparation for exec()...");
291         pid = safe_fork();
292         if (pid > 0) {
293                 /* Parent */
294
295                 /* Wait for the child to connect to our socket :*/
296                 debug(LOG_DEBUG, "Waiting for child to connect on internal socket");
297                 len = sizeof(sa_un);
298                 if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){
299                         debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));
300                         close(sock);
301                         return;
302                 }
303
304                 close(sock);
305
306                 debug(LOG_DEBUG, "Received connection from child.  Sending them all existing clients");
307
308                 /* The child is connected. Send them over the socket the existing clients */
309                 LOCK_CLIENT_LIST();
310                 client = client_get_first_client();
311                 while (client) {
312                         /* Send this client */
313                         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);
314                         debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);
315                         len = 0;
316                         while (len != strlen(tempstring)) {
317                                 written = write(fd, (tempstring + len), strlen(tempstring) - len);
318                                 if (written == -1) {
319                                         debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));
320                                         free(tempstring);
321                                         break;
322                                 }
323                                 else {
324                                         len += written;
325                                 }
326                         }
327                         free(tempstring);
328                         client = client->next;
329                 }
330                 UNLOCK_CLIENT_LIST();
331
332                 close(fd);
333
334                 debug(LOG_INFO, "Sent all existing clients to child.  Committing suicide!");
335
336                 shutdown(afd, 2);
337                 close(afd);
338
339                 /* Our job in life is done. Commit suicide! */
340                 wdctl_stop(afd);
341         }
342         else {
343                 /* Child */
344                 close(wdctl_socket_server);
345                 close(icmp_fd);
346                 close(sock);
347                 shutdown(afd, 2);
348                 close(afd);
349                 debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);
350                 setsid();
351                 execvp(restartargv[0], restartargv);
352                 /* If we've reached here the exec() failed - die quickly and silently */
353                 debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
354                 debug(LOG_ERR, "Exiting without cleanup");
355                 exit(1);
356         }
357
358 }
359
360 static void
361 wdctl_reset(int fd, char *arg)
362 {
363         t_client        *node;
364
365         debug(LOG_DEBUG, "Entering wdctl_reset...");
366        
367         LOCK_CLIENT_LIST();
368         debug(LOG_DEBUG, "Argument: %s (@%x)", arg, arg);
369        
370         /* We get the node or return... */
371         if ((node = client_list_find_by_ip(arg)) != NULL);
372         else if ((node = client_list_find_by_mac(arg)) != NULL);
373         else {
374                 debug(LOG_DEBUG, "Client not found.");
375                 UNLOCK_CLIENT_LIST();
376                 write(fd, "No", 2);
377                 return;
378         }
379
380         debug(LOG_DEBUG, "Got node %x.", node);
381        
382         /* deny.... */
383         /* TODO: maybe just deleting the connection is not best... But this
384          * is a manual command, I don't anticipate it'll be that useful. */
385         fw_deny(node->ip, node->mac, node->fw_connection_state);
386         client_list_delete(node);
387        
388         UNLOCK_CLIENT_LIST();
389        
390         write(fd, "Yes", 3);
391        
392         debug(LOG_DEBUG, "Exiting wdctl_reset...");
393 }
Note: See TracBrowser for help on using the browser.