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