2006-04-14 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / tools / watchgnupg.c
blob6cb570fbcecb5b4c47379e125916737a8911d61e
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 2 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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <stdarg.h>
30 #include <assert.h>
31 #include <unistd.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <fcntl.h>
35 #include <time.h>
37 #define PGM "watchgnupg"
39 /* Allow for a standalone build. */
40 #ifdef VERSION
41 #define MYVERSION_LINE PGM " (GnuPG) " VERSION
42 #define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
43 #else
44 #define MYVERSION_LINE PGM
45 #define BUGREPORT_LINE ""
46 #endif
48 #ifndef PF_LOCAL
49 # ifdef PF_UNIX
50 # define PF_LOCAL PF_UNIX
51 # else
52 # define PF_LOCAL AF_UNIX
53 # endif
54 # ifndef AF_LOCAL
55 # define AF_LOCAL AF_UNIX
56 # endif
57 #endif
60 static int verbose;
63 static void
64 die (const char *format, ...)
66 va_list arg_ptr;
68 fflush (stdout);
69 fprintf (stderr, "%s: ", PGM);
71 va_start (arg_ptr, format);
72 vfprintf (stderr, format, arg_ptr);
73 va_end (arg_ptr);
74 putc ('\n', stderr);
76 exit (1);
80 /* static void */
81 /* err (const char *format, ...) */
82 /* { */
83 /* va_list arg_ptr; */
85 /* fflush (stdout); */
86 /* fprintf (stderr, "%s: ", PGM); */
88 /* va_start (arg_ptr, format); */
89 /* vfprintf (stderr, format, arg_ptr); */
90 /* va_end (arg_ptr); */
91 /* putc ('\n', stderr); */
92 /* } */
94 static void *
95 xmalloc (size_t n)
97 void *p = malloc (n);
98 if (!p)
99 die ("out of core");
100 return p;
103 static void *
104 xcalloc (size_t n, size_t m)
106 void *p = calloc (n, m);
107 if (!p)
108 die ("out of core");
109 return p;
112 static void *
113 xrealloc (void *old, size_t n)
115 void *p = realloc (old, n);
116 if (!p)
117 die ("out of core");
118 return p;
122 struct client_s {
123 struct client_s *next;
124 int fd;
125 size_t size; /* Allocated size of buffer. */
126 size_t len; /* Current length of buffer. */
127 unsigned char *buffer; /* Buffer to with data already read. */
130 typedef struct client_s *client_t;
134 static void
135 print_fd_and_time (int fd)
137 struct tm *tp;
138 time_t atime = time (NULL);
140 tp = localtime (&atime);
141 printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
143 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
144 tp->tm_hour, tp->tm_min, tp->tm_sec );
148 /* Print LINE for the client identified by C. Calling this function
149 witgh LINE set to NULL, will flush the internal buffer. */
150 static void
151 print_line (client_t c, const char *line)
153 const char *s;
154 size_t n;
156 if (!line)
158 if (c->buffer && c->len)
160 print_fd_and_time (c->fd);
161 fwrite (c->buffer, c->len, 1, stdout);
162 putc ('\n', stdout);
163 c->len = 0;
165 return;
168 while ((s = strchr (line, '\n')))
170 print_fd_and_time (c->fd);
171 if (c->buffer && c->len)
173 fwrite (c->buffer, c->len, 1, stdout);
174 c->len = 0;
176 fwrite (line, s - line + 1, 1, stdout);
177 line = s + 1;
179 n = strlen (line);
180 if (n)
182 if (c->len + n >= c->size)
184 c->size += ((n + 255) & ~255);
185 c->buffer = (c->buffer
186 ? xrealloc (c->buffer, c->size)
187 : xmalloc (c->size));
189 memcpy (c->buffer + c->len, line, n);
190 c->len += n;
195 static void
196 print_version (int with_help)
198 fputs (MYVERSION_LINE "\n"
199 "Copyright (C) 2004 Free Software Foundation, Inc.\n"
200 "This program comes with ABSOLUTELY NO WARRANTY.\n"
201 "This is free software, and you are welcome to redistribute it\n"
202 "under certain conditions. See the file COPYING for details.\n",
203 stdout);
205 if (with_help)
206 fputs ("\n"
207 "Usage: " PGM " [OPTIONS] SOCKETNAME\n"
208 "Open the local socket SOCKETNAME and display log messages\n"
209 "\n"
210 " --force delete an already existing socket file\n"
211 " --verbose enable extra informational output\n"
212 " --version print version of the program and exit\n"
213 " --help display this help and exit\n"
214 BUGREPORT_LINE, stdout );
216 exit (0);
219 int
220 main (int argc, char **argv)
222 int last_argc = -1;
223 int force = 0;
225 struct sockaddr_un srvr_addr;
226 socklen_t addrlen;
227 int server;
228 int flags;
229 client_t client_list = NULL;
231 if (argc)
233 argc--; argv++;
235 while (argc && last_argc != argc )
237 last_argc = argc;
238 if (!strcmp (*argv, "--"))
240 argc--; argv++;
241 break;
243 else if (!strcmp (*argv, "--version"))
244 print_version (0);
245 else if (!strcmp (*argv, "--help"))
246 print_version (1);
247 else if (!strcmp (*argv, "--verbose"))
249 verbose = 1;
250 argc--; argv++;
252 else if (!strcmp (*argv, "--force"))
254 force = 1;
255 argc--; argv++;
259 if (argc != 1)
261 fprintf (stderr, "usage: " PGM " socketname\n");
262 exit (1);
266 if (verbose)
267 fprintf (stderr, "opening socket `%s'\n", *argv);
269 setvbuf (stdout, NULL, _IOLBF, 0);
271 server = socket (PF_LOCAL, SOCK_STREAM, 0);
272 if (server == -1)
273 die ("socket() failed: %s\n", strerror (errno));
275 /* We better set the listening socket to non-blocking so that we
276 don't get bitten by race conditions in accept. The should not
277 happen for Unix Domain sockets but well, shit happens. */
278 flags = fcntl (server, F_GETFL, 0);
279 if (flags == -1)
280 die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
281 if ( fcntl (server, F_SETFL, (flags | O_NONBLOCK)) == -1)
282 die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
285 memset (&srvr_addr, 0, sizeof srvr_addr);
286 srvr_addr.sun_family = AF_LOCAL;
287 strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
288 srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
289 addrlen = (offsetof (struct sockaddr_un, sun_path)
290 + strlen (srvr_addr.sun_path) + 1);
293 again:
294 if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
296 if (errno == EADDRINUSE && force)
298 force = 0;
299 remove (srvr_addr.sun_path);
300 goto again;
302 die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
305 if (listen (server, 5))
306 die ("listen failed: %s\n", strerror (errno));
308 for (;;)
310 fd_set rfds;
311 int max_fd;
312 client_t client;
314 /* Usually we don't have that many connections, thus it is okay
315 to set them allways from scratch and don't maintain an active
316 fd_set. */
317 FD_ZERO (&rfds);
318 FD_SET (server, &rfds);
319 max_fd = server;
320 for (client = client_list; client; client = client->next)
321 if (client->fd != -1)
323 FD_SET (client->fd, &rfds);
324 if (client->fd > max_fd)
325 max_fd = client->fd;
328 if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
329 continue; /* Ignore any errors. */
331 if (FD_ISSET (server, &rfds)) /* New connection. */
333 struct sockaddr_un clnt_addr;
334 int fd;
336 addrlen = sizeof clnt_addr;
337 fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
338 if (fd == -1)
340 printf ("[accepting connection failed: %s]\n", strerror (errno));
342 else if (fd >= FD_SETSIZE)
344 close (fd);
345 printf ("[connection request denied: too many connections]\n");
347 else
349 for (client = client_list; client && client->fd != -1;
350 client = client->next)
352 if (!client)
354 client = xcalloc (1, sizeof *client);
355 client->next = client_list;
356 client_list = client;
358 client->fd = fd;
359 printf ("[client at fd %d connected]\n", client->fd);
362 for (client = client_list; client; client = client->next)
363 if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
365 char line[256];
366 int n;
368 n = read (client->fd, line, sizeof line - 1);
369 if (n < 0)
371 int save_errno = errno;
372 print_line (client, NULL); /* flush */
373 printf ("[client at fd %d read error: %s]\n",
374 client->fd, strerror (save_errno));
375 close (client->fd);
376 client->fd = -1;
378 else if (!n)
380 print_line (client, NULL); /* flush */
381 close (client->fd);
382 printf ("[client at fd %d disconnected]\n", client->fd);
383 client->fd = -1;
385 else
387 line[n] = 0;
388 print_line (client, line);
393 return 0;
398 Local Variables:
399 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
400 End: