2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* the logging subsystem */
8 #include "sysincludes.h"
13 #include "vsnprintf_r.h"
14 #include "snprinterr.h"
20 /* translate MSG level to SYSLOG level */
32 int shutup
; /* decrease msglevel by this value */
38 int exitstatus
; /* pass signal number to error exit */
39 bool withhostname
; /* in custom logs add hostname */
45 static void _diag_exit(int status
);
48 struct diag_opts diagopts
=
49 { NULL
, E_WARN
, 0, E_ERROR
, 0, NULL
, LOG_DAEMON
, false, 0, false, NULL
, true } ;
52 #if HAVE_CLOCK_GETTIME
54 #elif HAVE_PROTOTYPE_LIB_gettimeofday
59 int level
, int exitcode
, int handler
, const char *text
);
60 static void _msg(int level
, const char *buff
, const char *syslp
);
62 volatile sig_atomic_t diag_in_handler
; /* !=0 indicates to msg() that in signal handler */
63 volatile sig_atomic_t diag_immediate_msg
; /* !=0 prints messages even from within signal handler instead of deferring them */
64 volatile sig_atomic_t diag_immediate_exit
; /* !=0 calls exit() from diag_exit() even when in signal handler. For system() */
66 static struct wordent facilitynames
[] = {
67 {"auth", (void *)LOG_AUTH
},
69 {"authpriv", (void *)LOG_AUTHPRIV
},
72 {"console", (void *)LOG_CONSOLE
},
74 {"cron", (void *)LOG_CRON
},
75 {"daemon", (void *)LOG_DAEMON
},
77 {"ftp", (void *)LOG_FTP
},
79 {"kern", (void *)LOG_KERN
},
80 {"local0", (void *)LOG_LOCAL0
},
81 {"local1", (void *)LOG_LOCAL1
},
82 {"local2", (void *)LOG_LOCAL2
},
83 {"local3", (void *)LOG_LOCAL3
},
84 {"local4", (void *)LOG_LOCAL4
},
85 {"local5", (void *)LOG_LOCAL5
},
86 {"local6", (void *)LOG_LOCAL6
},
87 {"local7", (void *)LOG_LOCAL7
},
88 {"lpr", (void *)LOG_LPR
},
89 {"mail", (void *)LOG_MAIL
},
90 {"news", (void *)LOG_NEWS
},
92 {"security", (void *)LOG_SECURITY
},
94 {"syslog", (void *)LOG_SYSLOG
},
95 {"user", (void *)LOG_USER
},
96 {"uucp", (void *)LOG_UUCP
}
99 /* serialize message for sending from signal handlers */
102 #if HAVE_CLOCK_GETTIME
109 static int diaginitialized
;
110 static int diag_sock_send
= -1;
111 static int diag_sock_recv
= -1;
112 static volatile sig_atomic_t diag_msg_avail
= 0; /* !=0: messages from within signal handler may be waiting */
115 static int diag_sock_pair(void) {
118 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, handlersocks
) < 0) {
123 fcntl(handlersocks
[0], F_SETFD
, FD_CLOEXEC
);
124 fcntl(handlersocks
[1], F_SETFD
, FD_CLOEXEC
);
125 diag_sock_send
= handlersocks
[1];
126 diag_sock_recv
= handlersocks
[0];
127 #if !defined(MSG_DONTWAIT)
128 fcntl(diag_sock_send
, F_SETFL
, O_NONBLOCK
);
129 fcntl(diag_sock_recv
, F_SETFL
, O_NONBLOCK
);
131 fcntl(diag_sock_send
, F_SETFD
, FD_CLOEXEC
);
132 fcntl(diag_sock_recv
, F_SETFD
, FD_CLOEXEC
);
136 static int diag_init(void) {
137 if (diaginitialized
) {
141 /* gcc with GNU libc refuses to set this in the initializer */
142 diagopts
.logfile
= stderr
;
143 if (diagopts
.signalsafe
) {
144 if (diag_sock_pair() < 0) {
150 #define DIAG_INIT ((void)(diaginitialized || diag_init()))
153 void diag_set(char what
, const char *arg
) {
156 if (diagopts
.signalsafe
) {
157 if (diag_sock_send
>= 0) { Close(diag_sock_send
); diag_sock_send
= -1; }
158 if (diag_sock_recv
>= 0) { Close(diag_sock_recv
); diag_sock_recv
= -1; }
160 diagopts
.signalsafe
= false;
166 const struct wordent
*keywd
;
168 case 'y': diagopts
.syslog
= true;
171 keyw(facilitynames
, arg
,
172 sizeof(facilitynames
)/sizeof(struct wordent
))) == NULL
) {
173 Error1("unknown syslog facility \"%s\"", arg
);
175 diagopts
.logfacility
= (int)(size_t)keywd
->desc
;
178 openlog(diagopts
.progname
, LOG_PID
, diagopts
.logfacility
);
179 if (diagopts
.logfile
!= NULL
&& diagopts
.logfile
!= stderr
) {
180 fclose(diagopts
.logfile
);
182 diagopts
.logfile
= NULL
;
185 if (diagopts
.logfile
!= NULL
&& diagopts
.logfile
!= stderr
) {
186 fclose(diagopts
.logfile
);
188 if ((diagopts
.logfile
= fopen(arg
, "a")) == NULL
) {
189 Error2("cannot open log file \"%s\": %s", arg
, strerror(errno
));
193 if (diagopts
.logfile
!= NULL
&& diagopts
.logfile
!= stderr
) {
194 fclose(diagopts
.logfile
);
196 diagopts
.logfile
= stderr
; break; /* logging to stderr is default */
197 case 'p': diagopts
.progname
= arg
;
198 openlog(diagopts
.progname
, LOG_PID
, diagopts
.logfacility
);
200 case 'u': diagopts
.micros
= true; break;
201 default: msg(E_ERROR
, "unknown diagnostic option %c", what
);
205 void diag_set_int(char what
, int arg
) {
208 case 'D': diagopts
.msglevel
= arg
; break;
209 case 'e': diagopts
.exitlevel
= arg
; break;
210 case 'x': diagopts
.exitstatus
= arg
; break;
212 diagopts
.msglevel
= arg
;
214 case 'h': diagopts
.withhostname
= arg
;
215 if ((diagopts
.hostname
= getenv("HOSTNAME")) == NULL
) {
218 diagopts
.hostname
= strdup(ubuf
.nodename
);
222 diagopts
.shutup
= arg
;
223 diagopts
.exitlevel
-= arg
;
225 default: msg(E_ERROR
, "unknown diagnostic option %c", what
);
229 int diag_get_int(char what
) {
232 case 'y': return diagopts
.syslog
;
233 case 's': return diagopts
.logfile
== stderr
;
234 case 'd': case 'D': return diagopts
.msglevel
;
235 case 'e': return diagopts
.exitlevel
;
240 const char *diag_get_string(char what
) {
243 case 'p': return diagopts
.progname
;
248 /* make sure that the diag_sock fds do not have this num */
249 int diag_reserve_fd(int fd
) {
251 if (diag_sock_send
== fd
) {
252 diag_sock_send
= Dup(fd
);
255 if (diag_sock_recv
== fd
) {
256 diag_sock_recv
= Dup(fd
);
262 /* call this after a fork() from the child process to separate master/parent
263 sockets from child sockets */
265 Close(diag_sock_send
);
266 Close(diag_sock_recv
);
267 if (diagopts
.signalsafe
) {
268 return diag_sock_pair();
273 /* Linux and AIX syslog format:
274 Oct 4 17:10:37 hostname socat[52798]: D signal(13, 1)
276 void msg(int level
, const char *format
, ...) {
277 struct diag_dgram diag_dgram
;
280 /* does not perform a system call if nothing todo, thanks diag_msg_avail */
282 diag_dgram
._errno
= errno
; /* keep for passing from signal handler to sock.
283 reason is that strerror is definitely not
287 /* in normal program flow (not in signal handler) */
288 /* first flush the queue of datagrams from the socket */
289 if (diag_msg_avail
&& !diag_in_handler
) {
293 level
-= diagopts
.shutup
; /* decrease severity of messages? */
295 /* Just ignore this call when level too low for both logging and exiting */
296 if (level
< diagopts
.msglevel
&& level
< diagopts
.exitlevel
)
299 va_start(ap
, format
);
301 /* we do only a minimum in the outer parts which may run in a signal handler
302 these are: get actual time, level, serialized message and write them to socket
304 diag_dgram
.op
= DIAG_OP_MSG
;
305 #if HAVE_CLOCK_GETTIME
306 clock_gettime(CLOCK_REALTIME
, &diag_dgram
.now
);
307 #elif HAVE_PROTOTYPE_LIB_gettimeofday
308 gettimeofday(&diag_dgram
.now
, NULL
);
310 diag_dgram
.now
= time(NULL
);
312 diag_dgram
.level
= level
;
313 diag_dgram
.exitcode
= diagopts
.exitstatus
;
314 if (level
>= diagopts
.msglevel
)
315 vsnprintf_r(diag_dgram
.text
, sizeof(diag_dgram
.text
), format
, ap
);
317 diag_dgram
.text
[0] = '\0';
318 if (diagopts
.signalsafe
&& diag_in_handler
&& !diag_immediate_msg
) {
319 send(diag_sock_send
, &diag_dgram
, sizeof(diag_dgram
)-TEXTLEN
+ strlen(diag_dgram
.text
)+1,
320 0 /* for canonical reasons */
333 msg2(&diag_dgram
.now
, diag_dgram
.level
, diagopts
.exitstatus
, 0, diag_dgram
.text
);
338 #if HAVE_CLOCK_GETTIME
339 struct timespec
*now
,
340 #elif HAVE_PROTOTYPE_LIB_gettimeofday
345 int level
, /* E_INFO... */
346 int exitcode
, /* on exit use this exit code */
347 int handler
, /* message comes from signal handler */
350 unsigned long micros
;
355 char buff
[MSGLEN
+2], *bufp
= buff
, *syslp
= NULL
;
358 if (text
[0] != '\0') {
359 #if HAVE_CLOCK_GETTIME
361 #elif HAVE_PROTOTYPE_LIB_gettimeofday
366 /*! consider caching instead of recalculating many times per second */
368 bytes
= strftime(bufp
, 20, "%Y/%m/%d %H:%M:%S", localtime_r(&epoch
, &struct_tm
));
370 bytes
= snprintf(bufp
, 11, F_time
, epoch
);
374 if (diagopts
.micros
) {
375 #if HAVE_CLOCK_GETTIME
376 micros
= now
->tv_nsec
/1000;
377 #elif HAVE_PROTOTYPE_LIB_gettimeofday
378 micros
= now
->tv_usec
;
382 bufp
+= sprintf(bufp
, ".%06lu ", micros
);
388 if (diagopts
.withhostname
) {
389 bytes
= snprintf(bufp
, MSGLEN
-(bufp
-buff
), "%s ", diagopts
.hostname
);
390 if (bytes
>= MSGLEN
-(bufp
-buff
))
391 bytes
= MSGLEN
-(bufp
-buff
)-1;
394 bytes
= snprintf(bufp
, MSGLEN
-(bufp
-buff
), "%s["F_pid
"] ", diagopts
.progname
, getpid());
395 if (bytes
>= MSGLEN
-(bufp
-buff
))
396 bytes
= MSGLEN
-(bufp
-buff
)-1;
398 syslp
= bufp
; /* syslog prefixes with time etc.itself */
399 if (bufp
< buff
+MSGLEN
)
400 *bufp
++ = "DINWEF"[level
];
401 #if 0 /* only for debugging socat */
402 if (handler
) bufp
[-1] = tolower((unsigned char)bufp
[-1]); /* for debugging, low chars indicate messages from signal handlers */
404 if (bufp
< buff
+MSGLEN
)
406 strncpy(bufp
, text
, MSGLEN
-(bufp
-buff
));
407 bufp
[MSGLEN
-(bufp
-buff
)] = 0;
408 bufp
= strchr(bufp
, '\0');
410 _msg(level
, buff
, syslp
);
412 if (level
>= diagopts
.exitlevel
) {
413 if (E_NOTICE
>= diagopts
.msglevel
&& text
[0] != '\0') {
414 if ((syslp
- buff
) + 16 > MSGLEN
+1)
415 syslp
= buff
+ MSGLEN
- 15;
416 snprintf_r(syslp
, 16, "N exit(%d)\n", exitcode
?exitcode
:(diagopts
.exitstatus
?diagopts
.exitstatus
:1));
417 _msg(E_NOTICE
, buff
, syslp
);
419 exit(exitcode
?exitcode
:(diagopts
.exitstatus
?diagopts
.exitstatus
:1));
424 static void _msg(int level
, const char *buff
, const char *syslp
) {
425 if (diagopts
.syslog
) {
426 /* prevent format string attacks (thanks to CoKi) */
427 syslog(syslevel
[level
], "%s", syslp
);
429 if (diagopts
.logfile
) {
430 fputs(buff
, diagopts
.logfile
); fflush(diagopts
.logfile
);
435 /* handle the messages in the queue */
436 void diag_flush(void) {
437 struct diag_dgram recv_dgram
;
440 if (diag_msg_avail
== 0) {
445 if (!diagopts
.signalsafe
) {
449 while (recv(diag_sock_recv
, &recv_dgram
, sizeof(recv_dgram
)-1,
450 0 /* for canonical reasons */
455 recv_dgram
.text
[TEXTLEN
-1] = '\0';
456 switch (recv_dgram
.op
) {
458 /* we want the actual time, not when this dgram was sent */
459 #if HAVE_CLOCK_GETTIME
460 clock_gettime(CLOCK_REALTIME
, &recv_dgram
.now
);
461 #elif HAVE_PROTOTYPE_LIB_gettimeofday
462 gettimeofday(&recv_dgram
.now
, NULL
);
464 recv_dgram
.now
= time(NULL
);
466 if (E_NOTICE
>= diagopts
.msglevel
) {
467 snprintf_r(exitmsg
, sizeof(exitmsg
), "exit(%d)", recv_dgram
.exitcode
?recv_dgram
.exitcode
:1);
468 msg2(&recv_dgram
.now
, E_NOTICE
, recv_dgram
.exitcode
?recv_dgram
.exitcode
:1, 1, exitmsg
);
470 exit(recv_dgram
.exitcode
?recv_dgram
.exitcode
:1);
472 if (recv_dgram
._errno
) {
473 /* there might be a %m control in the string (glibc compatible,
474 replace with strerror(...errno) ) */
476 errno
= recv_dgram
._errno
;
477 snprinterr(text
, TEXTLEN
, recv_dgram
.text
);
478 msg2(&recv_dgram
.now
, recv_dgram
.level
, recv_dgram
.exitcode
, 1, text
);
480 msg2(&recv_dgram
.now
, recv_dgram
.level
, recv_dgram
.exitcode
, 1, recv_dgram
.text
);
488 /* use a new log output file descriptor that is dup'ed from the current one.
489 this is useful when socat logs to stderr but fd 2 should be redirected to
490 serve other purposes */
495 if (diagopts
.logfile
== NULL
) {
498 newfd
= dup(fileno(diagopts
.logfile
));
499 Fcntl_l(newfd
, F_SETFD
, FD_CLOEXEC
);
500 if (diagopts
.logfile
!= stderr
) {
501 fclose(diagopts
.logfile
);
504 diagopts
.logfile
= fdopen(newfd
, "w");
510 /* this function is kind of async-signal-safe exit(). When invoked from signal
511 handler it defers exit. */
512 void diag_exit(int status
) {
513 struct diag_dgram diag_dgram
;
515 if (diag_in_handler
&& !diag_immediate_exit
) {
516 diag_dgram
.op
= DIAG_OP_EXIT
;
517 diag_dgram
.exitcode
= status
;
518 send(diag_sock_send
, &diag_dgram
, sizeof(diag_dgram
)-TEXTLEN
,
519 0 /* for canonical reasons */
533 static void _diag_exit(int status
) {
538 /* a function that appears to the application like select() but that also
539 monitors the diag socket diag_sock_recv and processes its messages.
540 Do not call from within a signal handler. */
541 int diag_select(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
542 fd_set
*exceptfds
, struct timeval
*timeout
) {
544 fd_set save_readfds
, save_writefds
, save_exceptfds
;
546 if (readfds
) { memcpy(&save_readfds
, readfds
, sizeof(*readfds
)); }
547 if (writefds
) { memcpy(&save_writefds
, writefds
, sizeof(*writefds
)); }
548 if (exceptfds
) { memcpy(&save_exceptfds
, exceptfds
, sizeof(*exceptfds
)); }
551 FD_SET(diag_sock_recv
, readfds
);
552 result
= Select(nfds
, readfds
, writefds
,
554 if (!FD_ISSET(diag_sock_recv
, readfds
)) {
555 /* select terminated not due to diag_sock_recv, normalt continuation */
559 if (readfds
) { memcpy(readfds
, &save_readfds
, sizeof(*readfds
)); }
560 if (writefds
) { memcpy(writefds
, &save_writefds
, sizeof(*writefds
)); }
561 if (exceptfds
) { memcpy(exceptfds
, &save_exceptfds
, sizeof(*exceptfds
)); }