1 /*
2 ** Copyright (C) 2001-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8
9
10 /*
11 ** log.c
12 **
13 ** Library to support file-based logging. Cobbled together based on
14 ** the report.c code written by M. Duggan.
15 **
16 ** Suresh Konda
17 ** 4/23/2003
18 */
19
20
21 #include <silk/silk.h>
22
23 RCSIDENT("$SiLK: sklog.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
24
25 #include <silk/sklog.h>
26 #include <silk/skstringmap.h>
27 #include <silk/utils.h>
28 #include <syslog.h>
29 #include <sys/utsname.h>
30
31
32 /* LOCAL DEFINES AND TYPEDEFS */
33
34 /* size of buffer to use when using a syslog facility that does not
35 * provide vsyslog() */
36 #define MSGBUF_SIZE (4 * PATH_MAX)
37
38 /* size of our hostname field; not all systems seem to define
39 * HOST_NAME_MAX, so we'll use 255+1 from POSIX. */
40 #define SKLOG_HOST_NAME_MAX 256
41
42 /* size of date buffer */
43 #define SKLOG_DATE_BUFSIZ 32
44
45 /* hour at which to rotate the logs */
46 #define SKLOG_ROTATE_HOUR 0
47
48 /* when using log rotation, the suffix to add to file names */
49 #define SKLOG_SUFFIX ".log"
50
51 /* default log level */
52 #define SKLOG_DEFAULT_LEVEL LOG_INFO
53
54 /* default syslog facility */
55 #define SKLOG_SYSFACILITY LOG_USER
56
57 /* default syslog options */
58 #define SKLOG_SYSOPTIONS LOG_PID
59
60 /* number of command-line/config-file options; must agree with
61 * logOptions[] and other logOptions* constants. */
62 #define NUM_OPTIONS 7
63
64 /* invoke the proper logging function if the log has been setup and
65 * the log is open */
66 #define SKLOG_CALL_LOGGER(pri, fmt, args) \
67 if ( !(logctx && logctx->l_open && logctx->l_func)) { /*no-op*/ } \
68 else { logctx->l_func((pri), (fmt), (args)); }
69
70 /* a wrapper around SKLOG_CALL_LOGGER() that sets up and tears down
71 * the va_list. */
72 #define SKLOG_VARARG_CALL_LOGGER(pri, fmt) \
73 { \
74 va_list _args; \
75 va_start(_args, fmt); \
76 SKLOG_CALL_LOGGER((pri), (fmt), _args); \
77 va_end(_args); \
78 }
79
80 /* return non-zero if messages at level 'pri' are being logged */
81 #define SKLOG_INCLUDES_PRI(pri) \
82 logctx->l_priority & LOG_MASK(pri)
83
84 /* invoke the lock and unlock functions if they are defined */
85 #define SKLOG_LOCK() \
86 if ( !logctx->l_lock_fn) { /*no-op*/ } \
87 else { logctx->l_lock_fn(logctx->l_lock_data); }
88 #define SKLOG_UNLOCK() \
89 if ( !logctx->l_unlock_fn) { /*no-op*/ } \
90 else { logctx->l_unlock_fn(logctx->l_lock_data); }
91
92
93
94 /* possible logging destinations */
95 typedef enum {
96 /* no destination has been set */
97 SKLOG_DEST_NOT_SET = 0,
98 /* no logs will be written */
99 SKLOG_DEST_NONE,
100 /* write to a single log file */
101 SKLOG_DEST_PATH,
102 /* for legacy support, write to multiple files in a
103 * directory---support rotation and compression */
104 SKLOG_DEST_DIRECTORY,
105 /* write to stdout; same as DEST_PATH with FILE* of stdout */
106 SKLOG_DEST_STDOUT,
107 /* write to stderr; same as DEST_PATH with FILE* of stderr */
108 SKLOG_DEST_STDERR,
109 /* write using syslog() */
110 SKLOG_DEST_SYSLOG,
111 /* write to syslog() and to stderr. Adds LOG_PERROR to syslog */
112 SKLOG_DEST_BOTH
113 } sklog_dest_t;
114
115
116 /* internal log function */
117 typedef void (*sklog_fn_t)(int priority, const char *fmt, va_list args);
118
119 /* structure to support logging with syslog(3) */
120 typedef struct sklog_system_st {
121 int options;
122 int facility;
123 } sklog_system_t;
124
125 /* structure needed to hold everything to support logging outside of
126 * syslog */
127 typedef struct sklog_simple_st {
128 /* function to call to prepend the time/machine stamp to the message */
129 sklog_stamp_fn_t stamp_fn;
130 char machine_name[SKLOG_HOST_NAME_MAX];
131 char path[2 * (PATH_MAX + SKLOG_DATE_BUFSIZ)];
132 const char *app_name;
133 FILE *fp;
134 } sklog_simple_t;
135
136 /* structure used in conjuction with sklog_simple_t when log rotation
137 * is desired */
138 typedef struct sklog_rotated_st {
139 /* time of next scheduled log rotation */
140 time_t rolltime;
141 /* user command to run on the closed log file; if NULL, compress
142 * the file using SK_LOG_COMPRESSOR */
143 const char *post_rotate;
144 /* the directory in which to write all log files */
145 char dir[PATH_MAX];
146 /* basename of the log files; the date and SKLOG_SUFFIX will be
147 * appended. */
148 char basename[PATH_MAX];
149 } sklog_rotated_t;
150
151 /* the actual logging context */
152 typedef struct sklog_context_st {
153 /* holds the argument that the user provided to each option.
154 * Index is a value from logOptionsEnum */
155 const char *l_opt_values[NUM_OPTIONS];
156 /* holds all the syslog-specific info */
157 sklog_system_t l_sys;
158 /* holds the info when logging to a single file */
159 sklog_simple_t l_sim;
160 /* holds info required in addition to l_sim when using log rotatation */
161 sklog_rotated_t l_rot;
162 /* function to call to write a message to the log; varies
163 * depending on type of log being used */
164 sklog_fn_t l_func;
165 /* functions to call to lock and unlock the log. */
166 sklog_lock_fn_t l_lock_fn;
167 sklog_lock_fn_t l_unlock_fn;
168 sklog_lock_fn_t l_trylock_fn;
169 /* data passed to the l_lock_fn() and l_unlock_fn(). */
170 void *l_lock_data;
171 /* the command line invocation of the application */
172 char *l_cmd;
173 /* which levels of messages to log */
174 int l_priority;
175 /* what features users requested in sklogSetup() */
176 int l_features;
177 /* whether the log is open */
178 unsigned l_open :1;
179 /* whether stdout/stderr go to the log */
180 unsigned l_dup_stdout :1;
181 /* which log destination is being used */
182 sklog_dest_t l_dest;
183 } sklog_context_t;
184
185
186 /* LOCAL VARIABLE DEFINITIONS */
187
188 /* we have a static log structure, and a static pointer that we will
189 * point at it once the logger has been initialized. */
190 static sklog_context_t logger;
191 static sklog_context_t *logctx = NULL;
192
193 /* available destinations */
194 static const sk_stringmap_entry_t log_dest[] = {
195 {"none", SKLOG_DEST_NONE, NULL, NULL},
196 {"stdout", SKLOG_DEST_STDOUT, NULL, NULL},
197 {"stderr", SKLOG_DEST_STDERR, NULL, NULL},
198 {"syslog", SKLOG_DEST_SYSLOG, NULL, NULL},
199 #ifdef LOG_PERROR
200 {"both", SKLOG_DEST_BOTH, NULL, NULL},
201 #endif
202 SK_STRINGMAP_SENTINEL
203 };
204
205 /* available levels */
206 static const sk_stringmap_entry_t log_level[] = {
207 {"emerg", LOG_EMERG, NULL, NULL},
208 {"alert", LOG_ALERT, NULL, NULL},
209 {"crit", LOG_CRIT, NULL, NULL},
210 {"err", LOG_ERR, NULL, NULL},
211 {"warning", LOG_WARNING, NULL, NULL},
212 {"notice", LOG_NOTICE, NULL, NULL},
213 {"info", LOG_INFO, NULL, NULL},
214 {"debug", LOG_DEBUG, NULL, NULL},
215 SK_STRINGMAP_SENTINEL
216 };
217
218 /* available facilities */
219 static const sk_stringmap_entry_t log_facility[] = {
220 {"user", LOG_USER, NULL, NULL},
221 {"local0", LOG_LOCAL0, NULL, NULL},
222 {"local1", LOG_LOCAL1, NULL, NULL},
223 {"local2", LOG_LOCAL2, NULL, NULL},
224 {"local3", LOG_LOCAL3, NULL, NULL},
225 {"local4", LOG_LOCAL4, NULL, NULL},
226 {"local5", LOG_LOCAL5, NULL, NULL},
227 {"local6", LOG_LOCAL6, NULL, NULL},
228 {"local7", LOG_LOCAL7, NULL, NULL},
229 {"daemon", LOG_DAEMON, NULL, NULL},
230 SK_STRINGMAP_SENTINEL
231 };
232
233
234 /* OPTIONS SETUP */
235
236 /*
237 * Identifiers for each option.
238 */
239 typedef enum {
240 OPT_LOG_DIRECTORY,
241 OPT_LOG_BASENAME,
242 OPT_LOG_POST_ROTATE,
243 OPT_LOG_PATHNAME,
244 OPT_LOG_DESTINATION,
245 OPT_LOG_LEVEL,
246 OPT_LOG_SYSFACILITY
247 } logOptionsEnum;
248
249 /*
250 * Whether the option is used/required by the file-based (legacy)
251 * logging or syslog logging. Order must be kept in sync with
252 * logOptionsEnum.
253 */
254 static const int logOptionsIsUsed[] = {
255 SKLOG_FEATURE_LEGACY,
256 SKLOG_FEATURE_LEGACY,
257 SKLOG_FEATURE_LEGACY,
258 SKLOG_FEATURE_LEGACY,
259 SKLOG_FEATURE_SYSLOG,
260 SKLOG_FEATURE_SYSLOG | SKLOG_FEATURE_LEGACY,
261 SKLOG_FEATURE_SYSLOG
262 };
263
264 /*
265 * Array of options for command-line switches. Must keep in sync
266 * with logOptionsEnum and logOptionsIsUsed above.
267 */
268 static struct option logOptions[] = {
269 {"log-directory", REQUIRED_ARG, 0, OPT_LOG_DIRECTORY},
270 {"log-basename", REQUIRED_ARG, 0, OPT_LOG_BASENAME},
271 {"log-post-rotate", REQUIRED_ARG, 0, OPT_LOG_POST_ROTATE},
272 {"log-pathname", REQUIRED_ARG, 0, OPT_LOG_PATHNAME},
273 {"log-destination", REQUIRED_ARG, 0, OPT_LOG_DESTINATION},
274 {"log-level", REQUIRED_ARG, 0, OPT_LOG_LEVEL},
275 {"log-sysfacility", REQUIRED_ARG, 0, OPT_LOG_SYSFACILITY},
276 {0,0,0,0} /* sentinel entry */
277 };
278
279
280
281 /* LOCAL FUNCTION PROTOTYPES */
282
283 static void logCompress(char *file);
284 static size_t logMakeStamp(char *buf, size_t buflen);
285 static int logOptionsHandler(clientData cData, int opt_index, char *opt_arg);
286 static void logRotatedLog(int priority, const char *fmt, va_list args);
287 static int logRotatedOpen(void);
288 static void logSimpleClose(void);
289 static void logSimpleLog(int priority, const char *fmt, va_list args);
290 static int logSimpleOpen(void);
291 static int logStringifyCommand(int argc, char * const *argv);
292 static void logSimpleVPrintf(int priority, const char *fmt, va_list args);
293 static void logVSyslog(int priority, const char *fmt, va_list args);
294 static void logWriteCommandLine(void);
295
296
297 /* FUNCTION DEFINITIONS */
298
299 /*
300 * Only compile these functions when they have not been defined as
301 * macros.
302 */
303 #if !defined(EMERGMSG)
304 int
EMERGMSG(const char * fmt,...)305 EMERGMSG(
306 const char *fmt,
307 ...)
308 {
309 SKLOG_VARARG_CALL_LOGGER(LOG_EMERG, fmt);
310 return 0;
311 }
312 #endif
313
314
315 #if !defined(ALERTMSG)
316 int
ALERTMSG(const char * fmt,...)317 ALERTMSG(
318 const char *fmt,
319 ...)
320 {
321 SKLOG_VARARG_CALL_LOGGER(LOG_ALERT, fmt);
322 return 0;
323 }
324 #endif
325
326
327 #if !defined(CRITMSG)
328 int
CRITMSG(const char * fmt,...)329 CRITMSG(
330 const char *fmt,
331 ...)
332 {
333 SKLOG_VARARG_CALL_LOGGER(LOG_CRIT, fmt);
334 return 0;
335 }
336 #endif
337
338
339 #if !defined(ERRMSG)
340 int
ERRMSG(const char * fmt,...)341 ERRMSG(
342 const char *fmt,
343 ...)
344 {
345 SKLOG_VARARG_CALL_LOGGER(LOG_ERR, fmt);
346 return 0;
347 }
348 #endif
349
350
351 #if !defined(WARNINGMSG)
352 int
WARNINGMSG(const char * fmt,...)353 WARNINGMSG(
354 const char *fmt,
355 ...)
356 {
357 SKLOG_VARARG_CALL_LOGGER(LOG_WARNING, fmt);
358 return 0;
359 }
360 #endif
361
362
363 #if !defined(NOTICEMSG)
364 int
NOTICEMSG(const char * fmt,...)365 NOTICEMSG(
366 const char *fmt,
367 ...)
368 {
369 SKLOG_VARARG_CALL_LOGGER(LOG_NOTICE, fmt);
370 return 0;
371 }
372 #endif
373
374
375 #if !defined(INFOMSG)
376 int
INFOMSG(const char * fmt,...)377 INFOMSG(
378 const char *fmt,
379 ...)
380 {
381 SKLOG_VARARG_CALL_LOGGER(LOG_INFO, fmt);
382 return 0;
383 }
384 #endif
385
386
387 #if !defined(DEBUGMSG)
388 int
DEBUGMSG(const char * fmt,...)389 DEBUGMSG(
390 const char *fmt,
391 ...)
392 {
393 SKLOG_VARARG_CALL_LOGGER(LOG_DEBUG, fmt);
394 return 0;
395 }
396 #endif
397
398
399 /*
400 * logCompress(file);
401 *
402 * Run the user's post-rotate command or the SK_LOG_COMPRESSOR
403 * command to compress the log file 'file'. Will call free() on
404 * the 'file' parameter.
405 */
406 static void
logCompress(char * file)407 logCompress(
408 char *file)
409 {
410 long pid;
411
412 if (file == NULL) {
413 INFOMSG("logCompress passed NULL pointer");
414 return;
415 }
416
417 if (NULL == logctx->l_rot.post_rotate) {
418 #ifndef SK_LOG_COMPRESSOR
419 free(file);
420 return;
421 #else
422 char *command[4];
423 command[0] = (char *)SK_LOG_COMPRESSOR;
424 command[1] = (char *)"-f";
425 command[2] = file;
426 command[3] = (char *)NULL;
427 pid = skSubcommandExecute(command);
428 free(file);
429 #endif /* SK_LOG_COMPRESSOR */
430
431 } else if ('\0' == *logctx->l_rot.post_rotate) {
432 /* do nothing when post-rotate command is empty string */
433 free(file);
434 return;
435
436 } else {
437 char *expanded_cmd;
438
439 expanded_cmd = skSubcommandStringFill(logctx->l_rot.post_rotate,
440 "s", file);
441 free(file);
442 if (NULL == expanded_cmd) {
443 WARNINGMSG("Unable to allocate memory to create command string");
444 return;
445 }
446 DEBUGMSG("Running %s: %s",
447 logOptions[OPT_LOG_POST_ROTATE].name, expanded_cmd);
448 pid = skSubcommandExecuteShell(expanded_cmd);
449 free(expanded_cmd);
450 }
451
452 switch (pid) {
453 case -1:
454 ERRMSG("Unable to fork to run command: %s", strerror(errno));
455 break;
456 case -2:
457 NOTICEMSG("Error waiting for child: %s", strerror(errno));
458 break;
459 default:
460 assert(pid > 0);
461 break;
462 }
463 }
464
465
466 /*
467 * size = logMakeStamp(buf, buflen)
468 *
469 * Add a time, machine, application, and PID stamp on the front of
470 * the character array 'buf', whose length is 'buflen'. Return the
471 * number of characters written to the buffer.
472 */
473 static size_t
logMakeStamp(char * buf,size_t buflen)474 logMakeStamp(
475 char *buf,
476 size_t buflen)
477 {
478 time_t t;
479 struct tm ts;
480 size_t len;
481
482 t = time(NULL);
483 localtime_r(&t, &ts);
484 /* Format time as "May 4 01:02:03" */
485 len = strftime(buf, buflen, "%b %e %H:%M:%S", &ts);
486 assert(len < buflen);
487 len += snprintf(buf+len, buflen-len, " %s %s[%ld]: ",
488 logctx->l_sim.machine_name,
489 logctx->l_sim.app_name,
490 (long)getpid());
491 return len;
492 }
493
494
495 /*
496 * A simple options handler that stores the string 'opt_arg' in the
497 * array pointed to by cData.
498 */
499 static int
logOptionsHandler(clientData cData,int opt_index,char * opt_arg)500 logOptionsHandler(
501 clientData cData,
502 int opt_index,
503 char *opt_arg)
504 {
505 sklog_context_t *ctx = (sklog_context_t*)cData;
506
507 assert(ctx);
508 assert(opt_index < (int)(sizeof(ctx->l_opt_values)/sizeof(char*)));
509
510 ctx->l_opt_values[opt_index] = opt_arg;
511 return 0;
512 }
513
514
515 /*
516 * Register the command-line switches depending on the type of log
517 * feature requested.
518 */
519 static int
logOptionsSetup(int feature_flags)520 logOptionsSetup(
521 int feature_flags)
522 {
523 static struct option options_used[NUM_OPTIONS+1];
524 unsigned int opt_count = 0;
525 unsigned int i;
526
527 assert(NUM_OPTIONS == (sizeof(logOptions)/sizeof(struct option) - 1));
528 assert(NUM_OPTIONS == (sizeof(logOptionsIsUsed)/sizeof(int)));
529
530 /* loop through the options, copying those that the caller needs
531 * into 'options_used'. 'opt_count' is the number of 'struct
532 * option' entries we have */
533 for (i = 0; logOptions[i].name; ++i) {
534 if (feature_flags & logOptionsIsUsed[i]) {
535 /* use this option */
536 memcpy(&(options_used[opt_count]), &(logOptions[i]),
537 sizeof(struct option));
538 ++opt_count;
539 }
540 }
541
542 /* set sentinel */
543 memset(&(options_used[opt_count]), 0, sizeof(struct option));
544
545 /* register the options */
546 if (opt_count > 0) {
547 if (skOptionsRegister(options_used, &logOptionsHandler,
548 (clientData)logctx))
549 {
550 return -1;
551 }
552 }
553
554 return 0;
555 }
556
557
558 /*
559 * logRotatedLog(priority, fmt, args);
560 *
561 * Write a log message to a file that may need to be rotated.
562 *
563 * The logctx->l_func is set to this function when log rotation is
564 * enabled.
565 *
566 * Lock the mutex for the log. If the rollover-time for the log
567 * has passed call logRotatedOpen() to open a new log file. Use
568 * logSimpleVPrintf() to print the message to the log.
569 */
570 static void
logRotatedLog(int priority,const char * fmt,va_list args)571 logRotatedLog(
572 int priority,
573 const char *fmt,
574 va_list args)
575 {
576 char msgbuf[MSGBUF_SIZE];
577 char timebuf[SKTIMESTAMP_STRLEN];
578 uint32_t timeflags = SKTIMESTAMP_NOMSEC|SKTIMESTAMP_UTC|SKTIMESTAMP_ISO;
579 FILE *rotated_fp = NULL;
580 char *rotated_path = NULL;
581 int rv;
582
583 if (logctx && logctx->l_open && SKLOG_INCLUDES_PRI(priority)) {
584 SKLOG_LOCK();
585 if (logctx->l_rot.rolltime < time(NULL)) {
586 /* Must rotate logs. First, grab current log file. */
587 assert(logctx->l_sim.fp);
588 rotated_fp = logctx->l_sim.fp;
589 rotated_path = strdup(logctx->l_sim.path);
590 if (!rotated_path) {
591 (void)logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
592 fprintf(rotated_fp,
593 "%sLog not rotated--Unable to allocate pathname\n",
594 msgbuf);
595 SKLOG_UNLOCK();
596 return;
597 }
598
599 /* Log a message about rotating the log. */
600 (void)logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
601 fprintf(rotated_fp, "%sLog rollover\n", msgbuf);
602
603 /* Open the new log file */
604 rv = logRotatedOpen();
605 if (0 == rv) {
606 (void)logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
607 fprintf(logctx->l_sim.fp, "%sRotated log file at %s",
608 msgbuf, sktimestamp_r(timebuf, sktimeNow(), timeflags));
609 } else {
610 /* Could not open new file. Continue to use existing
611 * log file. */
612 (void)logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
613 fprintf(rotated_fp,
614 ("%sLog not rotated--"
615 "error opening log new log '%s': %s\n"),
616 msgbuf, logctx->l_sim.path, strerror(rv));
617 /* restore the old settings */
618 logctx->l_sim.fp = rotated_fp;
619 strncpy(logctx->l_sim.path, rotated_path,
620 sizeof(logctx->l_sim.path));
621 rotated_fp = NULL;
622 free(rotated_path);
623 rotated_path = NULL;
624 }
625 }
626
627 /* Print the original message to the log */
628 logSimpleVPrintf(priority, fmt, args);
629
630 SKLOG_UNLOCK();
631 }
632
633 /* if we rotated the log; redirect stdout and stderr into the new
634 * log file, close the existing log file, and compress it. */
635 if (rotated_fp) {
636 if (logctx->l_dup_stdout) {
637 int fd_log;
638
639 SKLOG_LOCK();
640 fd_log = fileno(logctx->l_sim.fp);
641 if (dup2(fd_log, STDOUT_FILENO) == -1) {
642 (void)logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
643 fprintf(logctx->l_sim.fp, "Cannot dup(stdout): %s",
644 strerror(errno));
645 }
646 if (dup2(fd_log, STDERR_FILENO) == -1) {
647 (void)logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
648 fprintf(logctx->l_sim.fp, "Cannot dup(stderr): %s",
649 strerror(errno));
650 }
651 SKLOG_UNLOCK();
652 }
653
654 fclose(rotated_fp);
655 logCompress(rotated_path);
656 }
657 }
658
659
660 /*
661 * errno = logRotatedOpen();
662 *
663 * Open a new log file when the caller has requested log rotation,
664 * and set the time when the next rotation will occur.
665 *
666 * This function will overwrite the current log path and log FILE
667 * pointer; this function does not close the current log file.
668 *
669 * Returns 0 on success. On failure, the errno of the system call
670 * that failed is returned. The rollover time will be advanced
671 * regardless if the function succeeds or fails.
672 */
673 static int
logRotatedOpen(void)674 logRotatedOpen(
675 void)
676 {
677 char date[SKLOG_DATE_BUFSIZ];
678 time_t t;
679 struct tm ts;
680 int rv;
681
682 /* get current time */
683 t = time(NULL);
684 localtime_r(&t, &ts);
685 strftime(date, sizeof(date), "%Y%m%d", &ts);
686
687 /* compute the roll-over time by setting hour to last second of
688 * today, adding a second to get to midnight, then adding the
689 * rollover hour. Do this before we rotate, so that if rotation
690 * fails we don't try again until the next rotation time. */
691 #ifndef SKLOG_TESTING_LOG
692 ts.tm_hour = 23;
693 ts.tm_min = 59;
694 ts.tm_sec = 59;
695 logctx->l_rot.rolltime = mktime(&ts) + 1 + (SKLOG_ROTATE_HOUR * 3600);
696 #else
697 /* rotate each minute */
698 strftime(date, sizeof(date), "%Y%m%d:%H:%M", &ts);
699
700 if (ts.tm_sec > 55) {
701 ++ts.tm_min;
702 }
703 ts.tm_sec = 0;
704 ++ts.tm_min;
705 logctx->l_rot.rolltime = mktime(&ts);
706 #endif /* SKLOG_TESTING_LOG */
707
708 /* fill in the simple path with the new name */
709 snprintf(logctx->l_sim.path, sizeof(logctx->l_sim.path), "%s/%s-%s%s",
710 logctx->l_rot.dir, logctx->l_rot.basename, date,
711 SKLOG_SUFFIX);
712
713 /* if this is the initial open, use logOpenSimple() to set the
714 * application and machine names. otherwise, just fopen() the
715 * new location */
716 if (logctx->l_sim.fp == NULL) {
717 rv = logSimpleOpen();
718 if (rv) {
719 return rv;
720 }
721 } else {
722 logctx->l_sim.fp = fopen(logctx->l_sim.path, "a");
723 if (NULL == logctx->l_sim.fp) {
724 return errno;
725 }
726 }
727
728 return 0;
729 }
730
731
732 /*
733 * logSimpleClose();
734 *
735 * Close the "simple" logger that writes to a file or to stdout/stderr.
736 */
737 static void
logSimpleClose(void)738 logSimpleClose(
739 void)
740 {
741 if (logctx->l_sim.fp) {
742 SKLOG_LOCK();
743
744 if ((logctx->l_sim.fp != stdout) && (logctx->l_sim.fp != stderr)) {
745 fclose(logctx->l_sim.fp);
746 }
747 logctx->l_sim.fp = NULL;
748
749 SKLOG_UNLOCK();
750 }
751 }
752
753
754 /*
755 * logSimpleLog(priority, fmt, args);
756 *
757 * Write a log message to a file that does not get rotated.
758 *
759 * The logctx->l_func is set to this function when log messages are
760 * written to a single file, to standard output, or to standard
761 * error.
762 *
763 * Lock the mutex for the log and call logSimpleVPrintf() to print
764 * a message to the log.
765 */
766 static void
logSimpleLog(int priority,const char * fmt,va_list args)767 logSimpleLog(
768 int priority,
769 const char *fmt,
770 va_list args)
771 {
772 if (logctx && logctx->l_open && SKLOG_INCLUDES_PRI(priority)) {
773 SKLOG_LOCK();
774
775 logSimpleVPrintf(priority, fmt, args);
776
777 SKLOG_UNLOCK();
778 }
779 }
780
781
782 /*
783 * errno = logSimpleOpen();
784 *
785 * Open a "simple" logger that writes to a file or to stdout or
786 * stderr. Will also fill the logctx with the names of the
787 * application and machine. Return 0 on success; on failure,
788 * return the errno of the system call that failed.
789 */
790 static int
logSimpleOpen(void)791 logSimpleOpen(
792 void)
793 {
794 sklog_simple_t *simplog = &logctx->l_sim;
795 struct utsname u;
796 char *cp;
797
798 simplog->app_name = skAppName();
799
800 if (NULL == simplog->stamp_fn) {
801 simplog->stamp_fn = &logMakeStamp;
802 }
803
804 /* set the machine name; use only host part of a FQDN */
805 if (uname(&u) == -1) {
806 return errno;
807 }
808 cp = strchr(u.nodename, '.');
809 if (cp) {
810 *cp = '\0';
811 }
812 strncpy(simplog->machine_name, u.nodename, sizeof(simplog->machine_name));
813 simplog->machine_name[sizeof(simplog->machine_name)-1] = '\0';
814
815 if (0 == strcmp("stdout", simplog->path)) {
816 simplog->fp = stdout;
817 } else if (0 == strcmp("stderr", simplog->path)) {
818 simplog->fp = stderr;
819 } else {
820 simplog->fp = fopen(simplog->path, "a");
821 if (NULL == simplog->fp) {
822 return errno;
823 }
824 }
825
826 return 0;
827 }
828
829
830 SK_DIAGNOSTIC_FORMAT_NONLITERAL_PUSH
831 /*
832 * logSimpleVPrintf(priority, fmt, args);
833 *
834 * Create a message using the format 'fmt' and variable list 'args'
835 * and write that message with the specified 'priority' to the
836 * current FILE pointer given in the simple-log. The message will
837 * be prepended by the result of calling the 'stamp_fn' member of
838 * the log. The caller must lock the mutex of the log before
839 * calling this function.
840 */
841 static void
logSimpleVPrintf(int priority,const char * fmt,va_list args)842 logSimpleVPrintf(
843 int priority,
844 const char *fmt,
845 va_list args)
846 {
847 char msgbuf[MSGBUF_SIZE];
848 size_t len;
849
850 SK_UNUSED_PARAM(priority);
851 assert(LOG_MASK(priority) & logctx->l_priority);
852
853 len = logctx->l_sim.stamp_fn(msgbuf, sizeof(msgbuf));
854 vsnprintf(msgbuf+len, (sizeof(msgbuf)-len), fmt, args);
855 fprintf(logctx->l_sim.fp, "%s\n", msgbuf);
856 fflush(logctx->l_sim.fp);
857 }
858
859 SK_DIAGNOSTIC_FORMAT_NONLITERAL_POP
860
861
862 /*
863 * status = logStringifyCommand(argc, argv);
864 *
865 * Create a string holding the comamnd line paramters and store it
866 * on the global context. Return 0 on success, or errno on
867 * failure.
868 */
869 static int
logStringifyCommand(int argc,char * const * argv)870 logStringifyCommand(
871 int argc,
872 char * const *argv)
873 {
874 size_t cmd_len;
875 size_t rem_len;
876 char *cp;
877 int i;
878
879 /* free an existing string */
880 if (logctx->l_cmd) {
881 free(logctx->l_cmd);
882 }
883
884 /* compute length of command string. use 3*argc since each arg is
885 * surrounded with quotes and has a space after it */
886 cmd_len = 1 + 3 * argc;
887 for (i = 0; i < argc; ++i) {
888 cmd_len += strlen(argv[i]);
889 }
890 logctx->l_cmd = (char*)malloc(cmd_len * sizeof(char));
891 if (!logctx->l_cmd) {
892 return errno;
893 }
894
895 cp = logctx->l_cmd;
896 rem_len = cmd_len;
897 *cp++ = '\'';
898 --rem_len;
899 for (i = 0; i < argc; ++i) {
900 if (i > 0) {
901 *cp++ = '\'';
902 *cp++ = ' ';
903 *cp++ = '\'';
904 rem_len -= 3;
905 }
906 strncpy(cp, argv[i], rem_len);
907 cp += strlen(argv[i]);
908 assert((cp - logctx->l_cmd) < (int)cmd_len);
909 rem_len = cmd_len - (cp - logctx->l_cmd);
910 }
911 *cp++ = '\'';
912 *cp = '\0';
913
914 return 0;
915 }
916
917
918 SK_DIAGNOSTIC_FORMAT_NONLITERAL_PUSH
919 /*
920 * logVSyslog(priority, fmt, args);
921 *
922 * Write a log message to syslog.
923 *
924 * The logctx->l_func is set to this function when writing to
925 * syslog and the OS does not provide vsyslog().
926 *
927 * Create a message using the format 'fmt' and variable list
928 * 'args', then write that message to syslog() with the specified
929 * 'priority'.
930 */
931 static void
logVSyslog(int priority,const char * fmt,va_list args)932 logVSyslog(
933 int priority,
934 const char *fmt,
935 va_list args)
936 {
937 char msgbuf[MSGBUF_SIZE];
938
939 vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
940 msgbuf[sizeof(msgbuf)-1] = '\0';
941 syslog(priority, "%s", msgbuf);
942 }
943
944 SK_DIAGNOSTIC_FORMAT_NONLITERAL_POP
945
946
947 /*
948 * logWriteCommandLine();
949 *
950 * Write the command line string stored on the global context to
951 * the log, free the string, and reset it to NULL. Assumes the log
952 * is open and the command line string is non NULL.
953 */
954 static void
logWriteCommandLine(void)955 logWriteCommandLine(
956 void)
957 {
958 assert(logctx && logctx->l_open && logctx->l_cmd);
959 NOTICEMSG("%s", logctx->l_cmd);
960 free(logctx->l_cmd);
961 logctx->l_cmd = NULL;
962 }
963
964
965 /* write the message to the log */
966 #if !defined(sklog)
967 void
sklog(int priority,const char * fmt,...)968 sklog(
969 int priority,
970 const char *fmt,
971 ...)
972 {
973 SKLOG_VARARG_CALL_LOGGER(priority, fmt);
974 }
975 #endif
976
977
978 void
sklogNonBlock(int priority,const char * fmt,...)979 sklogNonBlock(
980 int priority,
981 const char *fmt,
982 ...)
983 {
984 va_list args;
985 va_start(args, fmt);
986
987 if (logctx && logctx->l_open && logctx->l_func) {
988 switch (logctx->l_dest) {
989 case SKLOG_DEST_NOT_SET:
990 case SKLOG_DEST_NONE:
991 break;
992
993 case SKLOG_DEST_PATH:
994 case SKLOG_DEST_STDOUT:
995 case SKLOG_DEST_STDERR:
996 case SKLOG_DEST_DIRECTORY:
997 if (SKLOG_INCLUDES_PRI(priority)) {
998 if (logctx->l_trylock_fn) {
999 if (0 != logctx->l_trylock_fn(logctx->l_lock_data)) {
1000 /* cannot get lock */
1001 break;
1002 }
1003 }
1004 logSimpleVPrintf(priority, fmt, args);
1005 if (logctx->l_unlock_fn) {
1006 logctx->l_unlock_fn(logctx->l_lock_data);
1007 }
1008 }
1009 break;
1010
1011 case SKLOG_DEST_BOTH:
1012 case SKLOG_DEST_SYSLOG:
1013 logctx->l_func(priority, fmt, args);
1014 break;
1015 }
1016 }
1017
1018 va_end(args);
1019 }
1020
1021
1022 int
sklogCheckLevel(int level)1023 sklogCheckLevel(
1024 int level)
1025 {
1026 if (logctx) {
1027 return (SKLOG_INCLUDES_PRI(level) ? 1 : 0);
1028 }
1029 return 0;
1030 }
1031
1032
1033 /* close a log if open. */
1034 void
sklogClose(void)1035 sklogClose(
1036 void)
1037 {
1038 if (logctx && logctx->l_open) {
1039 NOTICEMSG("Stopped logging.");
1040 logctx->l_open = 0;
1041 switch (logctx->l_dest) {
1042 case SKLOG_DEST_NOT_SET:
1043 case SKLOG_DEST_NONE:
1044 break;
1045
1046 case SKLOG_DEST_PATH:
1047 case SKLOG_DEST_STDOUT:
1048 case SKLOG_DEST_STDERR:
1049 case SKLOG_DEST_DIRECTORY:
1050 logSimpleClose();
1051 break;
1052
1053 case SKLOG_DEST_BOTH:
1054 case SKLOG_DEST_SYSLOG:
1055 closelog();
1056 break;
1057 }
1058 logctx->l_func = NULL;
1059
1060 skAppSetFuncPrintFatalErr(NULL);
1061 }
1062 }
1063
1064
1065 /* create command line string; write it to the log if open */
1066 void
sklogCommandLine(int argc,char * const * argv)1067 sklogCommandLine(
1068 int argc,
1069 char * const *argv)
1070 {
1071 int rv;
1072
1073 if ( !logctx) {
1074 return;
1075 }
1076
1077 rv = logStringifyCommand(argc, argv);
1078 if (rv != 0) {
1079 return;
1080 }
1081
1082 if (logctx->l_open) {
1083 logWriteCommandLine();
1084 }
1085
1086 return;
1087 }
1088
1089
1090 void
sklogDisableRotation(void)1091 sklogDisableRotation(
1092 void)
1093 {
1094 if (logctx) {
1095 SKLOG_LOCK();
1096 logctx->l_rot.rolltime = (time_t)INT32_MAX;
1097 SKLOG_UNLOCK();
1098 }
1099 }
1100
1101
1102 /* get the destination file handle */
1103 FILE *
sklogGetDestination(void)1104 sklogGetDestination(
1105 void)
1106 {
1107 if (logctx) {
1108 switch (logctx->l_dest) {
1109 case SKLOG_DEST_DIRECTORY:
1110 case SKLOG_DEST_STDOUT:
1111 case SKLOG_DEST_STDERR:
1112 case SKLOG_DEST_PATH:
1113 return logctx->l_sim.fp;
1114
1115 case SKLOG_DEST_BOTH:
1116 return stderr;
1117
1118 default:
1119 return NULL;
1120 }
1121 }
1122
1123 return NULL;
1124 }
1125
1126
1127 /* get the log directory */
1128 char *
sklogGetDirectory(char * buf,size_t bufsize)1129 sklogGetDirectory(
1130 char *buf,
1131 size_t bufsize)
1132 {
1133 if ( !(logctx && logctx->l_rot.dir[0])) {
1134 return NULL;
1135 }
1136
1137 strncpy(buf, logctx->l_rot.dir, bufsize);
1138 if ('\0' != buf[bufsize-1]) {
1139 /* buffer too short */
1140 buf[bufsize-1] = '\0';
1141 return NULL;
1142 }
1143
1144 return buf;
1145 }
1146
1147
1148 /* get the log level */
1149 const char *
sklogGetLevel(void)1150 sklogGetLevel(
1151 void)
1152 {
1153 const sk_stringmap_entry_t *e;
1154
1155 if (logctx) {
1156 for (e = log_level; e->name != NULL; ++e) {
1157 if (LOG_UPTO(e->id) == logctx->l_priority) {
1158 return e->name;
1159 }
1160 }
1161 skAbort();
1162 }
1163 return NULL;
1164 }
1165
1166
1167 int
sklogGetMask(void)1168 sklogGetMask(
1169 void)
1170 {
1171 if (logctx) {
1172 return logctx->l_priority;
1173 }
1174 return 0;
1175 }
1176
1177
1178 /* open a log that has been Setup() and had its destination set */
1179 int
sklogOpen(void)1180 sklogOpen(
1181 void)
1182 {
1183 char timebuf[SKTIMESTAMP_STRLEN];
1184 uint32_t timeflags = SKTIMESTAMP_NOMSEC|SKTIMESTAMP_UTC|SKTIMESTAMP_ISO;
1185 int rv;
1186
1187 if (!logctx) {
1188 skAppPrintErr("Must setup the log before opening it");
1189 return -1;
1190 }
1191
1192 if (logctx->l_open) {
1193 /* called multiple times */
1194 return 0;
1195 }
1196
1197 switch (logctx->l_dest) {
1198 case SKLOG_DEST_NOT_SET:
1199 skAppPrintErr("Must set log destination prior to opening log");
1200 return -1;
1201
1202 case SKLOG_DEST_NONE:
1203 break;
1204
1205 case SKLOG_DEST_DIRECTORY:
1206 rv = logRotatedOpen();
1207 if (rv) {
1208 skAppPrintErr("Unable to open log file '%s': %s",
1209 logctx->l_sim.path, strerror(rv));
1210 return -1;
1211 }
1212 logctx->l_func = &logRotatedLog;
1213 break;
1214
1215 case SKLOG_DEST_STDOUT:
1216 case SKLOG_DEST_STDERR:
1217 case SKLOG_DEST_PATH:
1218 rv = logSimpleOpen();
1219 if (rv) {
1220 skAppPrintErr("Unable to open log file '%s': %s",
1221 logctx->l_sim.path, strerror(rv));
1222 return -1;
1223 }
1224 logctx->l_func = &logSimpleLog;
1225 break;
1226
1227 case SKLOG_DEST_BOTH:
1228 #ifdef LOG_PERROR
1229 logctx->l_sys.options |= LOG_PERROR;
1230 #endif
1231 /* FALLTHROUGH */
1232 case SKLOG_DEST_SYSLOG:
1233 openlog(skAppName(), logctx->l_sys.options,
1234 logctx->l_sys.facility);
1235 #ifdef SK_HAVE_VSYSLOG
1236 logctx->l_func = &vsyslog;
1237 #else
1238 logctx->l_func = &logVSyslog;
1239 #endif
1240 break;
1241 }
1242
1243 logctx->l_open = 1;
1244
1245 NOTICEMSG("Started logging at %sZ",
1246 sktimestamp_r(timebuf, sktimeNow(), timeflags));
1247
1248 if (logctx->l_cmd) {
1249 logWriteCommandLine();
1250 }
1251
1252 skAppSetFuncPrintFatalErr(CRITMSG);
1253
1254 return 0;
1255 }
1256
1257
1258 /* print the usage of the options defined by this library */
1259 void
sklogOptionsUsage(FILE * fp)1260 sklogOptionsUsage(
1261 FILE *fp)
1262 {
1263 #ifdef SK_LOG_COMPRESSOR
1264 const char *post_rotate = SK_LOG_COMPRESSOR " -f %s";
1265 #else
1266 const char *post_rotate = "";
1267 #endif
1268 int i, j;
1269 int features = INT32_MAX;
1270
1271 if (logctx) {
1272 features = logctx->l_features;
1273 }
1274
1275 for (i = 0; logOptions[i].name; ++i) {
1276 /* skip options that are not part of our feature set */
1277 if ( !(logOptionsIsUsed[i] & features)) {
1278 continue;
1279 }
1280
1281 fprintf(fp, "--%s %s. ",
1282 logOptions[i].name, SK_OPTION_HAS_ARG(logOptions[i]));
1283 switch ((logOptionsEnum)i) {
1284 case OPT_LOG_DIRECTORY:
1285 fprintf(fp,
1286 ("Write log files to this directory and enable log\n"
1287 "\trotatation; must be the complete path to an existing"
1288 " directory"));
1289 break;
1290
1291 case OPT_LOG_BASENAME:
1292 fprintf(fp, ("Use this name as the basename for files in the\n"
1293 "\t%s. Def. '%s'"),
1294 logOptions[OPT_LOG_DIRECTORY].name, skAppName());
1295 break;
1296
1297 case OPT_LOG_POST_ROTATE:
1298 fprintf(fp,
1299 ("Run this command on the previous day's log file\n"
1300 "\tafter log rotatation."
1301 " Each \"%%s\" in the command is replaced by the\n"
1302 "\tfile's complete path."
1303 " When set to the empty string, no action is\n"
1304 "\ttaken. Def. '%s'"),
1305 post_rotate);
1306 break;
1307
1308 case OPT_LOG_PATHNAME:
1309 fprintf(fp,
1310 ("Write log messages to this single file and disable\n"
1311 "\tlog rotation; must be a complete pathname"));
1312 break;
1313
1314 case OPT_LOG_DESTINATION:
1315 fprintf(fp, ("Specify the log destination. Acceptable values:\n"
1316 "\t"));
1317 for (j = 0; log_dest[j].name; ++j) {
1318 fprintf(fp, "'%s', ", log_dest[j].name);
1319 }
1320 fprintf(fp, "or\n\tcomplete path to a log file");
1321 break;
1322
1323 case OPT_LOG_LEVEL:
1324 fprintf(fp, ("Enable logging of messages of this severity."
1325 " Def. "));
1326 for (j = 0; log_level[j].name; ++j) {
1327 if (SKLOG_DEFAULT_LEVEL == log_level[j].id) {
1328 fprintf(fp, "%s\n", log_level[j].name);
1329 break;
1330 }
1331 }
1332 fprintf(fp, "\tChoices: %s", log_level[0].name);
1333 for (j = 1; log_level[j].name; ++j) {
1334 fprintf(fp, ", %s", log_level[j].name);
1335 }
1336 break;
1337
1338 case OPT_LOG_SYSFACILITY:
1339 fprintf(fp, ("Set the facility to use for syslog() messages.\n"
1340 "\tDef. "));
1341 for (j = 0; log_facility[j].name; ++j) {
1342 if (SKLOG_SYSFACILITY == log_facility[j].id) {
1343 fprintf(fp, ("%s (%" PRIu32 "). "),
1344 log_facility[j].name, log_facility[j].id);
1345 break;
1346 }
1347 }
1348 fprintf(fp,
1349 ("Specify as an integer or one of the following names:\n"
1350 "\t%s"),
1351 log_facility[0].name);
1352 for (j = 1; log_facility[j].name; ++j) {
1353 fprintf(fp, ",%s", log_facility[j].name);
1354 }
1355 fprintf(fp, ".\n\tSee syslog(3) and"
1356 " /usr/include/sys/syslog.h for integer values");
1357 break;
1358 }
1359 fprintf(fp, "\n");
1360 }
1361 }
1362
1363
1364 /* verify we got all the options we needed. */
1365 int
sklogOptionsVerify(void)1366 sklogOptionsVerify(
1367 void)
1368 {
1369 int dest_count = 0;
1370 int err_count = 0;
1371
1372 if (!logctx) {
1373 skAppPrintErr("Must setup the log before verifying");
1374 return -1;
1375 }
1376
1377 /* only one of directory, pathname, or destination may be given,
1378 * and one must be given */
1379 if (logctx->l_opt_values[OPT_LOG_DIRECTORY] != NULL) {
1380 ++dest_count;
1381 }
1382 if (logctx->l_opt_values[OPT_LOG_PATHNAME] != NULL) {
1383 ++dest_count;
1384 }
1385 if (logctx->l_opt_values[OPT_LOG_DESTINATION] != NULL) {
1386 ++dest_count;
1387 }
1388
1389 if (dest_count == 0) {
1390 ++err_count;
1391 if ((logctx->l_features & (SKLOG_FEATURE_LEGACY|SKLOG_FEATURE_SYSLOG))
1392 == (SKLOG_FEATURE_LEGACY|SKLOG_FEATURE_SYSLOG))
1393 {
1394 skAppPrintErr("One of --%s, --%s, or --%s is required",
1395 logOptions[OPT_LOG_DIRECTORY].name,
1396 logOptions[OPT_LOG_PATHNAME].name,
1397 logOptions[OPT_LOG_DESTINATION].name);
1398 } else if (logctx->l_features & SKLOG_FEATURE_LEGACY) {
1399 skAppPrintErr("Either --%s or --%s is required",
1400 logOptions[OPT_LOG_DIRECTORY].name,
1401 logOptions[OPT_LOG_PATHNAME].name);
1402 } else if (logctx->l_features & SKLOG_FEATURE_SYSLOG) {
1403 skAppPrintErr("The --%s switch is required",
1404 logOptions[OPT_LOG_DESTINATION].name);
1405 }
1406 } else if (dest_count > 1) {
1407 ++err_count;
1408 if ((logctx->l_features & (SKLOG_FEATURE_LEGACY|SKLOG_FEATURE_SYSLOG))
1409 == (SKLOG_FEATURE_LEGACY|SKLOG_FEATURE_SYSLOG))
1410 {
1411 skAppPrintErr("Only one of --%s, --%s, or --%s may be specified",
1412 logOptions[OPT_LOG_DIRECTORY].name,
1413 logOptions[OPT_LOG_PATHNAME].name,
1414 logOptions[OPT_LOG_DESTINATION].name);
1415 } else if (logctx->l_features & SKLOG_FEATURE_LEGACY) {
1416 skAppPrintErr("Only one of --%s or --%s may be specified",
1417 logOptions[OPT_LOG_DIRECTORY].name,
1418 logOptions[OPT_LOG_PATHNAME].name);
1419 } else {
1420 skAbort();
1421 }
1422 }
1423
1424 if (logctx->l_opt_values[OPT_LOG_BASENAME]
1425 && !logctx->l_opt_values[OPT_LOG_DIRECTORY])
1426 {
1427 ++err_count;
1428 skAppPrintErr("May only use --%s when --%s is specified",
1429 logOptions[OPT_LOG_BASENAME].name,
1430 logOptions[OPT_LOG_DIRECTORY].name);
1431 }
1432 if (logctx->l_opt_values[OPT_LOG_POST_ROTATE]
1433 && !logctx->l_opt_values[OPT_LOG_DIRECTORY])
1434 {
1435 ++err_count;
1436 skAppPrintErr("May only use --%s when --%s is specified",
1437 logOptions[OPT_LOG_POST_ROTATE].name,
1438 logOptions[OPT_LOG_DIRECTORY].name);
1439 }
1440
1441 if (logctx->l_opt_values[OPT_LOG_DIRECTORY]) {
1442 if (sklogSetDirectory(logctx->l_opt_values[OPT_LOG_DIRECTORY],
1443 logctx->l_opt_values[OPT_LOG_BASENAME]))
1444 {
1445 ++err_count;
1446 }
1447 if (logctx->l_opt_values[OPT_LOG_POST_ROTATE]) {
1448 if (sklogSetPostRotateCommand(
1449 logctx->l_opt_values[OPT_LOG_POST_ROTATE]))
1450 {
1451 ++err_count;
1452 }
1453 }
1454 }
1455 if (logctx->l_opt_values[OPT_LOG_PATHNAME]) {
1456 if (logctx->l_opt_values[OPT_LOG_PATHNAME][0] != '/') {
1457 ++err_count;
1458 skAppPrintErr(("Invalid %s '%s': A complete path is required"
1459 " and value does not begin with a slash"),
1460 logOptions[OPT_LOG_PATHNAME].name,
1461 logctx->l_opt_values[OPT_LOG_PATHNAME]);
1462 } else if (sklogSetDestination(logctx->l_opt_values[OPT_LOG_PATHNAME]))
1463 {
1464 ++err_count;
1465 }
1466 }
1467 if (logctx->l_opt_values[OPT_LOG_DESTINATION]) {
1468 if (sklogSetDestination(logctx->l_opt_values[OPT_LOG_DESTINATION])) {
1469 ++err_count;
1470 }
1471 }
1472
1473 if (logctx->l_opt_values[OPT_LOG_LEVEL]) {
1474 if (sklogSetLevel(logctx->l_opt_values[OPT_LOG_LEVEL])) {
1475 ++err_count;
1476 }
1477 }
1478
1479 if (logctx->l_opt_values[OPT_LOG_SYSFACILITY]) {
1480 if (sklogSetFacilityByName(logctx->l_opt_values[OPT_LOG_SYSFACILITY])){
1481 ++err_count;
1482 }
1483 }
1484
1485 if (err_count) {
1486 return -1;
1487 }
1488 return 0;
1489 }
1490
1491
1492 /* redirect stdout and stderr to the log or to /dev/null */
1493 int
sklogRedirectStandardStreams(char * buf,size_t bufsize)1494 sklogRedirectStandardStreams(
1495 char *buf,
1496 size_t bufsize)
1497 {
1498 int fd_log = -1;
1499 int rv = 0;
1500
1501 if (!logctx) {
1502 skAppPrintErr("Must setup the log before redirecting stdout");
1503 return -1;
1504 }
1505 if (!logctx->l_open) {
1506 if (buf) {
1507 snprintf(buf, bufsize,
1508 "May not redirect stdout prior to opening log");
1509 }
1510 return -1;
1511 }
1512
1513 SKLOG_LOCK();
1514 switch (logctx->l_dest) {
1515 case SKLOG_DEST_NOT_SET:
1516 SKLOG_UNLOCK();
1517 skAbortBadCase(logctx->l_dest);
1518
1519 case SKLOG_DEST_STDOUT:
1520 case SKLOG_DEST_STDERR:
1521 case SKLOG_DEST_BOTH:
1522 /* Do not redirect anything */
1523 goto END;
1524
1525 case SKLOG_DEST_NONE:
1526 case SKLOG_DEST_SYSLOG:
1527 /* redirect stdout and stderr to /dev/null */
1528 fd_log = open("/dev/null", O_RDWR);
1529 if (-1 == fd_log) {
1530 if (buf) {
1531 snprintf(buf, bufsize, "Cannot open /dev/null: %s",
1532 strerror(errno));
1533 }
1534 rv = -1;
1535 goto END;
1536 }
1537 break;
1538
1539 case SKLOG_DEST_PATH:
1540 case SKLOG_DEST_DIRECTORY:
1541 /* redirect stdout and stderr to the log */
1542 logctx->l_dup_stdout = 1;
1543 fd_log = fileno(logctx->l_sim.fp);
1544 break;
1545 }
1546
1547 if (-1 != fd_log) {
1548 if (dup2(fd_log, STDOUT_FILENO) == -1) {
1549 if (buf) {
1550 snprintf(buf, bufsize, "Cannot dup(stdout): %s",
1551 strerror(errno));
1552 }
1553 rv = -1;
1554 goto END;
1555 }
1556 if (dup2(fd_log, STDERR_FILENO) == -1) {
1557 if (buf) {
1558 snprintf(buf, bufsize, "Cannot dup(stderr): %s",
1559 strerror(errno));
1560 }
1561 rv = -1;
1562 goto END;
1563 }
1564 }
1565
1566 END:
1567 SKLOG_UNLOCK();
1568 return rv;
1569 }
1570
1571 /* set the destination */
1572 int
sklogSetDestination(const char * destination)1573 sklogSetDestination(
1574 const char *destination)
1575 {
1576 sk_stringmap_t *str_map = NULL;
1577 sk_stringmap_status_t rv_map;
1578 sk_stringmap_entry_t *map_entry;
1579 int rv = -1;
1580
1581 if (!logctx) {
1582 skAppPrintErr("Must setup the log before setting the destination");
1583 return -1;
1584 }
1585 if (logctx->l_open) {
1586 skAppPrintErr("Cannot set destination after opening log");
1587 return -1;
1588 }
1589
1590 if (destination[0] == '/') {
1591 /* treat it as a pathname */
1592 logctx->l_dest = SKLOG_DEST_PATH;
1593 if (skDirExists(destination)) {
1594 skAppPrintErr(("Invalid %s '%s':"
1595 " Value must name a file, not a directory"),
1596 logOptions[OPT_LOG_DESTINATION].name, destination);
1597 return -1;
1598 }
1599 strncpy(logctx->l_sim.path, destination, sizeof(logctx->l_sim.path));
1600 if ('\0' != logctx->l_sim.path[sizeof(logctx->l_sim.path)-1]) {
1601 skAppPrintErr("Invalid %s: The path is too long",
1602 logOptions[OPT_LOG_DESTINATION].name);
1603 return -1;
1604 }
1605 return 0;
1606 }
1607 /* else, see which of the possible destinations it matches */
1608
1609 /* create a stringmap of the available entries */
1610 if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
1611 skAppPrintErr("Unable to create stringmap");
1612 goto END;
1613 }
1614 if (skStringMapAddEntries(str_map, -1, log_dest) != SKSTRINGMAP_OK) {
1615 goto END;
1616 }
1617
1618 /* attempt to match */
1619 rv_map = skStringMapGetByName(str_map, destination, &map_entry);
1620 switch (rv_map) {
1621 case SKSTRINGMAP_OK:
1622 logctx->l_dest = (sklog_dest_t)map_entry->id;
1623 rv = 0;
1624 break;
1625
1626 case SKSTRINGMAP_PARSE_AMBIGUOUS:
1627 skAppPrintErr("Invalid %s '%s': Value is ambiguous",
1628 logOptions[OPT_LOG_DESTINATION].name, destination);
1629 goto END;
1630
1631 case SKSTRINGMAP_PARSE_NO_MATCH:
1632 skAppPrintErr(("Invalid %s '%s': Value is not a complete path and"
1633 " does not match known keys"),
1634 logOptions[OPT_LOG_DESTINATION].name, destination);
1635 goto END;
1636
1637 default:
1638 skAppPrintErr(("Invalid %s '%s':"
1639 "Unexpected return value from string-map parser (%d)"),
1640 logOptions[OPT_LOG_DESTINATION].name, destination,
1641 rv_map);
1642 goto END;
1643 }
1644
1645 if (logctx->l_dest == SKLOG_DEST_STDOUT) {
1646 strncpy(logctx->l_sim.path, "stdout", sizeof(logctx->l_sim.path));
1647 } else if (logctx->l_dest == SKLOG_DEST_STDERR) {
1648 strncpy(logctx->l_sim.path, "stderr", sizeof(logctx->l_sim.path));
1649 }
1650
1651 END:
1652 if (str_map) {
1653 skStringMapDestroy(str_map);
1654 }
1655 return rv;
1656 }
1657
1658
1659 /* set the logger to use a directory with log rotation */
1660 int
sklogSetDirectory(const char * dir_name,const char * base_name)1661 sklogSetDirectory(
1662 const char *dir_name,
1663 const char *base_name)
1664 {
1665 if (!logctx) {
1666 skAppPrintErr("Must setup the log before setting the directory");
1667 return -1;
1668 }
1669 if (logctx->l_open) {
1670 skAppPrintErr("Cannot set directory after opening log.");
1671 return -1;
1672 }
1673
1674 /* verify basename, or use skAppName if basename was not given */
1675 if (base_name == NULL || base_name[0] == '\0') {
1676 base_name = skAppName();
1677 } else if (strchr(base_name, '/')) {
1678 skAppPrintErr("Invalid %s '%s': Value may not contain '/'",
1679 logOptions[OPT_LOG_BASENAME].name, base_name);
1680 return -1;
1681 }
1682
1683 /* verify directory name */
1684 if (skOptionsCheckDirectory(dir_name, logOptions[OPT_LOG_DIRECTORY].name)){
1685 return -1;
1686 }
1687
1688 /* copy directory name */
1689 strncpy(logctx->l_rot.dir, dir_name, sizeof(logctx->l_rot.dir));
1690 if ('\0' != logctx->l_rot.dir[sizeof(logctx->l_rot.dir)-1]) {
1691 skAppPrintErr("Invalid %s '%s': Value is too long",
1692 logOptions[OPT_LOG_DIRECTORY].name, dir_name);
1693 return -1;
1694 }
1695
1696 /* copy base name */
1697 strncpy(logctx->l_rot.basename, base_name, sizeof(logctx->l_rot.basename));
1698 if ('\0' != logctx->l_rot.basename[sizeof(logctx->l_rot.basename)-1]) {
1699 skAppPrintErr("Invalid %s '%s': Value is too long",
1700 logOptions[OPT_LOG_BASENAME].name, base_name);
1701 return -1;
1702 }
1703
1704 logctx->l_dest = SKLOG_DEST_DIRECTORY;
1705 return 0;
1706 }
1707
1708
1709 /* set the facility for syslog() */
1710 int
sklogSetFacility(int facility)1711 sklogSetFacility(
1712 int facility)
1713 {
1714 if (!logctx) {
1715 skAppPrintErr("Must setup the log before setting the facility");
1716 return -1;
1717 }
1718 if (logctx->l_open) {
1719 skAppPrintErr("Cannot set facility after opening log.");
1720 return -1;
1721 }
1722
1723 if (logctx->l_dest == SKLOG_DEST_BOTH
1724 || logctx->l_dest == SKLOG_DEST_SYSLOG)
1725 {
1726 logctx->l_sys.facility = facility;
1727 return 0;
1728 }
1729
1730 skAppPrintErr("Cannot set facility unless %s is 'syslog' or 'both'",
1731 logOptions[OPT_LOG_DESTINATION].name);
1732 return -1;
1733 }
1734
1735
1736 /* set the facility for syslog() by name; can be a name or an integer */
1737 int
sklogSetFacilityByName(const char * name_or_number)1738 sklogSetFacilityByName(
1739 const char *name_or_number)
1740 {
1741 sk_stringmap_t *str_map = NULL;
1742 sk_stringmap_status_t rv_map;
1743 sk_stringmap_entry_t *found_entry;
1744 uint32_t facility;
1745 int rv;
1746
1747 if (!logctx) {
1748 skAppPrintErr("Must setup the log before setting the facility");
1749 return -1;
1750 }
1751
1752 /* try to parse the facility as a number */
1753 rv = skStringParseUint32(&facility, name_or_number, 0, INT32_MAX);
1754 if (rv == 0) {
1755 /* was parsable as a number */
1756 return sklogSetFacility(facility);
1757 }
1758
1759 /* a return value of SKUTILS_ERR_BAD_CHAR means the value was
1760 * unparsable--we will try to treat it as a name. any other value
1761 * indicates an error */
1762 if (rv != SKUTILS_ERR_BAD_CHAR) {
1763 skAppPrintErr("Invalid %s '%s': %s",
1764 logOptions[OPT_LOG_SYSFACILITY].name, name_or_number,
1765 skStringParseStrerror(rv));
1766 return -1;
1767 }
1768
1769 /* reset rv */
1770 rv = -1;
1771
1772 /* create a stringmap of the available levels */
1773 if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
1774 skAppPrintErr("Unable to create stringmap");
1775 goto END;
1776 }
1777 if (skStringMapAddEntries(str_map, -1, log_facility) != SKSTRINGMAP_OK) {
1778 goto END;
1779 }
1780
1781 /* attempt to match */
1782 rv_map = skStringMapGetByName(str_map, name_or_number, &found_entry);
1783 switch (rv_map) {
1784 case SKSTRINGMAP_OK:
1785 rv = sklogSetFacility(found_entry->id);
1786 break;
1787
1788 case SKSTRINGMAP_PARSE_AMBIGUOUS:
1789 skAppPrintErr("Invalid %s '%s': Value is ambiguous",
1790 logOptions[OPT_LOG_SYSFACILITY].name, name_or_number);
1791 break;
1792
1793 case SKSTRINGMAP_PARSE_NO_MATCH:
1794 skAppPrintErr("Invalid %s '%s': Value is not recognized",
1795 logOptions[OPT_LOG_SYSFACILITY].name, name_or_number);
1796 break;
1797
1798 default:
1799 skAppPrintErr(("Invalid %s '%s':"
1800 " Unexpected return value from string-map parser (%d)"),
1801 logOptions[OPT_LOG_SYSFACILITY].name, name_or_number,
1802 rv_map);
1803 break;
1804 }
1805
1806 END:
1807 if (str_map) {
1808 skStringMapDestroy(str_map);
1809 }
1810 return rv;
1811 }
1812
1813
1814 /* set logging level to all levels through 'level', a string. */
1815 int
sklogSetLevel(const char * level)1816 sklogSetLevel(
1817 const char *level)
1818 {
1819 sk_stringmap_t *str_map = NULL;
1820 sk_stringmap_status_t rv_map;
1821 sk_stringmap_entry_t *found_entry;
1822 int rv = -1;
1823
1824 if (!logctx) {
1825 skAppPrintErr("Must setup the log before setting the level");
1826 return -1;
1827 }
1828
1829 /* create a stringmap of the available levels */
1830 if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
1831 skAppPrintErr("Unable to create stringmap");
1832 goto END;
1833 }
1834 if (skStringMapAddEntries(str_map, -1, log_level) != SKSTRINGMAP_OK) {
1835 goto END;
1836 }
1837
1838 /* attempt to match */
1839 rv_map = skStringMapGetByName(str_map, level, &found_entry);
1840 switch (rv_map) {
1841 case SKSTRINGMAP_OK:
1842 sklogSetMask(LOG_UPTO(found_entry->id));
1843 rv = 0;
1844 break;
1845
1846 case SKSTRINGMAP_PARSE_AMBIGUOUS:
1847 skAppPrintErr("Invalid %s '%s': Value is ambiguous",
1848 logOptions[OPT_LOG_LEVEL].name, level);
1849 break;
1850
1851 case SKSTRINGMAP_PARSE_NO_MATCH:
1852 skAppPrintErr("Invalid %s '%s': Value is not recognized",
1853 logOptions[OPT_LOG_LEVEL].name, level);
1854 break;
1855
1856 default:
1857 skAppPrintErr(("Invalid %s '%s':"
1858 " Unexpected return value from string-map parser (%d)"),
1859 logOptions[OPT_LOG_LEVEL].name, level, rv_map);
1860 break;
1861 }
1862
1863 END:
1864 if (str_map) {
1865 skStringMapDestroy(str_map);
1866 }
1867 return rv;
1868 }
1869
1870
1871 /* set lock and unlock functions */
1872 int
sklogSetLocking(sklog_lock_fn_t locker,sklog_lock_fn_t unlocker,sklog_lock_fn_t try_locker,void * data)1873 sklogSetLocking(
1874 sklog_lock_fn_t locker,
1875 sklog_lock_fn_t unlocker,
1876 sklog_lock_fn_t try_locker,
1877 void *data)
1878 {
1879 if (!logctx) {
1880 skAppPrintErr("Must setup the log before setting lock functions");
1881 return -1;
1882 }
1883 logctx->l_lock_fn = locker;
1884 logctx->l_unlock_fn = unlocker;
1885 logctx->l_trylock_fn = try_locker;
1886 logctx->l_lock_data = data;
1887 return 0;
1888 }
1889
1890
1891 /* set the mask for the logger */
1892 int
sklogSetMask(int new_mask)1893 sklogSetMask(
1894 int new_mask)
1895 {
1896 int old_mask;
1897
1898 if (!logctx) {
1899 skAppPrintErr("Must setup the log before setting the mask");
1900 return -1;
1901 }
1902 old_mask = logctx->l_priority;
1903 logctx->l_priority = new_mask;
1904
1905 switch (logctx->l_dest) {
1906 case SKLOG_DEST_NOT_SET:
1907 case SKLOG_DEST_NONE:
1908 case SKLOG_DEST_PATH:
1909 case SKLOG_DEST_DIRECTORY:
1910 case SKLOG_DEST_STDOUT:
1911 case SKLOG_DEST_STDERR:
1912 break;
1913
1914 case SKLOG_DEST_BOTH:
1915 case SKLOG_DEST_SYSLOG:
1916 old_mask = setlogmask(new_mask);
1917 break;
1918 }
1919
1920 return old_mask;
1921 }
1922
1923
1924 int
sklogSetPostRotateCommand(const char * command)1925 sklogSetPostRotateCommand(
1926 const char *command)
1927 {
1928 size_t rv;
1929
1930 if (!logctx) {
1931 skAppPrintErr("Must setup the log before setting post-rotate command");
1932 return -1;
1933 }
1934 if (logctx->l_dest != SKLOG_DEST_DIRECTORY) {
1935 skAppPrintErr("Post-rotate command is ignored unless"
1936 " log-rotation is used");
1937 return 0;
1938 }
1939
1940 if (NULL == command) {
1941 if (logctx->l_rot.post_rotate) {
1942 free((char*)logctx->l_rot.post_rotate);
1943 logctx->l_rot.post_rotate = NULL;
1944 }
1945 return 0;
1946 }
1947
1948 rv = skSubcommandStringCheck(command, "s");
1949 if (rv) {
1950 switch (command[rv]) {
1951 case '\0':
1952 skAppPrintErr(("Invalid %s command '%s':"
1953 " '%%' appears at end of string"),
1954 logOptions[OPT_LOG_POST_ROTATE].name, command);
1955 return -1;
1956 default:
1957 skAppPrintErr(("Invalid %s command '%s':"
1958 " Unknown conversion '%%%c'"),
1959 logOptions[OPT_LOG_POST_ROTATE].name, command,
1960 command[rv]);
1961 return -1;
1962 }
1963 }
1964
1965 logctx->l_rot.post_rotate = strdup(command);
1966 if (NULL == logctx->l_rot.post_rotate) {
1967 skAppPrintErr("Unable to allocate space for %s command",
1968 logOptions[OPT_LOG_POST_ROTATE].name);
1969 return -1;
1970 }
1971
1972 return 0;
1973 }
1974
1975
1976 /* set the function to make that timestamp. will be used instead of
1977 * logMakeStamp(). */
1978 int
sklogSetStampFunction(sklog_stamp_fn_t makestamp)1979 sklogSetStampFunction(
1980 sklog_stamp_fn_t makestamp)
1981 {
1982 if (!logctx) {
1983 skAppPrintErr("Must setup the log before setting lock functions");
1984 return -1;
1985 }
1986 if (logctx->l_dest == SKLOG_DEST_BOTH
1987 || logctx->l_dest == SKLOG_DEST_SYSLOG)
1988 {
1989 skAppPrintErr("Stamp function is ignored when syslog() is used");
1990 return 0;
1991 }
1992 if (makestamp == NULL) {
1993 skAppPrintErr("Stamp function cannot be NULL");
1994 return -1;
1995 }
1996 logctx->l_sim.stamp_fn = makestamp;
1997 return 0;
1998 }
1999
2000
2001 /* initialize all variables for logging */
2002 int
sklogSetup(int feature_flags)2003 sklogSetup(
2004 int feature_flags)
2005 {
2006 if (logctx) {
2007 skAppPrintErr("Ignoring multiple calls to sklogSetup()");
2008 return 0;
2009 }
2010
2011 /* initialize the logging context */
2012 logctx = &logger;
2013 memset(logctx, 0, sizeof(sklog_context_t));
2014 logctx->l_dest = SKLOG_DEST_NOT_SET;
2015 logctx->l_priority = LOG_UPTO(SKLOG_DEFAULT_LEVEL);
2016 logctx->l_sys.options = SKLOG_SYSOPTIONS;
2017 logctx->l_sys.facility = SKLOG_SYSFACILITY;
2018 logctx->l_features = feature_flags;
2019
2020 if (logOptionsSetup(feature_flags)) {
2021 return -1;
2022 }
2023
2024 return 0;
2025 }
2026
2027
2028 /* close the logger */
2029 void
sklogTeardown(void)2030 sklogTeardown(
2031 void)
2032 {
2033 if (logctx == NULL) {
2034 /* either never set up or already shut down */
2035 return;
2036 }
2037
2038 sklogClose();
2039
2040 if (logctx->l_cmd) {
2041 free(logctx->l_cmd);
2042 }
2043 if (logctx->l_rot.post_rotate) {
2044 free((char*)logctx->l_rot.post_rotate);
2045 }
2046 memset(logctx, 0, sizeof(sklog_context_t));
2047 logctx = NULL;
2048 }
2049
2050
2051 int
EMERGMSG_v(const char * fmt,va_list args)2052 EMERGMSG_v(
2053 const char *fmt,
2054 va_list args)
2055 {
2056 SKLOG_CALL_LOGGER(LOG_EMERG, fmt, args);
2057 return 0;
2058 }
2059
2060
2061 int
ALERTMSG_v(const char * fmt,va_list args)2062 ALERTMSG_v(
2063 const char *fmt,
2064 va_list args)
2065 {
2066 SKLOG_CALL_LOGGER(LOG_ALERT, fmt, args);
2067 return 0;
2068 }
2069
2070
2071 int
CRITMSG_v(const char * fmt,va_list args)2072 CRITMSG_v(
2073 const char *fmt,
2074 va_list args)
2075 {
2076 SKLOG_CALL_LOGGER(LOG_CRIT, fmt, args);
2077 return 0;
2078 }
2079
2080
2081 int
ERRMSG_v(const char * fmt,va_list args)2082 ERRMSG_v(
2083 const char *fmt,
2084 va_list args)
2085 {
2086 SKLOG_CALL_LOGGER(LOG_ERR, fmt, args);
2087 return 0;
2088 }
2089
2090
2091 int
WARNINGMSG_v(const char * fmt,va_list args)2092 WARNINGMSG_v(
2093 const char *fmt,
2094 va_list args)
2095 {
2096 SKLOG_CALL_LOGGER(LOG_WARNING, fmt, args);
2097 return 0;
2098 }
2099
2100
2101 int
NOTICEMSG_v(const char * fmt,va_list args)2102 NOTICEMSG_v(
2103 const char *fmt,
2104 va_list args)
2105 {
2106 SKLOG_CALL_LOGGER(LOG_NOTICE, fmt, args);
2107 return 0;
2108 }
2109
2110
2111 int
INFOMSG_v(const char * fmt,va_list args)2112 INFOMSG_v(
2113 const char *fmt,
2114 va_list args)
2115 {
2116 SKLOG_CALL_LOGGER(LOG_INFO, fmt, args);
2117 return 0;
2118 }
2119
2120
2121 int
DEBUGMSG_v(const char * fmt,va_list args)2122 DEBUGMSG_v(
2123 const char *fmt,
2124 va_list args)
2125 {
2126 SKLOG_CALL_LOGGER(LOG_DEBUG, fmt, args);
2127 return 0;
2128 }
2129
2130
2131 /* write the log message */
2132 void
sklogv(int priority,const char * fmt,va_list args)2133 sklogv(
2134 int priority,
2135 const char *fmt,
2136 va_list args)
2137 {
2138 SKLOG_CALL_LOGGER(priority, fmt, args);
2139 }
2140
2141
2142 /*
2143 ** Local Variables:
2144 ** mode:c
2145 ** indent-tabs-mode:nil
2146 ** c-basic-offset:4
2147 ** End:
2148 */
2149