]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/logger.c
Reworked logger
[netatalk.git] / libatalk / util / logger.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 /* =========================================================================
6
7        logger.c is part of the utils section in the libatalk library, 
8         which is part of the netatalk project.  
9
10        logger.c was written by Simon Bazley (sibaz@sibaz.com)
11
12        I believe libatalk is released under the L/GPL licence.  
13        Just incase, it is, thats the licence I'm applying to this file.
14        Netatalk 2001 (c)
15
16    ==========================================================================
17
18        Logger.c is intended as an alternative to syslog for logging
19
20    ========================================================================= */
21
22 #include <stdio.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <syslog.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <time.h>
33
34 #include <atalk/boolean.h>
35
36 #define LOGGER_C
37 #include <atalk/logger.h>
38 #undef LOGGER_C
39
40 #define OPEN_LOGS_AS_UID 0
41
42 #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
43
44 /* =========================================================================
45     Config
46    ========================================================================= */
47
48 /* Main log config container, must be globally visible */
49 log_config_t log_config = {
50   0,                              /* Initialized ? 0 = no */
51   0,                              /* No filelogging setup yet */
52   {0},                            /* processname */
53   0,                              /* syslog opened ? */
54   logfacility_daemon,             /* syslog facility to use */  
55   logoption_ndelay|logoption_pid, /* logging options for syslog */
56   0                               /* log level for syslog */
57 };
58
59 /* Default log config: log nothing to files.
60       0:    not set individually
61       NULL: Name of file
62       -1:   logfiles fd
63       0:   Log Level
64       0:    Display options */
65 #define DEFAULT_LOG_CONFIG {0, NULL, -1, 0, 0}
66
67 filelog_conf_t file_configs[logtype_end_of_list_marker] = {
68     DEFAULT_LOG_CONFIG, /* logtype_default */
69     DEFAULT_LOG_CONFIG, /* logtype_core */
70     DEFAULT_LOG_CONFIG, /* logtype_logger */
71     DEFAULT_LOG_CONFIG, /* logtype_cnid */
72     DEFAULT_LOG_CONFIG, /* logtype_afpd */
73     DEFAULT_LOG_CONFIG, /* logtype_atalkd */
74     DEFAULT_LOG_CONFIG, /* logtype_papd */
75     DEFAULT_LOG_CONFIG  /* logtype_uams */
76 };
77
78 /* These are used by the LOG macro to store __FILE__ and __LINE__ */
79 char *log_src_filename;
80 int  log_src_linenumber;
81
82 /* Array to store text to list given a log type */
83 static const char *arr_logtype_strings[] =  LOGTYPE_STRING_IDENTIFIERS;
84 static const int num_logtype_strings = COUNT_ARRAY(arr_logtype_strings);
85
86 /* Array for charachters representing log severity in the log file */
87 static const char arr_loglevel_chars[] = {'-','S', 'E', 'W', 'N', 'I', 'D'};
88 static const int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars);
89
90 static const char *arr_loglevel_strings[] = LOGLEVEL_STRING_IDENTIFIERS;
91 static const int num_loglevel_strings = COUNT_ARRAY(arr_loglevel_strings);
92
93 /* =========================================================================
94     Internal function definitions
95    ========================================================================= */
96
97 void generate_message_details(char *message_details_buffer, 
98                               int message_details_buffer_length,
99                               int display_options,
100                               enum loglevels loglevel, enum logtypes logtype)
101 {
102     char   *ptr = message_details_buffer;
103     int    templen;
104     int    len = message_details_buffer_length;
105
106     *ptr = 0;
107
108     /* Print date */
109     time_t thetime;
110     time(&thetime);
111
112     strftime(ptr, len, "%b %d %H:%M:%S ", localtime(&thetime));
113     templen = strlen(ptr);
114     len -= templen;
115     ptr += templen;
116
117     /* Process name */
118     strncpy(ptr, log_config.processname, len);
119     templen = strlen(ptr);
120     len -= templen;
121     ptr += templen;
122     
123     /* PID */
124     pid_t pid = getpid();
125     templen = snprintf(ptr, len, "[%d]", pid);
126     len -= templen;
127     ptr += templen;
128
129     /* Source info ? */
130     if ( ! (display_options & logoption_nsrcinfo)) {
131         templen = snprintf(ptr, len, " {%s:%d}", log_src_filename, log_src_linenumber);
132         len -= templen;
133         ptr += templen;
134     }
135
136     /* Errorlevel */
137     if ((loglevel/10) >= (num_loglevel_chars-1))
138         templen = snprintf(ptr, len,  " (%c%d:", arr_loglevel_chars[num_loglevel_chars-1], loglevel / 10 - 1);
139     else
140         templen = snprintf(ptr, len, " (%c:", arr_loglevel_chars[loglevel/10]);
141     len -= templen;
142     ptr += templen;    
143
144     /* Errortype */
145     const char *logtype_string;
146     if (logtype<num_logtype_strings) {
147         templen = snprintf(ptr, len, "%s", arr_logtype_strings[logtype]);
148         len -= templen;
149         ptr += templen;    
150     }
151
152     strncat(ptr, "): ", len);
153 }
154
155 int get_syslog_equivalent(enum loglevels loglevel)
156 {
157   switch (loglevel/10)
158   {
159     /* The question is we know how bad it is for us,
160                     but how should that translate in the syslogs?  */
161     case 1: /* severe */
162       return LOG_ERR;
163     case 2: /* error */
164       return LOG_ERR;
165     case 3: /* warning */
166       return LOG_WARNING;
167     case 4: /* note */
168       return LOG_NOTICE;
169     case 5: /* information */
170       return LOG_INFO;
171     default: /* debug */
172       return LOG_DEBUG;
173   }
174 }
175
176 /* =========================================================================
177     Global function definitions
178    ========================================================================= */
179
180 void log_init(void)
181 {
182 #ifdef LOGFILEPATH
183     log_setup(LOGFILEPATH, log_warning, logtype_default);
184 #else
185     syslog_setup(log_warning, 0,
186                  log_config.syslog_display_options,
187                  log_config.facility);
188 #endif
189 }
190
191 bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype)
192 {
193     uid_t process_uid;
194
195     if (loglevel == 0) {
196         /* Disable */
197         if (file_configs[logtype].set) {
198             if (file_configs[logtype].filename) {
199                 free(file_configs[logtype].filename);
200                 file_configs[logtype].filename = NULL;
201             }
202             close(file_configs[logtype].fd);
203             file_configs[logtype].fd = -1;
204             file_configs[logtype].level = 0;
205             file_configs[logtype].set = 0;
206
207             /* if disabling default also set all "default using" levels to 0 */
208             if (logtype == logtype_default) {
209                 while (logtype != logtype_end_of_list_marker) {
210                     if ( ! (file_configs[logtype].set))
211                         file_configs[logtype].level = 0;
212                     logtype++;
213                 }
214             }
215         }
216
217         return true;
218     }
219
220     /* Safety check */
221     if (NULL == filename)
222         return false;
223
224     /* Resetting existing config ? */
225     if (file_configs[logtype].set && file_configs[logtype].filename) {
226         free(file_configs[logtype].filename);
227         file_configs[logtype].filename == NULL;
228         close(file_configs[logtype].fd);
229         file_configs[logtype].fd = -1;
230         file_configs[logtype].level = 0;
231         file_configs[logtype].set = 0;
232     }
233
234     /* Set new values */
235     file_configs[logtype].filename = strdup(filename);
236     file_configs[logtype].level = loglevel;
237
238
239     /* Open log file as OPEN_LOGS_AS_UID*/
240     process_uid = getuid();
241     setuid(OPEN_LOGS_AS_UID);
242     file_configs[logtype].fd = open( file_configs[logtype].filename,
243                                      O_CREAT | O_WRONLY | O_APPEND,
244                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
245     setuid(process_uid);
246
247     /* Check for error opening/creating logfile */
248     if (-1 == file_configs[logtype].fd) {
249         free(file_configs[logtype].filename);
250         file_configs[logtype].filename = NULL;
251         file_configs[logtype].level = -1;
252         file_configs[logtype].set = 0;
253         return false;
254     }
255     
256     file_configs[logtype].set = 1;
257     log_config.filelogging = 1;
258     log_config.inited = 1;
259
260     /* Here's how we make it possible to LOG to a logtype like "logtype_afpd" */
261     /* which then uses the default logtype setup if it isn't setup itself: */
262     /* we just copy the loglevel from default to all logtypes that are not setup. */
263     /* In "make_log_entry" we then check for the logtypes if they arent setup */
264     /* and use default then. We must provide accessible values for all logtypes */
265     /* in order to make it easy and fast to check the loglevels in the LOG macro! */
266
267     if (logtype == logtype_default) {
268         while (logtype != logtype_end_of_list_marker) {
269             if ( ! (file_configs[logtype].set))
270                 file_configs[logtype].level = loglevel;
271             logtype++;
272         }
273         logtype = logtype_default;
274     }
275
276     LOG(log_info, logtype_logger, "Setup file logging: type: %s, level: %s, file: %s",
277         arr_logtype_strings[logtype], arr_loglevel_strings[loglevel/10], file_configs[logtype].filename);
278
279     return true;
280 }
281
282 /* logtype is ignored, it's just one for all */
283 void syslog_setup(int loglevel, enum logtypes logtype, 
284                   int display_options, int facility)
285 {
286     log_config.syslog_level = loglevel;
287     log_config.syslog_display_options = display_options;
288     log_config.facility = facility;
289
290     log_config.inited = 1;
291
292     LOG(log_info, logtype_logger, "Setup syslog logging: type: %s, level: %s",
293         arr_logtype_strings[logtype], arr_loglevel_strings[loglevel/10]);
294 }
295
296 void log_close()
297 {
298 }
299
300 /* This function sets up the processname */
301 void set_processname(char *processname)
302 {
303   strncpy(log_config.processname, processname, 15);
304   log_config.processname[15] = 0;
305 }
306
307 /* -------------------------------------------------------------------------
308     make_log_entry has 1 main flaws:
309       The message in its entirity, must fit into the tempbuffer.  
310       So it must be shorter than MAXLOGSIZE
311    ------------------------------------------------------------------------- */
312 void make_log_entry(enum loglevels loglevel, enum logtypes logtype, 
313                     char *message, ...)
314 {
315     size_t n = 0;
316     int fd;
317   va_list args;
318   char temp_buffer[MAXLOGSIZE];
319   /* fn is not reentrant but is used in signal handler 
320    * with LOGGER it's a little late source name and line number
321    * are already changed.
322   */
323   static int inlog = 0;
324
325   char log_details_buffer[MAXLOGSIZE];
326   uid_t process_uid;
327   if (inlog)
328      return;
329   inlog = 1;
330
331   /* Initialise the Messages */
332   va_start(args, message);
333   vsnprintf(temp_buffer, sizeof(temp_buffer), message, args);
334   va_end(args);
335   strncat(temp_buffer, "\n", MAXLOGSIZE);
336
337   generate_message_details(log_details_buffer, sizeof(log_details_buffer),
338                            file_configs[loglevel].set ? 
339                                file_configs[loglevel].display_options : 
340                                file_configs[logtype_default].display_options, 
341                            loglevel, logtype);
342
343   /* Check if requested logtype is setup */
344   if (file_configs[loglevel].set)
345       /* Yes */
346       fd = file_configs[loglevel].fd;
347   else
348       /* No: use default */
349       fd = file_configs[logtype_default].fd;
350
351   /* If default wasnt setup its fd is -1 */
352   if (fd > 0) {
353       write( fd, log_details_buffer, strlen(log_details_buffer) );
354       write( fd, temp_buffer, strlen(temp_buffer) );
355   }
356
357   inlog = 0;
358 }
359
360 /* Called by the LOG macro for syslog messages */
361 void make_syslog_entry(enum loglevels loglevel, enum logtypes logtype, char *message, ...)
362 {
363     va_list args;
364     char log_buffer[MAXLOGSIZE];
365     /* fn is not reentrant but is used in signal handler 
366      * with LOGGER it's a little late source name and line number
367      * are already changed.
368      */
369     static int inlog = 0;
370
371     if (inlog)
372         return;
373     inlog = 1;
374
375     if ( ! (log_config.syslog_opened) ) {
376         openlog(log_config.processname, log_config.syslog_display_options, 
377                 log_config.facility);
378         log_config.syslog_opened = 1;
379     }
380   
381     /* Initialise the Messages */
382     va_start(args, message);
383     vsnprintf(log_buffer, sizeof(log_buffer), message, args);
384     va_end(args);
385
386     syslog(get_syslog_equivalent(loglevel), "%s", log_buffer);
387
388     inlog = 0;
389 }
390
391 /* 
392  * This is called from the afpd.conf parsing code.
393  * If filename == NULL its for syslog logging, otherwise its for file-logging.
394  * "unsetuplog" calls with loglevel == NULL.
395  * loglevel == NULL means:
396  *    if logtype == default
397  *       disable logging
398  *    else 
399  *       set to default logging
400  */
401
402   /* -[un]setuplog <logtype> <loglevel> [<filename>]*/
403 void setuplog(char *logtype, char *loglevel, char *filename)
404 {
405   int typenum, levelnum;
406
407   /* Parse logtype */
408   for( typenum=0; typenum < num_logtype_strings; typenum++) {
409       if (strcasecmp(logtype, arr_logtype_strings[typenum]) == 0)
410           break;
411   }
412   if (typenum >= num_logtype_strings) {
413       return;
414   }
415
416   /* Parse loglevel */
417   if (loglevel == NULL) {
418       levelnum = 0;
419   } else {
420       for(levelnum=1; levelnum < num_loglevel_strings; levelnum++) {
421           if (strcasecmp(loglevel, arr_loglevel_strings[levelnum]) == 0)
422               break;
423       }
424       if (levelnum >= num_loglevel_strings) {
425           return;
426       }
427   }
428
429   /* now match the order of the text string with the actual enum value (10 times) */
430   levelnum *= 10;
431   
432   /* is this a syslog setup or a filelog setup ? */
433   if (filename == NULL) {
434       /* must be syslog */
435       syslog_setup(levelnum, 0, 
436                    log_config.syslog_display_options,
437                    log_config.facility);
438   } else {
439       /* this must be a filelog */
440       log_setup(filename, levelnum, typenum);
441   }
442
443   return;
444 }