]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/logger.c
logger: remove flood protection and allocate messages
[netatalk.git] / libatalk / util / logger.c
1
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5
6 /* =========================================================================
7
8 logger.c was written by Simon Bazley (sibaz@sibaz.com)
9
10 I believe libatalk is released under the L/GPL licence.
11 Just incase, it is, thats the licence I'm applying to this file.
12 Netatalk 2001 (c)
13
14 ========================================================================= */
15
16 #include <stdio.h>
17 #include <limits.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <syslog.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <sys/uio.h>
26 #include <unistd.h>
27 #include <sys/time.h>
28 #include <time.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdbool.h>
32
33 #include <atalk/util.h>
34 #include <atalk/logger.h>
35 #include <atalk/unix.h>
36
37 #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
38
39 #define MAXLOGSIZE 512
40
41 #define LOGLEVEL_STRING_IDENTIFIERS { \
42   "-",                      \
43   "severe",                       \
44   "error",                        \
45   "warn",                         \
46   "note",                         \
47   "info",                         \
48   "debug",                        \
49   "debug6",                       \
50   "debug7",                       \
51   "debug8",                       \
52   "debug9",                       \
53   "maxdebug"}                        
54
55 /* these are the string identifiers corresponding to each logtype */
56 #define LOGTYPE_STRING_IDENTIFIERS { \
57   "Default",                         \
58   "Logger",                          \
59   "CNID",                            \
60   "AFPDaemon",                       \
61   "DSI",                             \
62   "UAMS",                            \
63   "FCE",                             \
64   "ad",                              \
65   "Spotlight",                       \
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_none:        no logging by default
80    0:               Display options */
81 #define DEFAULT_LOG_CONFIG {0, 0, -1, log_none, 0}
82
83 UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = {
84     DEFAULT_LOG_CONFIG, /* logtype_default */
85     DEFAULT_LOG_CONFIG, /* logtype_logger */
86     DEFAULT_LOG_CONFIG, /* logtype_cnid */
87     DEFAULT_LOG_CONFIG, /* logtype_afpd */
88     DEFAULT_LOG_CONFIG, /* logtype_dsi */
89     DEFAULT_LOG_CONFIG, /* logtype_uams */
90     DEFAULT_LOG_CONFIG, /* logtype_fce */
91     DEFAULT_LOG_CONFIG, /* logtype_ad */
92     DEFAULT_LOG_CONFIG  /* logtype_sl */
93 };
94
95 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility);
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 static int generate_message(char **message_details_buffer,
117                             char *user_message,
118                             int display_options,
119                             enum loglevels loglevel,
120                             enum logtypes logtype)
121 {
122     char *details;
123     int    len;
124     struct timeval tv;
125     pid_t  pid;
126     char buf[256];
127
128     gettimeofday(&tv, NULL);
129     strftime(buf, sizeof(buf), "%b %d %H:%M:%S.", localtime(&tv.tv_sec));
130     pid = getpid();
131     const char *basename = strrchr(log_src_filename, '/');
132     if (basename) {
133         basename++;
134     } else {
135         basename = log_src_filename;
136     }
137
138
139     len = asprintf(&details,
140                    "%s%06u %s[%d] {%s:%d} (%s:%s): %s\n",
141                    buf,
142                    (int)tv.tv_usec,
143                    log_config.processname,
144                    pid,
145                    basename,
146                    log_src_linenumber,
147                    arr_loglevel_strings[loglevel],
148                    arr_logtype_strings[logtype],
149                    user_message);
150     if (len == -1) {
151         *message_details_buffer = "";
152         return -1;
153     }
154     *message_details_buffer = details;
155     return len;
156 }
157
158 static int get_syslog_equivalent(enum loglevels loglevel)
159 {
160     switch (loglevel)
161     {
162         /* The question is we know how bad it is for us,
163            but how should that translate in the syslogs?  */
164     case 1: /* severe */
165         return LOG_ERR;
166     case 2: /* error */
167         return LOG_ERR;
168     case 3: /* warning */
169         return LOG_WARNING;
170     case 4: /* note */
171         return LOG_NOTICE;
172     case 5: /* information */
173         return LOG_INFO;
174     default: /* debug */
175         return LOG_DEBUG;
176     }
177 }
178
179 /* Called by the LOG macro for syslog messages */
180 static void make_syslog_entry(enum loglevels loglevel, enum logtypes logtype _U_, char *message)
181 {
182     if ( !log_config.syslog_opened ) {
183         openlog(log_config.processname,
184                 log_config.syslog_display_options,
185                 log_config.syslog_facility);
186         log_config.syslog_opened = true;
187     }
188
189     syslog(get_syslog_equivalent(loglevel), "%s", message);
190 }
191
192 static void log_init(void)
193 {
194     syslog_setup(log_info,
195                  logtype_default,
196                  logoption_ndelay | logoption_pid,
197                  logfacility_daemon);
198 }
199
200 static void log_setup(const char *filename, enum loglevels loglevel, enum logtypes logtype)
201 {
202     if (loglevel == 0) {
203         /* Disable */
204         if (type_configs[logtype].set) {
205             if (type_configs[logtype].fd != -1)
206                 close(type_configs[logtype].fd);
207             type_configs[logtype].fd = -1;
208             type_configs[logtype].level = -1;
209             type_configs[logtype].set = false;
210
211             /* if disabling default also set all "default using" levels to 0 */
212             if (logtype == logtype_default) {
213                 while (logtype != logtype_end_of_list_marker) {
214                     if ( ! (type_configs[logtype].set))
215                         type_configs[logtype].level = -1;
216                     logtype++;
217                 }
218             }
219         }
220         return;
221     }
222
223     /* Safety check */
224     if (NULL == filename)
225         return;
226
227     /* Resetting existing config ? */
228     if (type_configs[logtype].set) {
229         if (type_configs[logtype].fd != -1)
230             close(type_configs[logtype].fd);
231         type_configs[logtype].fd = -1;
232         type_configs[logtype].level = -1;
233         type_configs[logtype].set = false;
234         type_configs[logtype].syslog = false;
235
236         /* Reset configs using default */
237         if (logtype == logtype_default) {
238             int typeiter = 0;
239             while (typeiter != logtype_end_of_list_marker) {
240                 if (type_configs[typeiter].set == false) {
241                     type_configs[typeiter].level = -1;
242                     type_configs[typeiter].syslog = false;
243                 }
244                 typeiter++;
245             }
246         }
247     }
248
249     /* Set new values */
250     type_configs[logtype].level = loglevel;
251
252     /* Open log file as OPEN_LOGS_AS_UID*/
253
254     /* Is it /dev/tty ? */
255     if (strcmp(filename, "/dev/tty") == 0) {
256         type_configs[logtype].fd = 1; /* stdout */
257
258     /* Does it end in "XXXXXX" ? debug reguest via SIGINT */
259     } else if (strcmp(filename + strlen(filename) - 6, "XXXXXX") == 0) {
260         char *tmp = strdup(filename);
261         type_configs[logtype].fd = mkstemp(tmp);
262         free(tmp);
263
264     } else {
265         become_root();
266         type_configs[logtype].fd = open(filename,
267                                         O_CREAT | O_WRONLY | O_APPEND,
268                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
269         become_root();
270     }
271
272     /* Check for error opening/creating logfile */
273     if (type_configs[logtype].fd == -1) {
274         type_configs[logtype].level = -1;
275         type_configs[logtype].set = false;
276         return;
277     }
278
279     fcntl(type_configs[logtype].fd, F_SETFD, FD_CLOEXEC);
280     type_configs[logtype].set = true;
281     log_config.inited = true;
282
283     /* Here's how we make it possible to LOG to a logtype like "logtype_afpd" */
284     /* which then uses the default logtype setup if it isn't setup itself: */
285     /* we just copy the loglevel from default to all logtypes that are not setup. */
286     /* In "make_log_entry" we then check for the logtypes if they arent setup */
287     /* and use default then. We must provide accessible values for all logtypes */
288     /* in order to make it easy and fast to check the loglevels in the LOG macro! */
289
290     if (logtype == logtype_default) {
291         int typeiter = 0;
292         while (typeiter != logtype_end_of_list_marker) {
293             if ( ! (type_configs[typeiter].set))
294                 type_configs[typeiter].level = loglevel;
295             typeiter++;
296         }
297     }
298
299     LOG(log_debug, logtype_logger, "Setup file logging: type: %s, level: %s, file: %s",
300         arr_logtype_strings[logtype], arr_loglevel_strings[loglevel], filename);
301 }
302
303 /* Setup syslog logging */
304 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility)
305 {
306     /* 
307      * FIXME:
308      * this currently doesn't care if logtype is already logging to a file.
309      * Fortunately currently there's no way a user could trigger this as afpd.conf
310      * is not re-read on SIGHUP.
311      */
312
313     type_configs[logtype].level = loglevel;
314     type_configs[logtype].set = true;
315     type_configs[logtype].syslog = true;
316     log_config.syslog_display_options = display_options;
317     log_config.syslog_facility = facility;
318
319     /* Setting default logging? Then set all logtype not set individually */
320     if (logtype == logtype_default) {
321         int typeiter = 0;
322         while (typeiter != logtype_end_of_list_marker) {
323             if ( ! (type_configs[typeiter].set)) {
324                 type_configs[typeiter].level = loglevel;
325                 type_configs[typeiter].syslog = true;
326             }
327             typeiter++;
328         }
329     }
330
331     log_config.inited = 1;
332
333     LOG(log_info, logtype_logger, "Set syslog logging to level: %s",
334         arr_loglevel_strings[loglevel]);
335 }
336
337 /*
338  * If filename == NULL its for syslog logging, otherwise its for file-logging.
339  * "unsetuplog" calls with loglevel == NULL.
340  * loglevel == NULL means:
341  *    if logtype == default
342  *       disable logging
343  *    else
344  *       set to default logging
345  */
346 static void setuplog_internal(const char *loglevel, const char *logtype, const char *filename)
347 {
348     unsigned int typenum, levelnum;
349
350     /* Parse logtype */
351     for( typenum=0; typenum < num_logtype_strings; typenum++) {
352         if (strcasecmp(logtype, arr_logtype_strings[typenum]) == 0)
353             break;
354     }
355     if (typenum >= num_logtype_strings) {
356         return;
357     }
358
359     /* Parse loglevel */
360     if (loglevel == NULL) {
361         levelnum = 0;
362     } else {
363         for(levelnum=1; levelnum < num_loglevel_strings; levelnum++) {
364             if (strcasecmp(loglevel, arr_loglevel_strings[levelnum]) == 0)
365                 break;
366         }
367         if (levelnum >= num_loglevel_strings) {
368             return;
369         }
370     }
371
372     /* is this a syslog setup or a filelog setup ? */
373     if (filename == NULL) {
374         /* must be syslog */
375         syslog_setup(levelnum,
376                      typenum,
377                      logoption_ndelay | logoption_pid,
378                      logfacility_daemon);
379     } else {
380         /* this must be a filelog */
381         log_setup(filename, levelnum, typenum);
382     }
383
384     return;
385 }
386
387 /* =========================================================================
388    Global function definitions
389    ========================================================================= */
390
391 /* This function sets up the processname */
392 void set_processname(const char *processname)
393 {
394     strncpy(log_config.processname, processname, 15);
395     log_config.processname[15] = 0;
396 }
397
398 /* -------------------------------------------------------------------------
399    make_log_entry has 1 main flaws:
400    The message in its entirity, must fit into the tempbuffer.
401    So it must be shorter than MAXLOGSIZE
402    ------------------------------------------------------------------------- */
403 void make_log_entry(enum loglevels loglevel, enum logtypes logtype,
404                     const char *file, int line, char *message, ...)
405 {
406     /* fn is not reentrant but is used in signal handler
407      * with LOGGER it's a little late source name and line number
408      * are already changed. */
409     static int inlog = 0;
410     int fd, len;
411     char *user_message, *log_message;
412     va_list args;
413
414     if (inlog)
415         return;
416
417     inlog = 1;
418
419     if (!log_config.inited) {
420       log_init();
421     }
422     
423     if (type_configs[logtype].syslog) {
424         if (type_configs[logtype].level >= loglevel) {
425             /* Initialise the Messages and send it to syslog */
426             va_start(args, message);
427             len = vasprintf(&user_message, message, args);
428             va_end(args);
429             if (len == -1) {
430                 return;
431             }
432             make_syslog_entry(loglevel, logtype, user_message);
433             free(user_message);
434         }
435         inlog = 0;
436         return;
437     }
438
439     /* logging to a file */
440
441     log_src_filename = file;
442     log_src_linenumber = line;
443
444     /* Check if requested logtype is setup */
445     if (type_configs[logtype].set) {
446         /* Yes */
447         fd = type_configs[logtype].fd;
448     } else {
449         /* No: use default */
450         fd = type_configs[logtype_default].fd;
451     }
452
453     if (fd < 0) {
454         /* no where to send the output, give up */
455         goto exit;
456     }
457
458     /* Initialise the Messages */
459     va_start(args, message);
460     len = vasprintf(&user_message, message, args);
461     if (len == -1) {
462         goto exit;
463     }
464     va_end(args);
465
466     len = generate_message(&log_message,
467                            user_message,
468                            type_configs[logtype].set ?
469                            type_configs[logtype].display_options :
470                            type_configs[logtype_default].display_options,
471                            loglevel, logtype);
472     if (len == -1) {
473         goto exit;
474     }
475     write(fd, log_message, len);
476     free(log_message);
477     free(user_message);
478
479 exit:
480     inlog = 0;
481 }
482
483 void setuplog(const char *logstr, const char *logfile)
484 {
485     char *ptr, *save;
486     char *logtype, *loglevel;
487     char c;
488
489     save = ptr = strdup(logstr);
490
491     ptr = strtok(ptr, ", ");
492
493     while (ptr) {
494         while (*ptr) {
495             while (*ptr && isspace(*ptr))
496                 ptr++;
497
498             logtype = ptr;
499             ptr = strpbrk(ptr, ":");
500             if (!ptr)
501                 break;
502             *ptr = 0;
503
504             ptr++;
505             loglevel = ptr;
506             while (*ptr && !isspace(*ptr))
507                 ptr++;
508             c = *ptr;
509             *ptr = 0;
510             setuplog_internal(loglevel, logtype, logfile);
511             *ptr = c;
512         }
513         ptr = strtok(NULL, ", ");
514     }
515
516     free(save);
517 }
518