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-2013 Julian Seward
12       jseward@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))
panic(const char * str)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))
my_assert_fail(const char * expr,const char * file,int line,const char * fn)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 
set_nonblocking(int sd)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 
set_blocking(int sd)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 
copyout(char * buf,int nbuf)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 
read_from_sd(int sd)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 
snooze(void)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 */
atoi_with_bound(const char * str,int bound)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 # */
atoi_portno(const char * str)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 
usage(void)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 
banner(const char * str)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 
exit_routine(void)238 static void exit_routine ( void )
239 {
240    banner("exited");
241    exit(0);
242 }
243 
244 
sigint_handler(int signo)245 static void sigint_handler ( int signo )
246 {
247    exit_routine();
248 }
249 
250 
main(int argc,char ** argv)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