Line data Source code
1 : /* watchgnupg.c - Socket server for GnuPG logs
2 : * Copyright (C) 2003, 2004, 2010 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #ifdef HAVE_CONFIG_H
21 : #include <config.h>
22 : #endif
23 : #include <stdio.h>
24 : #include <stdlib.h>
25 : #include <stddef.h>
26 : #include <string.h>
27 : #include <errno.h>
28 : #include <stdarg.h>
29 : #include <assert.h>
30 : #include <unistd.h>
31 : #include <sys/socket.h>
32 : #include <sys/un.h>
33 : #include <netinet/in.h>
34 : #include <arpa/inet.h>
35 : #include <fcntl.h>
36 : #include <time.h>
37 : #ifdef HAVE_SYS_SELECT_H
38 : # include <sys/select.h>
39 : #endif
40 :
41 : #define PGM "watchgnupg"
42 :
43 : /* Allow for a standalone build on most systems. */
44 : #ifdef VERSION
45 : #define MYVERSION_LINE PGM " ("GNUPG_NAME") " VERSION
46 : #define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
47 : #else
48 : #define MYVERSION_LINE PGM " (standalone build) " __DATE__
49 : #define BUGREPORT_LINE ""
50 : #endif
51 : #if !defined(SUN_LEN) || !defined(PF_LOCAL) || !defined(AF_LOCAL)
52 : #define GNUPG_COMMON_NEED_AFLOCAL
53 : #include "../common/mischelp.h"
54 : #endif
55 :
56 :
57 : static int verbose;
58 : static int time_only;
59 :
60 : static void
61 0 : die (const char *format, ...)
62 : {
63 : va_list arg_ptr;
64 :
65 0 : fflush (stdout);
66 0 : fprintf (stderr, "%s: ", PGM);
67 :
68 0 : va_start (arg_ptr, format);
69 0 : vfprintf (stderr, format, arg_ptr);
70 0 : va_end (arg_ptr);
71 0 : putc ('\n', stderr);
72 :
73 0 : exit (1);
74 : }
75 :
76 :
77 : static void
78 0 : err (const char *format, ...)
79 : {
80 : va_list arg_ptr;
81 :
82 0 : fflush (stdout);
83 0 : fprintf (stderr, "%s: ", PGM);
84 :
85 0 : va_start (arg_ptr, format);
86 0 : vfprintf (stderr, format, arg_ptr);
87 0 : va_end (arg_ptr);
88 0 : putc ('\n', stderr);
89 0 : }
90 :
91 : static void *
92 0 : xmalloc (size_t n)
93 : {
94 0 : void *p = malloc (n);
95 0 : if (!p)
96 0 : die ("out of core");
97 0 : return p;
98 : }
99 :
100 : static void *
101 0 : xcalloc (size_t n, size_t m)
102 : {
103 0 : void *p = calloc (n, m);
104 0 : if (!p)
105 0 : die ("out of core");
106 0 : return p;
107 : }
108 :
109 : static void *
110 0 : xrealloc (void *old, size_t n)
111 : {
112 0 : void *p = realloc (old, n);
113 0 : if (!p)
114 0 : die ("out of core");
115 0 : return p;
116 : }
117 :
118 :
119 : struct client_s {
120 : struct client_s *next;
121 : int fd;
122 : size_t size; /* Allocated size of buffer. */
123 : size_t len; /* Current length of buffer. */
124 : unsigned char *buffer; /* Buffer to with data already read. */
125 :
126 : };
127 : typedef struct client_s *client_t;
128 :
129 : /* The list of all connected peers. */
130 : static client_t client_list;
131 :
132 :
133 :
134 :
135 : static void
136 0 : print_fd_and_time (int fd)
137 : {
138 : struct tm *tp;
139 0 : time_t atime = time (NULL);
140 :
141 0 : tp = localtime (&atime);
142 0 : if (time_only)
143 0 : printf ("%3d - %02d:%02d:%02d ",
144 : fd,
145 : tp->tm_hour, tp->tm_min, tp->tm_sec );
146 : else
147 0 : printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
148 : fd,
149 0 : 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
150 : tp->tm_hour, tp->tm_min, tp->tm_sec );
151 0 : }
152 :
153 :
154 : /* Print LINE for the client identified by C. Calling this function
155 : with LINE set to NULL, will flush the internal buffer. */
156 : static void
157 0 : print_line (client_t c, const char *line)
158 : {
159 : const char *s;
160 : size_t n;
161 :
162 0 : if (!line)
163 : {
164 0 : if (c->buffer && c->len)
165 : {
166 0 : print_fd_and_time (c->fd);
167 0 : fwrite (c->buffer, c->len, 1, stdout);
168 0 : putc ('\n', stdout);
169 0 : c->len = 0;
170 : }
171 0 : return;
172 : }
173 :
174 0 : while ((s = strchr (line, '\n')))
175 : {
176 0 : print_fd_and_time (c->fd);
177 0 : if (c->buffer && c->len)
178 : {
179 0 : fwrite (c->buffer, c->len, 1, stdout);
180 0 : c->len = 0;
181 : }
182 0 : fwrite (line, s - line + 1, 1, stdout);
183 0 : line = s + 1;
184 : }
185 0 : n = strlen (line);
186 0 : if (n)
187 : {
188 0 : if (c->len + n >= c->size)
189 : {
190 0 : c->size += ((n + 255) & ~255);
191 0 : c->buffer = (c->buffer
192 0 : ? xrealloc (c->buffer, c->size)
193 0 : : xmalloc (c->size));
194 : }
195 0 : memcpy (c->buffer + c->len, line, n);
196 0 : c->len += n;
197 : }
198 : }
199 :
200 :
201 : static void
202 0 : setup_client (int server_fd, int is_un)
203 : {
204 : struct sockaddr_un addr_un;
205 : struct sockaddr_in addr_in;
206 : struct sockaddr *addr;
207 : socklen_t addrlen;
208 : int fd;
209 : client_t client;
210 :
211 0 : if (is_un)
212 : {
213 0 : addr = (struct sockaddr *)&addr_un;
214 0 : addrlen = sizeof addr_un;
215 : }
216 : else
217 : {
218 0 : addr = (struct sockaddr *)&addr_in;
219 0 : addrlen = sizeof addr_in;
220 : }
221 :
222 0 : fd = accept (server_fd, addr, &addrlen);
223 0 : if (fd == -1)
224 : {
225 0 : printf ("[accepting %s connection failed: %s]\n",
226 0 : is_un? "local":"tcp", strerror (errno));
227 : }
228 0 : else if (fd >= FD_SETSIZE)
229 : {
230 0 : close (fd);
231 0 : printf ("[connection request denied: too many connections]\n");
232 : }
233 : else
234 : {
235 0 : for (client = client_list; client && client->fd != -1;
236 0 : client = client->next)
237 : ;
238 0 : if (!client)
239 : {
240 0 : client = xcalloc (1, sizeof *client);
241 0 : client->next = client_list;
242 0 : client_list = client;
243 : }
244 0 : client->fd = fd;
245 0 : printf ("[client at fd %d connected (%s)]\n",
246 : client->fd, is_un? "local":"tcp");
247 : }
248 0 : }
249 :
250 :
251 :
252 : static void
253 0 : print_version (int with_help)
254 : {
255 0 : fputs (MYVERSION_LINE "\n"
256 : "Copyright (C) 2010 Free Software Foundation, Inc.\n"
257 : "License GPLv3+: "
258 : "GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
259 : "This is free software: you are free to change and redistribute it.\n"
260 : "There is NO WARRANTY, to the extent permitted by law.\n",
261 : stdout);
262 0 : if (with_help)
263 0 : fputs
264 : ("\n"
265 : "Usage: " PGM " [OPTIONS] SOCKETNAME\n"
266 : " " PGM " [OPTIONS] PORT [SOCKETNAME]\n"
267 : "Open the local socket SOCKETNAME (or the TCP port PORT)\n"
268 : "and display log messages\n"
269 : "\n"
270 : " --tcp listen on a TCP port and optionally on a local socket\n"
271 : " --force delete an already existing socket file\n"
272 : " --verbose enable extra informational output\n"
273 : " --time-only print only the time; not a full timestamp\n"
274 : " --version print version of the program and exit\n"
275 : " --help display this help and exit\n"
276 : BUGREPORT_LINE, stdout );
277 :
278 0 : exit (0);
279 : }
280 :
281 : int
282 0 : main (int argc, char **argv)
283 : {
284 0 : int last_argc = -1;
285 0 : int force = 0;
286 0 : int tcp = 0;
287 :
288 : struct sockaddr_un srvr_addr_un;
289 : struct sockaddr_in srvr_addr_in;
290 0 : struct sockaddr *addr_in = NULL;
291 0 : struct sockaddr *addr_un = NULL;
292 : socklen_t addrlen_in, addrlen_un;
293 : unsigned short port;
294 : int server_un, server_in;
295 : int flags;
296 :
297 0 : if (argc)
298 : {
299 0 : argc--; argv++;
300 : }
301 0 : while (argc && last_argc != argc )
302 : {
303 0 : last_argc = argc;
304 0 : if (!strcmp (*argv, "--"))
305 : {
306 0 : argc--; argv++;
307 0 : break;
308 : }
309 0 : else if (!strcmp (*argv, "--version"))
310 0 : print_version (0);
311 0 : else if (!strcmp (*argv, "--help"))
312 0 : print_version (1);
313 0 : else if (!strcmp (*argv, "--verbose"))
314 : {
315 0 : verbose = 1;
316 0 : argc--; argv++;
317 : }
318 0 : else if (!strcmp (*argv, "--time-only"))
319 : {
320 0 : time_only = 1;
321 0 : argc--; argv++;
322 : }
323 0 : else if (!strcmp (*argv, "--force"))
324 : {
325 0 : force = 1;
326 0 : argc--; argv++;
327 : }
328 0 : else if (!strcmp (*argv, "--tcp"))
329 : {
330 0 : tcp = 1;
331 0 : argc--; argv++;
332 : }
333 : }
334 :
335 0 : if (!((!tcp && argc == 1) || (tcp && (argc == 1 || argc == 2))))
336 : {
337 0 : fprintf (stderr, "usage: " PGM " socketname\n"
338 : " " PGM " --tcp port [socketname]\n");
339 0 : exit (1);
340 : }
341 :
342 0 : if (tcp)
343 : {
344 0 : port = atoi (*argv);
345 0 : argc--; argv++;
346 : }
347 : else
348 : {
349 0 : port = 0;
350 : }
351 :
352 0 : setvbuf (stdout, NULL, _IOLBF, 0);
353 :
354 0 : if (tcp)
355 : {
356 0 : int i = 1;
357 0 : server_in = socket (PF_INET, SOCK_STREAM, 0);
358 0 : if (server_in == -1)
359 0 : die ("socket(PF_INET) failed: %s\n", strerror (errno));
360 0 : if (setsockopt (server_in, SOL_SOCKET, SO_REUSEADDR,
361 : (unsigned char *)&i, sizeof (i)))
362 0 : err ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
363 0 : if (verbose)
364 0 : fprintf (stderr, "listening on port %hu\n", port);
365 : }
366 : else
367 0 : server_in = -1;
368 :
369 0 : if (argc)
370 : {
371 0 : server_un = socket (PF_LOCAL, SOCK_STREAM, 0);
372 0 : if (server_un == -1)
373 0 : die ("socket(PF_LOCAL) failed: %s\n", strerror (errno));
374 0 : if (verbose)
375 0 : fprintf (stderr, "listening on socket '%s'\n", *argv);
376 : }
377 : else
378 0 : server_un = -1;
379 :
380 : /* We better set the listening socket to non-blocking so that we
381 : don't get bitten by race conditions in accept. The should not
382 : happen for Unix Domain sockets but well, shit happens. */
383 0 : if (server_in != -1)
384 : {
385 0 : flags = fcntl (server_in, F_GETFL, 0);
386 0 : if (flags == -1)
387 0 : die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
388 0 : if ( fcntl (server_in, F_SETFL, (flags | O_NONBLOCK)) == -1)
389 0 : die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
390 : }
391 0 : if (server_un != -1)
392 : {
393 0 : flags = fcntl (server_un, F_GETFL, 0);
394 0 : if (flags == -1)
395 0 : die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
396 0 : if ( fcntl (server_un, F_SETFL, (flags | O_NONBLOCK)) == -1)
397 0 : die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
398 : }
399 :
400 0 : if (tcp)
401 : {
402 0 : memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
403 0 : srvr_addr_in.sin_family = AF_INET;
404 0 : srvr_addr_in.sin_port = htons (port);
405 0 : srvr_addr_in.sin_addr.s_addr = htonl (INADDR_ANY);
406 0 : addr_in = (struct sockaddr *)&srvr_addr_in;
407 0 : addrlen_in = sizeof srvr_addr_in;
408 : }
409 0 : if (argc)
410 : {
411 0 : memset (&srvr_addr_un, 0, sizeof srvr_addr_un);
412 0 : srvr_addr_un.sun_family = AF_LOCAL;
413 0 : strncpy (srvr_addr_un.sun_path, *argv, sizeof (srvr_addr_un.sun_path)-1);
414 0 : srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0;
415 0 : addr_un = (struct sockaddr *)&srvr_addr_un;
416 0 : addrlen_un = SUN_LEN (&srvr_addr_un);
417 : }
418 : else
419 0 : addrlen_un = 0; /* Silent gcc. */
420 :
421 0 : if (server_in != -1 && bind (server_in, addr_in, addrlen_in))
422 0 : die ("bind to port %hu failed: %s\n", port, strerror (errno));
423 :
424 : again:
425 0 : if (server_un != -1 && bind (server_un, addr_un, addrlen_un))
426 : {
427 0 : if (errno == EADDRINUSE && force)
428 : {
429 0 : force = 0;
430 0 : remove (srvr_addr_un.sun_path);
431 0 : goto again;
432 : }
433 : else
434 0 : die ("bind to '%s' failed: %s\n", *argv, strerror (errno));
435 : }
436 :
437 0 : if (server_in != -1 && listen (server_in, 5))
438 0 : die ("listen on inet failed: %s\n", strerror (errno));
439 0 : if (server_un != -1 && listen (server_un, 5))
440 0 : die ("listen on local failed: %s\n", strerror (errno));
441 :
442 : for (;;)
443 : {
444 : fd_set rfds;
445 : int max_fd;
446 : client_t client;
447 :
448 : /* Usually we don't have that many connections, thus it is okay
449 : to set them allways from scratch and don't maintain an active
450 : fd_set. */
451 0 : FD_ZERO (&rfds);
452 0 : max_fd = -1;
453 0 : if (server_in != -1)
454 : {
455 0 : FD_SET (server_in, &rfds);
456 0 : max_fd = server_in;
457 : }
458 0 : if (server_un != -1)
459 : {
460 0 : FD_SET (server_un, &rfds);
461 0 : if (server_un > max_fd)
462 0 : max_fd = server_un;
463 : }
464 0 : for (client = client_list; client; client = client->next)
465 0 : if (client->fd != -1)
466 : {
467 0 : FD_SET (client->fd, &rfds);
468 0 : if (client->fd > max_fd)
469 0 : max_fd = client->fd;
470 : }
471 :
472 0 : if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
473 0 : continue; /* Ignore any errors. */
474 :
475 0 : if (server_in != -1 && FD_ISSET (server_in, &rfds))
476 0 : setup_client (server_in, 0);
477 0 : if (server_un != -1 && FD_ISSET (server_un, &rfds))
478 0 : setup_client (server_un, 1);
479 :
480 0 : for (client = client_list; client; client = client->next)
481 0 : if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
482 : {
483 : char line[256];
484 : int n;
485 :
486 0 : n = read (client->fd, line, sizeof line - 1);
487 0 : if (n < 0)
488 : {
489 0 : int save_errno = errno;
490 0 : print_line (client, NULL); /* flush */
491 0 : printf ("[client at fd %d read error: %s]\n",
492 : client->fd, strerror (save_errno));
493 0 : close (client->fd);
494 0 : client->fd = -1;
495 : }
496 0 : else if (!n)
497 : {
498 0 : print_line (client, NULL); /* flush */
499 0 : close (client->fd);
500 0 : printf ("[client at fd %d disconnected]\n", client->fd);
501 0 : client->fd = -1;
502 : }
503 : else
504 : {
505 0 : line[n] = 0;
506 0 : print_line (client, line);
507 : }
508 : }
509 0 : }
510 :
511 : return 0;
512 : }
513 :
514 :
515 : /*
516 : Local Variables:
517 : compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
518 : End:
519 : */
|