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