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