]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/logger.c
Better error reporting for afp_ldap.conf parsing
[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     enum loglevels 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     enum loglevels 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 unsigned 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 unsigned int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars);
140
141 static const char *arr_loglevel_strings[] = LOGLEVEL_STRING_IDENTIFIERS;
142 static const unsigned 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     unsigned 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     /* Is it /dev/tty ? */
353     if (strcmp(file_configs[logtype].filename, "/dev/tty") == 0) {
354         file_configs[logtype].fd = open( file_configs[logtype].filename, O_WRONLY);
355     } else {
356         process_uid = geteuid();
357         if (process_uid) {
358             if (seteuid(OPEN_LOGS_AS_UID) == -1) {
359                 /* XXX failing silently */
360                 return;
361             }
362         }
363         file_configs[logtype].fd = open( file_configs[logtype].filename,
364                                          O_CREAT | O_WRONLY | O_APPEND,
365                                          S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
366         if (process_uid) {
367             if (seteuid(process_uid) == -1) {
368                 LOG(log_error, logtype_logger, "can't seteuid back %s", strerror(errno));
369                 exit(EXITERR_SYS);
370             }
371         }
372     }
373
374     /* Check for error opening/creating logfile */
375     if (-1 == file_configs[logtype].fd) {
376         free(file_configs[logtype].filename);
377         file_configs[logtype].filename = NULL;
378         file_configs[logtype].level = -1;
379         file_configs[logtype].set = 0;
380         return;
381     }
382
383     fcntl(file_configs[logtype].fd, F_SETFD, FD_CLOEXEC);
384     file_configs[logtype].set = 1;
385     log_config.filelogging = 1;
386     log_config.inited = 1;
387
388     /* Here's how we make it possible to LOG to a logtype like "logtype_afpd" */
389     /* which then uses the default logtype setup if it isn't setup itself: */
390     /* we just copy the loglevel from default to all logtypes that are not setup. */
391     /* In "make_log_entry" we then check for the logtypes if they arent setup */
392     /* and use default then. We must provide accessible values for all logtypes */
393     /* in order to make it easy and fast to check the loglevels in the LOG macro! */
394
395     if (logtype == logtype_default) {
396         while (logtype != logtype_end_of_list_marker) {
397             if ( ! (file_configs[logtype].set))
398                 file_configs[logtype].level = loglevel;
399             logtype++;
400         }
401         logtype = logtype_default;
402     }
403
404     LOG(log_debug, logtype_logger, "Setup file logging: type: %s, level: %s, file: %s",
405         arr_logtype_strings[logtype], arr_loglevel_strings[loglevel], file_configs[logtype].filename);
406 }
407
408 /* logtype is ignored, it's just one for all */
409 void syslog_setup(int loglevel, enum logtypes logtype _U_,
410                   int display_options, int facility)
411 {
412     log_config.syslog_level = loglevel;
413     log_config.syslog_display_options = display_options;
414     log_config.facility = facility;
415
416     log_config.inited = 1;
417
418     LOG(log_note, logtype_logger, "Set syslog logging to level: %s",
419         arr_loglevel_strings[loglevel]);
420 }
421
422 void log_close(void)
423 {
424 }
425
426 /* This function sets up the processname */
427 void set_processname(const char *processname)
428 {
429     strncpy(log_config.processname, processname, 15);
430     log_config.processname[15] = 0;
431 }
432
433 /* Called by the LOG macro for syslog messages */
434 static void make_syslog_entry(enum loglevels loglevel, enum logtypes logtype _U_, char *message)
435 {
436     if ( !log_config.syslog_opened ) {
437         openlog(log_config.processname, log_config.syslog_display_options,
438                 log_config.facility);
439         log_config.syslog_opened = 1;
440     }
441
442     syslog(get_syslog_equivalent(loglevel), "%s", message);
443 }
444
445 /* -------------------------------------------------------------------------
446    make_log_entry has 1 main flaws:
447    The message in its entirity, must fit into the tempbuffer.
448    So it must be shorter than MAXLOGSIZE
449    ------------------------------------------------------------------------- */
450 void make_log_entry(enum loglevels loglevel, enum logtypes logtype,
451                     const char *file, int line, char *message, ...)
452 {
453     /* fn is not reentrant but is used in signal handler
454      * with LOGGER it's a little late source name and line number
455      * are already changed. */
456     static int inlog = 0;
457     int fd, len;
458     char temp_buffer[MAXLOGSIZE];
459     char log_details_buffer[MAXLOGSIZE];
460     va_list args;
461     struct iovec iov[2];
462
463     if (inlog)
464         return;
465
466     inlog = 1;
467
468     if (!log_config.inited) {
469       log_init();
470     }
471     
472     if (file_configs[logtype].level >= loglevel) {
473       log_src_filename = file;
474       log_src_linenumber = line;
475     }
476     else if (!log_config.filelogging && log_config.syslog_level >= loglevel) {
477        /* Initialise the Messages */
478        va_start(args, message);
479        vsnprintf(temp_buffer, MAXLOGSIZE -1, message, args);
480        va_end(args);
481        temp_buffer[MAXLOGSIZE -1] = 0;
482        make_syslog_entry(loglevel, logtype, temp_buffer);
483        inlog = 0;
484        return;
485     }
486     else {
487        inlog = 0;
488        return;
489     }
490
491     /* Check if requested logtype is setup */
492     if (file_configs[logtype].set)
493         /* Yes */
494         fd = file_configs[logtype].fd;
495     else
496         /* No: use default */
497         fd = file_configs[logtype_default].fd;
498
499     if (fd < 0) {
500         /* no where to send the output, give up */
501         return;
502     }
503
504     /* Initialise the Messages */
505     va_start(args, message);
506     len = vsnprintf(temp_buffer, MAXLOGSIZE -1, message, args);
507     va_end(args);
508
509     /* Append \n */
510     if (len ==-1 || len >= MAXLOGSIZE -1) {
511         /* vsnprintf hit the buffer size*/
512         temp_buffer[MAXLOGSIZE-2] = '\n';
513         temp_buffer[MAXLOGSIZE-1] = 0;
514     }
515     else {
516         temp_buffer[len] = '\n';
517         temp_buffer[len+1] = 0;
518     }
519
520     generate_message_details(log_details_buffer, sizeof(log_details_buffer),
521                              file_configs[logtype].set ?
522                              file_configs[logtype].display_options :
523                              file_configs[logtype_default].display_options,
524                              loglevel, logtype);
525
526
527     /* If default wasnt setup its fd is -1 */
528     iov[0].iov_base = log_details_buffer;
529     iov[0].iov_len = strlen(log_details_buffer);
530     iov[1].iov_base = temp_buffer;
531     iov[1].iov_len = strlen(temp_buffer);
532     writev( fd,  iov, 2);
533
534     inlog = 0;
535 }
536
537
538 void setuplog(const char *logstr)
539 {
540     char *ptr, *ptrbak, *logtype, *loglevel = NULL, *filename = NULL;
541     ptr = strdup(logstr);
542     ptrbak = ptr;
543
544     /* logtype */
545     logtype = ptr;
546
547     /* get loglevel */
548     ptr = strpbrk(ptr, " \t");
549     if (ptr) {
550         *ptr++ = 0;
551         while (*ptr && isspace(*ptr))
552             ptr++;
553         loglevel = ptr;
554
555         /* get filename */
556         ptr = strpbrk(ptr, " \t");
557         if (ptr) {
558             *ptr++ = 0;
559             while (*ptr && isspace(*ptr))
560                 ptr++;
561         }
562         filename = ptr;
563         if (filename && *filename == 0)
564             filename = NULL;
565     }
566
567     /* finally call setuplog, filename can be NULL */
568     setuplog_internal(loglevel, logtype, filename);
569
570     free(ptrbak);
571 }
572
573 void unsetuplog(const char *logstr)
574 {
575     char *str, *logtype, *filename;
576
577     str = strdup(logstr);
578
579     /* logtype */
580     logtype = str;
581
582     /* get filename, can be NULL */
583     strtok(str, " \t");
584     filename = strtok(NULL, " \t");
585
586     /* finally call setuplog, filename can be NULL */
587     setuplog_internal(NULL, str, filename);
588
589     free(str);
590 }