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