| 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 |
} |
|---|