nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /* source: error.c */
2 /* Copyright Gerhard Rieger */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4  
5 /* the logging subsystem */
6  
7 #include "config.h"
8 #include "sysincludes.h"
9  
10 #include "mytypes.h"
11 #include "compat.h"
12 #include "utils.h"
13 #include "vsnprintf_r.h"
14 #include "snprinterr.h"
15  
16 #include "error.h"
17 #include "sycls.h"
18  
19 /* translate MSG level to SYSLOG level */
20 int syslevel[] = {
21 LOG_DEBUG,
22 LOG_INFO,
23 LOG_NOTICE,
24 LOG_WARNING,
25 LOG_ERR,
26 LOG_CRIT };
27  
28 struct diag_opts {
29 const char *progname;
30 int msglevel;
31 int exitlevel;
32 int syslog;
33 FILE *logfile;
34 int logfacility;
35 bool micros;
36 int exitstatus; /* pass signal number to error exit */
37 bool withhostname; /* in custom logs add hostname */
38 char *hostname;
39 } ;
40  
41  
42 static void _diag_exit(int status);
43  
44  
45 struct diag_opts diagopts =
46 { NULL, E_ERROR, E_ERROR, 0, NULL, LOG_DAEMON, false, 0 } ;
47  
48 static void msg2(
49 #if HAVE_CLOCK_GETTIME
50 struct timespec *now,
51 #elif HAVE_GETTIMEOFDAY
52 struct timeval *now,
53 #else
54 time_t *now,
55 #endif
56 int level, int exitcode, int handler, const char *text);
57 static void _msg(int level, const char *buff, const char *syslp);
58 sig_atomic_t diag_in_handler; /* !=0 indicates to msg() that in signal handler */
59 sig_atomic_t diag_immediate_msg; /* !=0 prints messages even from within signal handler instead of deferring them */
60 sig_atomic_t diag_immediate_exit; /* !=0 calls exit() from diag_exit() even when in signal handler. For system() */
61  
62 static struct wordent facilitynames[] = {
63 {"auth", (void *)LOG_AUTH},
64 #ifdef LOG_AUTHPRIV
65 {"authpriv", (void *)LOG_AUTHPRIV},
66 #endif
67 #ifdef LOG_CONSOLE
68 {"console", (void *)LOG_CONSOLE},
69 #endif
70 {"cron", (void *)LOG_CRON},
71 {"daemon", (void *)LOG_DAEMON},
72 #ifdef LOG_FTP
73 {"ftp", (void *)LOG_FTP},
74 #endif
75 {"kern", (void *)LOG_KERN},
76 {"local0", (void *)LOG_LOCAL0},
77 {"local1", (void *)LOG_LOCAL1},
78 {"local2", (void *)LOG_LOCAL2},
79 {"local3", (void *)LOG_LOCAL3},
80 {"local4", (void *)LOG_LOCAL4},
81 {"local5", (void *)LOG_LOCAL5},
82 {"local6", (void *)LOG_LOCAL6},
83 {"local7", (void *)LOG_LOCAL7},
84 {"lpr", (void *)LOG_LPR},
85 {"mail", (void *)LOG_MAIL},
86 {"news", (void *)LOG_NEWS},
87 #ifdef LOG_SECURITY
88 {"security", (void *)LOG_SECURITY},
89 #endif
90 {"syslog", (void *)LOG_SYSLOG},
91 {"user", (void *)LOG_USER},
92 {"uucp", (void *)LOG_UUCP}
93 } ;
94  
95 /* serialize message for sending from signal handlers */
96 struct sermsg {
97 int severity;
98 #if HAVE_CLOCK_GETTIME
99 struct timespec ts;
100 #else
101 struct timeval tv;
102 #endif
103 } ;
104  
105 static int diaginitialized;
106 static int diag_sock_send = -1;
107 static int diag_sock_recv = -1;
108 static int diag_msg_avail = 0; /* !=0: messages from within signal handler may be waiting */
109  
110 static int diag_init(void) {
111 int handlersocks[2];
112  
113 if (diaginitialized) {
114 return 0;
115 }
116 diaginitialized = 1;
117 /* gcc with GNU libc refuses to set this in the initializer */
118 diagopts.logfile = stderr;
119 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, handlersocks) < 0) {
120 diag_sock_send = -1;
121 diag_sock_recv = -1;
122 return -1;
123 }
124 diag_sock_send = handlersocks[1];
125 diag_sock_recv = handlersocks[0];
126 return 0;
127 }
128 #define DIAG_INIT ((void)(diaginitialized || diag_init()))
129  
130  
131 void diag_set(char what, const char *arg) {
132 DIAG_INIT;
133 switch (what) {
134 const struct wordent *keywd;
135  
136 case 'y': diagopts.syslog = true;
137 if (arg && arg[0]) {
138 if ((keywd =
139 keyw(facilitynames, arg,
140 sizeof(facilitynames)/sizeof(struct wordent))) == NULL) {
141 Error1("unknown syslog facility \"%s\"", arg);
142 } else {
143 diagopts.logfacility = (int)(size_t)keywd->desc;
144 }
145 }
146 openlog(diagopts.progname, LOG_PID, diagopts.logfacility);
147 if (diagopts.logfile != NULL && diagopts.logfile != stderr) {
148 fclose(diagopts.logfile);
149 }
150 diagopts.logfile = NULL;
151 break;
152 case 'f':
153 if (diagopts.logfile != NULL && diagopts.logfile != stderr) {
154 fclose(diagopts.logfile);
155 }
156 if ((diagopts.logfile = fopen(arg, "a")) == NULL) {
157 Error2("cannot open log file \"%s\": %s", arg, strerror(errno));
158 }
159 break;
160 case 's':
161 if (diagopts.logfile != NULL && diagopts.logfile != stderr) {
162 fclose(diagopts.logfile);
163 }
164 diagopts.logfile = stderr; break; /* logging to stderr is default */
165 case 'p': diagopts.progname = arg;
166 openlog(diagopts.progname, LOG_PID, diagopts.logfacility);
167 break;
168 case 'd': --diagopts.msglevel; break;
169 case 'u': diagopts.micros = true; break;
170 default: msg(E_ERROR, "unknown diagnostic option %c", what);
171 }
172 }
173  
174 void diag_set_int(char what, int arg) {
175 DIAG_INIT;
176 switch (what) {
177 case 'D': diagopts.msglevel = arg; break;
178 case 'e': diagopts.exitlevel = arg; break;
179 case 'x': diagopts.exitstatus = arg; break;
180 case 'h': diagopts.withhostname = arg;
181 if ((diagopts.hostname = getenv("HOSTNAME")) == NULL) {
182 struct utsname ubuf;
183 uname(&ubuf);
184 diagopts.hostname = strdup(ubuf.nodename);
185 }
186 break;
187 default: msg(E_ERROR, "unknown diagnostic option %c", what);
188 }
189 }
190  
191 int diag_get_int(char what) {
192 DIAG_INIT;
193 switch (what) {
194 case 'y': return diagopts.syslog;
195 case 's': return diagopts.logfile == stderr;
196 case 'd': case 'D': return diagopts.msglevel;
197 case 'e': return diagopts.exitlevel;
198 }
199 return -1;
200 }
201  
202 const char *diag_get_string(char what) {
203 DIAG_INIT;
204 switch (what) {
205 case 'p': return diagopts.progname;
206 }
207 return NULL;
208 }
209  
210  
211 /* Linux and AIX syslog format:
212 Oct 4 17:10:37 hostname socat[52798]: D signal(13, 1)
213 */
214 void msg(int level, const char *format, ...) {
215 struct diag_dgram diag_dgram;
216 va_list ap;
217  
218 /* does not perform a system call if nothing todo, thanks diag_msg_avail */
219  
220 diag_dgram._errno = errno; /* keep for passing from signal handler to sock.
221 reason is that strerror is definitely not
222 async-signal-safe */
223 DIAG_INIT;
224  
225 /* in normal program flow (not in signal handler) */
226 /* first flush the queue of datagrams from the socket */
227 if (diag_msg_avail && !diag_in_handler) {
228 diag_msg_avail = 0; /* _before_ flush to prevent inconsistent state when signal occurs inbetween */
229 diag_flush();
230 }
231  
232 if (level < diagopts.msglevel) { va_end(ap); return; }
233 va_start(ap, format);
234  
235 /* we do only a minimum in the outer parts which may run in a signal handler
236 these are: get actual time, level, serialized message and write them to socket
237 */
238 diag_dgram.op = DIAG_OP_MSG;
239 #if HAVE_CLOCK_GETTIME
240 clock_gettime(CLOCK_REALTIME, &diag_dgram.now);
241 #elif HAVE_GETTIMEOFDAY
242 gettimeofday(&diag_dgram.now, NULL);
243 #else
244 diag_dgram.now = time(NULL);
245 #endif
246 diag_dgram.level = level;
247 diag_dgram.exitcode = diagopts.exitstatus;
248 vsnprintf_r(diag_dgram.text, sizeof(diag_dgram.text), format, ap);
249 if (diag_in_handler) {
250 send(diag_sock_send, &diag_dgram, sizeof(diag_dgram)-TEXTLEN + strlen(diag_dgram.text)+1, MSG_DONTWAIT
251 #ifdef MSG_NOSIGNAL
252 |MSG_NOSIGNAL
253 #endif
254 );
255 diag_msg_avail = 1;
256 va_end(ap);
257 return;
258 }
259  
260 msg2(&diag_dgram.now, diag_dgram.level, diagopts.exitstatus, 0, diag_dgram.text);
261 va_end(ap); return;
262 }
263  
264 void msg2(
265 #if HAVE_CLOCK_GETTIME
266 struct timespec *now,
267 #elif HAVE_GETTIMEOFDAY
268 struct timeval *now,
269 #else
270 time_t *now,
271 #endif
272 int level, /* E_INFO... */
273 int exitcode, /* on exit use this exit code */
274 int handler, /* message comes from signal handler */
275 const char *text) {
276 time_t epoch;
277 unsigned long micros;
278 #if HAVE_STRFTIME
279 struct tm struct_tm;
280 #endif
281 #define BUFLEN 512
282 char buff[BUFLEN], *bufp, *syslp;
283 size_t bytes;
284  
285 #if HAVE_CLOCK_GETTIME
286 epoch = now->tv_sec;
287 #elif HAVE_GETTIMEOFDAY
288 epoch = now->tv_sec;
289 #else
290 epoch = *now;
291 #endif
292 #if HAVE_STRFTIME
293 bytes = strftime(buff, 20, "%Y/%m/%d %H:%M:%S", localtime_r(&epoch, &struct_tm));
294 buff[bytes] = '\0';
295 #else
296 bytes = snprintf(buff, 11, F_time, epoch);
297 #endif
298 if (diagopts.micros) {
299 #if HAVE_CLOCK_GETTIME
300 micros = now->tv_nsec/1000;
301 #elif HAVE_GETTIMEOFDAY
302 micros = now->tv_usec;
303 #else
304 micros = 0;
305 #endif
306 bytes += sprintf(buff+19, ".%06lu ", micros);
307 } else {
308 buff[19] = ' '; buff[20] = '\0';
309 }
310 bytes = strlen(buff);
311  
312 bufp = buff + bytes;
313 if (diagopts.withhostname) {
314 bytes = sprintf(bufp, "%s ", diagopts.hostname), bufp+=bytes;
315 }
316 bytes = sprintf(bufp, "%s["F_pid".%lu] ",
317 diagopts.progname, getpid(), pthread_self());
318 bufp += bytes;
319 syslp = bufp;
320 *bufp++ = "DINWEF"[level];
321 #if 0 /* only for debugging socat */
322 if (handler) bufp[-1] = tolower(bufp[-1]);*/ /* for debugging, low chars indicates messages within signal handlers */
323 #endif
324 *bufp++ = ' ';
325 strncpy(bufp, text, BUFLEN-(bufp-buff)-1);
326 strcat(bufp, "\n");
327 _msg(level, buff, syslp);
328 if (level >= diagopts.exitlevel) {
329 if (E_NOTICE >= diagopts.msglevel) {
330 snprintf_r(syslp, 16, "N exit(%d)\n", exitcode?exitcode:(diagopts.exitstatus?diagopts.exitstatus:1));
331 _msg(E_NOTICE, buff, syslp);
332 }
333 exit(exitcode?exitcode:(diagopts.exitstatus?diagopts.exitstatus:1));
334 }
335 }
336  
337  
338 static void _msg(int level, const char *buff, const char *syslp) {
339 if (diagopts.syslog) {
340 /* prevent format string attacks (thanks to CoKi) */
341 syslog(syslevel[level], "%s", syslp);
342 }
343 if (diagopts.logfile) {
344 fputs(buff, diagopts.logfile); fflush(diagopts.logfile);
345 }
346 }
347  
348  
349 /* handle the messages in the queue */
350 void diag_flush(void) {
351 struct diag_dgram recv_dgram;
352 char exitmsg[20];
353 while (recv(diag_sock_recv, &recv_dgram, sizeof(recv_dgram)-1, MSG_DONTWAIT) > 0) {
354 recv_dgram.text[TEXTLEN-1] = '\0';
355 switch (recv_dgram.op) {
356 case DIAG_OP_EXIT:
357 /* we want the actual time, not when this dgram was sent */
358 #if HAVE_CLOCK_GETTIME
359 clock_gettime(CLOCK_REALTIME, &recv_dgram.now);
360 #elif HAVE_GETTIMEOFDAY
361 gettimeofday(&recv_dgram.now, NULL);
362 #else
363 recv_dgram.now = time(NULL);
364 #endif
365 if (E_NOTICE >= diagopts.msglevel) {
366 snprintf_r(exitmsg, sizeof(exitmsg), "exit(%d)\n", recv_dgram.exitcode?recv_dgram.exitcode:1);
367 msg2(&recv_dgram.now, E_NOTICE, recv_dgram.exitcode?recv_dgram.exitcode:1, 1, exitmsg);
368 }
369 exit(recv_dgram.exitcode?recv_dgram.exitcode:1);
370 case DIAG_OP_MSG:
371 if (recv_dgram._errno) {
372 /* there might be a %m control in the string (glibc compatible,
373 replace with strerror(...errno) ) */
374 char text[TEXTLEN];
375 errno = recv_dgram._errno;
376 snprinterr(text, TEXTLEN, recv_dgram.text);
377 msg2(&recv_dgram.now, recv_dgram.level, recv_dgram.exitcode, 1, text);
378 } else {
379 msg2(&recv_dgram.now, recv_dgram.level, recv_dgram.exitcode, 1, recv_dgram.text);
380 }
381 break;
382 }
383 }
384 }
385  
386  
387 /* use a new log output file descriptor that is dup'ed from the current one.
388 this is useful when socat logs to stderr but fd 2 should be redirected to
389 serve other purposes */
390 int diag_dup(void) {
391 int newfd;
392  
393 DIAG_INIT;
394 if (diagopts.logfile == NULL) {
395 return -1;
396 }
397 newfd = dup(fileno(diagopts.logfile));
398 if (diagopts.logfile != stderr) {
399 fclose(diagopts.logfile);
400 }
401 if (newfd >= 0) {
402 diagopts.logfile = fdopen(newfd, "w");
403 }
404 return newfd;
405 }
406  
407  
408 /* this function is kind of async-signal-safe exit(). When invoked from signal
409 handler it defers exit. */
410 void diag_exit(int status) {
411 struct diag_dgram diag_dgram;
412  
413 if (diag_in_handler && !diag_immediate_exit) {
414 diag_dgram.op = DIAG_OP_EXIT;
415 diag_dgram.exitcode = status;
416 send(diag_sock_send, &diag_dgram, sizeof(diag_dgram)-TEXTLEN, MSG_DONTWAIT
417 #ifdef MSG_NOSIGNAL
418 |MSG_NOSIGNAL
419 #endif
420 );
421 return;
422 }
423 _diag_exit(status);
424 }
425  
426 static void _diag_exit(int status) {
427 Exit(status);
428 }
429  
430  
431 /* a function that appears to the application like select() but that also
432 monitors the diag socket diag_sock_recv and processes its messages.
433 Do not call from within a signal handler. */
434 int diag_select(int nfds, fd_set *readfds, fd_set *writefds,
435 fd_set *exceptfds, struct timeval *timeout) {
436 int result;
437 fd_set save_readfds, save_writefds, save_exceptfds;
438  
439 if (readfds) { memcpy(&save_readfds, readfds, sizeof(*readfds)); }
440 if (writefds) { memcpy(&save_writefds, writefds, sizeof(*writefds)); }
441 if (exceptfds) { memcpy(&save_exceptfds, exceptfds, sizeof(*exceptfds)); }
442  
443 while (1) {
444 FD_SET(diag_sock_recv, readfds);
445 result = Select(nfds, readfds, writefds,
446 exceptfds, timeout);
447 if (!FD_ISSET(diag_sock_recv, readfds)) {
448 /* select terminated not due to diag_sock_recv, normalt continuation */
449 break;
450 }
451 diag_flush();
452 if (readfds) { memcpy(readfds, &save_readfds, sizeof(*readfds)); }
453 if (writefds) { memcpy(writefds, &save_writefds, sizeof(*writefds)); }
454 if (exceptfds) { memcpy(exceptfds, &save_exceptfds, sizeof(*exceptfds)); }
455 }
456 return result;
457 }