2008-05-26 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / tools / watchgnupg.c
blob213a2cb16e48649ef3cae338d15e359c7e089a1c
1 /* watchgnupg.c - Socket server for GnuPG logs
2 * Copyright (C) 2003, 2004 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 <http://www.gnu.org/licenses/>.
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 <fcntl.h>
34 #include <time.h>
36 #define PGM "watchgnupg"
38 /* Allow for a standalone build. */
39 #ifdef VERSION
40 #define MYVERSION_LINE PGM " (GnuPG) " VERSION
41 #define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
42 #else
43 #define MYVERSION_LINE PGM
44 #define BUGREPORT_LINE ""
45 #endif
47 #ifndef PF_LOCAL
48 # ifdef PF_UNIX
49 # define PF_LOCAL PF_UNIX
50 # else
51 # define PF_LOCAL AF_UNIX
52 # endif
53 # ifndef AF_LOCAL
54 # define AF_LOCAL AF_UNIX
55 # endif
56 #endif
59 static int verbose;
62 static void
63 die (const char *format, ...)
65 va_list arg_ptr;
67 fflush (stdout);
68 fprintf (stderr, "%s: ", PGM);
70 va_start (arg_ptr, format);
71 vfprintf (stderr, format, arg_ptr);
72 va_end (arg_ptr);
73 putc ('\n', stderr);
75 exit (1);
79 /* static void */
80 /* err (const char *format, ...) */
81 /* { */
82 /* va_list arg_ptr; */
84 /* fflush (stdout); */
85 /* fprintf (stderr, "%s: ", PGM); */
87 /* va_start (arg_ptr, format); */
88 /* vfprintf (stderr, format, arg_ptr); */
89 /* va_end (arg_ptr); */
90 /* putc ('\n', stderr); */
91 /* } */
93 static void *
94 xmalloc (size_t n)
96 void *p = malloc (n);
97 if (!p)
98 die ("out of core");
99 return p;
102 static void *
103 xcalloc (size_t n, size_t m)
105 void *p = calloc (n, m);
106 if (!p)
107 die ("out of core");
108 return p;
111 static void *
112 xrealloc (void *old, size_t n)
114 void *p = realloc (old, n);
115 if (!p)
116 die ("out of core");
117 return p;
121 struct client_s {
122 struct client_s *next;
123 int fd;
124 size_t size; /* Allocated size of buffer. */
125 size_t len; /* Current length of buffer. */
126 unsigned char *buffer; /* Buffer to with data already read. */
129 typedef struct client_s *client_t;
133 static void
134 print_fd_and_time (int fd)
136 struct tm *tp;
137 time_t atime = time (NULL);
139 tp = localtime (&atime);
140 printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
142 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
143 tp->tm_hour, tp->tm_min, tp->tm_sec );
147 /* Print LINE for the client identified by C. Calling this function
148 witgh LINE set to NULL, will flush the internal buffer. */
149 static void
150 print_line (client_t c, const char *line)
152 const char *s;
153 size_t n;
155 if (!line)
157 if (c->buffer && c->len)
159 print_fd_and_time (c->fd);
160 fwrite (c->buffer, c->len, 1, stdout);
161 putc ('\n', stdout);
162 c->len = 0;
164 return;
167 while ((s = strchr (line, '\n')))
169 print_fd_and_time (c->fd);
170 if (c->buffer && c->len)
172 fwrite (c->buffer, c->len, 1, stdout);
173 c->len = 0;
175 fwrite (line, s - line + 1, 1, stdout);
176 line = s + 1;
178 n = strlen (line);
179 if (n)
181 if (c->len + n >= c->size)
183 c->size += ((n + 255) & ~255);
184 c->buffer = (c->buffer
185 ? xrealloc (c->buffer, c->size)
186 : xmalloc (c->size));
188 memcpy (c->buffer + c->len, line, n);
189 c->len += n;
194 static void
195 print_version (int with_help)
197 fputs (MYVERSION_LINE "\n"
198 "Copyright (C) 2004 Free Software Foundation, Inc.\n"
199 "This program comes with ABSOLUTELY NO WARRANTY.\n"
200 "This is free software, and you are welcome to redistribute it\n"
201 "under certain conditions. See the file COPYING for details.\n",
202 stdout);
204 if (with_help)
205 fputs ("\n"
206 "Usage: " PGM " [OPTIONS] SOCKETNAME\n"
207 "Open the local socket SOCKETNAME and display log messages\n"
208 "\n"
209 " --force delete an already existing socket file\n"
210 " --verbose enable extra informational output\n"
211 " --version print version of the program and exit\n"
212 " --help display this help and exit\n"
213 BUGREPORT_LINE, stdout );
215 exit (0);
218 int
219 main (int argc, char **argv)
221 int last_argc = -1;
222 int force = 0;
224 struct sockaddr_un srvr_addr;
225 socklen_t addrlen;
226 int server;
227 int flags;
228 client_t client_list = NULL;
230 if (argc)
232 argc--; argv++;
234 while (argc && last_argc != argc )
236 last_argc = argc;
237 if (!strcmp (*argv, "--"))
239 argc--; argv++;
240 break;
242 else if (!strcmp (*argv, "--version"))
243 print_version (0);
244 else if (!strcmp (*argv, "--help"))
245 print_version (1);
246 else if (!strcmp (*argv, "--verbose"))
248 verbose = 1;
249 argc--; argv++;
251 else if (!strcmp (*argv, "--force"))
253 force = 1;
254 argc--; argv++;
258 if (argc != 1)
260 fprintf (stderr, "usage: " PGM " socketname\n");
261 exit (1);
265 if (verbose)
266 fprintf (stderr, "opening socket `%s'\n", *argv);
268 setvbuf (stdout, NULL, _IOLBF, 0);
270 server = socket (PF_LOCAL, SOCK_STREAM, 0);
271 if (server == -1)
272 die ("socket() failed: %s\n", strerror (errno));
274 /* We better set the listening socket to non-blocking so that we
275 don't get bitten by race conditions in accept. The should not
276 happen for Unix Domain sockets but well, shit happens. */
277 flags = fcntl (server, F_GETFL, 0);
278 if (flags == -1)
279 die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
280 if ( fcntl (server, F_SETFL, (flags | O_NONBLOCK)) == -1)
281 die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
284 memset (&srvr_addr, 0, sizeof srvr_addr);
285 srvr_addr.sun_family = AF_LOCAL;
286 strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
287 srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
288 addrlen = (offsetof (struct sockaddr_un, sun_path)
289 + strlen (srvr_addr.sun_path) + 1);
292 again:
293 if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
295 if (errno == EADDRINUSE && force)
297 force = 0;
298 remove (srvr_addr.sun_path);
299 goto again;
301 die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
304 if (listen (server, 5))
305 die ("listen failed: %s\n", strerror (errno));
307 for (;;)
309 fd_set rfds;
310 int max_fd;
311 client_t client;
313 /* Usually we don't have that many connections, thus it is okay
314 to set them allways from scratch and don't maintain an active
315 fd_set. */
316 FD_ZERO (&rfds);
317 FD_SET (server, &rfds);
318 max_fd = server;
319 for (client = client_list; client; client = client->next)
320 if (client->fd != -1)
322 FD_SET (client->fd, &rfds);
323 if (client->fd > max_fd)
324 max_fd = client->fd;
327 if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
328 continue; /* Ignore any errors. */
330 if (FD_ISSET (server, &rfds)) /* New connection. */
332 struct sockaddr_un clnt_addr;
333 int fd;
335 addrlen = sizeof clnt_addr;
336 fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
337 if (fd == -1)
339 printf ("[accepting connection failed: %s]\n", strerror (errno));
341 else if (fd >= FD_SETSIZE)
343 close (fd);
344 printf ("[connection request denied: too many connections]\n");
346 else
348 for (client = client_list; client && client->fd != -1;
349 client = client->next)
351 if (!client)
353 client = xcalloc (1, sizeof *client);
354 client->next = client_list;
355 client_list = client;
357 client->fd = fd;
358 printf ("[client at fd %d connected]\n", client->fd);
361 for (client = client_list; client; client = client->next)
362 if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
364 char line[256];
365 int n;
367 n = read (client->fd, line, sizeof line - 1);
368 if (n < 0)
370 int save_errno = errno;
371 print_line (client, NULL); /* flush */
372 printf ("[client at fd %d read error: %s]\n",
373 client->fd, strerror (save_errno));
374 close (client->fd);
375 client->fd = -1;
377 else if (!n)
379 print_line (client, NULL); /* flush */
380 close (client->fd);
381 printf ("[client at fd %d disconnected]\n", client->fd);
382 client->fd = -1;
384 else
386 line[n] = 0;
387 print_line (client, line);
392 return 0;
397 Local Variables:
398 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
399 End: