2 /*--------------------------------------------------------------------*/
3 /*--- A simple program to listen for valgrind logfile data. ---*/
4 /*--- valgrind-listener.c ---*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Valgrind, a dynamic binary instrumentation
11 Copyright (C) 2000-2015 Julian Seward
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
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
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
58 /*---------------------------------------------------------------*/
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
65 /* The maximum allowable number of concurrent connections. */
66 unsigned M_CONNECTIONS
= 0;
68 /*---------------------------------------------------------------*/
70 __attribute__ ((noreturn
))
71 static void panic ( const char* str
)
74 "\nvalgrind-listener: the "
75 "'impossible' happened:\n %s\n", str
);
77 "Please report this bug at: %s\n\n", VG_BUGS_TO
);
81 __attribute__ ((noreturn
))
82 static void my_assert_fail ( const char* expr
, const char* file
, int line
, const char* fn
)
85 "\nvalgrind-listener: %s:%d (%s): Assertion '%s' failed.\n",
86 file
, line
, fn
, expr
);
88 "Please report this bug at: %s\n\n", VG_BUGS_TO
);
94 #define assert(expr) \
95 ((void) ((expr) ? 0 : \
96 (my_assert_fail (VG_STRINGIFY(expr), \
98 __PRETTY_FUNCTION__), 0)))
101 /*---------------------------------------------------------------*/
103 /* holds the fds for connections; zero if slot not in use. */
106 struct pollfd
*conn_pollfd
;
109 static void set_nonblocking ( int sd
)
112 res
= fcntl(sd
, F_GETFL
);
113 res
= fcntl(sd
, F_SETFL
, res
| O_NONBLOCK
);
115 perror("fcntl failed");
116 panic("set_nonblocking");
120 static void set_blocking ( int sd
)
123 res
= fcntl(sd
, F_GETFL
);
124 res
= fcntl(sd
, F_SETFL
, res
& ~O_NONBLOCK
);
126 perror("fcntl failed");
127 panic("set_blocking");
132 static void copyout ( char* buf
, int nbuf
)
135 for (i
= 0; i
< nbuf
; i
++) {
136 if (buf
[i
] == '\n') {
137 fprintf(stdout
, "\n(%d) ", conn_count
);
139 __attribute__((unused
)) size_t ignored
140 = fwrite(&buf
[i
], 1, 1, stdout
);
146 static int read_from_sd ( int sd
)
152 n
= read(sd
, buf
, 99);
153 if (n
<= 0) return 0; /* closed */
158 n
= read(sd
, buf
, 100);
159 if (n
<= 0) return 1; /* not closed */
165 static void snooze ( void )
169 req
.tv_nsec
= 200 * 1000 * 1000;
170 nanosleep(&req
,NULL
);
174 /* returns 0 if negative, or > BOUND or invalid characters were found */
175 static int atoi_with_bound ( const char* str
, int bound
)
181 if (*str
< '0' || *str
> '9')
183 n
= 10*n
+ (int)(*str
- '0');
191 /* returns 0 if invalid, else port # */
192 static int atoi_portno ( const char* str
)
194 int n
= atoi_with_bound(str
, 65536);
202 static void usage ( void )
208 " valgrind-listener [--exit-at-zero|-e] [--max-connect=INT] [port-number]\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"
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"
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"
223 M_CONNECTIONS_DEFAULT
, M_CONNECTIONS_MAX
, VG_CLO_DEFAULT_LOGPORT
229 static void banner ( const char* str
)
233 printf("valgrind-listener %s at %s", str
, ctime(&t
));
238 static void exit_routine ( void )
245 static void sigint_handler ( int signo
)
251 int main (int argc
, char** argv
)
253 int i
, j
, k
, res
, one
;
255 socklen_t client_len
;
256 struct sockaddr_in client_addr
, server_addr
;
258 char /*bool*/ exit_when_zero
= 0;
259 int port
= VG_CLO_DEFAULT_LOGPORT
;
261 for (i
= 1; i
< argc
; i
++) {
262 if (0==strcmp(argv
[i
], "--exit-at-zero")
263 || 0==strcmp(argv
[i
], "-e")) {
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
)
272 if (atoi_portno(argv
[i
]) > 0) {
273 port
= atoi_portno(argv
[i
]);
279 if (M_CONNECTIONS
== 0) // nothing specified on command line
280 M_CONNECTIONS
= M_CONNECTIONS_DEFAULT
;
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");
290 signal(SIGINT
, sigint_handler
);
293 for (i
= 0; i
< M_CONNECTIONS
; i
++)
297 main_sd
= socket(AF_INET
, SOCK_STREAM
, 0);
299 perror("cannot open socket ");
300 panic("main -- create socket");
303 /* allow address reuse to avoid "address already in use" errors */
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");
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
);
317 if (bind(main_sd
, (struct sockaddr
*) &server_addr
,
318 sizeof(server_addr
) ) < 0) {
319 perror("cannot bind port ");
320 panic("main -- bind port");
323 res
= listen(main_sd
,M_CONNECTIONS
);
325 perror("listen failed ");
326 panic("main -- listen");
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. */
341 res
= poll(&ufd
, 1, 0);
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
,
349 perror("cannot accept connection ");
350 panic("main -- accept connection");
353 /* find a place to put it. */
355 for (i
= 0; i
< M_CONNECTIONS
; i
++)
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",
369 printf("\n(%d) -------------------- CONNECT "
370 "--------------------\n(%d)\n(%d) ",
371 conn_count
, conn_count
, conn_count
);
376 /* We've processed all new connect requests. Listen for changes
377 to the current set of fds. */
379 for (i
= 0; i
< M_CONNECTIONS
; i
++) {
382 conn_pollfd
[j
].fd
= conn_fd
[i
];
383 conn_pollfd
[j
].events
= POLLIN
/* | POLLHUP | POLLNVAL */;
384 conn_pollfd
[j
].revents
= 0;
388 res
= poll(conn_pollfd
, j
, 0 /* return immediately. */ );
390 perror("poll(main) failed");
391 panic("poll(main) failed");
394 /* nothing happened. go round again. */
399 /* inspect the fds. */
400 for (i
= 0; i
< j
; i
++) {
402 if (conn_pollfd
[i
].revents
& POLLIN
) {
403 /* data is available on this fd */
404 res
= read_from_sd(conn_pollfd
[i
].fd
);
407 /* the connection has been closed. */
408 close(conn_pollfd
[i
].fd
);
409 /* this fd has been closed or otherwise gone bad; forget
411 for (k
= 0; k
< M_CONNECTIONS
; k
++)
412 if (conn_fd
[k
] == conn_pollfd
[i
].fd
)
414 assert(k
< M_CONNECTIONS
);
417 printf("\n(%d) ------------------- DISCONNECT "
418 "-------------------\n(%d)\n(%d) ",
419 conn_count
, conn_count
, conn_count
);
421 if (conn_count
== 0 && exit_when_zero
) {
429 } /* for (i = 0; i < j; i++) */
437 /*--------------------------------------------------------------------*/
438 /*--- end valgrind-listener.c ---*/
439 /*--------------------------------------------------------------------*/