Add finger support to dirmngr.
[gnupg.git] / common / logging.c
1 /* logging.c - Useful logging functions
2  * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
3  *               2009, 2010 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 3 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, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include <config.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <stddef.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_W32_SYSTEM
33 # include <windows.h>
34 #else /*!HAVE_W32_SYSTEM*/
35 # include <sys/socket.h>
36 # include <sys/un.h>
37 # include <netinet/in.h>
38 # include <arpa/inet.h>
39 #endif /*!HAVE_W32_SYSTEM*/
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <assert.h>
43
44
45 #define JNLIB_NEED_LOG_LOGV 1
46 #define JNLIB_NEED_AFLOCAL 1
47 #include "libjnlib-config.h"
48 #include "logging.h"
49
50 #ifdef HAVE_W32_SYSTEM
51 # define S_IRGRP S_IRUSR
52 # define S_IROTH S_IRUSR
53 # define S_IWGRP S_IWUSR
54 # define S_IWOTH S_IWUSR
55 #endif
56
57
58 #ifdef HAVE_W32CE_SYSTEM
59 # define isatty(a)  (0)
60 #endif
61
62 #undef WITH_IPV6
63 #if defined (AF_INET6) && defined(PF_INET) \
64     && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
65 # define WITH_IPV6 1
66 #endif
67
68 #ifndef EAFNOSUPPORT
69 # define EAFNOSUPPORT EINVAL
70 #endif
71 #ifndef INADDR_NONE  /* Slowaris is missing that.  */
72 #define INADDR_NONE  ((unsigned long)(-1))
73 #endif /*INADDR_NONE*/
74
75 #ifdef HAVE_W32_SYSTEM
76 #define sock_close(a)  closesocket(a)
77 #else
78 #define sock_close(a)  close(a)
79 #endif
80
81
82 static estream_t logstream;
83 static int log_socket = -1;
84 static char prefix_buffer[80];
85 static int with_time;
86 static int with_prefix;
87 static int with_pid;
88 static int (*get_pid_suffix_cb)(unsigned long *r_value);
89 static int running_detached;
90 static int force_prefixes;
91
92 static int missing_lf;
93 static int errorcount;
94
95
96 int
97 log_get_errorcount (int clear)
98 {
99     int n = errorcount;
100     if( clear )
101         errorcount = 0;
102     return n;
103 }
104
105 void
106 log_inc_errorcount (void)
107 {
108    errorcount++;
109 }
110
111
112 /* The following 3 functions are used by es_fopencookie to write logs
113    to a socket.  */
114 struct fun_cookie_s
115 {
116   int fd;
117   int quiet;
118   int want_socket;
119   int is_socket;
120 #ifdef HAVE_W32CE_SYSTEM
121   int use_writefile;
122 #endif
123   char name[1];
124 };
125
126
127 /* Write NBYTES of BUFFER to file descriptor FD. */
128 static int
129 writen (int fd, const void *buffer, size_t nbytes, int is_socket)
130 {
131   const char *buf = buffer;
132   size_t nleft = nbytes;
133   int nwritten;
134 #ifndef HAVE_W32_SYSTEM
135   (void)is_socket; /* Not required.  */
136 #endif
137
138   while (nleft > 0)
139     {
140 #ifdef HAVE_W32_SYSTEM
141       if (is_socket)
142         nwritten = send (fd, buf, nleft, 0);
143       else
144 #endif
145         nwritten = write (fd, buf, nleft);
146
147       if (nwritten < 0 && errno == EINTR)
148         continue;
149       if (nwritten < 0)
150         return -1;
151       nleft -= nwritten;
152       buf = buf + nwritten;
153     }
154
155   return 0;
156 }
157
158
159 /* Returns true if STR represents a valid port number in decimal
160    notation and no garbage is following.  */
161 static int
162 parse_portno (const char *str, unsigned short *r_port)
163 {
164   unsigned int value;
165
166   for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
167     {
168       value = value * 10 + (*str - '0');
169       if (value > 65535)
170         return 0;
171     }
172   if (*str || !value)
173     return 0;
174
175   *r_port = value;
176   return 1;
177 }
178
179
180 static ssize_t
181 fun_writer (void *cookie_arg, const void *buffer, size_t size)
182 {
183   struct fun_cookie_s *cookie = cookie_arg;
184
185   /* FIXME: Use only estream with a callback for socket writing.  This
186      avoids the ugly mix of fd and estream code.  */
187
188   /* Note that we always try to reconnect to the socket but print
189      error messages only the first time an error occured.  If
190      RUNNING_DETACHED is set we don't fall back to stderr and even do
191      not print any error messages.  This is needed because detached
192      processes often close stderr and by writing to file descriptor 2
193      we might send the log message to a file not intended for logging
194      (e.g. a pipe or network connection). */
195   if (cookie->want_socket && cookie->fd == -1)
196     {
197 #ifdef WITH_IPV6
198       struct sockaddr_in6 srvr_addr_in6;
199 #endif
200       struct sockaddr_in srvr_addr_in;
201 #ifndef HAVE_W32_SYSTEM
202       struct sockaddr_un srvr_addr_un;
203 #endif
204       size_t addrlen;
205       struct sockaddr *srvr_addr = NULL;
206       unsigned short port = 0;
207       int af = AF_LOCAL;
208       int pf = PF_LOCAL;
209       const char *name = cookie->name;
210
211       /* Not yet open or meanwhile closed due to an error. */
212       cookie->is_socket = 0;
213
214       /* Check whether this is a TCP socket or a local socket.  */
215       if (!strncmp (name, "tcp://", 6) && name[6])
216         {
217           name += 6;
218           af = AF_INET;
219           pf = PF_INET;
220         }
221 #ifndef HAVE_W32_SYSTEM
222       else if (!strncmp (name, "socket://", 9) && name[9])
223         name += 9;
224 #endif
225
226       if (af == AF_LOCAL)
227         {
228 #ifdef HAVE_W32_SYSTEM
229           addrlen = 0;
230 #else
231           memset (&srvr_addr, 0, sizeof srvr_addr);
232           srvr_addr_un.sun_family = af;
233           strncpy (srvr_addr_un.sun_path,
234                    name, sizeof (srvr_addr_un.sun_path)-1);
235           srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0;
236           srvr_addr = (struct sockaddr *)&srvr_addr_un;
237           addrlen = SUN_LEN (&srvr_addr_un);
238 #endif
239         }
240       else
241         {
242           char *addrstr, *p;
243           void *addrbuf = NULL;
244
245           addrstr = jnlib_malloc (strlen (name) + 1);
246           if (!addrstr)
247             addrlen = 0; /* This indicates an error.  */
248           else if (*name == '[')
249             {
250               /* Check for IPv6 literal address.  */
251               strcpy (addrstr, name+1);
252               p = strchr (addrstr, ']');
253               if (!p || p[1] != ':' || !parse_portno (p+2, &port))
254                 {
255                   jnlib_set_errno (EINVAL);
256                   addrlen = 0;
257                 }
258               else
259                 {
260                   *p = 0;
261 #ifdef WITH_IPV6
262                   af = AF_INET6;
263                   pf = PF_INET6;
264                   memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
265                   srvr_addr_in6.sin6_family = af;
266                   srvr_addr_in6.sin6_port = htons (port);
267                   addrbuf = &srvr_addr_in6.sin6_addr;
268                   srvr_addr = (struct sockaddr *)&srvr_addr_in6;
269                   addrlen = sizeof srvr_addr_in6;
270 #else
271                   jnlib_set_errno (EAFNOSUPPORT);
272                   addrlen = 0;
273 #endif
274                 }
275             }
276           else
277             {
278               /* Check for IPv4 literal address.  */
279               strcpy (addrstr, name);
280               p = strchr (addrstr, ':');
281               if (!p || !parse_portno (p+1, &port))
282                 {
283                   jnlib_set_errno (EINVAL);
284                   addrlen = 0;
285                 }
286               else
287                 {
288                   *p = 0;
289                   memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
290                   srvr_addr_in.sin_family = af;
291                   srvr_addr_in.sin_port = htons (port);
292                   addrbuf = &srvr_addr_in.sin_addr;
293                   srvr_addr = (struct sockaddr *)&srvr_addr_in;
294                   addrlen = sizeof srvr_addr_in;
295                 }
296             }
297
298           if (addrlen)
299             {
300 #ifdef HAVE_INET_PTON
301               if (inet_pton (af, addrstr, addrbuf) != 1)
302                 addrlen = 0;
303 #else /*!HAVE_INET_PTON*/
304               /* We need to use the old function.  If we are here v6
305                  support isn't enabled anyway and thus we can do fine
306                  without.  Note that Windows has a compatible inet_pton
307                  function named inetPton, but only since Vista.  */
308               srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
309               if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
310                 addrlen = 0;
311 #endif /*!HAVE_INET_PTON*/
312             }
313
314           jnlib_free (addrstr);
315         }
316
317       cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1;
318       if (cookie->fd == -1)
319         {
320           if (!cookie->quiet && !running_detached
321               && isatty (es_fileno (es_stderr)))
322             es_fprintf (es_stderr, "failed to create socket for logging: %s\n",
323                         strerror(errno));
324         }
325       else
326         {
327           if (connect (cookie->fd, srvr_addr, addrlen) == -1)
328             {
329               if (!cookie->quiet && !running_detached
330                   && isatty (es_fileno (es_stderr)))
331                 es_fprintf (es_stderr, "can't connect to `%s': %s\n",
332                             cookie->name, strerror(errno));
333               sock_close (cookie->fd);
334               cookie->fd = -1;
335             }
336         }
337
338       if (cookie->fd == -1)
339         {
340           if (!running_detached)
341             {
342               /* Due to all the problems with apps not running
343                  detached but being called with stderr closed or used
344                  for a different purposes, it does not make sense to
345                  switch to stderr.  We therefore disable it. */
346               if (!cookie->quiet)
347                 {
348                   /* fputs ("switching logging to stderr\n", stderr);*/
349                   cookie->quiet = 1;
350                 }
351               cookie->fd = -1; /*fileno (stderr);*/
352             }
353         }
354       else /* Connection has been established. */
355         {
356           cookie->quiet = 0;
357           cookie->is_socket = 1;
358         }
359     }
360
361   log_socket = cookie->fd;
362   if (cookie->fd != -1)
363     {
364 #ifdef HAVE_W32CE_SYSTEM
365       if (cookie->use_writefile)
366         {
367           DWORD nwritten;
368
369           WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL);
370           return (ssize_t)size; /* Okay.  */
371         }
372 #endif
373       if (!writen (cookie->fd, buffer, size, cookie->is_socket))
374         return (ssize_t)size; /* Okay. */
375     }
376
377   if (!running_detached && cookie->fd != -1
378       && isatty (es_fileno (es_stderr)))
379     {
380       if (*cookie->name)
381         es_fprintf (es_stderr, "error writing to `%s': %s\n",
382                     cookie->name, strerror(errno));
383       else
384         es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n",
385                     cookie->fd, strerror(errno));
386     }
387   if (cookie->is_socket && cookie->fd != -1)
388     {
389       sock_close (cookie->fd);
390       cookie->fd = -1;
391       log_socket = -1;
392     }
393
394   return (ssize_t)size;
395 }
396
397
398 static int
399 fun_closer (void *cookie_arg)
400 {
401   struct fun_cookie_s *cookie = cookie_arg;
402
403   if (cookie->fd != -1 && cookie->fd != 2)
404     sock_close (cookie->fd);
405   jnlib_free (cookie);
406   log_socket = -1;
407   return 0;
408 }
409
410
411 /* Common function to either set the logging to a file or a file
412    descriptor. */
413 static void
414 set_file_fd (const char *name, int fd)
415 {
416   estream_t fp;
417   int want_socket;
418 #ifdef HAVE_W32CE_SYSTEM
419   int use_writefile = 0;
420 #endif
421   struct fun_cookie_s *cookie;
422
423   /* Close an open log stream.  */
424   if (logstream)
425     {
426       es_fclose (logstream);
427       logstream = NULL;
428     }
429
430   /* Figure out what kind of logging we want.  */
431   if (name && !strcmp (name, "-"))
432     {
433       name = NULL;
434       fd = es_fileno (es_stderr);
435     }
436
437   want_socket = 0;
438   if (name && !strncmp (name, "tcp://", 6) && name[6])
439     want_socket = 1;
440 #ifndef HAVE_W32_SYSTEM
441   else if (name && !strncmp (name, "socket://", 9) && name[9])
442     want_socket = 2;
443 #endif /*HAVE_W32_SYSTEM*/
444 #ifdef HAVE_W32CE_SYSTEM
445   else if (name && !strcmp (name, "GPG2:"))
446     {
447       HANDLE hd;
448
449       ActivateDevice (L"Drivers\\GnuPG_Log", 0);
450       /* Ignore a filename and write the debug output to the GPG2:
451          device.  */
452       hd = CreateFile (L"GPG2:", GENERIC_WRITE,
453                        FILE_SHARE_READ | FILE_SHARE_WRITE,
454                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
455       fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd;
456       name = NULL;
457       force_prefixes = 1;
458       use_writefile = 1;
459     }
460 #endif /*HAVE_W32CE_SYSTEM*/
461
462   /* Setup a new stream.  */
463
464   /* The xmalloc below is justified because we can expect that this
465      function is called only during initialization and there is no
466      easy way out of this error condition.  */
467   cookie = jnlib_xmalloc (sizeof *cookie + (name? strlen (name):0));
468   strcpy (cookie->name, name? name:"");
469   cookie->quiet = 0;
470   cookie->is_socket = 0;
471   cookie->want_socket = want_socket;
472 #ifdef HAVE_W32CE_SYSTEM
473   cookie->use_writefile = use_writefile;
474 #endif
475   if (!name)
476     cookie->fd = fd;
477   else if (want_socket)
478     cookie->fd = -1;
479   else
480     {
481       do
482         cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT,
483                            (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH));
484       while (cookie->fd == -1 && errno == EINTR);
485     }
486   log_socket = cookie->fd;
487
488   {
489     es_cookie_io_functions_t io = { NULL };
490     io.func_write = fun_writer;
491     io.func_close = fun_closer;
492
493     fp = es_fopencookie (cookie, "w", io);
494   }
495
496   /* On error default to a stderr based estream.  */
497   if (!fp)
498     fp = es_stderr;
499
500   es_setvbuf (fp, NULL, _IOLBF, 0);
501
502   logstream = fp;
503
504   /* We always need to print the prefix and the pid for socket mode,
505      so that the server reading the socket can do something
506      meaningful. */
507   force_prefixes = want_socket;
508
509   missing_lf = 0;
510 }
511
512
513 /* Set the file to write log to.  The special names NULL and "-" may
514    be used to select stderr and names formatted like
515    "socket:///home/foo/mylogs" may be used to write the logging to the
516    socket "/home/foo/mylogs".  If the connection to the socket fails
517    or a write error is detected, the function writes to stderr and
518    tries the next time again to connect the socket.
519   */
520 void
521 log_set_file (const char *name)
522 {
523   set_file_fd (name? name: "-", -1);
524 }
525
526 void
527 log_set_fd (int fd)
528 {
529   set_file_fd (NULL, fd);
530 }
531
532
533 void
534 log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value))
535 {
536   get_pid_suffix_cb = cb;
537 }
538
539
540 void
541 log_set_prefix (const char *text, unsigned int flags)
542 {
543   if (text)
544     {
545       strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
546       prefix_buffer[sizeof (prefix_buffer)-1] = 0;
547     }
548
549   with_prefix = (flags & JNLIB_LOG_WITH_PREFIX);
550   with_time = (flags & JNLIB_LOG_WITH_TIME);
551   with_pid  = (flags & JNLIB_LOG_WITH_PID);
552   running_detached = (flags & JNLIB_LOG_RUN_DETACHED);
553 }
554
555
556 const char *
557 log_get_prefix (unsigned int *flags)
558 {
559   if (flags)
560     {
561       *flags = 0;
562       if (with_prefix)
563         *flags |= JNLIB_LOG_WITH_PREFIX;
564       if (with_time)
565         *flags |= JNLIB_LOG_WITH_TIME;
566       if (with_pid)
567         *flags |= JNLIB_LOG_WITH_PID;
568       if (running_detached)
569         *flags |= JNLIB_LOG_RUN_DETACHED;
570     }
571   return prefix_buffer;
572 }
573
574 /* This function returns true if the file descriptor FD is in use for
575    logging.  This is preferable over a test using log_get_fd in that
576    it allows the logging code to use more then one file descriptor.  */
577 int
578 log_test_fd (int fd)
579 {
580   if (logstream)
581     {
582       int tmp = es_fileno (logstream);
583       if ( tmp != -1 && tmp == fd)
584         return 1;
585     }
586   if (log_socket != -1 && log_socket == fd)
587     return 1;
588   return 0;
589 }
590
591 int
592 log_get_fd ()
593 {
594   return logstream? es_fileno(logstream) : -1;
595 }
596
597 estream_t
598 log_get_stream ()
599 {
600   if (!logstream)
601     {
602       log_set_file (NULL); /* Make sure a log stream has been set.  */
603       assert (logstream);
604     }
605   return logstream;
606 }
607
608 static void
609 do_logv (int level, int ignore_arg_ptr, const char *fmt, va_list arg_ptr)
610 {
611   if (!logstream)
612     {
613 #ifdef HAVE_W32_SYSTEM
614       char *tmp;
615
616       tmp = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG",
617                                             "DefaultLogFile");
618       log_set_file (tmp && *tmp? tmp : NULL);
619       jnlib_free (tmp);
620 #else
621       log_set_file (NULL); /* Make sure a log stream has been set.  */
622 #endif
623       assert (logstream);
624     }
625
626   es_flockfile (logstream);
627   if (missing_lf && level != JNLIB_LOG_CONT)
628     es_putc_unlocked ('\n', logstream );
629   missing_lf = 0;
630
631   if (level != JNLIB_LOG_CONT)
632     { /* Note this does not work for multiple line logging as we would
633        * need to print to a buffer first */
634       if (with_time && !force_prefixes)
635         {
636           struct tm *tp;
637           time_t atime = time (NULL);
638
639           tp = localtime (&atime);
640           es_fprintf_unlocked (logstream, "%04d-%02d-%02d %02d:%02d:%02d ",
641                                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
642                                tp->tm_hour, tp->tm_min, tp->tm_sec );
643         }
644       if (with_prefix || force_prefixes)
645         es_fputs_unlocked (prefix_buffer, logstream);
646       if (with_pid || force_prefixes)
647         {
648           unsigned long pidsuf;
649           int pidfmt;
650
651           if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf)))
652             es_fprintf_unlocked (logstream, pidfmt == 1? "[%u.%lu]":"[%u.%lx]",
653                                  (unsigned int)getpid (), pidsuf);
654           else
655             es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ());
656         }
657       if (!with_time || force_prefixes)
658         es_putc_unlocked (':', logstream);
659       /* A leading backspace suppresses the extra space so that we can
660          correctly output, programname, filename and linenumber. */
661       if (fmt && *fmt == '\b')
662         fmt++;
663       else
664         es_putc_unlocked (' ', logstream);
665     }
666
667   switch (level)
668     {
669     case JNLIB_LOG_BEGIN: break;
670     case JNLIB_LOG_CONT: break;
671     case JNLIB_LOG_INFO: break;
672     case JNLIB_LOG_WARN: break;
673     case JNLIB_LOG_ERROR: break;
674     case JNLIB_LOG_FATAL: es_fputs_unlocked ("Fatal: ",logstream ); break;
675     case JNLIB_LOG_BUG:   es_fputs_unlocked ("Ohhhh jeeee: ", logstream); break;
676     case JNLIB_LOG_DEBUG: es_fputs_unlocked ("DBG: ", logstream ); break;
677     default:
678       es_fprintf_unlocked (logstream,"[Unknown log level %d]: ", level);
679       break;
680     }
681
682   if (fmt)
683     {
684       if (ignore_arg_ptr)
685         es_fputs_unlocked (fmt, logstream);
686       else
687         es_vfprintf_unlocked (logstream, fmt, arg_ptr);
688       if (*fmt && fmt[strlen(fmt)-1] != '\n')
689         missing_lf = 1;
690     }
691
692   if (level == JNLIB_LOG_FATAL)
693     {
694       if (missing_lf)
695         es_putc_unlocked ('\n', logstream);
696       es_funlockfile (logstream);
697       exit (2);
698     }
699   else if (level == JNLIB_LOG_BUG)
700     {
701       if (missing_lf)
702         es_putc_unlocked ('\n', logstream );
703       es_funlockfile (logstream);
704       abort ();
705     }
706   else
707     es_funlockfile (logstream);
708 }
709
710
711 void
712 log_log (int level, const char *fmt, ...)
713 {
714   va_list arg_ptr ;
715
716   va_start (arg_ptr, fmt) ;
717   do_logv (level, 0, fmt, arg_ptr);
718   va_end (arg_ptr);
719 }
720
721
722 void
723 log_logv (int level, const char *fmt, va_list arg_ptr)
724 {
725   do_logv (level, 0, fmt, arg_ptr);
726 }
727
728
729 static void
730 do_log_ignore_arg (int level, const char *str, ...)
731 {
732   va_list arg_ptr;
733   va_start (arg_ptr, str);
734   do_logv (level, 1, str, arg_ptr);
735   va_end (arg_ptr);
736 }
737
738
739 void
740 log_string (int level, const char *string)
741 {
742   /* We need a dummy arg_ptr, but there is no portable way to create
743      one.  So we call the do_logv function through a variadic wrapper.
744      MB: Why not just use "%s"?  */
745   do_log_ignore_arg (level, string);
746 }
747
748
749 void
750 log_info (const char *fmt, ...)
751 {
752   va_list arg_ptr ;
753
754   va_start (arg_ptr, fmt);
755   do_logv (JNLIB_LOG_INFO, 0, fmt, arg_ptr);
756   va_end (arg_ptr);
757 }
758
759
760 void
761 log_error (const char *fmt, ...)
762 {
763   va_list arg_ptr ;
764
765   va_start (arg_ptr, fmt);
766   do_logv (JNLIB_LOG_ERROR, 0, fmt, arg_ptr);
767   va_end (arg_ptr);
768   /* Protect against counter overflow.  */
769   if (errorcount < 30000)
770     errorcount++;
771 }
772
773
774 void
775 log_fatal (const char *fmt, ...)
776 {
777   va_list arg_ptr ;
778
779   va_start (arg_ptr, fmt);
780   do_logv (JNLIB_LOG_FATAL, 0, fmt, arg_ptr);
781   va_end (arg_ptr);
782   abort (); /* Never called; just to make the compiler happy.  */
783 }
784
785
786 void
787 log_bug (const char *fmt, ...)
788 {
789   va_list arg_ptr ;
790
791   va_start (arg_ptr, fmt);
792   do_logv (JNLIB_LOG_BUG, 0, fmt, arg_ptr);
793   va_end (arg_ptr);
794   abort (); /* Never called; just to make the compiler happy.  */
795 }
796
797
798 void
799 log_debug (const char *fmt, ...)
800 {
801   va_list arg_ptr ;
802
803   va_start (arg_ptr, fmt);
804   do_logv (JNLIB_LOG_DEBUG, 0, fmt, arg_ptr);
805   va_end (arg_ptr);
806 }
807
808
809 void
810 log_printf (const char *fmt, ...)
811 {
812   va_list arg_ptr;
813
814   va_start (arg_ptr, fmt);
815   do_logv (fmt ? JNLIB_LOG_CONT : JNLIB_LOG_BEGIN, 0, fmt, arg_ptr);
816   va_end (arg_ptr);
817 }
818
819
820 /* Flush the log - this is useful to make sure that the trailing
821    linefeed has been printed.  */
822 void
823 log_flush (void)
824 {
825   do_log_ignore_arg (JNLIB_LOG_CONT, NULL);
826 }
827
828
829 /* Print a hexdump of BUFFER.  With TEXT of NULL print just the raw
830    dump, with TEXT just an empty string, print a trailing linefeed,
831    otherwise print an entire debug line. */
832 void
833 log_printhex (const char *text, const void *buffer, size_t length)
834 {
835   if (text && *text)
836     log_debug ("%s ", text);
837   if (length)
838     {
839       const unsigned char *p = buffer;
840       log_printf ("%02X", *p);
841       for (length--, p++; length--; p++)
842         log_printf (" %02X", *p);
843     }
844   if (text)
845     log_printf ("\n");
846 }
847
848
849 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
850 void
851 bug_at( const char *file, int line, const char *func )
852 {
853   log_log (JNLIB_LOG_BUG, ("... this is a bug (%s:%d:%s)\n"), file, line, func);
854   abort (); /* Never called; just to make the compiler happy.  */
855 }
856 #else
857 void
858 bug_at( const char *file, int line )
859 {
860   log_log (JNLIB_LOG_BUG, _("you found a bug ... (%s:%d)\n"), file, line);
861   abort (); /* Never called; just to make the compiler happy.  */
862 }
863 #endif