5 /* ==========================================================================
7 logger.c is part of the utils section in the libatalk library,
8 which is part of the netatalk project.
10 logger.c was written by Simon Bazley (sibaz@sibaz.com)
12 I believe libatalk is released under the L/GPL licence.
14 Just incase, it is, thats the licence I'm applying to this file.
18 ==========================================================================
20 Logger.c is intended as an alternative to syslog for logging
22 ---------------------------------------------------------------
24 The initial plan is to create a structure for general information needed
27 Initally I'll hard code the neccesary stuff to start a log, this should
28 probably be moved elsewhere when some code is written to read the log
29 file locations from the config files.
31 As a more longterm idea, I'll code this so that the data struct can be
32 duplicated to allow multiple concurrent log files, although this is
33 probably a recipe for wasted resources.
35 ========================================================================== */
41 #include <sys/types.h>
45 #include <atalk/boolean.h>
46 #include <atalk/logger.h>
48 #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
49 #undef KEEP_LOGFILES_OPEN
53 #undef DEBUG_OUTPUT_TO_SCREEN
54 #undef CHECK_STAT_ON_NEW_FILES
55 #undef CHECK_ACCESS_ON_NEW_FILES
57 /* ==========================================================================
58 External function declarations
59 ========================================================================== */
61 /* setup the internal variables used by the logger (called automatically) */
64 /* Setup the log filename and the loglevel, and the type of log it is. */
65 bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype, int display_options);
67 /* Setup the Level and type of log that will be logged to syslog. */
68 void syslog_setup(enum loglevels loglevel, enum logtypes logtype, int display_options, int facility);
70 /* finish up and close the logs */
73 /* This function sets up the processname */
74 void set_processname(char *processname);
77 void make_log(enum loglevels loglevel, enum logtypes logtype, char *message, ...);
78 #ifndef DISABLE_LOGGER
79 make_log_func set_log_location(char *srcfilename, int srclinenumber);
81 /* ==========================================================================
83 ========================================================================== */
85 /* A structure containing object level stuff */
86 struct tag_log_file_data {
87 char log_filename[PATH_MAX]; /* Name of file */
88 FILE *log_file; /* FILE pointer to file */
89 enum loglevels log_level; /* Log Level to put in this file */
93 typedef struct tag_log_file_data log_file_data_pair[2];
95 /* A structure containg class level stuff */
96 struct tag_global_log_data {
99 char *temp_src_filename;
100 int temp_src_linenumber;
101 char processname[16];
103 char *log_file_directory; /* Path of directory containing log files */
104 log_file_data_pair **logs;
107 struct what_to_print_array {
109 bool print_processname;
117 /* ==========================================================================
118 Internal function declarations
119 ========================================================================== */
121 void generate_message_details(char *message_details_buffer,
122 int message_details_buffer_length,
123 struct tag_log_file_data *log_struct,
124 enum loglevels loglevel, enum logtypes logtype);
126 int get_syslog_equivalent(enum loglevels loglevel);
128 static char *get_command_name(char *commandpath);
130 /* ==========================================================================
132 ========================================================================== */
134 /* A populated instance */
136 static log_file_data_pair default_log_file_data_pair = {
138 /* log_filename */ "\0\0\0\0\0\0\0\0",
140 /* log_level */ log_debug,
141 /* display_options */ logoption_pid
144 /* log_filename */ LOGFILEPATH,
146 /* log_level */ log_debug,
147 /* display_options */ logoption_pid
150 static log_file_data_pair *log_file_data_array[logtype_end_of_list_marker] =
151 {&default_log_file_data_pair};
153 /* The class (populated) */
154 static struct tag_global_log_data global_log_data = {
155 /* struct_size */ sizeof(struct tag_global_log_data),
156 /* temp_src_filename */ NULL,
157 /* temp_src_linenumber */ 0,
158 /* processname */ "",
159 /* log_file_directory */ "",
163 /* macro to get access to the array */
164 #define log_file_arr (global_log_data.logs)
166 /* Array to store text to list given a log type */
167 static const char * arr_logtype_strings[] = LOGTYPE_STRING_IDENTIFIERS;
168 static const int num_logtype_strings = COUNT_ARRAY(arr_logtype_strings);
170 /* Array for charachters representing log severity in the log file */
171 static const char arr_loglevel_chars[] = {'S', 'E', 'W', 'N', 'I', 'D'};
172 static const int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars);
174 #else /* #ifndef DISABLE_LOGGER */
175 char *disabled_logger_processname=NULL;
176 #endif /* DISABLE_LOGGER */
177 /* ==========================================================================
178 Global function definitions
179 ========================================================================== */
181 #ifndef DISABLE_LOGGER
183 /* remember I'm keeping a copy of the actual char * Filename, so you mustn't
184 delete it, until you've finished with the log. Also you're responsible
185 for deleting it when you have finished with it. */
188 if (global_log_data.logs==NULL)
190 /* first check default_log_file_data_pair */
192 /* next clear out the log_file_data_array */
193 memset(log_file_data_array, 0, sizeof(log_file_data_array));
194 /* now set default_log_file_data_pairs */
195 log_file_data_array[0] = &default_log_file_data_pair;
197 /* now setup the global_log_data struct */
198 global_log_data.logs = log_file_data_array;
201 #endif /* #ifndef DISABLE_LOGGER */
203 bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype, int display_options)
205 #ifndef DISABLE_LOGGER
215 log_file_data_pair *logs;
219 logs = log_file_arr[logtype];
223 logs = (log_file_data_pair *)malloc(sizeof(log_file_data_pair));
226 LOG(log_severe, logtype_logger, "can't calloc in log_setup");
230 /* memcpy(logs, log_file_arr[logtype_default], sizeof(log_file_data_pair)); */
231 log_file_arr[logtype] = logs;
232 (*logs)[1].log_file = NULL;
236 if ( ((*logs)[1].log_file == stdout) && ((*logs)[1].log_file != NULL) )
238 fclose((*logs)[1].log_file);
239 (*logs)[1].log_file = NULL;
242 if (strlen(global_log_data.log_file_directory)>0)
244 lastchar[0] = global_log_data.log_file_directory[strlen(global_log_data.log_file_directory)-1];
246 if (lastchar[0] == '/' || lastchar[0] == '\\' || lastchar[0] == ':')
249 /* this should probably be a platform specific path separator */
257 #ifdef DEBUG_OUTPUT_TO_SCREEN
258 printf("filename is %s stored at location %p\n", (*logs)[1].log_filename,
259 (*logs)[1].log_filename);
260 #endif /* DEBUG_OUTPUT_TO_SCREEN */
261 if (filename == NULL)
263 strncpy((*logs)[1].log_filename, (*(log_file_arr[0]))[1].log_filename, PATH_MAX);
267 sprintf((*logs)[1].log_filename, "%s%s%s", global_log_data.log_file_directory,
270 (*logs)[1].log_level = loglevel;
271 (*logs)[1].display_options = display_options;
273 #ifdef DEBUG_OUTPUT_TO_SCREEN
274 printf("filename is %s stored at location %p\n", (*logs)[1].log_filename,
275 (*logs)[1].log_filename);
276 #endif /* DEBUG_OUTPUT_TO_SCREEN */
278 #ifdef CHECK_STAT_ON_NEW_FILES
282 #ifdef DEBUG_OUTPUT_TO_SCREEN
283 printf("about to stat file %s\n", (*logs)[1].log_filename);
285 firstattempt = stat((*logs)[1].log_filename, &statbuf);
287 if (firstattempt == -1)
289 #ifdef DEBUG_OUTPUT_TO_SCREEN
290 printf("about to call Log with %d, %d, %s, %s\n", log_note, logtype_logger,
291 "can't stat Logfile", (*logs)[1].log_filename);
294 /* syslog(LOG_INFO, "stat failed"); */
295 LOG(log_warning, logtype_logger, "stat fails on file %s",
296 (*logs)[1].log_filename);
298 if (strlen(global_log_data.log_file_directory)>0)
300 retval = stat(global_log_data.log_file_directory, &statbuf);
303 #ifdef DEBUG_OUTPUT_TO_SCREEN
304 printf("can't stat dir either so I'm giving up\n");
306 LOG(log_severe, logtype_logger, "can't stat directory %s either",
307 global_log_data.log_file_directory);
313 #ifdef CHECK_ACCESS_ON_NEW_FILES
314 access = ((statbuf.st_uid == uid)?(statbuf.st_mode & S_IWUSR):0) +
315 ((statbuf.st_gid == gid)?(statbuf.st_mode & S_IWGRP):0) +
316 (statbuf.st_mode & S_IWOTH);
320 #ifdef DEBUG_OUTPUT_TO_SCREEN
321 printf("failing with %d, %d, %s, %s\n", log_note, logtype_logger,
322 "can't access Logfile %s", (*logs)[1].log_filename);
325 LOG(log_note, logtype_logger, "can't access file %s",
326 (*logs)[1].log_filename);
329 #endif /* CHECK_ACCESS_ON_NEW_FILES */
330 #endif /* CHECK_STAT_ON_NEW_FILES */
331 #ifdef KEEP_LOGFILES_OPEN
332 if ((*logs)[1].log_file!=NULL)
333 fclose((*logs)[1].log_file);
335 (*logs)[1].log_file = fopen((*logs)[1].log_filename, "at");
336 if ((*logs)[1].log_file == NULL)
338 LOG(log_severe, logtype_logger, "can't open Logfile %s", (*logs)[1].log_filename);
343 LOG(log_info, logtype_logger, "Log setup complete");
345 #endif /* DISABLE_LOGGER */
350 void syslog_setup(enum loglevels loglevel, enum logtypes logtype, int display_options, int facility)
352 #ifndef DISABLE_LOGGER
353 log_file_data_pair *logs;
357 logs = log_file_arr[logtype];
361 logs = (log_file_data_pair *)malloc(sizeof(log_file_data_pair));
364 LOG(log_severe, logtype_logger, "can't calloc in log_setup");
368 memcpy(logs, log_file_arr[logtype_default], sizeof(log_file_data_pair));
369 log_file_arr[logtype] = logs;
373 (*logs)[0].log_file = NULL;
374 (*logs)[0].log_filename[0] = 0;
375 (*logs)[0].log_level = loglevel;
376 (*logs)[0].display_options = display_options;
378 openlog(global_log_data.processname, (*logs)[0].display_options, facility);
380 LOG(log_info, logtype_logger, "SysLog setup complete");
381 #else /* DISABLE_LOGGER */
382 /* behave like a normal openlog call */
383 openlog(disabled_logger_processname, display_options, facility);
384 #endif /* DISABLE_LOGGER */
389 #ifndef DISABLE_LOGGER
390 log_file_data_pair *logs;
393 LOG(log_info, logtype_logger, "Closing logs");
395 for(n=(sizeof(log_file_arr)-1);n>0;n--)
397 logs = log_file_arr[n];
398 #ifdef KEEP_LOGFILES_OPEN
399 if ((*logs)[1].log_file!=NULL)
400 fclose((*logs)[1].log_file);
401 #endif /* KEEP_LOGFILES_OPEN */
404 #ifdef DEBUG_OUTPUT_TO_SCREEN
405 printf("Freeing log_data %d, stored at %p\n", n, logs);
406 printf("\t(filename) %s\t(type) %s\n", (*logs)[1].log_filename,
407 ((n<num_logtype_strings)?arr_logtype_strings[n]:""));
408 #endif /* DEBUG_OUTPUT_TO_SCREEN */
411 log_file_arr[n] = NULL;
413 #ifdef DEBUG_OUTPUT_TO_SCREEN
414 printf("Freeing log_data %d, stored at %p\n", n, log_file_arr[n]);
415 printf("\t(filename) %s\t(type) %s\n", (*(log_file_arr[n]))[1].log_filename,
416 ((n<num_logtype_strings)?arr_logtype_strings[n]:""));
417 #endif /* DEBUG_OUTPUT_TO_SCREEN */
418 #endif /* DISABLE_LOGGER */
423 /* This function sets up the processname */
424 void set_processname(char *processname)
426 #ifndef DISABLE_LOGGER
427 /* strncpy(global_log_data.processname, GetCommandName(processname), 15); */
428 strncpy(global_log_data.processname, processname, 15);
429 global_log_data.processname[15] = 0;
430 #else /* DISABLE_LOGGER */
431 disabled_logger_processname = processname;
432 #endif /* DISABLE_LOGGER */
435 #ifndef DISABLE_LOGGER
436 /* This is called by the macro so set the location of the caller of Log */
437 make_log_func set_log_location(char *srcfilename, int srclinenumber)
439 #ifdef DEBUG_OUTPUT_TO_SCREEN
440 printf("Setting Log Location\n");
442 global_log_data.temp_src_filename = srcfilename;
443 global_log_data.temp_src_linenumber = srclinenumber;
445 return make_log_entry;
447 #endif /* DISABLE_LOGGER */
449 /* --------------------------------------------------------------------------
450 MakeLog has 1 main flaws:
451 The message in its entirity, must fit into the tempbuffer.
452 So it must be shorter than MAXLOGSIZE
453 -------------------------------------------------------------------------- */
454 void make_log_entry(enum loglevels loglevel, enum logtypes logtype, char *message, ...)
457 char log_buffer[MAXLOGSIZE];
458 #ifndef DISABLE_LOGGER
459 char log_details_buffer[MAXLOGSIZE];
460 bool message_overran_log_buffer = false;
462 log_file_data_pair *logs;
466 logs = log_file_arr[logtype];
470 logs = log_file_arr[logtype_default];
472 #ifdef DEBUG_OUTPUT_TO_SCREEN
473 printf("Making Log\n");
476 #endif /* DISABLE_LOGGER */
478 /* Initialise the Messages */
479 va_start(args, message);
481 vsnprintf(log_buffer, sizeof(log_buffer), message, args);
483 /* Finished with args for now */
486 #ifdef DISABLE_LOGGER
487 syslog(get_syslog_equivalent(loglevel), "%s", log_buffer);
488 #else /* DISABLE_LOGGER */
491 /* check if sysloglevel is high enough */
492 if ((*logs)[0].log_level>=loglevel)
494 int sysloglevel = get_syslog_equivalent(loglevel);
496 generate_message_details(log_details_buffer, sizeof(log_details_buffer),
497 &(*logs)[0], loglevel, logtype);
499 #ifdef DEBUG_OUTPUT_TO_SCREEN
500 printf("About to log %s %s\n", log_details_buffer, log_buffer);
501 printf("about to do syslog\n");
502 printf("done onw syslog\n");
504 syslog(sysloglevel, "%s: %s", log_details_buffer, log_buffer);
505 /* syslog(sysloglevel, "%s:%s: %s", log_levelString, log_typeString, LogBuffer); */
510 #ifdef DEBUG_OUTPUT_TO_SCREEN
511 printf("about to do the filelog\n");
513 /* check if log_level is high enough */
514 if ((*logs)[1].log_level>=loglevel) {
516 #ifdef DEBUG_OUTPUT_TO_SCREEN
517 printf("Open the Log, FILE* is %p\n", (*logs)[1].log_file);
519 /* if log isn't open, open it */
520 if ((*logs)[1].log_file==NULL) {
521 #ifdef DEBUG_OUTPUT_TO_SCREEN
522 printf("Opening the Log, filename is %s\n", (*logs)[1].log_filename);
524 (*logs)[1].log_file = fopen((*logs)[1].log_filename, "at");
525 if ((*logs)[1].log_file == NULL)
527 (*logs)[1].log_file = stdout;
528 LOG(log_severe, logtype_logger, "can't open Logfile %s", (*logs)[1].log_filename);
532 generate_message_details(log_details_buffer, sizeof(log_details_buffer),
533 &(*logs)[1], loglevel, logtype);
535 #ifdef DEBUG_OUTPUT_TO_SCREEN
536 printf("Files open, lets log\n");
537 printf("FILE* is %p\n", (*logs)[1].log_file);
538 printf("%s: %s\n", log_details_buffer, log_buffer);
541 fprintf((*logs)[1].log_file, "%s: %s\n", log_details_buffer, log_buffer);
543 #ifndef KEEP_LOGFILES_OPEN
544 if ((*logs)[1].log_file != stdout)
546 #ifdef DEBUG_OUTPUT_TO_SCREEN
547 printf("Closing %s\n", (*logs)[1].log_filename);
549 fclose((*logs)[1].log_file);
550 (*logs)[1].log_file = NULL;
551 #ifdef DEBUG_OUTPUT_TO_SCREEN
559 global_log_data.temp_src_filename = NULL;
560 global_log_data.temp_src_linenumber = 0;
561 #endif /* DISABLE_LOGGER */
564 #ifndef DISABLE_LOGGER
565 void load_proccessname_from_proc()
567 pid_t pid = getpid();
568 char buffer[PATH_MAX];
573 sprintf(buffer, "/proc/%d/stat", pid);
574 statfile = fopen(buffer, "rt");
575 fgets(buffer, PATH_MAX-1, statfile);
578 ptr = (char *)strrchr(buffer, ')');
580 memset(procname, 0, sizeof procname);
581 sscanf(buffer, "%d (%15c", &pid, procname); /* comm[16] in kernel */
583 set_processname(procname);
586 /* ==========================================================================
587 Internal function definitions
588 ========================================================================== */
590 static char *get_command_name(char *commandpath)
593 #ifdef DEBUG_OUTPUT_TO_SCREEN
594 printf("getting command name %s\n",commandpath);
596 ptr = (char *)strrchr(commandpath, '/');
602 #ifdef DEBUG_OUTPUT_TO_SCREEN
603 printf("Concluded %s\n", ptr);
608 void workout_what_to_print(struct what_to_print_array *what_to_print, struct tag_log_file_data *log_struct)
610 /* is this a syslog entry? */
611 if (log_struct->log_filename[0]==NULL)
613 what_to_print->print_datetime = false;
614 what_to_print->print_processname = false;
615 what_to_print->print_pid = false;
619 what_to_print->print_datetime = true;
620 what_to_print->print_processname = true;
622 /* pid is dealt with at the syslog level if we're syslogging */
623 what_to_print->print_pid = (((log_struct->display_options & logoption_pid) == 0)?false:true);
626 what_to_print->print_srcfile = (((log_struct->display_options & logoption_nfile) == 0)?true:false);
627 what_to_print->print_srcline = (((log_struct->display_options & logoption_nline) == 0)?true:false);
629 what_to_print->print_errlevel = true;
630 what_to_print->print_errtype = true;
633 void generate_message_details(char *message_details_buffer,
634 int message_details_buffer_length,
635 struct tag_log_file_data *log_struct,
636 enum loglevels loglevel, enum logtypes logtype)
639 char processinfo[64];
641 char *ptr = message_details_buffer;
643 int len = message_details_buffer_length;
645 char log_buffer[MAXLOGSIZE];
646 const char *logtype_string;
648 char loglevel_string[12]; /* max int size is 2 billion, or 10 digits */
649 bool message_overran_log_buffer = false;
651 struct what_to_print_array what_to_print;
653 workout_what_to_print(&what_to_print, log_struct);
655 #ifdef DEBUG_OUTPUT_TO_SCREEN
656 printf("Making MessageDetails\n");
665 if (what_to_print.print_datetime)
670 /* some people might prefer localtime() to gmtime() */
671 strftime(ptr, len, "%b %d %H:%M:%S", gmtime(&thetime));
672 #ifdef DEBUG_OUTPUT_TO_SCREEN
673 printf("date is %s\n", ptr);
676 templen = strlen(ptr);
678 if (what_to_print.print_processname || what_to_print.print_pid)
679 strncat(ptr, " ", len);
681 strncat(ptr, ":", len);
693 if (what_to_print.print_processname)
695 strncpy(ptr, global_log_data.processname, len);
697 templen = strlen(ptr);
702 if (what_to_print.print_pid)
704 pid_t pid = getpid();
706 sprintf(ptr, "[%d]", pid);
708 templen = strlen(ptr);
713 if (what_to_print.print_srcfile || what_to_print.print_srcline)
715 char sprintf_buffer[8];
718 sprintf_buffer[0] = '[';
719 if (what_to_print.print_srcfile)
721 strcpy(&sprintf_buffer[1], "%s");
722 buff_ptr = &sprintf_buffer[3];
724 if (what_to_print.print_srcfile && what_to_print.print_srcline)
726 strcpy(&sprintf_buffer[3], ":");
727 buff_ptr = &sprintf_buffer[4];
729 if (what_to_print.print_srcline)
731 strcpy(buff_ptr, "%d");
732 buff_ptr = &buff_ptr[2];
734 strcpy(buff_ptr, "]");
736 /* ok sprintf string is ready, now is the 1st parameter src or linenumber */
737 if (what_to_print.print_srcfile)
739 sprintf(ptr, sprintf_buffer,
740 global_log_data.temp_src_filename, global_log_data.temp_src_linenumber);
744 sprintf(ptr, sprintf_buffer, global_log_data.temp_src_linenumber);
747 #ifdef DEBUG_OUTPUT_TO_SCREEN
748 printf("Process info is %s\n", ptr);
751 templen = strlen(ptr);
757 if (what_to_print.print_processname || what_to_print.print_pid ||
758 what_to_print.print_srcfile || what_to_print.print_srcline)
760 strncat(ptr, ": ", len);
766 loglevel_string[0] = 0;
767 ptr = loglevel_string;
770 if (what_to_print.print_errlevel)
772 if ((loglevel/10) >= (num_loglevel_chars-1))
774 sprintf(ptr, "%c%d", arr_loglevel_chars[num_loglevel_chars-1],
779 sprintf(ptr, "%c", arr_loglevel_chars[loglevel/10]);
782 templen = strlen(ptr);
787 if (what_to_print.print_errtype)
789 const char *logtype_string;
791 /* get string represnetation of the Log Type */
792 if (logtype<num_logtype_strings)
793 logtype_string = arr_logtype_strings[logtype];
797 if (what_to_print.print_errlevel)
799 strncat(ptr, ":", len);
803 sprintf(ptr, "%s", logtype_string);
806 message_details_buffer[message_details_buffer_length-1] = 0;
808 #ifdef DEBUG_OUTPUT_TO_SCREEN
809 printf("Message Details are %s\n", message_details_buffer);
812 #endif /* DISABLE_LOGGER */
814 int get_syslog_equivalent(enum loglevels loglevel)
818 /* The question is we know how bad it is for us,
819 but how should that translate in the syslogs? */
824 case 2: /* warning */
828 case 4: /* information */