Home | History | Annotate | Download | only in auxprogs
      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-2017 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