blob: b0925366df5d03949f45105646773d4d01904507 [file] [log] [blame]
sewardj5ed7a392002-11-03 23:03:24 +00001
2/*--------------------------------------------------------------------*/
3/*--- A simple program to listen for valgrind logfile data. ---*/
4/*--- valgrind-listener.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
njnb9c427c2004-12-01 14:14:42 +00008 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
sewardj5ed7a392002-11-03 23:03:24 +000010
Elliott Hughesed398002017-06-21 14:41:24 -070011 Copyright (C) 2000-2017 Julian Seward
sewardj5ed7a392002-11-03 23:03:24 +000012 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
tom5c46da82010-04-29 09:01:21 +000035/* 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
sewardj5ed7a392002-11-03 23:03:24 +000045#include <stdio.h>
sewardjad19c692002-11-09 10:24:01 +000046#include <unistd.h>
sewardj5ed7a392002-11-03 23:03:24 +000047#include <string.h>
sewardj5ed7a392002-11-03 23:03:24 +000048#include <time.h>
49#include <fcntl.h>
50#include <stdlib.h>
sewardjad19c692002-11-09 10:24:01 +000051#include <signal.h>
52#include <sys/poll.h>
53#include <sys/types.h>
54#include <sys/socket.h>
55#include <netinet/in.h>
56
sewardj43354092002-11-06 00:15:50 +000057
sewardj5ed7a392002-11-03 23:03:24 +000058/*---------------------------------------------------------------*/
59
florianfba8dd72014-12-29 22:07:35 +000060/* 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
sewardj5ed7a392002-11-03 23:03:24 +000064
florianfba8dd72014-12-29 22:07:35 +000065/* The maximum allowable number of concurrent connections. */
66unsigned M_CONNECTIONS = 0;
sewardj43354092002-11-06 00:15:50 +000067
68/*---------------------------------------------------------------*/
69
70__attribute__ ((noreturn))
florian3c536012012-10-20 19:28:13 +000071static void panic ( const char* str )
sewardj43354092002-11-06 00:15:50 +000072{
73 fprintf(stderr,
74 "\nvalgrind-listener: the "
njn02bc4b82005-05-15 17:28:26 +000075 "'impossible' happened:\n %s\n", str);
sewardj43354092002-11-06 00:15:50 +000076 fprintf(stderr,
nethercote421281e2003-11-20 16:20:55 +000077 "Please report this bug at: %s\n\n", VG_BUGS_TO);
sewardj43354092002-11-06 00:15:50 +000078 exit(1);
79}
80
81__attribute__ ((noreturn))
florian3c536012012-10-20 19:28:13 +000082static void my_assert_fail ( const char* expr, const char* file, int line, const char* fn )
sewardj43354092002-11-06 00:15:50 +000083{
84 fprintf(stderr,
njn02bc4b82005-05-15 17:28:26 +000085 "\nvalgrind-listener: %s:%d (%s): Assertion '%s' failed.\n",
sewardj43354092002-11-06 00:15:50 +000086 file, line, fn, expr );
87 fprintf(stderr,
nethercote421281e2003-11-20 16:20:55 +000088 "Please report this bug at: %s\n\n", VG_BUGS_TO);
sewardj43354092002-11-06 00:15:50 +000089 exit(1);
90}
91
92#undef assert
sewardj43354092002-11-06 00:15:50 +000093
sewardj43354092002-11-06 00:15:50 +000094#define assert(expr) \
95 ((void) ((expr) ? 0 : \
sewardj7d15e512005-09-30 01:20:47 +000096 (my_assert_fail (VG_STRINGIFY(expr), \
sewardj43354092002-11-06 00:15:50 +000097 __FILE__, __LINE__, \
98 __PRETTY_FUNCTION__), 0)))
99
sewardj5ed7a392002-11-03 23:03:24 +0000100
101/*---------------------------------------------------------------*/
102
103/* holds the fds for connections; zero if slot not in use. */
104int conn_count = 0;
florianfba8dd72014-12-29 22:07:35 +0000105int *conn_fd;
106struct pollfd *conn_pollfd;
sewardj5ed7a392002-11-03 23:03:24 +0000107
108
njn778f7f82005-05-17 21:07:46 +0000109static void set_nonblocking ( int sd )
sewardj5ed7a392002-11-03 23:03:24 +0000110{
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");
sewardj43354092002-11-06 00:15:50 +0000116 panic("set_nonblocking");
sewardj5ed7a392002-11-03 23:03:24 +0000117 }
118}
119
njn778f7f82005-05-17 21:07:46 +0000120static void set_blocking ( int sd )
sewardj5ed7a392002-11-03 23:03:24 +0000121{
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");
sewardj43354092002-11-06 00:15:50 +0000127 panic("set_blocking");
sewardj5ed7a392002-11-03 23:03:24 +0000128 }
129}
130
131
njn778f7f82005-05-17 21:07:46 +0000132static void copyout ( char* buf, int nbuf )
sewardj5ed7a392002-11-03 23:03:24 +0000133{
134 int i;
135 for (i = 0; i < nbuf; i++) {
sewardj43354092002-11-06 00:15:50 +0000136 if (buf[i] == '\n') {
137 fprintf(stdout, "\n(%d) ", conn_count);
138 } else {
sewardjbbaef872008-11-01 23:55:32 +0000139 __attribute__((unused)) size_t ignored
140 = fwrite(&buf[i], 1, 1, stdout);
sewardj43354092002-11-06 00:15:50 +0000141 }
sewardj5ed7a392002-11-03 23:03:24 +0000142 }
sewardj7d3d1e62002-11-03 23:27:40 +0000143 fflush(stdout);
sewardj5ed7a392002-11-03 23:03:24 +0000144}
145
njn778f7f82005-05-17 21:07:46 +0000146static int read_from_sd ( int sd )
sewardj5ed7a392002-11-03 23:03:24 +0000147{
sewardjad19c692002-11-09 10:24:01 +0000148 char buf[100];
149 int n;
sewardj5ed7a392002-11-03 23:03:24 +0000150
sewardjad19c692002-11-09 10:24:01 +0000151 set_blocking(sd);
152 n = read(sd, buf, 99);
153 if (n <= 0) return 0; /* closed */
154 copyout(buf, n);
sewardj5ed7a392002-11-03 23:03:24 +0000155
sewardjad19c692002-11-09 10:24:01 +0000156 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 }
sewardj5ed7a392002-11-03 23:03:24 +0000162}
163
164
njn778f7f82005-05-17 21:07:46 +0000165static void snooze ( void )
sewardj5ed7a392002-11-03 23:03:24 +0000166{
sewardj43354092002-11-06 00:15:50 +0000167 struct timespec req;
168 req.tv_sec = 0;
169 req.tv_nsec = 200 * 1000 * 1000;
170 nanosleep(&req,NULL);
sewardj5ed7a392002-11-03 23:03:24 +0000171}
172
173
florianfba8dd72014-12-29 22:07:35 +0000174/* returns 0 if negative, or > BOUND or invalid characters were found */
175static int atoi_with_bound ( const char* str, int bound )
sewardjad19c692002-11-09 10:24:01 +0000176{
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++;
florianfba8dd72014-12-29 22:07:35 +0000185 if (n >= bound)
sewardjad19c692002-11-09 10:24:01 +0000186 return 0;
187 }
florianfba8dd72014-12-29 22:07:35 +0000188 return n;
189}
190
191/* returns 0 if invalid, else port # */
192static int atoi_portno ( const char* str )
193{
194 int n = atoi_with_bound(str, 65536);
195
sewardjad19c692002-11-09 10:24:01 +0000196 if (n < 1024)
197 return 0;
198 return n;
199}
200
201
njn778f7f82005-05-17 21:07:46 +0000202static void usage ( void )
sewardjad19c692002-11-09 10:24:01 +0000203{
204 fprintf(stderr,
205 "\n"
206 "usage is:\n"
207 "\n"
florianfba8dd72014-12-29 22:07:35 +0000208 " valgrind-listener [--exit-at-zero|-e] [--max-connect=INT] [port-number]\n"
sewardjad19c692002-11-09 10:24:01 +0000209 "\n"
sewardjcbf216e2002-11-13 19:42:30 +0000210 " where --exit-at-zero or -e causes the listener to exit\n"
sewardjad19c692002-11-09 10:24:01 +0000211 " when the number of connections falls back to zero\n"
212 " (the default is to keep listening forever)\n"
213 "\n"
florianfba8dd72014-12-29 22:07:35 +0000214 " --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"
sewardjad19c692002-11-09 10:24:01 +0000218 " 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 ,
florianfba8dd72014-12-29 22:07:35 +0000223 M_CONNECTIONS_DEFAULT, M_CONNECTIONS_MAX, VG_CLO_DEFAULT_LOGPORT
sewardjad19c692002-11-09 10:24:01 +0000224 );
225 exit(1);
226}
227
228
florian3c536012012-10-20 19:28:13 +0000229static void banner ( const char* str )
sewardjad19c692002-11-09 10:24:01 +0000230{
231 time_t t;
232 t = time(NULL);
233 printf("valgrind-listener %s at %s", str, ctime(&t));
sewardj68948f72002-11-16 20:15:16 +0000234 fflush(stdout);
sewardjad19c692002-11-09 10:24:01 +0000235}
236
237
njn778f7f82005-05-17 21:07:46 +0000238static void exit_routine ( void )
sewardjad19c692002-11-09 10:24:01 +0000239{
240 banner("exited");
241 exit(0);
242}
243
244
njn778f7f82005-05-17 21:07:46 +0000245static void sigint_handler ( int signo )
sewardjad19c692002-11-09 10:24:01 +0000246{
247 exit_routine();
248}
249
250
sewardj43354092002-11-06 00:15:50 +0000251int main (int argc, char** argv)
252{
sewardj9ac747a2003-07-06 01:34:13 +0000253 int i, j, k, res, one;
sewardjcd475c12006-10-17 02:28:53 +0000254 int main_sd, new_sd;
255 socklen_t client_len;
sewardj43354092002-11-06 00:15:50 +0000256 struct sockaddr_in client_addr, server_addr;
sewardj5ed7a392002-11-03 23:03:24 +0000257
sewardjad19c692002-11-09 10:24:01 +0000258 char /*bool*/ exit_when_zero = 0;
259 int port = VG_CLO_DEFAULT_LOGPORT;
260
261 for (i = 1; i < argc; i++) {
sewardjcbf216e2002-11-13 19:42:30 +0000262 if (0==strcmp(argv[i], "--exit-at-zero")
sewardjad19c692002-11-09 10:24:01 +0000263 || 0==strcmp(argv[i], "-e")) {
264 exit_when_zero = 1;
265 }
florianfba8dd72014-12-29 22:07:35 +0000266 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 }
sewardjad19c692002-11-09 10:24:01 +0000271 else
272 if (atoi_portno(argv[i]) > 0) {
273 port = atoi_portno(argv[i]);
274 }
275 else
276 usage();
277 }
278
florianfba8dd72014-12-29 22:07:35 +0000279 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
sewardjad19c692002-11-09 10:24:01 +0000289 banner("started");
290 signal(SIGINT, sigint_handler);
291
sewardj43354092002-11-06 00:15:50 +0000292 conn_count = 0;
293 for (i = 0; i < M_CONNECTIONS; i++)
294 conn_fd[i] = 0;
sewardj5ed7a392002-11-03 23:03:24 +0000295
sewardj43354092002-11-06 00:15:50 +0000296 /* create socket */
297 main_sd = socket(AF_INET, SOCK_STREAM, 0);
sewardj5ed7a392002-11-03 23:03:24 +0000298 if (main_sd < 0) {
sewardj43354092002-11-06 00:15:50 +0000299 perror("cannot open socket ");
300 panic("main -- create socket");
301 }
sewardj9ac747a2003-07-06 01:34:13 +0000302
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
sewardj43354092002-11-06 00:15:50 +0000312 /* bind server port */
313 server_addr.sin_family = AF_INET;
314 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sewardjad19c692002-11-09 10:24:01 +0000315 server_addr.sin_port = htons(port);
sewardj5ed7a392002-11-03 23:03:24 +0000316
sewardj43354092002-11-06 00:15:50 +0000317 if (bind(main_sd, (struct sockaddr *) &server_addr,
318 sizeof(server_addr) ) < 0) {
319 perror("cannot bind port ");
320 panic("main -- bind port");
321 }
sewardj5ed7a392002-11-03 23:03:24 +0000322
sewardj43354092002-11-06 00:15:50 +0000323 res = listen(main_sd,M_CONNECTIONS);
324 if (res != 0) {
325 perror("listen failed ");
326 panic("main -- listen");
327 }
sewardj5ed7a392002-11-03 23:03:24 +0000328
sewardjad19c692002-11-09 10:24:01 +0000329 while (1) {
sewardj5ed7a392002-11-03 23:03:24 +0000330
sewardj43354092002-11-06 00:15:50 +0000331 snooze();
sewardj5ed7a392002-11-03 23:03:24 +0000332
sewardj43354092002-11-06 00:15:50 +0000333 /* 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;
sewardj5ed7a392002-11-03 23:03:24 +0000343
sewardj43354092002-11-06 00:15:50 +0000344 /* ok, we have someone waiting to connect. Get the sd. */
345 client_len = sizeof(client_addr);
sewardjcd475c12006-10-17 02:28:53 +0000346 new_sd = accept(main_sd, (struct sockaddr *)&client_addr,
sewardj43354092002-11-06 00:15:50 +0000347 &client_len);
sewardjad19c692002-11-09 10:24:01 +0000348 if (new_sd < 0) {
sewardj43354092002-11-06 00:15:50 +0000349 perror("cannot accept connection ");
350 panic("main -- accept connection");
351 }
352
353 /* find a place to put it. */
sewardjad19c692002-11-09 10:24:01 +0000354 assert(new_sd > 0);
sewardj43354092002-11-06 00:15:50 +0000355 for (i = 0; i < M_CONNECTIONS; i++)
356 if (conn_fd[i] == 0)
357 break;
358
359 if (i >= M_CONNECTIONS) {
florianfba8dd72014-12-29 22:07:35 +0000360 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);
sewardj43354092002-11-06 00:15:50 +0000365 }
366
sewardjad19c692002-11-09 10:24:01 +0000367 conn_fd[i] = new_sd;
sewardj43354092002-11-06 00:15:50 +0000368 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. */
sewardje188abf2003-07-20 22:39:58 +0000408 close(conn_pollfd[i].fd);
sewardj43354092002-11-06 00:15:50 +0000409 /* this fd has been closed or otherwise gone bad; forget
sewardje188abf2003-07-20 22:39:58 +0000410 about it. */
sewardj43354092002-11-06 00:15:50 +0000411 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);
sewardjad19c692002-11-09 10:24:01 +0000421 if (conn_count == 0 && exit_when_zero) {
422 printf("\n");
sewardj68948f72002-11-16 20:15:16 +0000423 fflush(stdout);
sewardjad19c692002-11-09 10:24:01 +0000424 exit_routine();
425 }
sewardj43354092002-11-06 00:15:50 +0000426 }
sewardj5ed7a392002-11-03 23:03:24 +0000427 }
428
sewardj43354092002-11-06 00:15:50 +0000429 } /* for (i = 0; i < j; i++) */
430
sewardjad19c692002-11-09 10:24:01 +0000431 } /* while (1) */
sewardj5ed7a392002-11-03 23:03:24 +0000432
sewardjad19c692002-11-09 10:24:01 +0000433 /* NOTREACHED */
sewardj5ed7a392002-11-03 23:03:24 +0000434}
435
436
437/*--------------------------------------------------------------------*/
438/*--- end valgrind-listener.c ---*/
439/*--------------------------------------------------------------------*/