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