Support DSA2.
[gnupg.git] / tools / watchgnupg.c
1 /* watchgnupg.c - Socket server for GnuPG logs
2  *      Copyright (C) 2003, 2004 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 <fcntl.h>
34 #include <time.h>
35
36 #define PGM "watchgnupg"
37
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
46
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
57
58
59 static int verbose;
60
61
62 static void
63 die (const char *format, ...)
64 {
65   va_list arg_ptr;
66
67   fflush (stdout);
68   fprintf (stderr, "%s: ", PGM);
69
70   va_start (arg_ptr, format);
71   vfprintf (stderr, format, arg_ptr);
72   va_end (arg_ptr);
73   putc ('\n', stderr);
74
75   exit (1);
76 }
77
78
79 /* static void */
80 /* err (const char *format, ...) */
81 /* { */
82 /*   va_list arg_ptr; */
83
84 /*   fflush (stdout); */
85 /*   fprintf (stderr, "%s: ", PGM); */
86
87 /*   va_start (arg_ptr, format); */
88 /*   vfprintf (stderr, format, arg_ptr); */
89 /*   va_end (arg_ptr); */
90 /*   putc ('\n', stderr); */
91 /* } */
92
93 static void *
94 xmalloc (size_t n)
95 {
96   void *p = malloc (n);
97   if (!p)
98     die ("out of core");
99   return p;
100 }
101
102 static void *
103 xcalloc (size_t n, size_t m)
104 {
105   void *p = calloc (n, m);
106   if (!p)
107     die ("out of core");
108   return p;
109 }
110
111 static void *
112 xrealloc (void *old, size_t n)
113 {
114   void *p = realloc (old, n);
115   if (!p)
116     die ("out of core");
117   return p;
118 }
119     
120
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. */
127   
128 };
129 typedef struct client_s *client_t;
130
131
132
133 static void
134 print_fd_and_time (int fd)
135 {
136   struct tm *tp;
137   time_t atime = time (NULL);
138   
139   tp = localtime (&atime);
140   printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
141           fd,
142           1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
143           tp->tm_hour, tp->tm_min, tp->tm_sec );
144 }
145
146
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)
151 {
152   const char *s;
153   size_t n;
154
155   if (!line)
156     {
157       if (c->buffer && c->len)
158         {
159           print_fd_and_time (c->fd);
160           fwrite (c->buffer, c->len, 1, stdout); 
161           putc ('\n', stdout);
162           c->len = 0;
163         }
164       return;
165     }
166
167   while ((s = strchr (line, '\n')))
168     {
169       print_fd_and_time (c->fd);
170       if (c->buffer && c->len)
171         {
172           fwrite (c->buffer, c->len, 1, stdout); 
173           c->len = 0;
174         }
175       fwrite (line, s - line + 1, 1, stdout); 
176       line = s + 1;
177     }
178   n = strlen (line);
179   if (n)
180     {
181       if (c->len + n >= c->size)
182         {
183           c->size += ((n + 255) & ~255);
184           c->buffer = (c->buffer
185                        ? xrealloc (c->buffer, c->size)
186                        : xmalloc (c->size));
187         }
188       memcpy (c->buffer + c->len, line, n);
189       c->len += n;
190     }
191 }
192
193
194 static void
195 print_version (int with_help)
196 {
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);
203         
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 );
214   
215   exit (0);
216 }
217
218 int 
219 main (int argc, char **argv)
220 {
221   int last_argc = -1;
222   int force = 0;
223
224   struct sockaddr_un srvr_addr;
225   socklen_t addrlen;
226   int server;
227   int flags;
228   client_t client_list = NULL;
229  
230   if (argc)
231     {
232       argc--; argv++;
233     }
234   while (argc && last_argc != argc )
235     {
236       last_argc = argc;
237       if (!strcmp (*argv, "--"))
238         {
239           argc--; argv++;
240           break;
241         }
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"))
247         {
248           verbose = 1;
249           argc--; argv++;
250         }
251       else if (!strcmp (*argv, "--force"))
252         {
253           force = 1;
254           argc--; argv++;
255         }
256     }          
257  
258   if (argc != 1)
259     {
260       fprintf (stderr, "usage: " PGM " socketname\n");
261       exit (1);
262     }
263
264
265   if (verbose)
266     fprintf (stderr, "opening socket `%s'\n", *argv);
267
268   setvbuf (stdout, NULL, _IOLBF, 0);
269
270   server = socket (PF_LOCAL, SOCK_STREAM, 0);
271   if (server == -1)
272     die ("socket() failed: %s\n", strerror (errno));
273
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));
282   
283
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);
290
291   
292  again:
293   if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
294     { 
295       if (errno == EADDRINUSE && force)
296         {
297           force = 0;
298           remove (srvr_addr.sun_path);
299           goto again;
300         }
301       die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
302     }
303
304   if (listen (server, 5))
305     die ("listen failed: %s\n", strerror (errno));
306
307   for (;;)
308     {
309       fd_set rfds;
310       int max_fd;
311       client_t client;
312
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)
321           {
322             FD_SET (client->fd, &rfds);
323             if (client->fd > max_fd)
324               max_fd = client->fd;
325           }
326
327       if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
328         continue;  /* Ignore any errors. */
329
330       if (FD_ISSET (server, &rfds)) /* New connection. */
331         { 
332           struct sockaddr_un clnt_addr;
333           int fd;
334
335           addrlen = sizeof clnt_addr;
336           fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
337           if (fd == -1)
338             {
339               printf ("[accepting connection failed: %s]\n", strerror (errno));
340             }
341           else if (fd >= FD_SETSIZE)
342             {
343               close (fd);
344               printf ("[connection request denied: too many connections]\n");
345             }
346           else 
347             {
348               for (client = client_list; client && client->fd != -1;
349                    client = client->next)
350                 ;
351               if (!client)
352                 {
353                   client = xcalloc (1, sizeof *client);
354                   client->next = client_list;
355                   client_list = client;
356                 }
357               client->fd = fd;
358               printf ("[client at fd %d connected]\n", client->fd);
359             }
360         }
361       for (client = client_list; client; client = client->next)
362         if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
363           {
364             char line[256];
365             int n;
366             
367             n = read (client->fd, line, sizeof line - 1);
368             if (n < 0)
369               {
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;
376               }
377             else if (!n) 
378               {
379                 print_line (client, NULL); /* flush */
380                 close (client->fd);
381                 printf ("[client at fd %d disconnected]\n", client->fd);
382                 client->fd = -1;
383               }
384             else
385               {
386                 line[n] = 0;
387                 print_line (client, line);
388               }
389           }
390     }
391
392   return 0;
393 }
394
395
396 /*
397 Local Variables:
398 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
399 End:
400 */