6 /* =========================================================================
8 logger.c was written by Simon Bazley (sibaz@sibaz.com)
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.
14 ========================================================================= */
22 #include <sys/types.h>
33 #include <atalk/util.h>
34 #include <atalk/logger.h>
35 #include <atalk/unix.h>
37 #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
39 #define MAXLOGSIZE 512
41 #define LOGLEVEL_STRING_IDENTIFIERS { \
55 /* these are the string identifiers corresponding to each logtype */
56 #define LOGTYPE_STRING_IDENTIFIERS { \
68 /* =========================================================================
70 ========================================================================= */
72 /* Main log config container */
73 log_config_t log_config = { 0 };
75 /* Default log config: log nothing to files.
79 log_none: no logging by default
81 #define DEFAULT_LOG_CONFIG {0, 0, -1, log_none, 0}
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 */
95 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility);
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;
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);
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);
109 static const char *arr_loglevel_strings[] = LOGLEVEL_STRING_IDENTIFIERS;
110 static const unsigned int num_loglevel_strings = COUNT_ARRAY(arr_loglevel_strings);
112 /* =========================================================================
113 Internal function definitions
114 ========================================================================= */
116 static int generate_message(char **message_details_buffer,
119 enum loglevels loglevel,
120 enum logtypes logtype)
128 gettimeofday(&tv, NULL);
129 strftime(buf, sizeof(buf), "%b %d %H:%M:%S.", localtime(&tv.tv_sec));
131 const char *basename = strrchr(log_src_filename, '/');
135 basename = log_src_filename;
139 len = asprintf(&details,
140 "%s%06u %s[%d] {%s:%d} (%s:%s): %s\n",
143 log_config.processname,
147 arr_loglevel_strings[loglevel],
148 arr_logtype_strings[logtype],
151 *message_details_buffer = "";
154 *message_details_buffer = details;
158 static int get_syslog_equivalent(enum loglevels loglevel)
162 /* The question is we know how bad it is for us,
163 but how should that translate in the syslogs? */
168 case 3: /* warning */
172 case 5: /* information */
179 /* Called by the LOG macro for syslog messages */
180 static void make_syslog_entry(enum loglevels loglevel, enum logtypes logtype _U_, char *message)
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;
189 syslog(get_syslog_equivalent(loglevel), "%s", message);
192 static void log_init(void)
194 syslog_setup(log_info,
196 logoption_ndelay | logoption_pid,
200 static void log_setup(const char *filename, enum loglevels loglevel, enum logtypes logtype)
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;
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;
224 if (NULL == filename)
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;
236 /* Reset configs using default */
237 if (logtype == logtype_default) {
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;
250 type_configs[logtype].level = loglevel;
252 /* Open log file as OPEN_LOGS_AS_UID*/
254 /* Is it /dev/tty ? */
255 if (strcmp(filename, "/dev/tty") == 0) {
256 type_configs[logtype].fd = 1; /* stdout */
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);
266 type_configs[logtype].fd = open(filename,
267 O_CREAT | O_WRONLY | O_APPEND,
268 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
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;
279 fcntl(type_configs[logtype].fd, F_SETFD, FD_CLOEXEC);
280 type_configs[logtype].set = true;
281 log_config.inited = true;
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! */
290 if (logtype == logtype_default) {
292 while (typeiter != logtype_end_of_list_marker) {
293 if ( ! (type_configs[typeiter].set))
294 type_configs[typeiter].level = loglevel;
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);
303 /* Setup syslog logging */
304 static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility)
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.
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;
319 /* Setting default logging? Then set all logtype not set individually */
320 if (logtype == logtype_default) {
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;
331 log_config.inited = 1;
333 LOG(log_info, logtype_logger, "Set syslog logging to level: %s",
334 arr_loglevel_strings[loglevel]);
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
344 * set to default logging
346 static void setuplog_internal(const char *loglevel, const char *logtype, const char *filename)
348 unsigned int typenum, levelnum;
351 for( typenum=0; typenum < num_logtype_strings; typenum++) {
352 if (strcasecmp(logtype, arr_logtype_strings[typenum]) == 0)
355 if (typenum >= num_logtype_strings) {
360 if (loglevel == NULL) {
363 for(levelnum=1; levelnum < num_loglevel_strings; levelnum++) {
364 if (strcasecmp(loglevel, arr_loglevel_strings[levelnum]) == 0)
367 if (levelnum >= num_loglevel_strings) {
372 /* is this a syslog setup or a filelog setup ? */
373 if (filename == NULL) {
375 syslog_setup(levelnum,
377 logoption_ndelay | logoption_pid,
380 /* this must be a filelog */
381 log_setup(filename, levelnum, typenum);
387 /* =========================================================================
388 Global function definitions
389 ========================================================================= */
391 /* This function sets up the processname */
392 void set_processname(const char *processname)
394 strncpy(log_config.processname, processname, 15);
395 log_config.processname[15] = 0;
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, ...)
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;
411 char *user_message, *log_message;
419 if (!log_config.inited) {
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);
432 make_syslog_entry(loglevel, logtype, user_message);
439 /* logging to a file */
441 log_src_filename = file;
442 log_src_linenumber = line;
444 /* Check if requested logtype is setup */
445 if (type_configs[logtype].set) {
447 fd = type_configs[logtype].fd;
449 /* No: use default */
450 fd = type_configs[logtype_default].fd;
454 /* no where to send the output, give up */
458 /* Initialise the Messages */
459 va_start(args, message);
460 len = vasprintf(&user_message, message, args);
466 len = generate_message(&log_message,
468 type_configs[logtype].set ?
469 type_configs[logtype].display_options :
470 type_configs[logtype_default].display_options,
475 write(fd, log_message, len);
483 void setuplog(const char *logstr, const char *logfile)
486 char *logtype, *loglevel;
489 save = ptr = strdup(logstr);
491 ptr = strtok(ptr, ", ");
495 while (*ptr && isspace(*ptr))
499 ptr = strpbrk(ptr, ":");
506 while (*ptr && !isspace(*ptr))
510 setuplog_internal(loglevel, logtype, logfile);
513 ptr = strtok(NULL, ", ");