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