Add simple tool to watch the log output of gnupg and related modules.
[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
32
33 #define PGM "watchgnupg"
34
35 static int verbose;
36 static int debug;
37
38
39 static void
40 die (const char *format, ...)
41 {
42   va_list arg_ptr;
43
44   fflush (stdout);
45   fprintf (stderr, "%s: ", PGM);
46
47   va_start (arg_ptr, format);
48   vfprintf (stderr, format, arg_ptr);
49   va_end (arg_ptr);
50   putc ('\n', stderr);
51
52   exit (1);
53 }
54
55
56 /* static void */
57 /* err (const char *format, ...) */
58 /* { */
59 /*   va_list arg_ptr; */
60
61 /*   fflush (stdout); */
62 /*   fprintf (stderr, "%s: ", PGM); */
63
64 /*   va_start (arg_ptr, format); */
65 /*   vfprintf (stderr, format, arg_ptr); */
66 /*   va_end (arg_ptr); */
67 /*   putc ('\n', stderr); */
68 /* } */
69
70 static void *
71 xmalloc (size_t n)
72 {
73   void *p = malloc (n);
74   if (!p)
75     die ("out of core");
76   return p;
77 }
78
79 static void *
80 xcalloc (size_t n, size_t m)
81 {
82   void *p = calloc (n, m);
83   if (!p)
84     die ("out of core");
85   return p;
86 }
87
88 static void *
89 xrealloc (void *old, size_t n)
90 {
91   void *p = realloc (old, n);
92   if (!p)
93     die ("out of core");
94   return p;
95 }
96     
97
98 struct client_s {
99   struct client_s *next;
100   int fd;
101   size_t size;  /* Allocated size of buffer. */
102   size_t len;   /* Current length of buffer. */
103   unsigned char *buffer; /* Buffer to with data already read. */
104   
105 };
106 typedef struct client_s *client_t;
107
108
109
110 /* Print LINE for the client identified by C.  Calling this function
111    witgh LINE set to NULL, will flush the internal buffer. */
112 static void
113 print_line (client_t c, const char *line)
114 {
115   const char *s;
116   size_t n;
117
118   if (!line)
119     {
120       if (c->buffer && c->len)
121         {
122           printf ("%3d - ", c->fd);
123           fwrite (c->buffer, c->len, 1, stdout); 
124           putc ('\n', stdout);
125           c->len = 0;
126         }
127       return;
128     }
129
130   while ((s = strchr (line, '\n')))
131     {
132       printf ("%3d - ", c->fd);
133       if (c->buffer && c->len)
134         {
135           fwrite (c->buffer, c->len, 1, stdout); 
136           c->len = 0;
137         }
138       fwrite (line, s - line + 1, 1, stdout); 
139       line = s + 1;
140     }
141   n = strlen (line);
142   if (!c->buffer)
143     {
144       c->size = 256 - (n + 256) % 256;
145       c->buffer = xmalloc (c->size);
146       memcpy (c->buffer, line, n);
147       c->len = n;
148     }
149   else 
150     {
151       if (c->len + n > c->size)
152         {
153           c->size += 256 - (n + 256) % 256;
154           c->buffer = xrealloc (c->buffer, c->size);
155         }
156       memcpy (c->buffer + c->len, line, n);
157       c->len += n;
158     }
159 }
160
161
162
163 int 
164 main (int argc, char **argv)
165 {
166   int last_argc = -1;
167   int force = 0;
168
169   struct sockaddr_un srvr_addr;
170   int addrlen;
171   int server;
172   client_t client_list = NULL;
173  
174   if (argc)
175     {
176       argc--; argv++;
177     }
178   while (argc && last_argc != argc )
179     {
180       last_argc = argc;
181       if (!strcmp (*argv, "--help"))
182         {
183           puts (
184                 "usage: " PGM " [options] socketname\n"
185                 "\n"
186                 "       Options are --verbose, --debug and --force");
187           exit (0);
188         }
189       if (!strcmp (*argv, "--verbose"))
190         {
191           verbose = 1;
192           argc--; argv++;
193         }
194       else if (!strcmp (*argv, "--debug"))
195         {
196           verbose = debug = 1;
197           argc--; argv++;
198         }
199       else if (!strcmp (*argv, "--force"))
200         {
201           force = 1;
202           argc--; argv++;
203         }
204     }          
205  
206   if (argc != 1)
207     {
208       die ("usage: " PGM " socketname\n");
209     }
210
211
212   if (verbose)
213     fprintf (stderr, "opening socket `%s'\n", *argv);
214
215   setvbuf (stdout, NULL, _IOLBF, 0);
216
217   server = socket (PF_LOCAL, SOCK_STREAM, 0);
218   if (server == -1)
219     die ("socket() failed: %s\n", strerror (errno));
220
221   memset (&srvr_addr, 0, sizeof srvr_addr);
222   srvr_addr.sun_family = AF_LOCAL;
223   strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
224   srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
225   addrlen = (offsetof (struct sockaddr_un, sun_path)
226              + strlen (srvr_addr.sun_path) + 1);
227
228   
229  again:
230   if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
231     { 
232       if (errno == EADDRINUSE && force)
233         {
234           force = 0;
235           remove (srvr_addr.sun_path);
236           goto again;
237         }
238       die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
239     }
240
241   if (listen (server, 5))
242     die ("listen failed: %s\n", strerror (errno));
243
244   for (;;)
245     {
246       fd_set rfds;
247       int max_fd;
248       client_t client;
249
250       /* Usually we don't have that many connections, thus it is okay
251          to set them al the time from scratch and don't maintain an
252          active fd_set. */
253       FD_ZERO (&rfds);
254       FD_SET (server, &rfds);
255       max_fd = server;
256       for (client = client_list; client; client = client->next)
257         if (client->fd != -1)
258           {
259             FD_SET (client->fd, &rfds);
260             if (client->fd > max_fd)
261               max_fd = client->fd;
262           }
263
264       if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
265         continue;  /* Ignore any errors. */
266
267       if (FD_ISSET (server, &rfds)) /* New connection. */
268         { 
269           struct sockaddr_un clnt_addr;
270           int fd;
271
272           addrlen = sizeof clnt_addr;
273           fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
274           if (fd == -1)
275             {
276               printf ("accepting connection failed: %s\n", strerror (errno));
277             }
278           else if (fd >= FD_SETSIZE)
279             {
280               close (fd);
281               printf ("[connection request denied: too many connections\n");
282             }
283           else 
284             {
285               for (client = client_list; client && client->fd != -1;
286                    client = client->next)
287                 ;
288               if (!client)
289                 {
290                   client = xcalloc (1, sizeof *client);
291                   client->next = client_list;
292                   client_list = client;
293                 }
294               client->fd = fd;
295               printf ("[client at fd %d connected]\n", client->fd);
296             }
297         }
298       for (client = client_list; client; client = client->next)
299         if (client->fd != -1)
300           {
301             char line[256];
302             int n;
303             
304             n = read (client->fd, line, sizeof line - 1);
305             if (n == 1)
306               {
307                 int save_errno = errno;
308                 print_line (client, NULL); /* flush */
309                 printf ("[client at fd %d read error: %s]\n",
310                         client->fd, strerror (save_errno));
311                 close (client->fd);
312                 client->fd = -1;
313               }
314             else if (!n) 
315               {
316                 print_line (client, NULL); /* flush */
317                 close (client->fd);
318                 printf ("[client at fd %d disconnected]\n", client->fd);
319                 client->fd = -1;
320               }
321             else
322               {
323                 line[n] = 0;
324                 print_line (client, line);
325               }
326           }
327     }
328
329   return 0;
330 }
331
332
333 /*
334 Local Variables:
335 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
336 End:
337 */