1 2 /*--------------------------------------------------------------------*/ 3 /*--- A simple program to listen for valgrind logfile data. ---*/ 4 /*--- valgrind-listener.c ---*/ 5 /*--------------------------------------------------------------------*/ 6 7 /* 8 This file is part of Valgrind, a dynamic binary instrumentation 9 framework. 10 11 Copyright (C) 2000-2015 Julian Seward 12 jseward (at) acm.org 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30 */ 31 32 33 /*---------------------------------------------------------------*/ 34 35 /* Include valgrind headers before system headers to avoid problems 36 with the system headers #defining things which are used as names 37 of structure members in vki headers. */ 38 39 #include "pub_core_basics.h" 40 #include "pub_core_libcassert.h" // For VG_BUGS_TO 41 #include "pub_core_vki.h" // Avoids warnings from 42 // pub_core_libcfile.h 43 #include "pub_core_libcfile.h" // For VG_CLO_DEFAULT_LOGPORT 44 45 #include <stdio.h> 46 #include <unistd.h> 47 #include <string.h> 48 #include <time.h> 49 #include <fcntl.h> 50 #include <stdlib.h> 51 #include <signal.h> 52 #include <sys/poll.h> 53 #include <sys/types.h> 54 #include <sys/socket.h> 55 #include <netinet/in.h> 56 57 58 /*---------------------------------------------------------------*/ 59 60 /* The default allowable number of concurrent connections. */ 61 #define M_CONNECTIONS_DEFAULT 50 62 /* The maximum allowable number of concurrent connections. */ 63 #define M_CONNECTIONS_MAX 5000 64 65 /* The maximum allowable number of concurrent connections. */ 66 unsigned M_CONNECTIONS = 0; 67 68 /*---------------------------------------------------------------*/ 69 70 __attribute__ ((noreturn)) 71 static void panic ( const char* str ) 72 { 73 fprintf(stderr, 74 "\nvalgrind-listener: the " 75 "'impossible' happened:\n %s\n", str); 76 fprintf(stderr, 77 "Please report this bug at: %s\n\n", VG_BUGS_TO); 78 exit(1); 79 } 80 81 __attribute__ ((noreturn)) 82 static void my_assert_fail ( const char* expr, const char* file, int line, const char* fn ) 83 { 84 fprintf(stderr, 85 "\nvalgrind-listener: %s:%d (%s): Assertion '%s' failed.\n", 86 file, line, fn, expr ); 87 fprintf(stderr, 88 "Please report this bug at: %s\n\n", VG_BUGS_TO); 89 exit(1); 90 } 91 92 #undef assert 93 94 #define assert(expr) \ 95 ((void) ((expr) ? 0 : \ 96 (my_assert_fail (VG_STRINGIFY(expr), \ 97 __FILE__, __LINE__, \ 98 __PRETTY_FUNCTION__), 0))) 99 100 101 /*---------------------------------------------------------------*/ 102 103 /* holds the fds for connections; zero if slot not in use. */ 104 int conn_count = 0; 105 int *conn_fd; 106 struct pollfd *conn_pollfd; 107 108 109 static void set_nonblocking ( int sd ) 110 { 111 int res; 112 res = fcntl(sd, F_GETFL); 113 res = fcntl(sd, F_SETFL, res | O_NONBLOCK); 114 if (res != 0) { 115 perror("fcntl failed"); 116 panic("set_nonblocking"); 117 } 118 } 119 120 static void set_blocking ( int sd ) 121 { 122 int res; 123 res = fcntl(sd, F_GETFL); 124 res = fcntl(sd, F_SETFL, res & ~O_NONBLOCK); 125 if (res != 0) { 126 perror("fcntl failed"); 127 panic("set_blocking"); 128 } 129 } 130 131 132 static void copyout ( char* buf, int nbuf ) 133 { 134 int i; 135 for (i = 0; i < nbuf; i++) { 136 if (buf[i] == '\n') { 137 fprintf(stdout, "\n(%d) ", conn_count); 138 } else { 139 __attribute__((unused)) size_t ignored 140 = fwrite(&buf[i], 1, 1, stdout); 141 } 142 } 143 fflush(stdout); 144 } 145 146 static int read_from_sd ( int sd ) 147 { 148 char buf[100]; 149 int n; 150 151 set_blocking(sd); 152 n = read(sd, buf, 99); 153 if (n <= 0) return 0; /* closed */ 154 copyout(buf, n); 155 156 set_nonblocking(sd); 157 while (1) { 158 n = read(sd, buf, 100); 159 if (n <= 0) return 1; /* not closed */ 160 copyout(buf, n); 161 } 162 } 163 164 165 static void snooze ( void ) 166 { 167 struct timespec req; 168 req.tv_sec = 0; 169 req.tv_nsec = 200 * 1000 * 1000; 170 nanosleep(&req,NULL); 171 } 172 173 174 /* returns 0 if negative, or > BOUND or invalid characters were found */ 175 static int atoi_with_bound ( const char* str, int bound ) 176 { 177 int n = 0; 178 while (1) { 179 if (*str == 0) 180 break; 181 if (*str < '0' || *str > '9') 182 return 0; 183 n = 10*n + (int)(*str - '0'); 184 str++; 185 if (n >= bound) 186 return 0; 187 } 188 return n; 189 } 190 191 /* returns 0 if invalid, else port # */ 192 static int atoi_portno ( const char* str ) 193 { 194 int n = atoi_with_bound(str, 65536); 195 196 if (n < 1024) 197 return 0; 198 return n; 199 } 200 201 202 static void usage ( void ) 203 { 204 fprintf(stderr, 205 "\n" 206 "usage is:\n" 207 "\n" 208 " valgrind-listener [--exit-at-zero|-e] [--max-connect=INT] [port-number]\n" 209 "\n" 210 " where --exit-at-zero or -e causes the listener to exit\n" 211 " when the number of connections falls back to zero\n" 212 " (the default is to keep listening forever)\n" 213 "\n" 214 " --max-connect=INT can be used to increase the maximum\n" 215 " number of connected processes (default = %d).\n" 216 " INT must be positive and less than %d.\n" 217 "\n" 218 " port-number is the default port on which to listen for\n" 219 " connections. It must be between 1024 and 65535.\n" 220 " Current default is %d.\n" 221 "\n" 222 , 223 M_CONNECTIONS_DEFAULT, M_CONNECTIONS_MAX, VG_CLO_DEFAULT_LOGPORT 224 ); 225 exit(1); 226 } 227 228 229 static void banner ( const char* str ) 230 { 231 time_t t; 232 t = time(NULL); 233 printf("valgrind-listener %s at %s", str, ctime(&t)); 234 fflush(stdout); 235 } 236 237 238 static void exit_routine ( void ) 239 { 240 banner("exited"); 241 exit(0); 242 } 243 244 245 static void sigint_handler ( int signo ) 246 { 247 exit_routine(); 248 } 249 250 251 int main (int argc, char** argv) 252 { 253 int i, j, k, res, one; 254 int main_sd, new_sd; 255 socklen_t client_len; 256 struct sockaddr_in client_addr, server_addr; 257 258 char /*bool*/ exit_when_zero = 0; 259 int port = VG_CLO_DEFAULT_LOGPORT; 260 261 for (i = 1; i < argc; i++) { 262 if (0==strcmp(argv[i], "--exit-at-zero") 263 || 0==strcmp(argv[i], "-e")) { 264 exit_when_zero = 1; 265 } 266 else if (0 == strncmp(argv[i], "--max-connect=", 14)) { 267 M_CONNECTIONS = atoi_with_bound(strchr(argv[i], '=') + 1, 5000); 268 if (M_CONNECTIONS <= 0 || M_CONNECTIONS > M_CONNECTIONS_MAX) 269 usage(); 270 } 271 else 272 if (atoi_portno(argv[i]) > 0) { 273 port = atoi_portno(argv[i]); 274 } 275 else 276 usage(); 277 } 278 279 if (M_CONNECTIONS == 0) // nothing specified on command line 280 M_CONNECTIONS = M_CONNECTIONS_DEFAULT; 281 282 conn_fd = malloc(M_CONNECTIONS * sizeof conn_fd[0]); 283 conn_pollfd = malloc(M_CONNECTIONS * sizeof conn_pollfd[0]); 284 if (conn_fd == NULL || conn_pollfd == NULL) { 285 fprintf(stderr, "Memory allocation failed; cannot continue.\n"); 286 exit(1); 287 } 288 289 banner("started"); 290 signal(SIGINT, sigint_handler); 291 292 conn_count = 0; 293 for (i = 0; i < M_CONNECTIONS; i++) 294 conn_fd[i] = 0; 295 296 /* create socket */ 297 main_sd = socket(AF_INET, SOCK_STREAM, 0); 298 if (main_sd < 0) { 299 perror("cannot open socket "); 300 panic("main -- create socket"); 301 } 302 303 /* allow address reuse to avoid "address already in use" errors */ 304 305 one = 1; 306 if (setsockopt(main_sd, SOL_SOCKET, SO_REUSEADDR, 307 &one, sizeof(int)) < 0) { 308 perror("cannot enable address reuse "); 309 panic("main -- enable address reuse"); 310 } 311 312 /* bind server port */ 313 server_addr.sin_family = AF_INET; 314 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 315 server_addr.sin_port = htons(port); 316 317 if (bind(main_sd, (struct sockaddr *) &server_addr, 318 sizeof(server_addr) ) < 0) { 319 perror("cannot bind port "); 320 panic("main -- bind port"); 321 } 322 323 res = listen(main_sd,M_CONNECTIONS); 324 if (res != 0) { 325 perror("listen failed "); 326 panic("main -- listen"); 327 } 328 329 while (1) { 330 331 snooze(); 332 333 /* enquire, using poll, whether there is any activity available on 334 the main socket descriptor. If so, someone is trying to 335 connect; get the fd and add it to our table thereof. */ 336 { struct pollfd ufd; 337 while (1) { 338 ufd.fd = main_sd; 339 ufd.events = POLLIN; 340 ufd.revents = 0; 341 res = poll(&ufd, 1, 0); 342 if (res == 0) break; 343 344 /* ok, we have someone waiting to connect. Get the sd. */ 345 client_len = sizeof(client_addr); 346 new_sd = accept(main_sd, (struct sockaddr *)&client_addr, 347 &client_len); 348 if (new_sd < 0) { 349 perror("cannot accept connection "); 350 panic("main -- accept connection"); 351 } 352 353 /* find a place to put it. */ 354 assert(new_sd > 0); 355 for (i = 0; i < M_CONNECTIONS; i++) 356 if (conn_fd[i] == 0) 357 break; 358 359 if (i >= M_CONNECTIONS) { 360 fprintf(stderr, "\n\nMore than %d concurrent connections.\n" 361 "Restart the listener giving --max-connect=INT on the\n" 362 "commandline to increase the limit.\n\n", 363 M_CONNECTIONS); 364 exit(1); 365 } 366 367 conn_fd[i] = new_sd; 368 conn_count++; 369 printf("\n(%d) -------------------- CONNECT " 370 "--------------------\n(%d)\n(%d) ", 371 conn_count, conn_count, conn_count); 372 fflush(stdout); 373 } /* while (1) */ 374 } 375 376 /* We've processed all new connect requests. Listen for changes 377 to the current set of fds. */ 378 j = 0; 379 for (i = 0; i < M_CONNECTIONS; i++) { 380 if (conn_fd[i] == 0) 381 continue; 382 conn_pollfd[j].fd = conn_fd[i]; 383 conn_pollfd[j].events = POLLIN /* | POLLHUP | POLLNVAL */; 384 conn_pollfd[j].revents = 0; 385 j++; 386 } 387 388 res = poll(conn_pollfd, j, 0 /* return immediately. */ ); 389 if (res < 0) { 390 perror("poll(main) failed"); 391 panic("poll(main) failed"); 392 } 393 394 /* nothing happened. go round again. */ 395 if (res == 0) { 396 continue; 397 } 398 399 /* inspect the fds. */ 400 for (i = 0; i < j; i++) { 401 402 if (conn_pollfd[i].revents & POLLIN) { 403 /* data is available on this fd */ 404 res = read_from_sd(conn_pollfd[i].fd); 405 406 if (res == 0) { 407 /* the connection has been closed. */ 408 close(conn_pollfd[i].fd); 409 /* this fd has been closed or otherwise gone bad; forget 410 about it. */ 411 for (k = 0; k < M_CONNECTIONS; k++) 412 if (conn_fd[k] == conn_pollfd[i].fd) 413 break; 414 assert(k < M_CONNECTIONS); 415 conn_fd[k] = 0; 416 conn_count--; 417 printf("\n(%d) ------------------- DISCONNECT " 418 "-------------------\n(%d)\n(%d) ", 419 conn_count, conn_count, conn_count); 420 fflush(stdout); 421 if (conn_count == 0 && exit_when_zero) { 422 printf("\n"); 423 fflush(stdout); 424 exit_routine(); 425 } 426 } 427 } 428 429 } /* for (i = 0; i < j; i++) */ 430 431 } /* while (1) */ 432 433 /* NOTREACHED */ 434 } 435 436 437 /*--------------------------------------------------------------------*/ 438 /*--- end valgrind-listener.c ---*/ 439 /*--------------------------------------------------------------------*/ 440