1 /* logging.c - Useful logging functions
2 * Copyright (C) 1998, 1999, 2000, 2001, 2003,
3 * 2004, 2005, 2006, 2009 Free Software Foundation, Inc.
5 * This file is part of JNLIB.
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.
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.
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/>.
30 #include <sys/types.h>
32 #ifndef HAVE_W32_SYSTEM
33 #include <sys/socket.h>
35 #endif /*!HAVE_W32_SYSTEM*/
41 #define JNLIB_NEED_LOG_LOGV 1
42 #include "libjnlib-config.h"
45 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
46 #define USE_FUNWRITER 1
49 #ifdef HAVE_FOPENCOOKIE
50 typedef ssize_t my_funopen_hook_ret_t
;
51 typedef size_t my_funopen_hook_size_t
;
53 typedef int my_funopen_hook_ret_t
;
54 typedef int my_funopen_hook_size_t
;
58 static FILE *logstream
;
59 static int log_socket
= -1;
60 static char prefix_buffer
[80];
62 static int with_prefix
;
64 static unsigned long (*get_tid_callback
)(void);
65 static int running_detached
;
66 static int force_prefixes
;
68 static int missing_lf
;
69 static int errorcount
;
73 log_get_errorcount (int clear
)
82 log_inc_errorcount (void)
88 /* The follwing 3 functions are used by funopen to write logs to a
99 /* Write NBYTES of BUFFER to file descriptor FD. */
101 writen (int fd
, const void *buffer
, size_t nbytes
)
103 const char *buf
= buffer
;
104 size_t nleft
= nbytes
;
109 nwritten
= write (fd
, buf
, nleft
);
110 if (nwritten
< 0 && errno
== EINTR
)
115 buf
= buf
+ nwritten
;
122 static my_funopen_hook_ret_t
123 fun_writer (void *cookie_arg
, const char *buffer
, my_funopen_hook_size_t size
)
125 struct fun_cookie_s
*cookie
= cookie_arg
;
127 /* Note that we always try to reconnect to the socket but print
128 error messages only the first time an error occured. If
129 RUNNING_DETACHED is set we don't fall back to stderr and even do
130 not print any error messages. This is needed because detached
131 processes often close stderr and by writing to file descriptor 2
132 we might send the log message to a file not intended for logging
133 (e.g. a pipe or network connection). */
134 if (cookie
->want_socket
&& cookie
->fd
== -1)
136 /* Not yet open or meanwhile closed due to an error. */
137 cookie
->is_socket
= 0;
138 cookie
->fd
= socket (PF_LOCAL
, SOCK_STREAM
, 0);
139 if (cookie
->fd
== -1)
141 if (!cookie
->quiet
&& !running_detached
142 && isatty (fileno (stderr
)))
143 fprintf (stderr
, "failed to create socket for logging: %s\n",
148 struct sockaddr_un addr
;
151 memset (&addr
, 0, sizeof addr
);
152 addr
.sun_family
= PF_LOCAL
;
153 strncpy (addr
.sun_path
, cookie
->name
, sizeof (addr
.sun_path
)-1);
154 addr
.sun_path
[sizeof (addr
.sun_path
)-1] = 0;
155 addrlen
= (offsetof (struct sockaddr_un
, sun_path
)
156 + strlen (addr
.sun_path
) + 1);
158 if (connect (cookie
->fd
, (struct sockaddr
*) &addr
, addrlen
) == -1)
160 if (!cookie
->quiet
&& !running_detached
161 && isatty (fileno (stderr
)))
162 fprintf (stderr
, "can't connect to `%s': %s\n",
163 cookie
->name
, strerror(errno
));
169 if (cookie
->fd
== -1)
171 if (!running_detached
)
173 /* Due to all the problems with apps not running
174 detahced but beeing caled with stderr closed or
175 used for a different purposes, it does not make
176 sense to switch to stderr. We tehrefore disable it. */
179 /* fputs ("switching logging to stderr\n", stderr);*/
182 cookie
->fd
= -1; /*fileno (stderr);*/
185 else /* Connection has been established. */
188 cookie
->is_socket
= 1;
192 log_socket
= cookie
->fd
;
193 if (cookie
->fd
!= -1 && !writen (cookie
->fd
, buffer
, size
))
194 return (my_funopen_hook_ret_t
)size
; /* Okay. */
196 if (!running_detached
&& cookie
->fd
!= -1
197 && isatty (fileno (stderr
)))
200 fprintf (stderr
, "error writing to `%s': %s\n",
201 cookie
->name
, strerror(errno
));
203 fprintf (stderr
, "error writing to file descriptor %d: %s\n",
204 cookie
->fd
, strerror(errno
));
206 if (cookie
->is_socket
&& cookie
->fd
!= -1)
213 return (my_funopen_hook_ret_t
)size
;
217 fun_closer (void *cookie_arg
)
219 struct fun_cookie_s
*cookie
= cookie_arg
;
221 if (cookie
->fd
!= -1 && cookie
->fd
!= 2)
227 #endif /*USE_FUNWRITER*/
231 /* Common function to either set the logging to a file or a file
234 set_file_fd (const char *name
, int fd
)
239 struct fun_cookie_s
*cookie
;
242 /* Close an open log stream. */
245 if (logstream
!= stderr
&& logstream
!= stdout
)
250 /* Figure out what kind of logging we want. */
251 if (name
&& !strcmp (name
, "-"))
254 fd
= fileno (stderr
);
259 want_socket
= (!strncmp (name
, "socket://", 9) && name
[9]);
268 /* Setup a new stream. */
270 cookie
= jnlib_xmalloc (sizeof *cookie
+ (name
? strlen (name
):0));
271 strcpy (cookie
->name
, name
? name
:"");
273 cookie
->is_socket
= 0;
274 cookie
->want_socket
= want_socket
;
277 else if (want_socket
)
282 cookie
->fd
= open (name
, O_WRONLY
|O_APPEND
|O_CREAT
,
283 (S_IRUSR
|S_IRGRP
|S_IROTH
|S_IWUSR
|S_IWGRP
|S_IWOTH
));
284 while (cookie
->fd
== -1 && errno
== EINTR
);
286 log_socket
= cookie
->fd
;
288 #ifdef HAVE_FOPENCOOKIE
290 cookie_io_functions_t io
= { NULL
};
291 io
.write
= fun_writer
;
292 io
.close
= fun_closer
;
294 fp
= fopencookie (cookie
, "w", io
);
296 #else /*!HAVE_FOPENCOOKIE*/
297 fp
= funopen (cookie
, NULL
, fun_writer
, NULL
, fun_closer
);
298 #endif /*!HAVE_FOPENCOOKIE*/
300 #else /*!USE_FUNWRITER*/
302 /* The system does not feature custom streams. Thus fallback to
306 fprintf (stderr
, "system does not support logging to a socket - "
311 fp
= fopen (name
, "a");
317 fp
= fdopen (fd
, "a");
321 #endif /*!USE_FUNWRITER*/
323 /* On error default to stderr. */
327 fprintf (stderr
, "failed to open log file `%s': %s\n",
328 name
, strerror(errno
));
330 fprintf (stderr
, "failed to fdopen file descriptor %d: %s\n",
331 fd
, strerror(errno
));
332 /* We need to make sure that there is a log stream. We use stderr. */
336 setvbuf (fp
, NULL
, _IOLBF
, 0);
340 /* We always need to print the prefix and the pid for socket mode,
341 so that the server reading the socket can do something
343 force_prefixes
= want_socket
;
349 /* Set the file to write log to. The special names NULL and "-" may
350 be used to select stderr and names formatted like
351 "socket:///home/foo/mylogs" may be used to write the logging to the
352 socket "/home/foo/mylogs". If the connection to the socket fails
353 or a write error is detected, the function writes to stderr and
354 tries the next time again to connect the socket.
357 log_set_file (const char *name
)
359 set_file_fd (name
? name
: "-", -1);
365 set_file_fd (NULL
, fd
);
370 log_set_get_tid_callback (unsigned long (*cb
)(void))
372 get_tid_callback
= cb
;
377 log_set_prefix (const char *text
, unsigned int flags
)
381 strncpy (prefix_buffer
, text
, sizeof (prefix_buffer
)-1);
382 prefix_buffer
[sizeof (prefix_buffer
)-1] = 0;
385 with_prefix
= (flags
& JNLIB_LOG_WITH_PREFIX
);
386 with_time
= (flags
& JNLIB_LOG_WITH_TIME
);
387 with_pid
= (flags
& JNLIB_LOG_WITH_PID
);
388 running_detached
= (flags
& JNLIB_LOG_RUN_DETACHED
);
393 log_get_prefix (unsigned int *flags
)
399 *flags
|= JNLIB_LOG_WITH_PREFIX
;
401 *flags
|= JNLIB_LOG_WITH_TIME
;
403 *flags
|= JNLIB_LOG_WITH_PID
;
404 if (running_detached
)
405 *flags
|= JNLIB_LOG_RUN_DETACHED
;
407 return prefix_buffer
;
410 /* This function returns true if the file descriptor FD is in use for
411 logging. This is preferable over a test using log_get_fd in that
412 it allows the logging code to use more then one file descriptor. */
418 int tmp
= fileno (logstream
);
419 if ( tmp
!= -1 && tmp
== fd
)
422 if (log_socket
!= -1 && log_socket
== fd
)
430 return fileno(logstream
?logstream
:stderr
);
436 /* FIXME: We should not return stderr here but initialize the log
437 stream properly. This might break more things than using stderr,
439 return logstream
?logstream
:stderr
;
443 do_logv (int level
, const char *fmt
, va_list arg_ptr
)
447 log_set_file (NULL
); /* Make sure a log stream has been set. */
451 if (missing_lf
&& level
!= JNLIB_LOG_CONT
)
452 putc('\n', logstream
);
455 if (level
!= JNLIB_LOG_CONT
)
456 { /* Note this does not work for multiple line logging as we would
457 * need to print to a buffer first */
458 if (with_time
&& !force_prefixes
)
461 time_t atime
= time (NULL
);
463 tp
= localtime (&atime
);
464 fprintf (logstream
, "%04d-%02d-%02d %02d:%02d:%02d ",
465 1900+tp
->tm_year
, tp
->tm_mon
+1, tp
->tm_mday
,
466 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
);
468 if (with_prefix
|| force_prefixes
)
469 fputs (prefix_buffer
, logstream
);
470 if (with_pid
|| force_prefixes
)
472 if (get_tid_callback
)
473 fprintf (logstream
, "[%u.%lx]",
474 (unsigned int)getpid (), get_tid_callback ());
476 fprintf (logstream
, "[%u]", (unsigned int)getpid ());
478 if (!with_time
|| force_prefixes
)
479 putc (':', logstream
);
480 /* A leading backspace suppresses the extra space so that we can
481 correctly output, programname, filename and linenumber. */
482 if (fmt
&& *fmt
== '\b')
485 putc (' ', logstream
);
490 case JNLIB_LOG_BEGIN
: break;
491 case JNLIB_LOG_CONT
: break;
492 case JNLIB_LOG_INFO
: break;
493 case JNLIB_LOG_WARN
: break;
494 case JNLIB_LOG_ERROR
: break;
495 case JNLIB_LOG_FATAL
: fputs("Fatal: ",logstream
); break;
496 case JNLIB_LOG_BUG
: fputs("Ohhhh jeeee: ", logstream
); break;
497 case JNLIB_LOG_DEBUG
: fputs("DBG: ", logstream
); break;
498 default: fprintf(logstream
,"[Unknown log level %d]: ", level
); break;
504 vfprintf(logstream
,fmt
,arg_ptr
) ;
505 if (*fmt
&& fmt
[strlen(fmt
)-1] != '\n')
507 #ifdef HAVE_W32_SYSTEM
513 if (level
== JNLIB_LOG_FATAL
)
516 putc('\n', logstream
);
519 if (level
== JNLIB_LOG_BUG
)
522 putc('\n', logstream
);
528 do_log( int level
, const char *fmt
, ... )
532 va_start( arg_ptr
, fmt
) ;
533 do_logv( level
, fmt
, arg_ptr
);
539 log_logv (int level
, const char *fmt
, va_list arg_ptr
)
541 do_logv (level
, fmt
, arg_ptr
);
545 log_info( const char *fmt
, ... )
549 va_start( arg_ptr
, fmt
) ;
550 do_logv( JNLIB_LOG_INFO
, fmt
, arg_ptr
);
555 log_error( const char *fmt
, ... )
559 va_start( arg_ptr
, fmt
) ;
560 do_logv( JNLIB_LOG_ERROR
, fmt
, arg_ptr
);
562 /* protect against counter overflow */
563 if( errorcount
< 30000 )
569 log_fatal( const char *fmt
, ... )
573 va_start( arg_ptr
, fmt
) ;
574 do_logv( JNLIB_LOG_FATAL
, fmt
, arg_ptr
);
576 abort(); /* never called, but it makes the compiler happy */
580 log_bug( const char *fmt
, ... )
584 va_start( arg_ptr
, fmt
) ;
585 do_logv( JNLIB_LOG_BUG
, fmt
, arg_ptr
);
587 abort(); /* never called, but it makes the compiler happy */
591 log_debug( const char *fmt
, ... )
595 va_start( arg_ptr
, fmt
) ;
596 do_logv( JNLIB_LOG_DEBUG
, fmt
, arg_ptr
);
602 log_printf (const char *fmt
, ...)
606 va_start (arg_ptr
, fmt
);
607 do_logv (fmt
? JNLIB_LOG_CONT
: JNLIB_LOG_BEGIN
, fmt
, arg_ptr
);
611 /* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
612 dump, with TEXT just an empty string, print a trailing linefeed,
613 otherwise print an entire debug line. */
615 log_printhex (const char *text
, const void *buffer
, size_t length
)
618 log_debug ("%s ", text
);
621 const unsigned char *p
= buffer
;
622 log_printf ("%02X", *p
);
623 for (length
--, p
++; length
--; p
++)
624 log_printf (" %02X", *p
);
631 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
633 bug_at( const char *file
, int line
, const char *func
)
635 do_log( JNLIB_LOG_BUG
,
636 ("... this is a bug (%s:%d:%s)\n"), file
, line
, func
);
637 abort(); /* never called, but it makes the compiler happy */
641 bug_at( const char *file
, int line
)
643 do_log( JNLIB_LOG_BUG
,
644 _("you found a bug ... (%s:%d)\n"), file
, line
);
645 abort(); /* never called, but it makes the compiler happy */