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