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