Fix for bug 537
[gnupg.git] / jnlib / logging.c
1 /* logging.c - Useful logging functions
2  * Copyright (C) 1998, 1999, 2000, 2001, 2003,
3  *               2004, 2005, 2006 Free Software Foundation, Inc.
4  *
5  * This file is part of JNLIB.
6  *
7  * JNLIB is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * JNLIB is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  */
22
23
24 #include <config.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include <stddef.h>
30 #include <errno.h>
31 #include <time.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #ifndef HAVE_W32_SYSTEM
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #endif /*!HAVE_W32_SYSTEM*/
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <assert.h>
41
42
43 #define JNLIB_NEED_LOG_LOGV 1
44 #include "libjnlib-config.h"
45 #include "logging.h"
46
47 #if defined (HAVE_FOPENCOOKIE) ||  defined (HAVE_FUNOPEN)
48 #define USE_FUNWRITER 1
49 #endif
50
51 static FILE *logstream;
52 static int log_socket = -1;
53 static char prefix_buffer[80];
54 static int with_time;
55 static int with_prefix;
56 static int with_pid;
57 static int running_detached;
58 static int force_prefixes;
59
60 static int missing_lf;
61 static int errorcount;
62
63
64 int
65 log_get_errorcount (int clear)
66 {
67     int n = errorcount;
68     if( clear )
69         errorcount = 0;
70     return n;
71 }
72
73 void
74 log_inc_errorcount (void)
75 {
76    errorcount++;
77 }
78
79
80 /* The follwing 3 functions are used by funopen to write logs to a
81    socket. */
82 #ifdef USE_FUNWRITER
83 struct fun_cookie_s {
84   int fd;
85   int quiet;
86   int want_socket;
87   int is_socket;
88   char name[1];
89 };
90
91 /* Write NBYTES of BUFFER to file descriptor FD. */
92 static int
93 writen (int fd, const void *buffer, size_t nbytes)
94 {
95   const char *buf = buffer;
96   size_t nleft = nbytes;
97   int nwritten;
98   
99   while (nleft > 0)
100     {
101       nwritten = write (fd, buf, nleft);
102       if (nwritten < 0 && errno == EINTR)
103         continue;
104       if (nwritten < 0)
105         return -1;
106       nleft -= nwritten;
107       buf = buf + nwritten;
108     }
109   
110   return 0;
111 }
112
113
114 static int 
115 fun_writer (void *cookie_arg, const char *buffer, size_t size)
116 {
117   struct fun_cookie_s *cookie = cookie_arg;
118
119   /* Note that we always try to reconnect to the socket but print
120      error messages only the first time an error occured.  If
121      RUNNING_DETACHED is set we don't fall back to stderr and even do
122      not print any error messages.  This is needed because detached
123      processes often close stderr and by writing to file descriptor 2
124      we might send the log message to a file not intended for logging
125      (e.g. a pipe or network connection). */
126   if (cookie->want_socket && cookie->fd == -1)
127     {
128       /* Not yet open or meanwhile closed due to an error. */
129       cookie->is_socket = 0;
130       cookie->fd = socket (PF_LOCAL, SOCK_STREAM, 0);
131       if (cookie->fd == -1)
132         {
133           if (!cookie->quiet && !running_detached
134               && isatty (fileno (stderr)))
135             fprintf (stderr, "failed to create socket for logging: %s\n",
136                      strerror(errno));
137         }
138       else
139         {
140           struct sockaddr_un addr;
141           size_t addrlen;
142           
143           memset (&addr, 0, sizeof addr);
144           addr.sun_family = PF_LOCAL;
145           strncpy (addr.sun_path, cookie->name, sizeof (addr.sun_path)-1);
146           addr.sun_path[sizeof (addr.sun_path)-1] = 0;
147           addrlen = (offsetof (struct sockaddr_un, sun_path)
148                      + strlen (addr.sun_path) + 1);
149       
150           if (connect (cookie->fd, (struct sockaddr *) &addr, addrlen) == -1)
151             {
152               if (!cookie->quiet && !running_detached
153                   && isatty (fileno (stderr)))
154                 fprintf (stderr, "can't connect to `%s': %s\n",
155                          cookie->name, strerror(errno));
156               close (cookie->fd);
157               cookie->fd = -1;
158             }
159         }
160       
161       if (cookie->fd == -1)
162         {
163           if (!running_detached)
164             {
165               /* Due to all the problems with apps not running
166                  detahced but beeing caled with stderr closed or
167                  used for a different purposes, it does not make
168                  sense to switch to stderr.  We tehrefore disable it. */
169               if (!cookie->quiet)
170                 {
171                   /* fputs ("switching logging to stderr\n", stderr);*/
172                   cookie->quiet = 1;
173                 }
174               cookie->fd = -1; /*fileno (stderr);*/
175             }
176         }
177       else /* Connection has been established. */
178         {
179           cookie->quiet = 0;
180           cookie->is_socket = 1;
181         }
182     }
183
184   log_socket = cookie->fd;
185   if (cookie->fd != -1 && !writen (cookie->fd, buffer, size))
186     return size; /* Okay. */ 
187
188   if (!running_detached && cookie->fd != -1
189       && isatty (fileno (stderr)))
190     {
191       if (*cookie->name)
192         fprintf (stderr, "error writing to `%s': %s\n",
193                  cookie->name, strerror(errno));
194       else
195         fprintf (stderr, "error writing to file descriptor %d: %s\n",
196                  cookie->fd, strerror(errno));
197     }
198   if (cookie->is_socket && cookie->fd != -1)
199     {
200       close (cookie->fd);
201       cookie->fd = -1;
202       log_socket = -1;
203     }
204
205   return size;
206 }
207
208 static int
209 fun_closer (void *cookie_arg)
210 {
211   struct fun_cookie_s *cookie = cookie_arg;
212
213   if (cookie->fd != -1)
214     close (cookie->fd);
215   jnlib_free (cookie);
216   log_socket = -1;
217   return 0;
218 }
219 #endif /*USE_FUNWRITER*/
220
221
222
223 /* Common function to either set the logging to a file or a file
224    descriptor. */
225 static void
226 set_file_fd (const char *name, int fd) 
227 {
228   FILE *fp;
229   int want_socket;
230 #ifdef USE_FUNWRITER
231   struct fun_cookie_s *cookie;
232 #endif
233
234   if (name && !strcmp (name, "-"))
235     {
236       name = NULL;
237       fd = fileno (stderr);
238     }
239
240   if (name)
241     {
242       want_socket = (!strncmp (name, "socket://", 9) && name[9]);
243       if (want_socket)
244         name += 9;
245     }
246   else
247     {
248       want_socket = 0;
249     }
250
251 #ifdef USE_FUNWRITER
252   cookie = jnlib_xmalloc (sizeof *cookie + (name? strlen (name):0));
253   strcpy (cookie->name, name? name:"");
254   cookie->quiet = 0;
255   cookie->is_socket = 0;
256   cookie->want_socket = want_socket;
257   if (!name)
258     cookie->fd = fd;
259   else if (want_socket)
260     cookie->fd = -1;
261   else
262     {
263       do
264         cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT,
265                            (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH));
266       while (cookie->fd == -1 && errno == EINTR);
267     }
268   log_socket = cookie->fd;
269
270 #ifdef HAVE_FOPENCOOKIE
271   {
272     cookie_io_functions_t io = { NULL };
273     io.write = fun_writer;
274     io.close = fun_closer;
275     
276     fp = fopencookie (cookie, "w", io);
277   }
278 #else /*!HAVE_FOPENCOOKIE*/
279   fp = funopen (cookie, NULL, fun_writer, NULL, fun_closer);
280 #endif /*!HAVE_FOPENCOOKIE*/
281
282 #else /*!USE_FUNWRITER*/
283
284   /* The system does not feature custom streams.  Thus fallback to
285      plain stdio. */
286   if (want_socket)
287     {
288       fprintf (stderr, "system does not support logging to a socket - "
289                "using stderr\n");
290       fp = stderr;
291     }
292   else if (name)
293     fp = fopen (name, "a");
294   else if (fd == 1)
295     fp = stdout;
296   else if (fd == 2)
297     fp = stderr;
298   else
299     fp = fdopen (fd, "a");
300
301   log_socket = -1; 
302
303 #endif /*!USE_FUNWRITER*/
304
305   /* On success close the old logstream right now, so that we are
306      really sure it has been closed. */
307   if (fp && logstream)
308     {
309       if (logstream != stderr && logstream != stdout)
310         fclose (logstream);
311       logstream = NULL;
312     }
313       
314   if (!fp)
315     {
316       if (name)
317         fprintf (stderr, "failed to open log file `%s': %s\n",
318                  name, strerror(errno));
319       else
320         fprintf (stderr, "failed to fdopen file descriptor %d: %s\n",
321                  fd, strerror(errno));
322       /* We need to make sure that there is a log stream.  We use stderr. */
323       fp = stderr;
324     }
325   else
326     setvbuf (fp, NULL, _IOLBF, 0);
327   
328   if (logstream && logstream != stderr && logstream != stdout)
329     fclose (logstream);
330   logstream = fp;
331
332   /* We always need to print the prefix and the pid for socket mode,
333      so that the server reading the socket can do something
334      meaningful. */
335   force_prefixes = want_socket;
336
337   missing_lf = 0;
338 }
339
340
341 /* Set the file to write log to.  The special names NULL and "-" may
342    be used to select stderr and names formatted like
343    "socket:///home/foo/mylogs" may be used to write the logging to the
344    socket "/home/foo/mylogs".  If the connection to the socket fails
345    or a write error is detected, the function writes to stderr and
346    tries the next time again to connect the socket.
347   */
348 void
349 log_set_file (const char *name) 
350 {
351   set_file_fd (name? name: "-", -1);
352 }
353
354 void
355 log_set_fd (int fd)
356 {
357   set_file_fd (NULL, fd);
358 }
359
360
361 void
362 log_set_prefix (const char *text, unsigned int flags)
363 {
364   if (text)
365     {
366       strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
367       prefix_buffer[sizeof (prefix_buffer)-1] = 0;
368     }
369   
370   with_prefix = (flags & JNLIB_LOG_WITH_PREFIX);
371   with_time = (flags & JNLIB_LOG_WITH_TIME);
372   with_pid  = (flags & JNLIB_LOG_WITH_PID);
373   running_detached = (flags & JNLIB_LOG_RUN_DETACHED);
374 }
375
376
377 const char *
378 log_get_prefix (unsigned int *flags)
379 {
380   if (flags)
381     {
382       *flags = 0;
383       if (with_prefix)
384         *flags |= JNLIB_LOG_WITH_PREFIX;
385       if (with_time)
386         *flags |= JNLIB_LOG_WITH_TIME;
387       if (with_pid)
388         *flags |= JNLIB_LOG_WITH_PID;
389       if (running_detached)
390         *flags |= JNLIB_LOG_RUN_DETACHED;
391     }
392   return prefix_buffer;
393 }
394
395 /* This function returns true if the file descriptor FD is in use for
396    logging.  This is preferable over a test using log_get_fd in that
397    it allows the logging code to use more then one file descriptor. */
398 int
399 log_test_fd (int fd)
400 {
401   if (logstream)
402     {
403       int tmp = fileno (logstream);
404       if ( tmp != -1 && tmp == fd)
405         return 1;
406     }
407   if (log_socket != -1 && log_socket == fd)
408     return 1;
409   return 0;
410 }
411
412 int
413 log_get_fd ()
414 {
415   return fileno(logstream?logstream:stderr);
416 }
417
418 FILE *
419 log_get_stream ()
420 {
421   /* FIXME: We should not return stderr here but initialize the log
422      stream properly.  This might break more things than using stderr,
423      though */
424   return logstream?logstream:stderr;
425 }
426
427 static void
428 do_logv (int level, const char *fmt, va_list arg_ptr)
429 {
430   if (!logstream)
431     {
432       log_set_file (NULL); /* Make sure a log stream has been set.  */
433       assert (logstream);
434     }
435
436   if (missing_lf && level != JNLIB_LOG_CONT)
437     putc('\n', logstream );
438   missing_lf = 0;
439
440   if (level != JNLIB_LOG_CONT)
441     { /* Note this does not work for multiple line logging as we would
442        * need to print to a buffer first */
443       if (with_time && !force_prefixes)
444         {
445           struct tm *tp;
446           time_t atime = time (NULL);
447           
448           tp = localtime (&atime);
449           fprintf (logstream, "%04d-%02d-%02d %02d:%02d:%02d ",
450                    1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
451                    tp->tm_hour, tp->tm_min, tp->tm_sec );
452         }
453       if (with_prefix || force_prefixes)
454         fputs (prefix_buffer, logstream);
455       if (with_pid || force_prefixes)
456         fprintf (logstream, "[%u]", (unsigned int)getpid ());
457       if (!with_time || force_prefixes)
458         putc (':', logstream);
459       /* A leading backspace suppresses the extra space so that we can
460          correctly output, programname, filename and linenumber. */
461       if (fmt && *fmt == '\b')
462         fmt++;
463       else
464         putc (' ', logstream);
465     }
466
467   switch (level)
468     {
469     case JNLIB_LOG_BEGIN: break;
470     case JNLIB_LOG_CONT: break;
471     case JNLIB_LOG_INFO: break;
472     case JNLIB_LOG_WARN: break;
473     case JNLIB_LOG_ERROR: break;
474     case JNLIB_LOG_FATAL: fputs("Fatal: ",logstream ); break;
475     case JNLIB_LOG_BUG: fputs("Ohhhh jeeee: ", logstream); break;
476     case JNLIB_LOG_DEBUG: fputs("DBG: ", logstream ); break;
477     default: fprintf(logstream,"[Unknown log level %d]: ", level ); break;
478     }
479
480
481   if (fmt)
482     {
483       vfprintf(logstream,fmt,arg_ptr) ;
484       if (*fmt && fmt[strlen(fmt)-1] != '\n')
485         missing_lf = 1;
486     }
487
488   if (level == JNLIB_LOG_FATAL)
489     {
490       if (missing_lf)
491         putc('\n', logstream );
492       exit(2);
493     }
494   if (level == JNLIB_LOG_BUG)
495     {
496       if (missing_lf)
497         putc('\n', logstream );
498       abort();
499     }
500 }
501
502 static void
503 do_log( int level, const char *fmt, ... )
504 {
505     va_list arg_ptr ;
506
507     va_start( arg_ptr, fmt ) ;
508     do_logv( level, fmt, arg_ptr );
509     va_end(arg_ptr);
510 }
511
512
513 void
514 log_logv (int level, const char *fmt, va_list arg_ptr)
515 {
516   do_logv (level, fmt, arg_ptr);
517 }
518
519 void
520 log_info( const char *fmt, ... )
521 {
522     va_list arg_ptr ;
523
524     va_start( arg_ptr, fmt ) ;
525     do_logv( JNLIB_LOG_INFO, fmt, arg_ptr );
526     va_end(arg_ptr);
527 }
528
529 void
530 log_error( const char *fmt, ... )
531 {
532     va_list arg_ptr ;
533
534     va_start( arg_ptr, fmt ) ;
535     do_logv( JNLIB_LOG_ERROR, fmt, arg_ptr );
536     va_end(arg_ptr);
537     /* protect against counter overflow */
538     if( errorcount < 30000 )
539         errorcount++;
540 }
541
542
543 void
544 log_fatal( const char *fmt, ... )
545 {
546     va_list arg_ptr ;
547
548     va_start( arg_ptr, fmt ) ;
549     do_logv( JNLIB_LOG_FATAL, fmt, arg_ptr );
550     va_end(arg_ptr);
551     abort(); /* never called, but it makes the compiler happy */
552 }
553
554 void
555 log_bug( const char *fmt, ... )
556 {
557     va_list arg_ptr ;
558
559     va_start( arg_ptr, fmt ) ;
560     do_logv( JNLIB_LOG_BUG, fmt, arg_ptr );
561     va_end(arg_ptr);
562     abort(); /* never called, but it makes the compiler happy */
563 }
564
565 void
566 log_debug( const char *fmt, ... )
567 {
568     va_list arg_ptr ;
569
570     va_start( arg_ptr, fmt ) ;
571     do_logv( JNLIB_LOG_DEBUG, fmt, arg_ptr );
572     va_end(arg_ptr);
573 }
574
575
576 void
577 log_printf (const char *fmt, ...)
578 {
579   va_list arg_ptr;
580
581   va_start (arg_ptr, fmt);
582   do_logv (fmt ? JNLIB_LOG_CONT : JNLIB_LOG_BEGIN, fmt, arg_ptr);
583   va_end (arg_ptr);
584 }
585
586 /* Print a hexdump of BUFFER.  With TEXT of NULL print just the raw
587    dump, with TEXT just an empty string, print a trailing linefeed,
588    otherwise print an entire debug line. */
589 void
590 log_printhex (const char *text, const void *buffer, size_t length)
591 {
592   if (text && *text)
593     log_debug ("%s ", text);
594   if (length)
595     {
596       const unsigned char *p = buffer;
597       log_printf ("%02X", *p);
598       for (length--, p++; length--; p++)
599         log_printf (" %02X", *p);
600     }
601   if (text)
602     log_printf ("\n");
603 }
604
605
606 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
607 void
608 bug_at( const char *file, int line, const char *func )
609 {
610     do_log( JNLIB_LOG_BUG,
611              ("... this is a bug (%s:%d:%s)\n"), file, line, func );
612     abort(); /* never called, but it makes the compiler happy */
613 }
614 #else
615 void
616 bug_at( const char *file, int line )
617 {
618     do_log( JNLIB_LOG_BUG,
619              _("you found a bug ... (%s:%d)\n"), file, line);
620     abort(); /* never called, but it makes the compiler happy */
621 }
622 #endif
623