2 /* Copyright Gerhard Rieger */
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"
17 #include "sysincludes.h"
20 /* translate MSG level to SYSLOG level */
37 int exitstatus
; /* pass signal number to error exit */
38 bool withhostname
; /* in custom logs add hostname */
43 static void _diag_exit(int status
);
46 struct diag_opts diagopts
=
47 { NULL
, E_ERROR
, E_ERROR
, 0, NULL
, LOG_DAEMON
, false, 0 } ;
50 #if HAVE_CLOCK_GETTIME
52 #elif HAVE_GETTIMEOFDAY
57 int level
, int exitcode
, int handler
, const char *text
);
58 static void _msg(int level
, const char *buff
, const char *syslp
);
60 sig_atomic_t diag_in_handler
; /* !=0 indicates to msg() that in signal handler */
61 sig_atomic_t diag_immediate_msg
; /* !=0 prints messages even from within signal handler instead of deferring them */
62 sig_atomic_t diag_immediate_exit
; /* !=0 calls exit() from diag_exit() even when in signal handler. For system() */
64 static struct wordent facilitynames
[] = {
65 {"auth", (void *)LOG_AUTH
},
67 {"authpriv", (void *)LOG_AUTHPRIV
},
70 {"console", (void *)LOG_CONSOLE
},
72 {"cron", (void *)LOG_CRON
},
73 {"daemon", (void *)LOG_DAEMON
},
75 {"ftp", (void *)LOG_FTP
},
77 {"kern", (void *)LOG_KERN
},
78 {"local0", (void *)LOG_LOCAL0
},
79 {"local1", (void *)LOG_LOCAL1
},
80 {"local2", (void *)LOG_LOCAL2
},
81 {"local3", (void *)LOG_LOCAL3
},
82 {"local4", (void *)LOG_LOCAL4
},
83 {"local5", (void *)LOG_LOCAL5
},
84 {"local6", (void *)LOG_LOCAL6
},
85 {"local7", (void *)LOG_LOCAL7
},
86 {"lpr", (void *)LOG_LPR
},
87 {"mail", (void *)LOG_MAIL
},
88 {"news", (void *)LOG_NEWS
},
90 {"security", (void *)LOG_SECURITY
},
92 {"syslog", (void *)LOG_SYSLOG
},
93 {"user", (void *)LOG_USER
},
94 {"uucp", (void *)LOG_UUCP
}
97 /* serialize message for sending from signal handlers */
100 #if HAVE_CLOCK_GETTIME
107 static int diaginitialized
;
108 static int diag_sock_send
= -1;
109 static int diag_sock_recv
= -1;
110 static int diag_msg_avail
= 0; /* !=0: messages from within signal handler may be waiting */
113 static int diag_init(void) {
116 if (diaginitialized
) {
120 /* gcc with GNU libc refuses to set this in the initializer */
121 diagopts
.logfile
= stderr
;
122 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, handlersocks
) < 0) {
127 diag_sock_send
= handlersocks
[1];
128 diag_sock_recv
= handlersocks
[0];
131 #define DIAG_INIT ((void)(diaginitialized || diag_init()))
134 void diag_set(char what
, const char *arg
) {
137 const struct wordent
*keywd
;
139 case 'y': diagopts
.syslog
= true;
142 keyw(facilitynames
, arg
,
143 sizeof(facilitynames
)/sizeof(struct wordent
))) == NULL
) {
144 Error1("unknown syslog facility \"%s\"", arg
);
146 diagopts
.logfacility
= (int)(size_t)keywd
->desc
;
149 openlog(diagopts
.progname
, LOG_PID
, diagopts
.logfacility
);
150 if (diagopts
.logfile
!= NULL
&& diagopts
.logfile
!= stderr
) {
151 fclose(diagopts
.logfile
);
153 diagopts
.logfile
= NULL
;
156 if (diagopts
.logfile
!= NULL
&& diagopts
.logfile
!= stderr
) {
157 fclose(diagopts
.logfile
);
159 if ((diagopts
.logfile
= fopen(arg
, "a")) == NULL
) {
160 Error2("cannot open log file \"%s\": %s", arg
, strerror(errno
));
164 if (diagopts
.logfile
!= NULL
&& diagopts
.logfile
!= stderr
) {
165 fclose(diagopts
.logfile
);
167 diagopts
.logfile
= stderr
; break; /* logging to stderr is default */
168 case 'p': diagopts
.progname
= arg
;
169 openlog(diagopts
.progname
, LOG_PID
, diagopts
.logfacility
);
171 case 'd': --diagopts
.msglevel
; break;
172 case 'u': diagopts
.micros
= true; break;
173 default: msg(E_ERROR
, "unknown diagnostic option %c", what
);
177 void diag_set_int(char what
, int arg
) {
180 case 'D': diagopts
.msglevel
= arg
; break;
181 case 'e': diagopts
.exitlevel
= arg
; break;
182 case 'x': diagopts
.exitstatus
= arg
; break;
183 case 'h': diagopts
.withhostname
= arg
;
184 if ((diagopts
.hostname
= getenv("HOSTNAME")) == NULL
) {
187 diagopts
.hostname
= strdup(ubuf
.nodename
);
190 default: msg(E_ERROR
, "unknown diagnostic option %c", what
);
194 int diag_get_int(char what
) {
197 case 'y': return diagopts
.syslog
;
198 case 's': return diagopts
.logfile
== stderr
;
199 case 'd': case 'D': return diagopts
.msglevel
;
200 case 'e': return diagopts
.exitlevel
;
205 const char *diag_get_string(char what
) {
208 case 'p': return diagopts
.progname
;
214 /* Linux and AIX syslog format:
215 Oct 4 17:10:37 hostname socat[52798]: D signal(13, 1)
217 void msg(int level
, const char *format
, ...) {
218 struct diag_dgram diag_dgram
;
221 /* does not perform a system call if nothing todo, thanks diag_msg_avail */
223 diag_dgram
._errno
= errno
; /* keep for passing from signal handler to sock.
224 reason is that strerror is definitely not
228 /* in normal program flow (not in signal handler) */
229 /* first flush the queue of datagrams from the socket */
230 if (diag_msg_avail
&& !diag_in_handler
) {
231 diag_msg_avail
= 0; /* _before_ flush to prevent inconsistent state when signal occurs inbetween */
235 if (level
< diagopts
.msglevel
) { va_end(ap
); return; }
236 va_start(ap
, format
);
238 /* we do only a minimum in the outer parts which may run in a signal handler
239 these are: get actual time, level, serialized message and write them to socket
241 diag_dgram
.op
= DIAG_OP_MSG
;
242 #if HAVE_CLOCK_GETTIME
243 clock_gettime(CLOCK_REALTIME
, &diag_dgram
.now
);
244 #elif HAVE_GETTIMEOFDAY
245 gettimeofday(&diag_dgram
.now
, NULL
);
247 diag_dgram
.now
= time(NULL
);
249 diag_dgram
.level
= level
;
250 diag_dgram
.exitcode
= diagopts
.exitstatus
;
251 vsnprintf_r(diag_dgram
.text
, sizeof(diag_dgram
.text
), format
, ap
);
252 if (diag_in_handler
&& !diag_immediate_msg
) {
253 send(diag_sock_send
, &diag_dgram
, sizeof(diag_dgram
)-TEXTLEN
+ strlen(diag_dgram
.text
)+1, MSG_DONTWAIT
263 msg2(&diag_dgram
.now
, diag_dgram
.level
, diagopts
.exitstatus
, 0, diag_dgram
.text
);
268 #if HAVE_CLOCK_GETTIME
269 struct timespec
*now
,
270 #elif HAVE_GETTIMEOFDAY
275 int level
, /* E_INFO... */
276 int exitcode
, /* on exit use this exit code */
277 int handler
, /* message comes from signal handler */
280 unsigned long micros
;
285 char buff
[BUFLEN
], *bufp
, *syslp
;
288 #if HAVE_CLOCK_GETTIME
290 #elif HAVE_GETTIMEOFDAY
296 bytes
= strftime(buff
, 20, "%Y/%m/%d %H:%M:%S", localtime_r(&epoch
, &struct_tm
));
299 bytes
= snprintf(buff
, 11, F_time
, epoch
);
301 if (diagopts
.micros
) {
302 #if HAVE_CLOCK_GETTIME
303 micros
= now
->tv_nsec
/1000;
304 #elif HAVE_GETTIMEOFDAY
305 micros
= now
->tv_usec
;
309 bytes
+= sprintf(buff
+19, ".%06lu ", micros
);
311 buff
[19] = ' '; buff
[20] = '\0';
313 bytes
= strlen(buff
);
316 if (diagopts
.withhostname
) {
317 bytes
= sprintf(bufp
, "%s ", diagopts
.hostname
), bufp
+=bytes
;
319 bytes
= sprintf(bufp
, "%s["F_pid
"] ", diagopts
.progname
, getpid());
322 *bufp
++ = "DINWEF"[level
];
323 #if 0 /* only for debugging socat */
324 if (handler
) bufp
[-1] = tolower(bufp
[-1]); /* for debugging, low chars indicate messages from signal handlers */
327 strncpy(bufp
, text
, BUFLEN
-(bufp
-buff
)-1);
329 _msg(level
, buff
, syslp
);
330 if (level
>= diagopts
.exitlevel
) {
331 if (E_NOTICE
>= diagopts
.msglevel
) {
332 snprintf_r(syslp
, 16, "N exit(%d)\n", exitcode
?exitcode
:(diagopts
.exitstatus
?diagopts
.exitstatus
:1));
333 _msg(E_NOTICE
, buff
, syslp
);
335 exit(exitcode
?exitcode
:(diagopts
.exitstatus
?diagopts
.exitstatus
:1));
340 static void _msg(int level
, const char *buff
, const char *syslp
) {
341 if (diagopts
.syslog
) {
342 /* prevent format string attacks (thanks to CoKi) */
343 syslog(syslevel
[level
], "%s", syslp
);
345 if (diagopts
.logfile
) {
346 fputs(buff
, diagopts
.logfile
); fflush(diagopts
.logfile
);
351 /* handle the messages in the queue */
352 void diag_flush(void) {
353 struct diag_dgram recv_dgram
;
355 while (recv(diag_sock_recv
, &recv_dgram
, sizeof(recv_dgram
)-1, MSG_DONTWAIT
) > 0) {
356 recv_dgram
.text
[TEXTLEN
-1] = '\0';
357 switch (recv_dgram
.op
) {
359 /* we want the actual time, not when this dgram was sent */
360 #if HAVE_CLOCK_GETTIME
361 clock_gettime(CLOCK_REALTIME
, &recv_dgram
.now
);
362 #elif HAVE_GETTIMEOFDAY
363 gettimeofday(&recv_dgram
.now
, NULL
);
365 recv_dgram
.now
= time(NULL
);
367 if (E_NOTICE
>= diagopts
.msglevel
) {
368 snprintf_r(exitmsg
, sizeof(exitmsg
), "exit(%d)", recv_dgram
.exitcode
?recv_dgram
.exitcode
:1);
369 msg2(&recv_dgram
.now
, E_NOTICE
, recv_dgram
.exitcode
?recv_dgram
.exitcode
:1, 1, exitmsg
);
371 exit(recv_dgram
.exitcode
?recv_dgram
.exitcode
:1);
373 if (recv_dgram
._errno
) {
374 /* there might be a %m control in the string (glibc compatible,
375 replace with strerror(...errno) ) */
377 errno
= recv_dgram
._errno
;
378 snprinterr(text
, TEXTLEN
, recv_dgram
.text
);
379 msg2(&recv_dgram
.now
, recv_dgram
.level
, recv_dgram
.exitcode
, 1, text
);
381 msg2(&recv_dgram
.now
, recv_dgram
.level
, recv_dgram
.exitcode
, 1, recv_dgram
.text
);
389 /* use a new log output file descriptor that is dup'ed from the current one.
390 this is useful when socat logs to stderr but fd 2 should be redirected to
391 serve other purposes */
396 if (diagopts
.logfile
== NULL
) {
399 newfd
= dup(fileno(diagopts
.logfile
));
400 if (diagopts
.logfile
!= stderr
) {
401 fclose(diagopts
.logfile
);
404 diagopts
.logfile
= fdopen(newfd
, "w");
410 /* this function is kind of async-signal-safe exit(). When invoked from signal
411 handler it defers exit. */
412 void diag_exit(int status
) {
413 struct diag_dgram diag_dgram
;
415 if (diag_in_handler
&& !diag_immediate_exit
) {
416 diag_dgram
.op
= DIAG_OP_EXIT
;
417 diag_dgram
.exitcode
= status
;
418 send(diag_sock_send
, &diag_dgram
, sizeof(diag_dgram
)-TEXTLEN
, MSG_DONTWAIT
428 static void _diag_exit(int status
) {
433 /* a function that appears to the application like select() but that also
434 monitors the diag socket diag_sock_recv and processes its messages.
435 Do not call from within a signal handler. */
436 int diag_select(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
437 fd_set
*exceptfds
, struct timeval
*timeout
) {
439 fd_set save_readfds
, save_writefds
, save_exceptfds
;
441 if (readfds
) { memcpy(&save_readfds
, readfds
, sizeof(*readfds
)); }
442 if (writefds
) { memcpy(&save_writefds
, writefds
, sizeof(*writefds
)); }
443 if (exceptfds
) { memcpy(&save_exceptfds
, exceptfds
, sizeof(*exceptfds
)); }
446 FD_SET(diag_sock_recv
, readfds
);
447 result
= Select(nfds
, readfds
, writefds
,
449 if (!FD_ISSET(diag_sock_recv
, readfds
)) {
450 /* select terminated not due to diag_sock_recv, normalt continuation */
454 if (readfds
) { memcpy(readfds
, &save_readfds
, sizeof(*readfds
)); }
455 if (writefds
) { memcpy(writefds
, &save_writefds
, sizeof(*writefds
)); }
456 if (exceptfds
) { memcpy(exceptfds
, &save_exceptfds
, sizeof(*exceptfds
)); }