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-2010 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 maximum allowable number concurrent connections. */ 61 #define M_CONNECTIONS 50 62 63 64 /*---------------------------------------------------------------*/ 65 66 __attribute__ ((noreturn)) 67 static void panic ( Char* str ) 68 { 69 fprintf(stderr, 70 "\nvalgrind-listener: the " 71 "'impossible' happened:\n %s\n", str); 72 fprintf(stderr, 73 "Please report this bug at: %s\n\n", VG_BUGS_TO); 74 exit(1); 75 } 76 77 __attribute__ ((noreturn)) 78 static void my_assert_fail ( const Char* expr, const Char* file, Int line, const Char* fn ) 79 { 80 fprintf(stderr, 81 "\nvalgrind-listener: %s:%d (%s): Assertion '%s' failed.\n", 82 file, line, fn, expr ); 83 fprintf(stderr, 84 "Please report this bug at: %s\n\n", VG_BUGS_TO); 85 exit(1); 86 } 87 88 #undef assert 89 90 #define assert(expr) \ 91 ((void) ((expr) ? 0 : \ 92 (my_assert_fail (VG_STRINGIFY(expr), \ 93 __FILE__, __LINE__, \ 94 __PRETTY_FUNCTION__), 0))) 95 96 97 /*---------------------------------------------------------------*/ 98 99 /* holds the fds for connections; zero if slot not in use. */ 100 int conn_count = 0; 101 int conn_fd[M_CONNECTIONS]; 102 struct pollfd conn_pollfd[M_CONNECTIONS]; 103 104 105 static void set_nonblocking ( int sd ) 106 { 107 int res; 108 res = fcntl(sd, F_GETFL); 109 res = fcntl(sd, F_SETFL, res | O_NONBLOCK); 110 if (res != 0) { 111 perror("fcntl failed"); 112 panic("set_nonblocking"); 113 } 114 } 115 116 static void set_blocking ( int sd ) 117 { 118 int res; 119 res = fcntl(sd, F_GETFL); 120 res = fcntl(sd, F_SETFL, res & ~O_NONBLOCK); 121 if (res != 0) { 122 perror("fcntl failed"); 123 panic("set_blocking"); 124 } 125 } 126 127 128 static void copyout ( char* buf, int nbuf ) 129 { 130 int i; 131 for (i = 0; i < nbuf; i++) { 132 if (buf[i] == '\n') { 133 fprintf(stdout, "\n(%d) ", conn_count); 134 } else { 135 __attribute__((unused)) size_t ignored 136 = fwrite(&buf[i], 1, 1, stdout); 137 } 138 } 139 fflush(stdout); 140 } 141 142 static int read_from_sd ( int sd ) 143 { 144 char buf[100]; 145 int n; 146 147 set_blocking(sd); 148 n = read(sd, buf, 99); 149 if (n <= 0) return 0; /* closed */ 150 copyout(buf, n); 151 152 set_nonblocking(sd); 153 while (1) { 154 n = read(sd, buf, 100); 155 if (n <= 0) return 1; /* not closed */ 156 copyout(buf, n); 157 } 158 } 159 160 161 static void snooze ( void ) 162 { 163 struct timespec req; 164 req.tv_sec = 0; 165 req.tv_nsec = 200 * 1000 * 1000; 166 nanosleep(&req,NULL); 167 } 168 169 170 /* returns 0 if invalid, else port # */ 171 static int atoi_portno ( char* str ) 172 { 173 int n = 0; 174 while (1) { 175 if (*str == 0) 176 break; 177 if (*str < '0' || *str > '9') 178 return 0; 179 n = 10*n + (int)(*str - '0'); 180 str++; 181 if (n >= 65536) 182 return 0; 183 } 184 if (n < 1024) 185 return 0; 186 return n; 187 } 188 189 190 static void usage ( void ) 191 { 192 fprintf(stderr, 193 "\n" 194 "usage is:\n" 195 "\n" 196 " valgrind-listener [--exit-at-zero|-e] [port-number]\n" 197 "\n" 198 " where --exit-at-zero or -e causes the listener to exit\n" 199 " when the number of connections falls back to zero\n" 200 " (the default is to keep listening forever)\n" 201 "\n" 202 " port-number is the default port on which to listen for\n" 203 " connections. It must be between 1024 and 65535.\n" 204 " Current default is %d.\n" 205 "\n" 206 , 207 VG_CLO_DEFAULT_LOGPORT 208 ); 209 exit(1); 210 } 211 212 213 static void banner ( char* str ) 214 { 215 time_t t; 216 t = time(NULL); 217 printf("valgrind-listener %s at %s", str, ctime(&t)); 218 fflush(stdout); 219 } 220 221 222 static void exit_routine ( void ) 223 { 224 banner("exited"); 225 exit(0); 226 } 227 228 229 static void sigint_handler ( int signo ) 230 { 231 exit_routine(); 232 } 233 234 235 int main (int argc, char** argv) 236 { 237 int i, j, k, res, one; 238 int main_sd, new_sd; 239 socklen_t client_len; 240 struct sockaddr_in client_addr, server_addr; 241 242 char /*bool*/ exit_when_zero = 0; 243 int port = VG_CLO_DEFAULT_LOGPORT; 244 245 for (i = 1; i < argc; i++) { 246 if (0==strcmp(argv[i], "--exit-at-zero") 247 || 0==strcmp(argv[i], "-e")) { 248 exit_when_zero = 1; 249 } 250 else 251 if (atoi_portno(argv[i]) > 0) { 252 port = atoi_portno(argv[i]); 253 } 254 else 255 usage(); 256 } 257 258 banner("started"); 259 signal(SIGINT, sigint_handler); 260 261 conn_count = 0; 262 for (i = 0; i < M_CONNECTIONS; i++) 263 conn_fd[i] = 0; 264 265 /* create socket */ 266 main_sd = socket(AF_INET, SOCK_STREAM, 0); 267 if (main_sd < 0) { 268 perror("cannot open socket "); 269 panic("main -- create socket"); 270 } 271 272 /* allow address reuse to avoid "address already in use" errors */ 273 274 one = 1; 275 if (setsockopt(main_sd, SOL_SOCKET, SO_REUSEADDR, 276 &one, sizeof(int)) < 0) { 277 perror("cannot enable address reuse "); 278 panic("main -- enable address reuse"); 279 } 280 281 /* bind server port */ 282 server_addr.sin_family = AF_INET; 283 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 284 server_addr.sin_port = htons(port); 285 286 if (bind(main_sd, (struct sockaddr *) &server_addr, 287 sizeof(server_addr) ) < 0) { 288 perror("cannot bind port "); 289 panic("main -- bind port"); 290 } 291 292 res = listen(main_sd,M_CONNECTIONS); 293 if (res != 0) { 294 perror("listen failed "); 295 panic("main -- listen"); 296 } 297 298 while (1) { 299 300 snooze(); 301 302 /* enquire, using poll, whether there is any activity available on 303 the main socket descriptor. If so, someone is trying to 304 connect; get the fd and add it to our table thereof. */ 305 { struct pollfd ufd; 306 while (1) { 307 ufd.fd = main_sd; 308 ufd.events = POLLIN; 309 ufd.revents = 0; 310 res = poll(&ufd, 1, 0); 311 if (res == 0) break; 312 313 /* ok, we have someone waiting to connect. Get the sd. */ 314 client_len = sizeof(client_addr); 315 new_sd = accept(main_sd, (struct sockaddr *)&client_addr, 316 &client_len); 317 if (new_sd < 0) { 318 perror("cannot accept connection "); 319 panic("main -- accept connection"); 320 } 321 322 /* find a place to put it. */ 323 assert(new_sd > 0); 324 for (i = 0; i < M_CONNECTIONS; i++) 325 if (conn_fd[i] == 0) 326 break; 327 328 if (i >= M_CONNECTIONS) { 329 fprintf(stderr, "Too many concurrent connections. " 330 "Increase M_CONNECTIONS and recompile.\n"); 331 panic("main -- too many concurrent connections"); 332 } 333 334 conn_fd[i] = new_sd; 335 conn_count++; 336 printf("\n(%d) -------------------- CONNECT " 337 "--------------------\n(%d)\n(%d) ", 338 conn_count, conn_count, conn_count); 339 fflush(stdout); 340 } /* while (1) */ 341 } 342 343 /* We've processed all new connect requests. Listen for changes 344 to the current set of fds. */ 345 j = 0; 346 for (i = 0; i < M_CONNECTIONS; i++) { 347 if (conn_fd[i] == 0) 348 continue; 349 conn_pollfd[j].fd = conn_fd[i]; 350 conn_pollfd[j].events = POLLIN /* | POLLHUP | POLLNVAL */; 351 conn_pollfd[j].revents = 0; 352 j++; 353 } 354 355 res = poll(conn_pollfd, j, 0 /* return immediately. */ ); 356 if (res < 0) { 357 perror("poll(main) failed"); 358 panic("poll(main) failed"); 359 } 360 361 /* nothing happened. go round again. */ 362 if (res == 0) { 363 continue; 364 } 365 366 /* inspect the fds. */ 367 for (i = 0; i < j; i++) { 368 369 if (conn_pollfd[i].revents & POLLIN) { 370 /* data is available on this fd */ 371 res = read_from_sd(conn_pollfd[i].fd); 372 373 if (res == 0) { 374 /* the connection has been closed. */ 375 close(conn_pollfd[i].fd); 376 /* this fd has been closed or otherwise gone bad; forget 377 about it. */ 378 for (k = 0; k < M_CONNECTIONS; k++) 379 if (conn_fd[k] == conn_pollfd[i].fd) 380 break; 381 assert(k < M_CONNECTIONS); 382 conn_fd[k] = 0; 383 conn_count--; 384 printf("\n(%d) ------------------- DISCONNECT " 385 "-------------------\n(%d)\n(%d) ", 386 conn_count, conn_count, conn_count); 387 fflush(stdout); 388 if (conn_count == 0 && exit_when_zero) { 389 printf("\n"); 390 fflush(stdout); 391 exit_routine(); 392 } 393 } 394 } 395 396 } /* for (i = 0; i < j; i++) */ 397 398 } /* while (1) */ 399 400 /* NOTREACHED */ 401 } 402 403 404 /*--------------------------------------------------------------------*/ 405 /*--- end valgrind-listener.c ---*/ 406 /*--------------------------------------------------------------------*/ 407