print the time.
[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 <time.h>
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 static void
111 print_fd_and_time (int fd)
112 {
113   struct tm *tp;
114   time_t atime = time (NULL);
115   
116   tp = localtime (&atime);
117   printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
118           fd,
119           1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
120           tp->tm_hour, tp->tm_min, tp->tm_sec );
121 }
122
123
124 /* Print LINE for the client identified by C.  Calling this function
125    witgh LINE set to NULL, will flush the internal buffer. */
126 static void
127 print_line (client_t c, const char *line)
128 {
129   const char *s;
130   size_t n;
131
132   if (!line)
133     {
134       if (c->buffer && c->len)
135         {
136           print_fd_and_time (c->fd);
137           fwrite (c->buffer, c->len, 1, stdout); 
138           putc ('\n', stdout);
139           c->len = 0;
140         }
141       return;
142     }
143
144   while ((s = strchr (line, '\n')))
145     {
146       print_fd_and_time (c->fd);
147       if (c->buffer && c->len)
148         {
149           fwrite (c->buffer, c->len, 1, stdout); 
150           c->len = 0;
151         }
152       fwrite (line, s - line + 1, 1, stdout); 
153       line = s + 1;
154     }
155   n = strlen (line);
156   if (n)
157     {
158       if (c->len + n >= c->size)
159         {
160           c->size += ((n + 255) & ~255);
161           c->buffer = (c->buffer
162                        ? xrealloc (c->buffer, c->size)
163                        : xmalloc (c->size));
164         }
165       memcpy (c->buffer + c->len, line, n);
166       c->len += n;
167     }
168 }
169
170
171
172 int 
173 main (int argc, char **argv)
174 {
175   int last_argc = -1;
176   int force = 0;
177
178   struct sockaddr_un srvr_addr;
179   int addrlen;
180   int server;
181   client_t client_list = NULL;
182  
183   if (argc)
184     {
185       argc--; argv++;
186     }
187   while (argc && last_argc != argc )
188     {
189       last_argc = argc;
190       if (!strcmp (*argv, "--help"))
191         {
192           puts (
193                 "usage: " PGM " [options] socketname\n"
194                 "\n"
195                 "       Options are --verbose, --debug and --force");
196           exit (0);
197         }
198       if (!strcmp (*argv, "--verbose"))
199         {
200           verbose = 1;
201           argc--; argv++;
202         }
203       else if (!strcmp (*argv, "--debug"))
204         {
205           verbose = debug = 1;
206           argc--; argv++;
207         }
208       else if (!strcmp (*argv, "--force"))
209         {
210           force = 1;
211           argc--; argv++;
212         }
213     }          
214  
215   if (argc != 1)
216     {
217       die ("usage: " PGM " socketname\n");
218     }
219
220
221   if (verbose)
222     fprintf (stderr, "opening socket `%s'\n", *argv);
223
224   setvbuf (stdout, NULL, _IOLBF, 0);
225
226   server = socket (PF_LOCAL, SOCK_STREAM, 0);
227   if (server == -1)
228     die ("socket() failed: %s\n", strerror (errno));
229
230   memset (&srvr_addr, 0, sizeof srvr_addr);
231   srvr_addr.sun_family = AF_LOCAL;
232   strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
233   srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
234   addrlen = (offsetof (struct sockaddr_un, sun_path)
235              + strlen (srvr_addr.sun_path) + 1);
236
237   
238  again:
239   if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
240     { 
241       if (errno == EADDRINUSE && force)
242         {
243           force = 0;
244           remove (srvr_addr.sun_path);
245           goto again;
246         }
247       die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
248     }
249
250   if (listen (server, 5))
251     die ("listen failed: %s\n", strerror (errno));
252
253   for (;;)
254     {
255       fd_set rfds;
256       int max_fd;
257       client_t client;
258
259       /* Usually we don't have that many connections, thus it is okay
260          to set them al the time from scratch and don't maintain an
261          active fd_set. */
262       FD_ZERO (&rfds);
263       FD_SET (server, &rfds);
264       max_fd = server;
265       for (client = client_list; client; client = client->next)
266         if (client->fd != -1)
267           {
268             FD_SET (client->fd, &rfds);
269             if (client->fd > max_fd)
270               max_fd = client->fd;
271           }
272
273       if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
274         continue;  /* Ignore any errors. */
275
276       if (FD_ISSET (server, &rfds)) /* New connection. */
277         { 
278           struct sockaddr_un clnt_addr;
279           int fd;
280
281           addrlen = sizeof clnt_addr;
282           fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
283           if (fd == -1)
284             {
285               printf ("accepting connection failed: %s\n", strerror (errno));
286             }
287           else if (fd >= FD_SETSIZE)
288             {
289               close (fd);
290               printf ("[connection request denied: too many connections\n");
291             }
292           else 
293             {
294               for (client = client_list; client && client->fd != -1;
295                    client = client->next)
296                 ;
297               if (!client)
298                 {
299                   client = xcalloc (1, sizeof *client);
300                   client->next = client_list;
301                   client_list = client;
302                 }
303               client->fd = fd;
304               printf ("[client at fd %d connected]\n", client->fd);
305             }
306         }
307       for (client = client_list; client; client = client->next)
308         if (client->fd != -1)
309           {
310             char line[256];
311             int n;
312             
313             n = read (client->fd, line, sizeof line - 1);
314             if (n == 1)
315               {
316                 int save_errno = errno;
317                 print_line (client, NULL); /* flush */
318                 printf ("[client at fd %d read error: %s]\n",
319                         client->fd, strerror (save_errno));
320                 close (client->fd);
321                 client->fd = -1;
322               }
323             else if (!n) 
324               {
325                 print_line (client, NULL); /* flush */
326                 close (client->fd);
327                 printf ("[client at fd %d disconnected]\n", client->fd);
328                 client->fd = -1;
329               }
330             else
331               {
332                 line[n] = 0;
333                 print_line (client, line);
334               }
335           }
336     }
337
338   return 0;
339 }
340
341
342 /*
343 Local Variables:
344 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
345 End:
346 */