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-2011 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))
67static 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))
78static 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. */
100int conn_count = 0;
101int           conn_fd[M_CONNECTIONS];
102struct pollfd conn_pollfd[M_CONNECTIONS];
103
104
105static 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
116static 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
128static 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
142static 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
161static 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 # */
171static 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
190static 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
213static 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
222static void exit_routine ( void )
223{
224   banner("exited");
225   exit(0);
226}
227
228
229static void sigint_handler ( int signo )
230{
231   exit_routine();
232}
233
234
235int 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