]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/logger.c
move most of a LOG macros in the logger function
[netatalk.git] / libatalk / util / logger.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 /* =========================================================================
6
7 logger.c was written by Simon Bazley (sibaz@sibaz.com)
8
9 I believe libatalk is released under the L/GPL licence.
10 Just incase, it is, thats the licence I'm applying to this file.
11 Netatalk 2001 (c)
12
13 ========================================================================= */
14
15 #include <stdio.h>
16 #include <limits.h>
17 #include <stdarg.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <syslog.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <sys/uio.h>
25 #include <unistd.h>
26 #include <sys/time.h>
27 #include <time.h>
28 #include <ctype.h>
29 #include <errno.h>
30
31 #include <atalk/boolean.h>
32 #include <atalk/util.h>
33
34 #include <atalk/logger.h>
35
36 #define OPEN_LOGS_AS_UID 0
37
38 #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
39
40 #define MAXLOGSIZE 512
41
42 #define LOGLEVEL_STRING_IDENTIFIERS { \
43   "LOG_NOTHING",                      \
44   "LOG_SEVERE",                       \
45   "LOG_ERROR",                        \
46   "LOG_WARN",                         \
47   "LOG_NOTE",                         \
48   "LOG_INFO",                         \
49   "LOG_DEBUG",                        \
50   "LOG_DEBUG6",                       \
51   "LOG_DEBUG7",                       \
52   "LOG_DEBUG8",                       \
53   "LOG_DEBUG9",                       \
54   "LOG_MAXDEBUG"}                        
55
56 /* these are the string identifiers corresponding to each logtype */
57 #define LOGTYPE_STRING_IDENTIFIERS { \
58   "Default",                         \
59   "Core",                            \
60   "Logger",                          \
61   "CNID",                            \
62   "AFPDaemon",                       \
63   "ATalkDaemon",                     \
64   "PAPDaemon",                       \
65   "UAMSDaemon",                      \
66                                      \
67   "end_of_list_marker"}              \
68
69 /* ========================================================================= 
70     Structure definitions
71    ========================================================================= */
72
73 /* Main log config */
74 typedef struct {
75     int   inited;                 /* file log config initialized ? */
76     int   filelogging;            /* Any level set to filelogging ? */
77                                   /* Deactivates syslog logging */
78     char  processname[16];
79     int   syslog_opened;          /* syslog opened ? */
80     int   facility;               /* syslog facility to use */
81     int   syslog_display_options;
82     int   syslog_level;           /* Log Level to send to syslog */
83 } log_config_t;
84
85 /* This stores the config and options for one filelog type (e.g. logger, afpd etc.) */
86 typedef struct {
87     int  set;                     /* set individually ? yes: changing default
88                                    * doesnt change it. no: it changes it.*/
89     char *filename;               /* Name of file */
90     int  fd;                      /* logfiles fd */
91     int  level;                   /* Log Level to put in this file */
92     int  display_options;
93 } filelog_conf_t;
94
95 /* =========================================================================
96    Config
97    ========================================================================= */
98
99 /* Main log config container, must be globally visible */
100 static log_config_t log_config = {
101     0,                  /* Initialized ? 0 = no */
102     0,                  /* No filelogging setup yet */
103     {0},                /* processname */
104     0,                  /* syslog opened ? */
105     logfacility_daemon,         /* syslog facility to use */
106     logoption_ndelay|logoption_pid, /* logging options for syslog */
107     0                               /* log level for syslog */
108 };
109
110 /* Default log config: log nothing to files.
111    0:    not set individually
112    NULL: Name of file
113    -1:   logfiles fd
114    0:   Log Level
115    0:    Display options */
116 #define DEFAULT_LOG_CONFIG {0, NULL, -1, 0, 0}
117
118 static filelog_conf_t file_configs[logtype_end_of_list_marker] = {
119     DEFAULT_LOG_CONFIG, /* logtype_default */
120     DEFAULT_LOG_CONFIG, /* logtype_core */
121     DEFAULT_LOG_CONFIG, /* logtype_logger */
122     DEFAULT_LOG_CONFIG, /* logtype_cnid */
123     DEFAULT_LOG_CONFIG, /* logtype_afpd */
124     DEFAULT_LOG_CONFIG, /* logtype_atalkd */
125     DEFAULT_LOG_CONFIG, /* logtype_papd */
126     DEFAULT_LOG_CONFIG  /* logtype_uams */
127 };
128
129 /* These are used by the LOG macro to store __FILE__ and __LINE__ */
130 static const char *log_src_filename;
131 static int  log_src_linenumber;
132
133 /* Array to store text to list given a log type */
134 static const char *arr_logtype_strings[] =  LOGTYPE_STRING_IDENTIFIERS;
135 static const int num_logtype_strings = COUNT_ARRAY(arr_logtype_strings);
136
137 /* Array for charachters representing log severity in the log file */
138 static const char arr_loglevel_chars[] = {'-','S', 'E', 'W', 'N', 'I', 'D'};
139 static const int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars);
140
141 static const char *arr_loglevel_strings[] = LOGLEVEL_STRING_IDENTIFIERS;
142 static const int num_loglevel_strings = COUNT_ARRAY(arr_loglevel_strings);
143
144 /* =========================================================================
145    Internal function definitions
146    ========================================================================= */
147
148 /*
149  * If filename == NULL its for syslog logging, otherwise its for file-logging.
150  * "unsetuplog" calls with loglevel == NULL.
151  * loglevel == NULL means:
152  *    if logtype == default
153  *       disable logging
154  *    else
155  *       set to default logging
156  */
157
158 /* -[un]setuplog <logtype> <loglevel> [<filename>]*/
159 static void setuplog_internal(const char *loglevel, const char *logtype, const char *filename)
160 {
161     int typenum, levelnum;
162
163     /* Parse logtype */
164     for( typenum=0; typenum < num_logtype_strings; typenum++) {
165         if (strcasecmp(logtype, arr_logtype_strings[typenum]) == 0)
166             break;
167     }
168     if (typenum >= num_logtype_strings) {
169         return;
170     }
171
172     /* Parse loglevel */
173     if (loglevel == NULL) {
174         levelnum = 0;
175     } else {
176         for(levelnum=1; levelnum < num_loglevel_strings; levelnum++) {
177             if (strcasecmp(loglevel, arr_loglevel_strings[levelnum]) == 0)
178                 break;
179         }
180         if (levelnum >= num_loglevel_strings) {
181             return;
182         }
183     }
184
185     /* is this a syslog setup or a filelog setup ? */
186     if (filename == NULL) {
187         /* must be syslog */
188         syslog_setup(levelnum, 0,
189                      log_config.syslog_display_options,
190                      log_config.facility);
191     } else {
192         /* this must be a filelog */
193         log_setup(filename, levelnum, typenum);
194     }
195
196     return;
197 }
198
199 static void generate_message_details(char *message_details_buffer,
200                                      int message_details_buffer_length,
201                                      int display_options,
202                                      enum loglevels loglevel, enum logtypes logtype)
203 {
204     char   *ptr = message_details_buffer;
205     int    templen;
206     int    len = message_details_buffer_length;
207     struct timeval tv;
208     pid_t  pid;
209
210     *ptr = 0;
211
212     /* Print time */
213     gettimeofday(&tv, NULL);
214     strftime(ptr, len, "%b %d %H:%M:%S.", localtime(&tv.tv_sec));
215     templen = strlen(ptr);
216     len -= templen;
217     ptr += templen;
218
219     templen = snprintf(ptr, len, "%06u ", (int)tv.tv_usec);
220     if (templen == -1 || templen >= len)
221         return;
222         
223     len -= templen;
224     ptr += templen;
225
226     /* Process name &&  PID */
227     pid = getpid();
228     templen = snprintf(ptr, len, "%s[%d]", log_config.processname, pid);
229     if (templen == -1 || templen >= len)
230         return;
231     len -= templen;
232     ptr += templen;
233
234     /* Source info ? */
235     if ( ! (display_options & logoption_nsrcinfo)) {
236         char *basename = strrchr(log_src_filename, '/');
237         if (basename)
238             templen = snprintf(ptr, len, " {%s:%d}", basename + 1, log_src_linenumber);
239         else
240             templen = snprintf(ptr, len, " {%s:%d}", log_src_filename, log_src_linenumber);
241         if (templen == -1 || templen >= len)
242             return;
243         len -= templen;
244         ptr += templen;
245     }
246
247     /* Errorlevel */
248     if (loglevel >= (num_loglevel_chars - 1))
249         templen = snprintf(ptr, len,  " (D%d:", loglevel - 1);
250     else
251         templen = snprintf(ptr, len, " (%c:", arr_loglevel_chars[loglevel]);
252
253     if (templen == -1 || templen >= len)
254         return;
255     len -= templen;
256     ptr += templen;
257
258     /* Errortype */
259     if (logtype<num_logtype_strings) {
260         templen = snprintf(ptr, len, "%s", arr_logtype_strings[logtype]);
261         if (templen == -1 || templen >= len)
262             return;
263         len -= templen;
264         ptr += templen;
265     }
266     
267     strncat(ptr, "): ", len);
268     ptr[len -1] = 0;
269 }
270
271 static int get_syslog_equivalent(enum loglevels loglevel)
272 {
273     switch (loglevel)
274     {
275         /* The question is we know how bad it is for us,
276            but how should that translate in the syslogs?  */
277     case 1: /* severe */
278         return LOG_ERR;
279     case 2: /* error */
280         return LOG_ERR;
281     case 3: /* warning */
282         return LOG_WARNING;
283     case 4: /* note */
284         return LOG_NOTICE;
285     case 5: /* information */
286         return LOG_INFO;
287     default: /* debug */
288         return LOG_DEBUG;
289     }
290 }
291
292 /* =========================================================================
293    Global function definitions
294    ========================================================================= */
295
296 void log_init(void)
297 {
298     syslog_setup(log_note, 0,
299                  log_config.syslog_display_options,
300                  log_config.facility);
301 }
302
303 void log_setup(const char *filename, enum loglevels loglevel, enum logtypes logtype)
304 {
305     uid_t process_uid;
306
307     if (loglevel == 0) {
308         /* Disable */
309         if (file_configs[logtype].set) {
310             if (file_configs[logtype].filename) {
311                 free(file_configs[logtype].filename);
312                 file_configs[logtype].filename = NULL;
313             }
314             close(file_configs[logtype].fd);
315             file_configs[logtype].fd = -1;
316             file_configs[logtype].level = 0;
317             file_configs[logtype].set = 0;
318
319             /* if disabling default also set all "default using" levels to 0 */
320             if (logtype == logtype_default) {
321                 while (logtype != logtype_end_of_list_marker) {
322                     if ( ! (file_configs[logtype].set))
323                         file_configs[logtype].level = 0;
324                     logtype++;
325                 }
326             }
327         }
328
329         return;
330     }
331
332     /* Safety check */
333     if (NULL == filename)
334         return;
335
336     /* Resetting existing config ? */
337     if (file_configs[logtype].set && file_configs[logtype].filename) {
338         free(file_configs[logtype].filename);
339         file_configs[logtype].filename = NULL;
340         close(file_configs[logtype].fd);
341         file_configs[logtype].fd = -1;
342         file_configs[logtype].level = 0;
343         file_configs[logtype].set = 0;
344     }
345
346     /* Set new values */
347     file_configs[logtype].filename = strdup(filename);
348     file_configs[logtype].level = loglevel;
349
350
351     /* Open log file as OPEN_LOGS_AS_UID*/
352     process_uid = geteuid();
353     if (process_uid) {
354         if (seteuid(OPEN_LOGS_AS_UID) == -1) {
355             /* XXX failing silently */
356             return;
357         }
358     }
359     file_configs[logtype].fd = open( file_configs[logtype].filename,
360                                      O_CREAT | O_WRONLY | O_APPEND,
361                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
362     if (process_uid) {
363         if (seteuid(process_uid) == -1) {
364             LOG(log_error, logtype_logger, "can't seteuid back %s", strerror(errno));
365             exit(EXITERR_SYS);
366         }
367     }
368
369     /* Check for error opening/creating logfile */
370     if (-1 == file_configs[logtype].fd) {
371         free(file_configs[logtype].filename);
372         file_configs[logtype].filename = NULL;
373         file_configs[logtype].level = -1;
374         file_configs[logtype].set = 0;
375         return;
376     }
377
378     fcntl(file_configs[logtype].fd, F_SETFD, FD_CLOEXEC);
379     file_configs[logtype].set = 1;
380     log_config.filelogging = 1;
381     log_config.inited = 1;
382
383     /* Here's how we make it possible to LOG to a logtype like "logtype_afpd" */
384     /* which then uses the default logtype setup if it isn't setup itself: */
385     /* we just copy the loglevel from default to all logtypes that are not setup. */
386     /* In "make_log_entry" we then check for the logtypes if they arent setup */
387     /* and use default then. We must provide accessible values for all logtypes */
388     /* in order to make it easy and fast to check the loglevels in the LOG macro! */
389
390     if (logtype == logtype_default) {
391         while (logtype != logtype_end_of_list_marker) {
392             if ( ! (file_configs[logtype].set))
393                 file_configs[logtype].level = loglevel;
394             logtype++;
395         }
396         logtype = logtype_default;
397     }
398
399     LOG(log_debug, logtype_logger, "Setup file logging: type: %s, level: %s, file: %s",
400         arr_logtype_strings[logtype], arr_loglevel_strings[loglevel], file_configs[logtype].filename);
401 }
402
403 /* logtype is ignored, it's just one for all */
404 void syslog_setup(int loglevel, enum logtypes logtype _U_,
405                   int display_options, int facility)
406 {
407     log_config.syslog_level = loglevel;
408     log_config.syslog_display_options = display_options;
409     log_config.facility = facility;
410
411     log_config.inited = 1;
412
413     LOG(log_note, logtype_logger, "Set syslog logging to level: %s",
414         arr_loglevel_strings[loglevel]);
415 }
416
417 void log_close(void)
418 {
419 }
420
421 /* This function sets up the processname */
422 void set_processname(const char *processname)
423 {
424     strncpy(log_config.processname, processname, 15);
425     log_config.processname[15] = 0;
426 }
427
428 /* Called by the LOG macro for syslog messages */
429 static void make_syslog_entry(enum loglevels loglevel, enum logtypes logtype _U_, char *message)
430 {
431     if ( !log_config.syslog_opened ) {
432         openlog(log_config.processname, log_config.syslog_display_options,
433                 log_config.facility);
434         log_config.syslog_opened = 1;
435     }
436
437     syslog(get_syslog_equivalent(loglevel), "%s", message);
438 }
439
440 /* -------------------------------------------------------------------------
441    make_log_entry has 1 main flaws:
442    The message in its entirity, must fit into the tempbuffer.
443    So it must be shorter than MAXLOGSIZE
444    ------------------------------------------------------------------------- */
445 void make_log_entry(enum loglevels loglevel, enum logtypes logtype,
446                     const char *file, int line, char *message, ...)
447 {
448     /* fn is not reentrant but is used in signal handler
449      * with LOGGER it's a little late source name and line number
450      * are already changed. */
451     static int inlog = 0;
452     int fd, len;
453     char temp_buffer[MAXLOGSIZE];
454     char log_details_buffer[MAXLOGSIZE];
455     va_list args;
456     struct iovec iov[2];
457
458     if (inlog)
459         return;
460
461     inlog = 1;
462
463     if (!log_config.inited) {
464       log_init();
465     }
466     
467     if (file_configs[logtype].level >= loglevel) {
468       log_src_filename = file;
469       log_src_linenumber = line;
470     }
471     else if (!log_config.filelogging && log_config.syslog_level >= loglevel) {
472        /* Initialise the Messages */
473        va_start(args, message);
474        vsnprintf(temp_buffer, MAXLOGSIZE -1, message, args);
475        va_end(args);
476        temp_buffer[MAXLOGSIZE -1] = 0;
477        make_syslog_entry(loglevel, logtype, temp_buffer);
478        inlog = 0;
479        return;
480     }
481     else {
482        inlog = 0;
483        return;
484     }
485
486     /* Check if requested logtype is setup */
487     if (file_configs[logtype].set)
488         /* Yes */
489         fd = file_configs[logtype].fd;
490     else
491         /* No: use default */
492         fd = file_configs[logtype_default].fd;
493
494     if (fd < 0) {
495         /* no where to send the output, give up */
496         return;
497     }
498
499     /* Initialise the Messages */
500     va_start(args, message);
501     len = vsnprintf(temp_buffer, MAXLOGSIZE -1, message, args);
502     va_end(args);
503
504     /* Append \n */
505     if (len ==-1 || len >= MAXLOGSIZE -1) {
506         /* vsnprintf hit the buffer size*/
507         temp_buffer[MAXLOGSIZE-2] = '\n';
508         temp_buffer[MAXLOGSIZE-1] = 0;
509     }
510     else {
511         temp_buffer[len] = '\n';
512         temp_buffer[len+1] = 0;
513     }
514
515     generate_message_details(log_details_buffer, sizeof(log_details_buffer),
516                              file_configs[logtype].set ?
517                              file_configs[logtype].display_options :
518                              file_configs[logtype_default].display_options,
519                              loglevel, logtype);
520
521
522     /* If default wasnt setup its fd is -1 */
523     iov[0].iov_base = log_details_buffer;
524     iov[0].iov_len = strlen(log_details_buffer);
525     iov[1].iov_base = temp_buffer;
526     iov[1].iov_len = strlen(temp_buffer);
527     writev( fd,  iov, 2);
528
529     inlog = 0;
530 }
531
532
533 void setuplog(const char *logstr)
534 {
535     char *ptr, *ptrbak, *logtype, *loglevel = NULL, *filename = NULL;
536     ptr = strdup(logstr);
537     ptrbak = ptr;
538
539     /* logtype */
540     logtype = ptr;
541
542     /* get loglevel */
543     ptr = strpbrk(ptr, " \t");
544     if (ptr) {
545         *ptr++ = 0;
546         while (*ptr && isspace(*ptr))
547             ptr++;
548         loglevel = ptr;
549
550         /* get filename */
551         ptr = strpbrk(ptr, " \t");
552         if (ptr) {
553             *ptr++ = 0;
554             while (*ptr && isspace(*ptr))
555                 ptr++;
556         }
557         filename = ptr;
558         if (filename && *filename == 0)
559             filename = NULL;
560     }
561
562     /* finally call setuplog, filename can be NULL */
563     setuplog_internal(loglevel, logtype, filename);
564
565     free(ptrbak);
566 }
567
568 void unsetuplog(const char *logstr)
569 {
570     char *str, *logtype, *filename;
571
572     str = strdup(logstr);
573
574     /* logtype */
575     logtype = str;
576
577     /* get filename, can be NULL */
578     strtok(str, " \t");
579     filename = strtok(NULL, " \t");
580
581     /* finally call setuplog, filename can be NULL */
582     setuplog_internal(NULL, str, filename);
583
584     free(str);
585 }