drd: Add a consistency check
[valgrind.git] / auxprogs / valgrind-listener.c
blob15b23abd949cfdf6147ade591f84426d2b3c50a9
2 /*--------------------------------------------------------------------*/
3 /*--- A simple program to listen for valgrind logfile data. ---*/
4 /*--- valgrind-listener.c ---*/
5 /*--------------------------------------------------------------------*/
7 /*
8 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
11 Copyright (C) 2000-2013 Julian Seward
12 jseward@acm.org
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.
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.
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.
29 The GNU General Public License is contained in the file COPYING.
33 /*---------------------------------------------------------------*/
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. */
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
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>
58 /*---------------------------------------------------------------*/
60 /* The maximum allowable number concurrent connections. */
61 #define M_CONNECTIONS 50
64 /*---------------------------------------------------------------*/
66 __attribute__ ((noreturn))
67 static void panic ( const char* str )
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);
77 __attribute__ ((noreturn))
78 static void my_assert_fail ( const char* expr, const char* file, int line, const char* fn )
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);
88 #undef assert
90 #define assert(expr) \
91 ((void) ((expr) ? 0 : \
92 (my_assert_fail (VG_STRINGIFY(expr), \
93 __FILE__, __LINE__, \
94 __PRETTY_FUNCTION__), 0)))
97 /*---------------------------------------------------------------*/
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];
105 static void set_nonblocking ( int sd )
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");
116 static void set_blocking ( int sd )
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");
128 static void copyout ( char* buf, int nbuf )
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);
139 fflush(stdout);
142 static int read_from_sd ( int sd )
144 char buf[100];
145 int n;
147 set_blocking(sd);
148 n = read(sd, buf, 99);
149 if (n <= 0) return 0; /* closed */
150 copyout(buf, n);
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);
161 static void snooze ( void )
163 struct timespec req;
164 req.tv_sec = 0;
165 req.tv_nsec = 200 * 1000 * 1000;
166 nanosleep(&req,NULL);
170 /* returns 0 if invalid, else port # */
171 static int atoi_portno ( const char* str )
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;
184 if (n < 1024)
185 return 0;
186 return n;
190 static void usage ( void )
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"
207 VG_CLO_DEFAULT_LOGPORT
209 exit(1);
213 static void banner ( const char* str )
215 time_t t;
216 t = time(NULL);
217 printf("valgrind-listener %s at %s", str, ctime(&t));
218 fflush(stdout);
222 static void exit_routine ( void )
224 banner("exited");
225 exit(0);
229 static void sigint_handler ( int signo )
231 exit_routine();
235 int main (int argc, char** argv)
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;
242 char /*bool*/ exit_when_zero = 0;
243 int port = VG_CLO_DEFAULT_LOGPORT;
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;
250 else
251 if (atoi_portno(argv[i]) > 0) {
252 port = atoi_portno(argv[i]);
254 else
255 usage();
258 banner("started");
259 signal(SIGINT, sigint_handler);
261 conn_count = 0;
262 for (i = 0; i < M_CONNECTIONS; i++)
263 conn_fd[i] = 0;
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");
272 /* allow address reuse to avoid "address already in use" errors */
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");
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);
286 if (bind(main_sd, (struct sockaddr *) &server_addr,
287 sizeof(server_addr) ) < 0) {
288 perror("cannot bind port ");
289 panic("main -- bind port");
292 res = listen(main_sd,M_CONNECTIONS);
293 if (res != 0) {
294 perror("listen failed ");
295 panic("main -- listen");
298 while (1) {
300 snooze();
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;
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");
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;
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");
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) */
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++;
355 res = poll(conn_pollfd, j, 0 /* return immediately. */ );
356 if (res < 0) {
357 perror("poll(main) failed");
358 panic("poll(main) failed");
361 /* nothing happened. go round again. */
362 if (res == 0) {
363 continue;
366 /* inspect the fds. */
367 for (i = 0; i < j; i++) {
369 if (conn_pollfd[i].revents & POLLIN) {
370 /* data is available on this fd */
371 res = read_from_sd(conn_pollfd[i].fd);
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();
396 } /* for (i = 0; i < j; i++) */
398 } /* while (1) */
400 /* NOTREACHED */
404 /*--------------------------------------------------------------------*/
405 /*--- end valgrind-listener.c ---*/
406 /*--------------------------------------------------------------------*/