dfb11f2de1a4a9c174bce007ef435dc4a303f72d
[gnupg.git] / tools / watchgnupg.c
1 /* watchgnupg.c - Socket server for GnuPG logs
2  *      Copyright (C) 2003 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 2 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <fcntl.h>
32 #include <time.h>
33
34 #define PGM "watchgnupg"
35
36 static int verbose;
37 static int debug;
38
39
40 static void
41 die (const char *format, ...)
42 {
43   va_list arg_ptr;
44
45   fflush (stdout);
46   fprintf (stderr, "%s: ", PGM);
47
48   va_start (arg_ptr, format);
49   vfprintf (stderr, format, arg_ptr);
50   va_end (arg_ptr);
51   putc ('\n', stderr);
52
53   exit (1);
54 }
55
56
57 /* static void */
58 /* err (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
71 static void *
72 xmalloc (size_t n)
73 {
74   void *p = malloc (n);
75   if (!p)
76     die ("out of core");
77   return p;
78 }
79
80 static void *
81 xcalloc (size_t n, size_t m)
82 {
83   void *p = calloc (n, m);
84   if (!p)
85     die ("out of core");
86   return p;
87 }
88
89 static void *
90 xrealloc (void *old, size_t n)
91 {
92   void *p = realloc (old, n);
93   if (!p)
94     die ("out of core");
95   return p;
96 }
97     
98
99 struct client_s {
100   struct client_s *next;
101   int fd;
102   size_t size;  /* Allocated size of buffer. */
103   size_t len;   /* Current length of buffer. */
104   unsigned char *buffer; /* Buffer to with data already read. */
105   
106 };
107 typedef struct client_s *client_t;
108
109
110
111 static void
112 print_fd_and_time (int fd)
113 {
114   struct tm *tp;
115   time_t atime = time (NULL);
116   
117   tp = localtime (&atime);
118   printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
119           fd,
120           1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
121           tp->tm_hour, tp->tm_min, tp->tm_sec );
122 }
123
124
125 /* Print LINE for the client identified by C.  Calling this function
126    witgh LINE set to NULL, will flush the internal buffer. */
127 static void
128 print_line (client_t c, const char *line)
129 {
130   const char *s;
131   size_t n;
132
133   if (!line)
134     {
135       if (c->buffer && c->len)
136         {
137           print_fd_and_time (c->fd);
138           fwrite (c->buffer, c->len, 1, stdout); 
139           putc ('\n', stdout);
140           c->len = 0;
141         }
142       return;
143     }
144
145   while ((s = strchr (line, '\n')))
146     {
147       print_fd_and_time (c->fd);
148       if (c->buffer && c->len)
149         {
150           fwrite (c->buffer, c->len, 1, stdout); 
151           c->len = 0;
152         }
153       fwrite (line, s - line + 1, 1, stdout); 
154       line = s + 1;
155     }
156   n = strlen (line);
157   if (n)
158     {
159       if (c->len + n >= c->size)
160         {
161           c->size += ((n + 255) & ~255);
162           c->buffer = (c->buffer
163                        ? xrealloc (c->buffer, c->size)
164                        : xmalloc (c->size));
165         }
166       memcpy (c->buffer + c->len, line, n);
167       c->len += n;
168     }
169 }
170
171
172
173 int 
174 main (int argc, char **argv)
175 {
176   int last_argc = -1;
177   int force = 0;
178
179   struct sockaddr_un srvr_addr;
180   int addrlen;
181   int server;
182   int flags;
183   client_t client_list = NULL;
184  
185   if (argc)
186     {
187       argc--; argv++;
188     }
189   while (argc && last_argc != argc )
190     {
191       last_argc = argc;
192       if (!strcmp (*argv, "--help"))
193         {
194           puts (
195                 "usage: " PGM " [options] socketname\n"
196                 "\n"
197                 "       Options are --verbose, --debug and --force");
198           exit (0);
199         }
200       if (!strcmp (*argv, "--verbose"))
201         {
202           verbose = 1;
203           argc--; argv++;
204         }
205       else if (!strcmp (*argv, "--debug"))
206         {
207           verbose = debug = 1;
208           argc--; argv++;
209         }
210       else if (!strcmp (*argv, "--force"))
211         {
212           force = 1;
213           argc--; argv++;
214         }
215     }          
216  
217   if (argc != 1)
218     {
219       die ("usage: " PGM " socketname\n");
220     }
221
222
223   if (verbose)
224     fprintf (stderr, "opening socket `%s'\n", *argv);
225
226   setvbuf (stdout, NULL, _IOLBF, 0);
227
228   server = socket (PF_LOCAL, SOCK_STREAM, 0);
229   if (server == -1)
230     die ("socket() failed: %s\n", strerror (errno));
231
232   /* We better set the listening socket to non-blocking so that we
233      don't get bitten by race conditions in accept.  The should not
234      happen for Unix Domain sockets but well, shit happens. */
235   flags = fcntl (server, F_GETFL, 0);
236   if (flags == -1)
237     die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
238   if ( fcntl (server, F_SETFL, (flags | O_NONBLOCK)) == -1)
239     die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
240   
241
242   memset (&srvr_addr, 0, sizeof srvr_addr);
243   srvr_addr.sun_family = AF_LOCAL;
244   strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
245   srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
246   addrlen = (offsetof (struct sockaddr_un, sun_path)
247              + strlen (srvr_addr.sun_path) + 1);
248
249   
250  again:
251   if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
252     { 
253       if (errno == EADDRINUSE && force)
254         {
255           force = 0;
256           remove (srvr_addr.sun_path);
257           goto again;
258         }
259       die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
260     }
261
262   if (listen (server, 5))
263     die ("listen failed: %s\n", strerror (errno));
264
265   for (;;)
266     {
267       fd_set rfds;
268       int max_fd;
269       client_t client;
270
271       /* Usually we don't have that many connections, thus it is okay
272          to set them allways from scratch and don't maintain an active
273          fd_set. */
274       FD_ZERO (&rfds);
275       FD_SET (server, &rfds);
276       max_fd = server;
277       for (client = client_list; client; client = client->next)
278         if (client->fd != -1)
279           {
280             FD_SET (client->fd, &rfds);
281             if (client->fd > max_fd)
282               max_fd = client->fd;
283           }
284
285       if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
286         continue;  /* Ignore any errors. */
287
288       if (FD_ISSET (server, &rfds)) /* New connection. */
289         { 
290           struct sockaddr_un clnt_addr;
291           int fd;
292
293           addrlen = sizeof clnt_addr;
294           fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
295           if (fd == -1)
296             {
297               printf ("[accepting connection failed: %s]\n", strerror (errno));
298             }
299           else if (fd >= FD_SETSIZE)
300             {
301               close (fd);
302               printf ("[connection request denied: too many connections]\n");
303             }
304           else 
305             {
306               for (client = client_list; client && client->fd != -1;
307                    client = client->next)
308                 ;
309               if (!client)
310                 {
311                   client = xcalloc (1, sizeof *client);
312                   client->next = client_list;
313                   client_list = client;
314                 }
315               client->fd = fd;
316               printf ("[client at fd %d connected]\n", client->fd);
317             }
318         }
319       for (client = client_list; client; client = client->next)
320         if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
321           {
322             char line[256];
323             int n;
324             
325             n = read (client->fd, line, sizeof line - 1);
326             if (n == 1)
327               {
328                 int save_errno = errno;
329                 print_line (client, NULL); /* flush */
330                 printf ("[client at fd %d read error: %s]\n",
331                         client->fd, strerror (save_errno));
332                 close (client->fd);
333                 client->fd = -1;
334               }
335             else if (!n) 
336               {
337                 print_line (client, NULL); /* flush */
338                 close (client->fd);
339                 printf ("[client at fd %d disconnected]\n", client->fd);
340                 client->fd = -1;
341               }
342             else
343               {
344                 line[n] = 0;
345                 print_line (client, line);
346               }
347           }
348     }
349
350   return 0;
351 }
352
353
354 /*
355 Local Variables:
356 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
357 End:
358 */