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