• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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))
panic(Char * str)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))
my_assert_fail(const Char * expr,const Char * file,Int line,const Char * fn)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 
set_nonblocking(int sd)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 
set_blocking(int sd)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 
copyout(char * buf,int nbuf)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 
read_from_sd(int sd)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 
snooze(void)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 # */
atoi_portno(char * str)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 
usage(void)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 
banner(char * str)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 
exit_routine(void)222 static void exit_routine ( void )
223 {
224    banner("exited");
225    exit(0);
226 }
227 
228 
sigint_handler(int signo)229 static void sigint_handler ( int signo )
230 {
231    exit_routine();
232 }
233 
234 
main(int argc,char ** argv)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