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 #define JNLIB_NEED_AFLOCAL 1
43 #include "libjnlib-config.h"
46 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
47 #define USE_FUNWRITER 1
50 #ifdef HAVE_FOPENCOOKIE
51 typedef ssize_t my_funopen_hook_ret_t
;
52 typedef size_t my_funopen_hook_size_t
;
54 typedef int my_funopen_hook_ret_t
;
55 typedef int my_funopen_hook_size_t
;
59 static FILE *logstream
;
60 static int log_socket
= -1;
61 static char prefix_buffer
[80];
63 static int with_prefix
;
65 static unsigned long (*get_tid_callback
)(void);
66 static int running_detached
;
67 static int force_prefixes
;
69 static int missing_lf
;
70 static int errorcount
;
74 log_get_errorcount (int clear
)
83 log_inc_errorcount (void)
89 /* The follwing 3 functions are used by funopen to write logs to a
100 /* Write NBYTES of BUFFER to file descriptor FD. */
102 writen (int fd
, const void *buffer
, size_t nbytes
)
104 const char *buf
= buffer
;
105 size_t nleft
= nbytes
;
110 nwritten
= write (fd
, buf
, nleft
);
111 if (nwritten
< 0 && errno
== EINTR
)
116 buf
= buf
+ nwritten
;
123 static my_funopen_hook_ret_t
124 fun_writer (void *cookie_arg
, const char *buffer
, my_funopen_hook_size_t size
)
126 struct fun_cookie_s
*cookie
= cookie_arg
;
128 /* Note that we always try to reconnect to the socket but print
129 error messages only the first time an error occured. If
130 RUNNING_DETACHED is set we don't fall back to stderr and even do
131 not print any error messages. This is needed because detached
132 processes often close stderr and by writing to file descriptor 2
133 we might send the log message to a file not intended for logging
134 (e.g. a pipe or network connection). */
135 if (cookie
->want_socket
&& cookie
->fd
== -1)
137 /* Not yet open or meanwhile closed due to an error. */
138 cookie
->is_socket
= 0;
139 cookie
->fd
= socket (PF_LOCAL
, SOCK_STREAM
, 0);
140 if (cookie
->fd
== -1)
142 if (!cookie
->quiet
&& !running_detached
143 && isatty (fileno (stderr
)))
144 fprintf (stderr
, "failed to create socket for logging: %s\n",
149 struct sockaddr_un addr
;
152 memset (&addr
, 0, sizeof addr
);
153 addr
.sun_family
= PF_LOCAL
;
154 strncpy (addr
.sun_path
, cookie
->name
, sizeof (addr
.sun_path
)-1);
155 addr
.sun_path
[sizeof (addr
.sun_path
)-1] = 0;
156 addrlen
= SUN_LEN (&addr
);
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 detached but being called with stderr closed or
175 used for a different purposes, it does not make
176 sense to switch to stderr. We therefore 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 /* The xmalloc below is justified because we can expect that this
271 function is called only during initialization and there is no
272 easy way out of this error condition. */
273 cookie
= jnlib_xmalloc (sizeof *cookie
+ (name
? strlen (name
):0));
274 strcpy (cookie
->name
, name
? name
:"");
276 cookie
->is_socket
= 0;
277 cookie
->want_socket
= want_socket
;
280 else if (want_socket
)
285 cookie
->fd
= open (name
, O_WRONLY
|O_APPEND
|O_CREAT
,
286 (S_IRUSR
|S_IRGRP
|S_IROTH
|S_IWUSR
|S_IWGRP
|S_IWOTH
));
287 while (cookie
->fd
== -1 && errno
== EINTR
);
289 log_socket
= cookie
->fd
;
291 #ifdef HAVE_FOPENCOOKIE
293 cookie_io_functions_t io
= { NULL
};
294 io
.write
= fun_writer
;
295 io
.close
= fun_closer
;
297 fp
= fopencookie (cookie
, "w", io
);
299 #else /*!HAVE_FOPENCOOKIE*/
300 fp
= funopen (cookie
, NULL
, fun_writer
, NULL
, fun_closer
);
301 #endif /*!HAVE_FOPENCOOKIE*/
303 #else /*!USE_FUNWRITER*/
305 /* The system does not feature custom streams. Thus fallback to
309 fprintf (stderr
, "system does not support logging to a socket - "
314 fp
= fopen (name
, "a");
320 fp
= fdopen (fd
, "a");
324 #endif /*!USE_FUNWRITER*/
326 /* On error default to stderr. */
330 fprintf (stderr
, "failed to open log file `%s': %s\n",
331 name
, strerror(errno
));
333 fprintf (stderr
, "failed to fdopen file descriptor %d: %s\n",
334 fd
, strerror(errno
));
335 /* We need to make sure that there is a log stream. We use stderr. */
339 setvbuf (fp
, NULL
, _IOLBF
, 0);
343 /* We always need to print the prefix and the pid for socket mode,
344 so that the server reading the socket can do something
346 force_prefixes
= want_socket
;
352 /* Set the file to write log to. The special names NULL and "-" may
353 be used to select stderr and names formatted like
354 "socket:///home/foo/mylogs" may be used to write the logging to the
355 socket "/home/foo/mylogs". If the connection to the socket fails
356 or a write error is detected, the function writes to stderr and
357 tries the next time again to connect the socket.
360 log_set_file (const char *name
)
362 set_file_fd (name
? name
: "-", -1);
368 set_file_fd (NULL
, fd
);
373 log_set_get_tid_callback (unsigned long (*cb
)(void))
375 get_tid_callback
= cb
;
380 log_set_prefix (const char *text
, unsigned int flags
)
384 strncpy (prefix_buffer
, text
, sizeof (prefix_buffer
)-1);
385 prefix_buffer
[sizeof (prefix_buffer
)-1] = 0;
388 with_prefix
= (flags
& JNLIB_LOG_WITH_PREFIX
);
389 with_time
= (flags
& JNLIB_LOG_WITH_TIME
);
390 with_pid
= (flags
& JNLIB_LOG_WITH_PID
);
391 running_detached
= (flags
& JNLIB_LOG_RUN_DETACHED
);
396 log_get_prefix (unsigned int *flags
)
402 *flags
|= JNLIB_LOG_WITH_PREFIX
;
404 *flags
|= JNLIB_LOG_WITH_TIME
;
406 *flags
|= JNLIB_LOG_WITH_PID
;
407 if (running_detached
)
408 *flags
|= JNLIB_LOG_RUN_DETACHED
;
410 return prefix_buffer
;
413 /* This function returns true if the file descriptor FD is in use for
414 logging. This is preferable over a test using log_get_fd in that
415 it allows the logging code to use more then one file descriptor. */
421 int tmp
= fileno (logstream
);
422 if ( tmp
!= -1 && tmp
== fd
)
425 if (log_socket
!= -1 && log_socket
== fd
)
433 return fileno(logstream
?logstream
:stderr
);
439 /* FIXME: We should not return stderr here but initialize the log
440 stream properly. This might break more things than using stderr,
442 return logstream
?logstream
:stderr
;
446 do_logv (int level
, const char *fmt
, va_list arg_ptr
)
450 log_set_file (NULL
); /* Make sure a log stream has been set. */
454 if (missing_lf
&& level
!= JNLIB_LOG_CONT
)
455 putc('\n', logstream
);
458 if (level
!= JNLIB_LOG_CONT
)
459 { /* Note this does not work for multiple line logging as we would
460 * need to print to a buffer first */
461 if (with_time
&& !force_prefixes
)
464 time_t atime
= time (NULL
);
466 tp
= localtime (&atime
);
467 fprintf (logstream
, "%04d-%02d-%02d %02d:%02d:%02d ",
468 1900+tp
->tm_year
, tp
->tm_mon
+1, tp
->tm_mday
,
469 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
);
471 if (with_prefix
|| force_prefixes
)
472 fputs (prefix_buffer
, logstream
);
473 if (with_pid
|| force_prefixes
)
475 if (get_tid_callback
)
476 fprintf (logstream
, "[%u.%lx]",
477 (unsigned int)getpid (), get_tid_callback ());
479 fprintf (logstream
, "[%u]", (unsigned int)getpid ());
481 if (!with_time
|| force_prefixes
)
482 putc (':', logstream
);
483 /* A leading backspace suppresses the extra space so that we can
484 correctly output, programname, filename and linenumber. */
485 if (fmt
&& *fmt
== '\b')
488 putc (' ', logstream
);
493 case JNLIB_LOG_BEGIN
: break;
494 case JNLIB_LOG_CONT
: break;
495 case JNLIB_LOG_INFO
: break;
496 case JNLIB_LOG_WARN
: break;
497 case JNLIB_LOG_ERROR
: break;
498 case JNLIB_LOG_FATAL
: fputs("Fatal: ",logstream
); break;
499 case JNLIB_LOG_BUG
: fputs("Ohhhh jeeee: ", logstream
); break;
500 case JNLIB_LOG_DEBUG
: fputs("DBG: ", logstream
); break;
501 default: fprintf(logstream
,"[Unknown log level %d]: ", level
); break;
507 vfprintf(logstream
,fmt
,arg_ptr
) ;
508 if (*fmt
&& fmt
[strlen(fmt
)-1] != '\n')
510 #ifdef HAVE_W32_SYSTEM
516 if (level
== JNLIB_LOG_FATAL
)
519 putc('\n', logstream
);
522 if (level
== JNLIB_LOG_BUG
)
525 putc('\n', logstream
);
531 do_log( int level
, const char *fmt
, ... )
535 va_start( arg_ptr
, fmt
) ;
536 do_logv( level
, fmt
, arg_ptr
);
542 log_logv (int level
, const char *fmt
, va_list arg_ptr
)
544 do_logv (level
, fmt
, arg_ptr
);
548 log_info( const char *fmt
, ... )
552 va_start( arg_ptr
, fmt
) ;
553 do_logv( JNLIB_LOG_INFO
, fmt
, arg_ptr
);
558 log_error( const char *fmt
, ... )
562 va_start( arg_ptr
, fmt
) ;
563 do_logv( JNLIB_LOG_ERROR
, fmt
, arg_ptr
);
565 /* protect against counter overflow */
566 if( errorcount
< 30000 )
572 log_fatal( const char *fmt
, ... )
576 va_start( arg_ptr
, fmt
) ;
577 do_logv( JNLIB_LOG_FATAL
, fmt
, arg_ptr
);
579 abort(); /* never called, but it makes the compiler happy */
583 log_bug( const char *fmt
, ... )
587 va_start( arg_ptr
, fmt
) ;
588 do_logv( JNLIB_LOG_BUG
, fmt
, arg_ptr
);
590 abort(); /* never called, but it makes the compiler happy */
594 log_debug( const char *fmt
, ... )
598 va_start( arg_ptr
, fmt
) ;
599 do_logv( JNLIB_LOG_DEBUG
, fmt
, arg_ptr
);
605 log_printf (const char *fmt
, ...)
609 va_start (arg_ptr
, fmt
);
610 do_logv (fmt
? JNLIB_LOG_CONT
: JNLIB_LOG_BEGIN
, fmt
, arg_ptr
);
614 /* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
615 dump, with TEXT just an empty string, print a trailing linefeed,
616 otherwise print an entire debug line. */
618 log_printhex (const char *text
, const void *buffer
, size_t length
)
621 log_debug ("%s ", text
);
624 const unsigned char *p
= buffer
;
625 log_printf ("%02X", *p
);
626 for (length
--, p
++; length
--; p
++)
627 log_printf (" %02X", *p
);
634 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
636 bug_at( const char *file
, int line
, const char *func
)
638 do_log( JNLIB_LOG_BUG
,
639 ("... this is a bug (%s:%d:%s)\n"), file
, line
, func
);
640 abort(); /* never called, but it makes the compiler happy */
644 bug_at( const char *file
, int line
)
646 do_log( JNLIB_LOG_BUG
,
647 _("you found a bug ... (%s:%d)\n"), file
, line
);
648 abort(); /* never called, but it makes the compiler happy */