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