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