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