| 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 | /* $Header$ */ |
|---|
| 22 | /** @file ping_thread.c |
|---|
| 23 | @brief Periodically checks in with the central auth server to make sure everything is running properly. |
|---|
| 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 <netinet/in.h> |
|---|
| 37 | #include <arpa/inet.h> |
|---|
| 38 | #include <netdb.h> |
|---|
| 39 | #include <unistd.h> |
|---|
| 40 | #include <syslog.h> |
|---|
| 41 | #include <signal.h> |
|---|
| 42 | #include <errno.h> |
|---|
| 43 | |
|---|
| 44 | #include "../config.h" |
|---|
| 45 | #include "common.h" |
|---|
| 46 | #include "conf.h" |
|---|
| 47 | #include "debug.h" |
|---|
| 48 | #include "ping_thread.h" |
|---|
| 49 | #include "util.h" |
|---|
| 50 | |
|---|
| 51 | static void ping(void); |
|---|
| 52 | |
|---|
| 53 | /** Launches a thread that periodically checks in with the wifidog auth server to perform heartbeat function. |
|---|
| 54 | @param arg NULL |
|---|
| 55 | @todo This thread loops infinitely, need a watchdog to verify that it is still running? |
|---|
| 56 | */ |
|---|
| 57 | void |
|---|
| 58 | thread_ping(void *arg) |
|---|
| 59 | { |
|---|
| 60 | pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
|---|
| 61 | pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER; |
|---|
| 62 | struct timespec timeout; |
|---|
| 63 | |
|---|
| 64 | while (1) { |
|---|
| 65 | /* Make sure we check the servers at the very begining */ |
|---|
| 66 | /** @todo Note that this will only help if the second server responds. The logic of the ping itslef should be changed so it iterates in the list until it finds one that responds ox exausts the list */ |
|---|
| 67 | debug(LOG_DEBUG, "Running ping()"); |
|---|
| 68 | ping(); |
|---|
| 69 | |
|---|
| 70 | /* Sleep for config.checkinterval seconds... */ |
|---|
| 71 | timeout.tv_sec = time(NULL) + |
|---|
| 72 | config_get_config()->checkinterval; |
|---|
| 73 | timeout.tv_nsec = 0; |
|---|
| 74 | |
|---|
| 75 | /* Mutex must be locked for pthread_cond_timedwait... */ |
|---|
| 76 | pthread_mutex_lock(&cond_mutex); |
|---|
| 77 | |
|---|
| 78 | /* Thread safe "sleep" */ |
|---|
| 79 | pthread_cond_timedwait(&cond, &cond_mutex, &timeout); |
|---|
| 80 | |
|---|
| 81 | /* No longer needs to be locked */ |
|---|
| 82 | pthread_mutex_unlock(&cond_mutex); |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | /** @internal |
|---|
| 87 | * This function does the actual request. |
|---|
| 88 | */ |
|---|
| 89 | static void |
|---|
| 90 | ping(void) |
|---|
| 91 | { |
|---|
| 92 | size_t numbytes, |
|---|
| 93 | totalbytes; |
|---|
| 94 | int sockfd, |
|---|
| 95 | nfds, |
|---|
| 96 | done, |
|---|
| 97 | i; |
|---|
| 98 | t_auth_serv *auth_server; |
|---|
| 99 | char request[MAX_BUF], |
|---|
| 100 | *tmp_addr; |
|---|
| 101 | struct in_addr *h_addr; |
|---|
| 102 | struct sockaddr_in their_addr; |
|---|
| 103 | fd_set readfds; |
|---|
| 104 | struct timeval timeout; |
|---|
| 105 | FILE * fh; |
|---|
| 106 | unsigned long int sys_uptime = 0; |
|---|
| 107 | unsigned int sys_memfree = 0; |
|---|
| 108 | float sys_load = 0; |
|---|
| 109 | |
|---|
| 110 | |
|---|
| 111 | debug(LOG_DEBUG, "Entering ping()"); |
|---|
| 112 | |
|---|
| 113 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { |
|---|
| 114 | debug(LOG_ERR, "socket(): %s", strerror(errno)); |
|---|
| 115 | exit(1); |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | auth_server = get_auth_server(); |
|---|
| 119 | |
|---|
| 120 | debug(LOG_DEBUG, "Using auth server %s", |
|---|
| 121 | auth_server->authserv_hostname); |
|---|
| 122 | |
|---|
| 123 | debug(LOG_DEBUG, "Resolving IP"); |
|---|
| 124 | if ((h_addr = (struct in_addr *)wd_gethostbyname(auth_server->authserv_hostname)) == NULL) { |
|---|
| 125 | debug(LOG_ERR, "Failed to resolve %s via gethostbyname" |
|---|
| 126 | "(): %s", auth_server->authserv_hostname, |
|---|
| 127 | strerror(errno)); |
|---|
| 128 | debug(LOG_ERR, "Bumping auth server to last in line."); |
|---|
| 129 | mark_auth_server_bad(auth_server); |
|---|
| 130 | close(sockfd); |
|---|
| 131 | return; |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | if (auth_server->last_ip == NULL) { |
|---|
| 135 | auth_server->last_ip = strdup(inet_ntoa(*h_addr)); |
|---|
| 136 | if (auth_server->last_ip == NULL) { |
|---|
| 137 | debug(LOG_CRIT, "Could not allocate memory, Banzai!"); |
|---|
| 138 | exit(-1); |
|---|
| 139 | } |
|---|
| 140 | } else { |
|---|
| 141 | tmp_addr = strdup(inet_ntoa(*h_addr)); |
|---|
| 142 | if (strcmp(auth_server->last_ip, tmp_addr) != 0) { |
|---|
| 143 | free(auth_server->last_ip); |
|---|
| 144 | auth_server->last_ip = tmp_addr; |
|---|
| 145 | fw_clear_authservers(); |
|---|
| 146 | fw_set_authservers(); |
|---|
| 147 | } else { |
|---|
| 148 | free(tmp_addr); |
|---|
| 149 | } |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | their_addr.sin_family = AF_INET; |
|---|
| 153 | their_addr.sin_port = htons(auth_server->authserv_http_port); |
|---|
| 154 | their_addr.sin_addr = *h_addr; |
|---|
| 155 | memset(&(their_addr.sin_zero), '\0', sizeof(their_addr.sin_zero)); |
|---|
| 156 | |
|---|
| 157 | debug(LOG_INFO, "Connecting to auth server %s on port %d", |
|---|
| 158 | auth_server->authserv_hostname, |
|---|
| 159 | auth_server->authserv_http_port); |
|---|
| 160 | |
|---|
| 161 | if (connect(sockfd, (struct sockaddr *)&their_addr, |
|---|
| 162 | sizeof(struct sockaddr)) == -1) { |
|---|
| 163 | debug(LOG_ERR, "connect(): %s", strerror(errno)); |
|---|
| 164 | debug(LOG_ERR, "Bumping auth server to last in line."); |
|---|
| 165 | mark_auth_server_bad(auth_server); |
|---|
| 166 | close(sockfd); |
|---|
| 167 | free(h_addr); |
|---|
| 168 | return; |
|---|
| 169 | } |
|---|
| 170 | free(h_addr); |
|---|
| 171 | |
|---|
| 172 | mark_online(); |
|---|
| 173 | |
|---|
| 174 | /* |
|---|
| 175 | * Populate uptime, memfree and load |
|---|
| 176 | */ |
|---|
| 177 | if ((fh = fopen("/proc/uptime", "r"))) { |
|---|
| 178 | fscanf(fh, "%lu", &sys_uptime); |
|---|
| 179 | fclose(fh); |
|---|
| 180 | } |
|---|
| 181 | if ((fh = fopen("/proc/meminfo", "r"))) { |
|---|
| 182 | while (!feof(fh)) { |
|---|
| 183 | if (fscanf(fh, "MemFree: %u", &sys_memfree) == 0) { |
|---|
| 184 | /* Not on this line */ |
|---|
| 185 | while (!feof(fh) && fgetc(fh) != '\n'); |
|---|
| 186 | } |
|---|
| 187 | else { |
|---|
| 188 | /* Found it */ |
|---|
| 189 | break; |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | fclose(fh); |
|---|
| 193 | } |
|---|
| 194 | if ((fh = fopen("/proc/loadavg", "r"))) { |
|---|
| 195 | fscanf(fh, "%f", &sys_load); |
|---|
| 196 | fclose(fh); |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | /* |
|---|
| 200 | * Prep & send request |
|---|
| 201 | */ |
|---|
| 202 | snprintf(request, sizeof(request) - 1, "GET %sping/?gw_id=%s&sys_uptime=%lu&sys_memfree=%u&sys_load=%.2f HTTP/1.0\n" |
|---|
| 203 | "User-Agent: WiFiDog %s\n" |
|---|
| 204 | "Host: %s\n" |
|---|
| 205 | "\n", |
|---|
| 206 | auth_server->authserv_path, |
|---|
| 207 | config_get_config()->gw_id, |
|---|
| 208 | sys_uptime, |
|---|
| 209 | sys_memfree, |
|---|
| 210 | sys_load, |
|---|
| 211 | VERSION, |
|---|
| 212 | auth_server->authserv_hostname); |
|---|
| 213 | |
|---|
| 214 | debug(LOG_DEBUG, "HTTP Request to Server: [%s]", request); |
|---|
| 215 | |
|---|
| 216 | send(sockfd, request, strlen(request), 0); |
|---|
| 217 | |
|---|
| 218 | debug(LOG_DEBUG, "Reading response"); |
|---|
| 219 | |
|---|
| 220 | numbytes = totalbytes = 0; |
|---|
| 221 | done = 0; |
|---|
| 222 | do { |
|---|
| 223 | FD_ZERO(&readfds); |
|---|
| 224 | FD_SET(sockfd, &readfds); |
|---|
| 225 | timeout.tv_sec = 30; /* XXX magic... 30 second */ |
|---|
| 226 | timeout.tv_usec = 0; |
|---|
| 227 | nfds = sockfd + 1; |
|---|
| 228 | |
|---|
| 229 | nfds = select(nfds, &readfds, NULL, NULL, &timeout); |
|---|
| 230 | |
|---|
| 231 | if (nfds > 0) { |
|---|
| 232 | /** We don't have to use FD_ISSET() because there |
|---|
| 233 | * was only one fd. */ |
|---|
| 234 | numbytes = read(sockfd, request + totalbytes, |
|---|
| 235 | MAX_BUF - (totalbytes + 1)); |
|---|
| 236 | if (numbytes < 0) { |
|---|
| 237 | debug(LOG_ERR, "read(): %s", strerror(errno)); |
|---|
| 238 | mark_auth_server_bad(auth_server); |
|---|
| 239 | close(sockfd); |
|---|
| 240 | return; |
|---|
| 241 | } else if (numbytes == 0) { |
|---|
| 242 | done = 1; |
|---|
| 243 | } else { |
|---|
| 244 | totalbytes += numbytes; |
|---|
| 245 | debug(LOG_DEBUG, "Read %d bytes, total now %d", |
|---|
| 246 | numbytes, totalbytes); |
|---|
| 247 | } |
|---|
| 248 | } else if (nfds == 0) { |
|---|
| 249 | debug(LOG_ERR, "select() timed out"); |
|---|
| 250 | mark_auth_server_bad(auth_server); |
|---|
| 251 | close(sockfd); |
|---|
| 252 | return; |
|---|
| 253 | } else if (nfds < 0) { |
|---|
| 254 | debug(LOG_ERR, "select(): %s", strerror(errno)); |
|---|
| 255 | mark_auth_server_bad(auth_server); |
|---|
| 256 | close(sockfd); |
|---|
| 257 | return; |
|---|
| 258 | } |
|---|
| 259 | } while (!done); |
|---|
| 260 | |
|---|
| 261 | debug(LOG_DEBUG, "Done reading reply, total %d bytes", totalbytes); |
|---|
| 262 | |
|---|
| 263 | numbytes = totalbytes; |
|---|
| 264 | |
|---|
| 265 | request[numbytes] = '\0'; |
|---|
| 266 | |
|---|
| 267 | close(sockfd); |
|---|
| 268 | |
|---|
| 269 | debug(LOG_DEBUG, "HTTP Response from Server: [%s]", request); |
|---|
| 270 | |
|---|
| 271 | if (!strstr(request, "Pong")) { |
|---|
| 272 | debug(LOG_ERR, "Primary auth server offline"); |
|---|
| 273 | debug(LOG_ERR, "Bumping auth server to last in line."); |
|---|
| 274 | mark_auth_server_bad(auth_server); |
|---|
| 275 | return; |
|---|
| 276 | } |
|---|
| 277 | |
|---|
| 278 | debug(LOG_DEBUG, "Auth Server Says: Pong"); |
|---|
| 279 | return; |
|---|
| 280 | } |
|---|