minor cleanups
[gnupg.git] / jnlib / logging.c
1 /* logging.c -  useful logging functions
2  * Copyright (C) 1998, 1999, 2000, 2001, 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
22 /* This file should replace logger.c in the future - for now it is not
23  * used by GnuPG but by GPA.
24  * It is a quite simple implemenation but sufficient for most purposes.
25  */
26
27 #include <config.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <stddef.h>
33 #include <errno.h>
34 #include <time.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38 #include <unistd.h>
39 #ifdef __MINGW32__
40 #  include <io.h>
41 #endif
42
43 #define JNLIB_NEED_LOG_LOGV 1
44 #include "libjnlib-config.h"
45 #include "logging.h"
46
47
48 static FILE *logstream;
49 static char prefix_buffer[80];
50 static int with_time;
51 static int with_prefix;
52 static int with_pid;
53 static int force_prefixes;
54
55 static int missing_lf;
56 static int errorcount;
57
58 #if 0
59 static void
60 write2stderr( const char *s )
61 {
62     write( 2, s, strlen(s) );
63 }
64
65
66 static void
67 do_die(int rc, const char *text )
68 {
69     write2stderr("\nFatal error: ");
70     write2stderr(text);
71     write2stderr("\n");
72     abort();
73 }
74 #endif
75
76 int
77 log_get_errorcount (int clear)
78 {
79     int n = errorcount;
80     if( clear )
81         errorcount = 0;
82     return n;
83 }
84
85 void
86 log_inc_errorcount (void)
87 {
88    errorcount++;
89 }
90
91
92 /* The follwing 3 functions are used by funopen to write logs to a
93    socket. */
94 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
95 struct fun_cookie_s {
96   int fd;
97   int quiet;
98   char name[1];
99 };
100
101 /* Write NBYTES of BUF to file descriptor FD. */
102 static int
103 writen (int fd, const unsigned char *buf, size_t nbytes)
104 {
105   size_t nleft = nbytes;
106   int nwritten;
107   
108   while (nleft > 0)
109     {
110       nwritten = write (fd, buf, nleft);
111       if (nwritten < 0 && errno == EINTR)
112         continue;
113       if (nwritten < 0)
114         return -1;
115       nleft -= nwritten;
116       buf = buf + nwritten;
117     }
118   
119   return 0;
120 }
121
122
123 static int 
124 fun_writer (void *cookie_arg, const char *buffer, size_t size)
125 {
126   struct fun_cookie_s *cookie = cookie_arg;
127
128   /* Note that we always try to reconnect to the socket but print error
129      messages only the first time an error occured. */
130   if (cookie->fd == -1 )
131     {
132       /* Note yet open or meanwhile closed due to an error. */
133       struct sockaddr_un addr;
134       size_t addrlen;
135
136       cookie->fd = socket (PF_LOCAL, SOCK_STREAM, 0);
137       if (cookie->fd == -1)
138         {
139           if (!cookie->quiet)
140             fprintf (stderr, "failed to create socket for logging: %s\n",
141                      strerror(errno));
142           goto failure;
143         }
144       
145       memset (&addr, 0, sizeof addr);
146       addr.sun_family = PF_LOCAL;
147       strncpy (addr.sun_path, cookie->name, sizeof (addr.sun_path)-1);
148       addr.sun_path[sizeof (addr.sun_path)-1] = 0;
149       addrlen = (offsetof (struct sockaddr_un, sun_path)
150                  + strlen (addr.sun_path) + 1);
151       
152       if (connect (cookie->fd, (struct sockaddr *) &addr, addrlen) == -1)
153         {
154           if (!cookie->quiet)
155             fprintf (stderr, "can't connect to `%s': %s\n",
156                      cookie->name, strerror(errno));
157           close (cookie->fd);
158           cookie->fd = -1;
159           goto failure;
160         }
161       /* Connection established. */
162       cookie->quiet = 0;
163     }
164   
165   if (!writen (cookie->fd, buffer, size))
166     return size; /* Okay. */ 
167
168   fprintf (stderr, "error writing to `%s': %s\n",
169            cookie->name, strerror(errno));
170   close (cookie->fd);
171   cookie->fd = -1;
172
173  failure: 
174   if (!cookie->quiet)
175     {
176       fputs ("switching logging to stderr\n", stderr);
177       cookie->quiet = 1;
178     }
179
180   fwrite (buffer, size, 1, stderr);
181   return size;
182 }
183
184 static int
185 fun_closer (void *cookie_arg)
186 {
187   struct fun_cookie_s *cookie = cookie_arg;
188
189   if (cookie->fd != -1)
190     close (cookie->fd);
191   jnlib_free (cookie);
192   return 0;
193 }
194 #endif /* HAVE_FOPENCOOKIE || HAVE_FUNOPEN */
195
196
197
198
199 /* Set the file to write log to.  The sepcial names NULL and "_" may
200    be used to select stderr and names formatted like
201    "socket:///home/foo/mylogs" may be used to write the logging to the
202    socket "/home/foo/mylogs".  If the connection to the socket fails
203    or a write error is detected, the function writes to stderr and
204    tries the next time again to connect the socket.
205   */
206 void
207 log_set_file (const char *name) 
208 {
209   FILE *fp;
210
211   force_prefixes = 0;
212   if (name && !strncmp (name, "socket://", 9) && name[9])
213     {
214 #if defined (HAVE_FOPENCOOKIE)||  defined (HAVE_FUNOPEN)
215       struct fun_cookie_s *cookie;
216
217       cookie = jnlib_xmalloc (sizeof *cookie + strlen (name+9));
218       cookie->fd = -1;
219       cookie->quiet = 0;
220       strcpy (cookie->name, name+9);
221
222 #ifdef HAVE_FOPENCOOKIE
223       {
224         cookie_io_functions_t io = { NULL };
225         io.write = fun_writer;
226         io.close = fun_closer;
227
228         fp = fopencookie (cookie, "w", io);
229       }
230 #else /*!HAVE_FOPENCOOKIE*/
231       {
232         fp = funopen (cookie, NULL, fun_writer, NULL, fun_closer);
233       }
234 #endif /*!HAVE_FOPENCOOKIE*/
235 #else /* Neither fopencookie nor funopen. */
236       {
237         fprintf (stderr, "system does not support logging to a socket - "
238                  "using stderr\n");
239         fp = stderr;
240       }
241 #endif /* Neither fopencookie nor funopen. */
242
243       /* We always need to print the prefix and the pid, so that the
244          server reading the socket can do something meanigful. */
245       force_prefixes = 1;
246     }
247   else
248     fp = (name && strcmp(name,"-"))? fopen (name, "a") : stderr;
249   if (!fp)
250     {
251       fprintf (stderr, "failed to open log file `%s': %s\n",
252                name? name:"[stderr]", strerror(errno));
253       return;
254     }
255   setvbuf (fp, NULL, _IOLBF, 0);
256   
257   if (logstream && logstream != stderr && logstream != stdout)
258     fclose (logstream);
259   logstream = fp;
260   missing_lf = 0;
261 }
262
263
264 void
265 log_set_fd (int fd)
266 {
267   FILE *fp;
268
269   force_prefixes = 0;
270   if (fd == 1)
271     fp = stdout;
272   else if (fd == 2)
273     fp = stderr;
274   else
275     fp = fdopen (fd, "a");
276   if (!fp)
277     {
278       fprintf (stderr, "failed to fdopen log fd %d: %s\n",
279                fd, strerror(errno));
280       return;
281     }
282   setvbuf (fp, NULL, _IOLBF, 0);
283   
284   if (logstream && logstream != stderr && logstream != stdout)
285     fclose( logstream);
286   logstream = fp;
287   missing_lf = 0;
288 }
289
290
291 void
292 log_set_prefix (const char *text, unsigned int flags)
293 {
294   if (text)
295     {
296       strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
297       prefix_buffer[sizeof (prefix_buffer)-1] = 0;
298     }
299   
300   with_prefix = (flags & 1);
301   with_time = (flags & 2);
302   with_pid  = (flags & 4);
303 }
304
305
306 const char *
307 log_get_prefix (unsigned int *flags)
308 {
309   if (flags)
310     {
311       *flags = 0;
312       if (with_prefix)
313         *flags |= 1;
314       if (with_time)
315         *flags |= 2;
316       if (with_pid)
317         *flags |=4;
318     }
319   return prefix_buffer;
320 }
321
322 int
323 log_get_fd()
324 {
325     return fileno(logstream?logstream:stderr);
326 }
327
328 FILE *
329 log_get_stream ()
330 {
331     return logstream?logstream:stderr;
332 }
333
334
335 static void
336 do_logv( int level, const char *fmt, va_list arg_ptr )
337 {
338   if (!logstream)
339     logstream = stderr;
340
341   if (missing_lf && level != JNLIB_LOG_CONT)
342     putc('\n', logstream );
343   missing_lf = 0;
344
345   if (level != JNLIB_LOG_CONT)
346     { /* Note this does not work for multiple line logging as we would
347        * need to print to a buffer first */
348       if (with_time && !force_prefixes)
349         {
350           struct tm *tp;
351           time_t atime = time (NULL);
352           
353           tp = localtime (&atime);
354           fprintf (logstream, "%04d-%02d-%02d %02d:%02d:%02d ",
355                    1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
356                    tp->tm_hour, tp->tm_min, tp->tm_sec );
357         }
358       if (with_prefix || force_prefixes)
359         fputs (prefix_buffer, logstream);
360       if (with_pid || force_prefixes)
361         fprintf (logstream, "[%u]", (unsigned int)getpid ());
362       if (!with_time || force_prefixes)
363         putc (':', logstream);
364       /* A leading backspace suppresses the extra space so that we can
365          correctly output, programname, filename and linenumber. */
366       if (fmt && *fmt == '\b')
367         fmt++;
368       else
369         putc (' ', logstream);
370     }
371
372   switch (level)
373     {
374     case JNLIB_LOG_BEGIN: break;
375     case JNLIB_LOG_CONT: break;
376     case JNLIB_LOG_INFO: break;
377     case JNLIB_LOG_WARN: break;
378     case JNLIB_LOG_ERROR: break;
379     case JNLIB_LOG_FATAL: fputs("Fatal: ",logstream ); break;
380     case JNLIB_LOG_BUG: fputs("Ohhhh jeeee: ", logstream); break;
381     case JNLIB_LOG_DEBUG: fputs("DBG: ", logstream ); break;
382     default: fprintf(logstream,"[Unknown log level %d]: ", level ); break;
383     }
384
385
386   if (fmt)
387     {
388       vfprintf(logstream,fmt,arg_ptr) ;
389       if (*fmt && fmt[strlen(fmt)-1] != '\n')
390         missing_lf = 1;
391     }
392
393   if (level == JNLIB_LOG_FATAL)
394     exit(2);
395   if (level == JNLIB_LOG_BUG)
396     abort();
397 }
398
399 static void
400 do_log( int level, const char *fmt, ... )
401 {
402     va_list arg_ptr ;
403
404     va_start( arg_ptr, fmt ) ;
405     do_logv( level, fmt, arg_ptr );
406     va_end(arg_ptr);
407 }
408
409
410 void
411 log_logv (int level, const char *fmt, va_list arg_ptr)
412 {
413   do_logv (level, fmt, arg_ptr);
414 }
415
416 void
417 log_info( const char *fmt, ... )
418 {
419     va_list arg_ptr ;
420
421     va_start( arg_ptr, fmt ) ;
422     do_logv( JNLIB_LOG_INFO, fmt, arg_ptr );
423     va_end(arg_ptr);
424 }
425
426 void
427 log_error( const char *fmt, ... )
428 {
429     va_list arg_ptr ;
430
431     va_start( arg_ptr, fmt ) ;
432     do_logv( JNLIB_LOG_ERROR, fmt, arg_ptr );
433     va_end(arg_ptr);
434     /* protect against counter overflow */
435     if( errorcount < 30000 )
436         errorcount++;
437 }
438
439
440 void
441 log_fatal( const char *fmt, ... )
442 {
443     va_list arg_ptr ;
444
445     va_start( arg_ptr, fmt ) ;
446     do_logv( JNLIB_LOG_FATAL, fmt, arg_ptr );
447     va_end(arg_ptr);
448     abort(); /* never called, but it makes the compiler happy */
449 }
450
451 void
452 log_bug( const char *fmt, ... )
453 {
454     va_list arg_ptr ;
455
456     va_start( arg_ptr, fmt ) ;
457     do_logv( JNLIB_LOG_BUG, fmt, arg_ptr );
458     va_end(arg_ptr);
459     abort(); /* never called, but it makes the compiler happy */
460 }
461
462 void
463 log_debug( const char *fmt, ... )
464 {
465     va_list arg_ptr ;
466
467     va_start( arg_ptr, fmt ) ;
468     do_logv( JNLIB_LOG_DEBUG, fmt, arg_ptr );
469     va_end(arg_ptr);
470 }
471
472
473 void
474 log_printf (const char *fmt, ...)
475 {
476   va_list arg_ptr;
477
478   va_start (arg_ptr, fmt);
479   do_logv (fmt ? JNLIB_LOG_CONT : JNLIB_LOG_BEGIN, fmt, arg_ptr);
480   va_end (arg_ptr);
481 }
482
483 /* Print a hexdump of BUFFER.  With TEXT of NULL print just the raw
484    dump, with TEXT just an empty string, print a trailing linefeed,
485    otherwise print an entire debug line. */
486 void
487 log_printhex (const char *text, const void *buffer, size_t length)
488 {
489   if (text && *text)
490     log_debug ("%s ", text);
491   if (length)
492     {
493       const unsigned char *p = buffer;
494       log_printf ("%02X", *p);
495       for (length--, p++; length--; p++)
496         log_printf (" %02X", *p);
497     }
498   if (text)
499     log_printf ("\n");
500 }
501
502
503 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
504 void
505 bug_at( const char *file, int line, const char *func )
506 {
507     do_log( JNLIB_LOG_BUG,
508              ("... this is a bug (%s:%d:%s)\n"), file, line, func );
509     abort(); /* never called, but it makes the compiler happy */
510 }
511 #else
512 void
513 bug_at( const char *file, int line )
514 {
515     do_log( JNLIB_LOG_BUG,
516              _("you found a bug ... (%s:%d)\n"), file, line);
517     abort(); /* never called, but it makes the compiler happy */
518 }
519 #endif
520