1 /* watchgnupg.c - Socket server for GnuPG logs
2 * Copyright (C) 2003, 2004, 2010 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
37 #ifdef HAVE_SYS_SELECT_H
38 # include <sys/select.h>
41 #define PGM "watchgnupg"
43 /* Allow for a standalone build on most systems. */
45 #define MYVERSION_LINE PGM " ("GNUPG_NAME") " VERSION
46 #define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
48 #define MYVERSION_LINE PGM " (standalone build) " __DATE__
49 #define BUGREPORT_LINE ""
51 #if !defined(SUN_LEN) || !defined(PF_LOCAL) || !defined(AF_LOCAL)
52 #define GNUPG_COMMON_NEED_AFLOCAL
53 #include "../common/mischelp.h"
61 die (const char *format, ...)
66 fprintf (stderr, "%s: ", PGM);
68 va_start (arg_ptr, format);
69 vfprintf (stderr, format, arg_ptr);
78 err (const char *format, ...)
83 fprintf (stderr, "%s: ", PGM);
85 va_start (arg_ptr, format);
86 vfprintf (stderr, format, arg_ptr);
101 xcalloc (size_t n, size_t m)
103 void *p = calloc (n, m);
110 xrealloc (void *old, size_t n)
112 void *p = realloc (old, n);
120 struct client_s *next;
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. */
127 typedef struct client_s *client_t;
129 /* The list of all connected peers. */
130 static client_t client_list;
136 print_fd_and_time (int fd)
139 time_t atime = time (NULL);
141 tp = localtime (&atime);
143 printf ("%3d - %02d:%02d:%02d ",
145 tp->tm_hour, tp->tm_min, tp->tm_sec );
147 printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
149 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
150 tp->tm_hour, tp->tm_min, tp->tm_sec );
154 /* Print LINE for the client identified by C. Calling this function
155 with LINE set to NULL, will flush the internal buffer. */
157 print_line (client_t c, const char *line)
164 if (c->buffer && c->len)
166 print_fd_and_time (c->fd);
167 fwrite (c->buffer, c->len, 1, stdout);
174 while ((s = strchr (line, '\n')))
176 print_fd_and_time (c->fd);
177 if (c->buffer && c->len)
179 fwrite (c->buffer, c->len, 1, stdout);
182 fwrite (line, s - line + 1, 1, stdout);
188 if (c->len + n >= c->size)
190 c->size += ((n + 255) & ~255);
191 c->buffer = (c->buffer
192 ? xrealloc (c->buffer, c->size)
193 : xmalloc (c->size));
195 memcpy (c->buffer + c->len, line, n);
202 setup_client (int server_fd, int is_un)
204 struct sockaddr_un addr_un;
205 struct sockaddr_in addr_in;
206 struct sockaddr *addr;
213 addr = (struct sockaddr *)&addr_un;
214 addrlen = sizeof addr_un;
218 addr = (struct sockaddr *)&addr_in;
219 addrlen = sizeof addr_in;
222 fd = accept (server_fd, addr, &addrlen);
225 printf ("[accepting %s connection failed: %s]\n",
226 is_un? "local":"tcp", strerror (errno));
228 else if (fd >= FD_SETSIZE)
231 printf ("[connection request denied: too many connections]\n");
235 for (client = client_list; client && client->fd != -1;
236 client = client->next)
240 client = xcalloc (1, sizeof *client);
241 client->next = client_list;
242 client_list = client;
245 printf ("[client at fd %d connected (%s)]\n",
246 client->fd, is_un? "local":"tcp");
253 print_version (int with_help)
255 fputs (MYVERSION_LINE "\n"
256 "Copyright (C) 2010 Free Software Foundation, Inc.\n"
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",
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"
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 );
282 main (int argc, char **argv)
288 struct sockaddr_un srvr_addr_un;
289 struct sockaddr_in srvr_addr_in;
290 struct sockaddr *addr_in = NULL;
291 struct sockaddr *addr_un = NULL;
292 socklen_t addrlen_in, addrlen_un;
294 int server_un, server_in;
301 while (argc && last_argc != argc )
304 if (!strcmp (*argv, "--"))
309 else if (!strcmp (*argv, "--version"))
311 else if (!strcmp (*argv, "--help"))
313 else if (!strcmp (*argv, "--verbose"))
318 else if (!strcmp (*argv, "--time-only"))
323 else if (!strcmp (*argv, "--force"))
328 else if (!strcmp (*argv, "--tcp"))
335 if (!((!tcp && argc == 1) || (tcp && (argc == 1 || argc == 2))))
337 fprintf (stderr, "usage: " PGM " socketname\n"
338 " " PGM " --tcp port [socketname]\n");
352 setvbuf (stdout, NULL, _IOLBF, 0);
357 server_in = socket (PF_INET, SOCK_STREAM, 0);
359 die ("socket(PF_INET) failed: %s\n", strerror (errno));
360 if (setsockopt (server_in, SOL_SOCKET, SO_REUSEADDR,
361 (unsigned char *)&i, sizeof (i)))
362 err ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
364 fprintf (stderr, "listening on port %hu\n", port);
371 server_un = socket (PF_LOCAL, SOCK_STREAM, 0);
373 die ("socket(PF_LOCAL) failed: %s\n", strerror (errno));
375 fprintf (stderr, "listening on socket '%s'\n", *argv);
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. */
385 flags = fcntl (server_in, F_GETFL, 0);
387 die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
388 if ( fcntl (server_in, F_SETFL, (flags | O_NONBLOCK)) == -1)
389 die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
393 flags = fcntl (server_un, F_GETFL, 0);
395 die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
396 if ( fcntl (server_un, F_SETFL, (flags | O_NONBLOCK)) == -1)
397 die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
402 memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
403 srvr_addr_in.sin_family = AF_INET;
404 srvr_addr_in.sin_port = htons (port);
405 srvr_addr_in.sin_addr.s_addr = htonl (INADDR_ANY);
406 addr_in = (struct sockaddr *)&srvr_addr_in;
407 addrlen_in = sizeof srvr_addr_in;
411 memset (&srvr_addr_un, 0, sizeof srvr_addr_un);
412 srvr_addr_un.sun_family = AF_LOCAL;
413 strncpy (srvr_addr_un.sun_path, *argv, sizeof (srvr_addr_un.sun_path)-1);
414 srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0;
415 addr_un = (struct sockaddr *)&srvr_addr_un;
416 addrlen_un = SUN_LEN (&srvr_addr_un);
419 addrlen_un = 0; /* Silent gcc. */
421 if (server_in != -1 && bind (server_in, addr_in, addrlen_in))
422 die ("bind to port %hu failed: %s\n", port, strerror (errno));
425 if (server_un != -1 && bind (server_un, addr_un, addrlen_un))
427 if (errno == EADDRINUSE && force)
430 remove (srvr_addr_un.sun_path);
434 die ("bind to '%s' failed: %s\n", *argv, strerror (errno));
437 if (server_in != -1 && listen (server_in, 5))
438 die ("listen on inet failed: %s\n", strerror (errno));
439 if (server_un != -1 && listen (server_un, 5))
440 die ("listen on local failed: %s\n", strerror (errno));
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
455 FD_SET (server_in, &rfds);
460 FD_SET (server_un, &rfds);
461 if (server_un > max_fd)
464 for (client = client_list; client; client = client->next)
465 if (client->fd != -1)
467 FD_SET (client->fd, &rfds);
468 if (client->fd > max_fd)
472 if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
473 continue; /* Ignore any errors. */
475 if (server_in != -1 && FD_ISSET (server_in, &rfds))
476 setup_client (server_in, 0);
477 if (server_un != -1 && FD_ISSET (server_un, &rfds))
478 setup_client (server_un, 1);
480 for (client = client_list; client; client = client->next)
481 if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
486 n = read (client->fd, line, sizeof line - 1);
489 int save_errno = errno;
490 print_line (client, NULL); /* flush */
491 printf ("[client at fd %d read error: %s]\n",
492 client->fd, strerror (save_errno));
498 print_line (client, NULL); /* flush */
500 printf ("[client at fd %d disconnected]\n", client->fd);
506 print_line (client, line);
517 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"