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 ========================================================================== */
43 #include <sys/types.h>
48 #include <atalk/boolean.h>
49 #include <atalk/logger.h>
51 #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
52 #undef KEEP_LOGFILES_OPEN
56 #undef DEBUG_OUTPUT_TO_SCREEN
57 #undef CHECK_STAT_ON_NEW_FILES
58 #undef CHECK_ACCESS_ON_NEW_FILES
60 /* ==========================================================================
61 External function declarations
62 ========================================================================== */
64 /* setup the internal variables used by the logger (called automatically) */
67 /* Setup the log filename and the loglevel, and the type of log it is. */
68 bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype, int display_options);
70 /* Setup the Level and type of log that will be logged to syslog. */
71 void syslog_setup(enum loglevels loglevel, enum logtypes logtype, int display_options, int facility);
73 /* finish up and close the logs */
76 /* This function sets up the processname */
77 void set_processname(char *processname);
80 void make_log(enum loglevels loglevel, enum logtypes logtype, char *message, ...);
81 #ifndef DISABLE_LOGGER
82 make_log_func set_log_location(char *srcfilename, int srclinenumber);
84 /* ==========================================================================
86 ========================================================================== */
88 /* A structure containing object level stuff */
89 struct tag_log_file_data {
90 char log_filename[PATH_MAX]; /* Name of file */
91 FILE *log_file; /* FILE pointer to file */
92 enum loglevels log_level; /* Log Level to put in this file */
96 typedef struct tag_log_file_data log_file_data_pair[2];
98 /* A structure containg class level stuff */
99 struct tag_global_log_data {
102 char *temp_src_filename;
103 int temp_src_linenumber;
104 char processname[16];
106 char *log_file_directory; /* Path of directory containing log files */
107 log_file_data_pair **logs;
110 struct what_to_print_array {
112 bool print_processname;
120 /* ==========================================================================
121 Internal function declarations
122 ========================================================================== */
124 void generate_message_details(char *message_details_buffer,
125 int message_details_buffer_length,
126 struct tag_log_file_data *log_struct,
127 enum loglevels loglevel, enum logtypes logtype);
129 int get_syslog_equivalent(enum loglevels loglevel);
131 static char *get_command_name(char *commandpath);
133 /* ==========================================================================
135 ========================================================================== */
137 /* A populated instance */
139 static log_file_data_pair default_log_file_data_pair = {
141 /* log_filename */ "\0\0\0\0\0\0\0\0",
143 /* log_level */ log_debug,
144 /* display_options */ logoption_pid
147 /* log_filename */ LOGFILEPATH,
149 /* log_level */ log_debug,
150 /* display_options */ logoption_pid
153 static log_file_data_pair *log_file_data_array[logtype_end_of_list_marker] =
154 {&default_log_file_data_pair};
156 /* The class (populated) */
157 static struct tag_global_log_data global_log_data = {
158 /* struct_size */ sizeof(struct tag_global_log_data),
159 /* temp_src_filename */ NULL,
160 /* temp_src_linenumber */ 0,
161 /* processname */ "",
162 /* log_file_directory */ "",
166 /* macro to get access to the array */
167 #define log_file_arr (global_log_data.logs)
169 /* Array to store text to list given a log type */
170 static const char * arr_logtype_strings[] = LOGTYPE_STRING_IDENTIFIERS;
171 static const int num_logtype_strings = COUNT_ARRAY(arr_logtype_strings);
173 /* Array for charachters representing log severity in the log file */
174 static const char arr_loglevel_chars[] = {'S', 'E', 'W', 'N', 'I', 'D'};
175 static const int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars);
177 #else /* #ifndef DISABLE_LOGGER */
178 char *disabled_logger_processname=NULL;
179 #endif /* DISABLE_LOGGER */
180 /* ==========================================================================
181 Global function definitions
182 ========================================================================== */
184 #ifndef DISABLE_LOGGER
186 /* remember I'm keeping a copy of the actual char * Filename, so you mustn't
187 delete it, until you've finished with the log. Also you're responsible
188 for deleting it when you have finished with it. */
191 if (global_log_data.logs==NULL)
193 /* first check default_log_file_data_pair */
195 /* next clear out the log_file_data_array */
196 memset(log_file_data_array, 0, sizeof(log_file_data_array));
197 /* now set default_log_file_data_pairs */
198 log_file_data_array[0] = &default_log_file_data_pair;
200 /* now setup the global_log_data struct */
201 global_log_data.logs = log_file_data_array;
204 #endif /* #ifndef DISABLE_LOGGER */
206 bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype, int display_options)
208 #ifndef DISABLE_LOGGER
218 log_file_data_pair *logs;
222 logs = log_file_arr[logtype];
226 logs = (log_file_data_pair *)malloc(sizeof(log_file_data_pair));
229 LOG(log_severe, logtype_logger, "can't calloc in log_setup");
233 /* memcpy(logs, log_file_arr[logtype_default], sizeof(log_file_data_pair)); */
234 log_file_arr[logtype] = logs;
235 (*logs)[1].log_file = NULL;
239 if ( ((*logs)[1].log_file == stdout) && ((*logs)[1].log_file != NULL) )
241 fclose((*logs)[1].log_file);
242 (*logs)[1].log_file = NULL;
245 if (strlen(global_log_data.log_file_directory)>0)
247 lastchar[0] = global_log_data.log_file_directory[strlen(global_log_data.log_file_directory)-1];
249 if (lastchar[0] == '/' || lastchar[0] == '\\' || lastchar[0] == ':')
252 /* this should probably be a platform specific path separator */
260 #ifdef DEBUG_OUTPUT_TO_SCREEN
261 printf("filename is %s stored at location %p\n", (*logs)[1].log_filename,
262 (*logs)[1].log_filename);
263 #endif /* DEBUG_OUTPUT_TO_SCREEN */
264 if (filename == NULL)
266 strncpy((*logs)[1].log_filename, (*(log_file_arr[0]))[1].log_filename, PATH_MAX);
270 sprintf((*logs)[1].log_filename, "%s%s%s", global_log_data.log_file_directory,
273 (*logs)[1].log_level = loglevel;
274 (*logs)[1].display_options = display_options;
276 #ifdef DEBUG_OUTPUT_TO_SCREEN
277 printf("filename is %s stored at location %p\n", (*logs)[1].log_filename,
278 (*logs)[1].log_filename);
279 #endif /* DEBUG_OUTPUT_TO_SCREEN */
281 #ifdef CHECK_STAT_ON_NEW_FILES
285 #ifdef DEBUG_OUTPUT_TO_SCREEN
286 printf("about to stat file %s\n", (*logs)[1].log_filename);
288 firstattempt = stat((*logs)[1].log_filename, &statbuf);
290 if (firstattempt == -1)
292 #ifdef DEBUG_OUTPUT_TO_SCREEN
293 printf("about to call Log with %d, %d, %s, %s\n", log_note, logtype_logger,
294 "can't stat Logfile", (*logs)[1].log_filename);
297 /* syslog(LOG_INFO, "stat failed"); */
298 LOG(log_warning, logtype_logger, "stat fails on file %s",
299 (*logs)[1].log_filename);
301 if (strlen(global_log_data.log_file_directory)>0)
303 retval = stat(global_log_data.log_file_directory, &statbuf);
306 #ifdef DEBUG_OUTPUT_TO_SCREEN
307 printf("can't stat dir either so I'm giving up\n");
309 LOG(log_severe, logtype_logger, "can't stat directory %s either",
310 global_log_data.log_file_directory);
316 #ifdef CHECK_ACCESS_ON_NEW_FILES
317 access = ((statbuf.st_uid == uid)?(statbuf.st_mode & S_IWUSR):0) +
318 ((statbuf.st_gid == gid)?(statbuf.st_mode & S_IWGRP):0) +
319 (statbuf.st_mode & S_IWOTH);
323 #ifdef DEBUG_OUTPUT_TO_SCREEN
324 printf("failing with %d, %d, %s, %s\n", log_note, logtype_logger,
325 "can't access Logfile %s", (*logs)[1].log_filename);
328 LOG(log_note, logtype_logger, "can't access file %s",
329 (*logs)[1].log_filename);
332 #endif /* CHECK_ACCESS_ON_NEW_FILES */
333 #endif /* CHECK_STAT_ON_NEW_FILES */
334 #ifdef KEEP_LOGFILES_OPEN
335 if ((*logs)[1].log_file!=NULL)
336 fclose((*logs)[1].log_file);
338 (*logs)[1].log_file = fopen((*logs)[1].log_filename, "at");
339 if ((*logs)[1].log_file == NULL)
341 LOG(log_severe, logtype_logger, "can't open Logfile %s", (*logs)[1].log_filename);
346 LOG(log_info, logtype_logger, "Log setup complete");
348 #endif /* DISABLE_LOGGER */
353 void syslog_setup(enum loglevels loglevel, enum logtypes logtype, int display_options, int facility)
355 #ifndef DISABLE_LOGGER
356 log_file_data_pair *logs;
360 logs = log_file_arr[logtype];
364 logs = (log_file_data_pair *)malloc(sizeof(log_file_data_pair));
367 LOG(log_severe, logtype_logger, "can't calloc in log_setup");
371 memcpy(logs, log_file_arr[logtype_default], sizeof(log_file_data_pair));
372 log_file_arr[logtype] = logs;
376 (*logs)[0].log_file = NULL;
377 (*logs)[0].log_filename[0] = 0;
378 (*logs)[0].log_level = loglevel;
379 (*logs)[0].display_options = display_options;
381 openlog(global_log_data.processname, (*logs)[0].display_options, facility);
383 LOG(log_info, logtype_logger, "SysLog setup complete");
384 #else /* DISABLE_LOGGER */
385 /* behave like a normal openlog call */
386 openlog(disabled_logger_processname, display_options, facility);
387 #endif /* DISABLE_LOGGER */
392 #ifndef DISABLE_LOGGER
393 log_file_data_pair *logs;
396 LOG(log_info, logtype_logger, "Closing logs");
398 for(n=(sizeof(log_file_arr)-1);n>0;n--)
400 logs = log_file_arr[n];
401 #ifdef KEEP_LOGFILES_OPEN
402 if ((*logs)[1].log_file!=NULL)
403 fclose((*logs)[1].log_file);
404 #endif /* KEEP_LOGFILES_OPEN */
407 #ifdef DEBUG_OUTPUT_TO_SCREEN
408 printf("Freeing log_data %d, stored at %p\n", n, logs);
409 printf("\t(filename) %s\t(type) %s\n", (*logs)[1].log_filename,
410 ((n<num_logtype_strings)?arr_logtype_strings[n]:""));
411 #endif /* DEBUG_OUTPUT_TO_SCREEN */
414 log_file_arr[n] = NULL;
416 #ifdef DEBUG_OUTPUT_TO_SCREEN
417 printf("Freeing log_data %d, stored at %p\n", n, log_file_arr[n]);
418 printf("\t(filename) %s\t(type) %s\n", (*(log_file_arr[n]))[1].log_filename,
419 ((n<num_logtype_strings)?arr_logtype_strings[n]:""));
420 #endif /* DEBUG_OUTPUT_TO_SCREEN */
421 #endif /* DISABLE_LOGGER */
426 /* This function sets up the processname */
427 void set_processname(char *processname)
429 #ifndef DISABLE_LOGGER
430 /* strncpy(global_log_data.processname, GetCommandName(processname), 15); */
431 strncpy(global_log_data.processname, processname, 15);
432 global_log_data.processname[15] = 0;
433 #else /* DISABLE_LOGGER */
434 disabled_logger_processname = processname;
435 #endif /* DISABLE_LOGGER */
438 #ifndef DISABLE_LOGGER
439 /* This is called by the macro so set the location of the caller of Log */
440 make_log_func set_log_location(char *srcfilename, int srclinenumber)
442 #ifdef DEBUG_OUTPUT_TO_SCREEN
443 printf("Setting Log Location\n");
445 global_log_data.temp_src_filename = srcfilename;
446 global_log_data.temp_src_linenumber = srclinenumber;
448 return make_log_entry;
450 #endif /* DISABLE_LOGGER */
452 /* --------------------------------------------------------------------------
453 MakeLog has 1 main flaws:
454 The message in its entirity, must fit into the tempbuffer.
455 So it must be shorter than MAXLOGSIZE
456 -------------------------------------------------------------------------- */
457 void make_log_entry(enum loglevels loglevel, enum logtypes logtype, char *message, ...)
460 char log_buffer[MAXLOGSIZE];
461 #ifndef DISABLE_LOGGER
462 char log_details_buffer[MAXLOGSIZE];
464 log_file_data_pair *logs;
468 logs = log_file_arr[logtype];
472 logs = log_file_arr[logtype_default];
474 #ifdef DEBUG_OUTPUT_TO_SCREEN
475 printf("Making Log\n");
478 #endif /* DISABLE_LOGGER */
480 /* Initialise the Messages */
481 va_start(args, message);
483 vsnprintf(log_buffer, sizeof(log_buffer), message, args);
485 /* Finished with args for now */
488 #ifdef DISABLE_LOGGER
489 syslog(get_syslog_equivalent(loglevel), "%s", log_buffer);
490 #else /* DISABLE_LOGGER */
493 /* check if sysloglevel is high enough */
494 if ((*logs)[0].log_level>=loglevel)
496 int sysloglevel = get_syslog_equivalent(loglevel);
498 generate_message_details(log_details_buffer, sizeof(log_details_buffer),
499 &(*logs)[0], loglevel, logtype);
501 #ifdef DEBUG_OUTPUT_TO_SCREEN
502 printf("About to log %s %s\n", log_details_buffer, log_buffer);
503 printf("about to do syslog\n");
504 printf("done onw syslog\n");
506 syslog(sysloglevel, "%s: %s", log_details_buffer, log_buffer);
507 /* syslog(sysloglevel, "%s:%s: %s", log_levelString, log_typeString, LogBuffer); */
512 #ifdef DEBUG_OUTPUT_TO_SCREEN
513 printf("about to do the filelog\n");
515 /* check if log_level is high enough */
516 if ((*logs)[1].log_level>=loglevel) {
518 #ifdef DEBUG_OUTPUT_TO_SCREEN
519 printf("Open the Log, FILE* is %p\n", (*logs)[1].log_file);
521 /* if log isn't open, open it */
522 if ((*logs)[1].log_file==NULL) {
523 #ifdef DEBUG_OUTPUT_TO_SCREEN
524 printf("Opening the Log, filename is %s\n", (*logs)[1].log_filename);
526 (*logs)[1].log_file = fopen((*logs)[1].log_filename, "at");
527 if ((*logs)[1].log_file == NULL)
529 (*logs)[1].log_file = stdout;
530 LOG(log_severe, logtype_logger, "can't open Logfile %s", (*logs)[1].log_filename);
534 generate_message_details(log_details_buffer, sizeof(log_details_buffer),
535 &(*logs)[1], loglevel, logtype);
537 #ifdef DEBUG_OUTPUT_TO_SCREEN
538 printf("Files open, lets log\n");
539 printf("FILE* is %p\n", (*logs)[1].log_file);
540 printf("%s: %s\n", log_details_buffer, log_buffer);
543 fprintf((*logs)[1].log_file, "%s: %s\n", log_details_buffer, log_buffer);
545 #ifndef KEEP_LOGFILES_OPEN
546 if ((*logs)[1].log_file != stdout)
548 #ifdef DEBUG_OUTPUT_TO_SCREEN
549 printf("Closing %s\n", (*logs)[1].log_filename);
551 fclose((*logs)[1].log_file);
552 (*logs)[1].log_file = NULL;
553 #ifdef DEBUG_OUTPUT_TO_SCREEN
561 global_log_data.temp_src_filename = NULL;
562 global_log_data.temp_src_linenumber = 0;
563 #endif /* DISABLE_LOGGER */
566 #ifndef DISABLE_LOGGER
567 void load_proccessname_from_proc()
569 pid_t pid = getpid();
570 char buffer[PATH_MAX];
575 sprintf(buffer, "/proc/%d/stat", pid);
576 statfile = fopen(buffer, "rt");
577 fgets(buffer, PATH_MAX-1, statfile);
580 ptr = (char *)strrchr(buffer, ')');
582 memset(procname, 0, sizeof procname);
583 sscanf(buffer, "%d (%15c", &pid, procname); /* comm[16] in kernel */
585 set_processname(procname);
588 /* ==========================================================================
589 Internal function definitions
590 ========================================================================== */
592 static char *get_command_name(char *commandpath)
595 #ifdef DEBUG_OUTPUT_TO_SCREEN
596 printf("getting command name %s\n",commandpath);
598 ptr = (char *)strrchr(commandpath, '/');
604 #ifdef DEBUG_OUTPUT_TO_SCREEN
605 printf("Concluded %s\n", ptr);
610 void workout_what_to_print(struct what_to_print_array *what_to_print, struct tag_log_file_data *log_struct)
612 /* is this a syslog entry? */
613 if (log_struct->log_filename[0]==0)
615 what_to_print->print_datetime = false;
616 what_to_print->print_processname = false;
617 what_to_print->print_pid = false;
621 what_to_print->print_datetime = true;
622 what_to_print->print_processname = true;
624 /* pid is dealt with at the syslog level if we're syslogging */
625 what_to_print->print_pid = (((log_struct->display_options & logoption_pid) == 0)?false:true);
628 what_to_print->print_srcfile = (((log_struct->display_options & logoption_nfile) == 0)?true:false);
629 what_to_print->print_srcline = (((log_struct->display_options & logoption_nline) == 0)?true:false);
631 what_to_print->print_errlevel = true;
632 what_to_print->print_errtype = true;
635 void generate_message_details(char *message_details_buffer,
636 int message_details_buffer_length,
637 struct tag_log_file_data *log_struct,
638 enum loglevels loglevel, enum logtypes logtype)
641 char processinfo[64];
643 char *ptr = message_details_buffer;
645 int len = message_details_buffer_length;
647 char log_buffer[MAXLOGSIZE];
648 const char *logtype_string;
650 char loglevel_string[12]; /* max int size is 2 billion, or 10 digits */
652 struct what_to_print_array what_to_print;
654 workout_what_to_print(&what_to_print, log_struct);
656 #ifdef DEBUG_OUTPUT_TO_SCREEN
657 printf("Making MessageDetails\n");
666 if (what_to_print.print_datetime)
671 /* some people might prefer localtime() to gmtime() */
672 strftime(ptr, len, "%b %d %H:%M:%S", gmtime(&thetime));
673 #ifdef DEBUG_OUTPUT_TO_SCREEN
674 printf("date is %s\n", ptr);
677 templen = strlen(ptr);
679 if (what_to_print.print_processname || what_to_print.print_pid)
680 strncat(ptr, " ", len);
682 strncat(ptr, ":", len);
694 if (what_to_print.print_processname)
696 strncpy(ptr, global_log_data.processname, len);
698 templen = strlen(ptr);
703 if (what_to_print.print_pid)
705 pid_t pid = getpid();
707 sprintf(ptr, "[%d]", pid);
709 templen = strlen(ptr);
714 if (what_to_print.print_srcfile || what_to_print.print_srcline)
716 char sprintf_buffer[8];
719 sprintf_buffer[0] = '[';
720 if (what_to_print.print_srcfile)
722 strcpy(&sprintf_buffer[1], "%s");
723 buff_ptr = &sprintf_buffer[3];
725 if (what_to_print.print_srcfile && what_to_print.print_srcline)
727 strcpy(&sprintf_buffer[3], ":");
728 buff_ptr = &sprintf_buffer[4];
730 if (what_to_print.print_srcline)
732 strcpy(buff_ptr, "%d");
733 buff_ptr = &buff_ptr[2];
735 strcpy(buff_ptr, "]");
737 /* ok sprintf string is ready, now is the 1st parameter src or linenumber */
738 if (what_to_print.print_srcfile)
740 sprintf(ptr, sprintf_buffer,
741 global_log_data.temp_src_filename, global_log_data.temp_src_linenumber);
745 sprintf(ptr, sprintf_buffer, global_log_data.temp_src_linenumber);
748 #ifdef DEBUG_OUTPUT_TO_SCREEN
749 printf("Process info is %s\n", ptr);
752 templen = strlen(ptr);
758 if (what_to_print.print_processname || what_to_print.print_pid ||
759 what_to_print.print_srcfile || what_to_print.print_srcline)
761 strncat(ptr, ": ", len);
767 loglevel_string[0] = 0;
768 ptr = loglevel_string;
771 if (what_to_print.print_errlevel)
773 if ((loglevel/10) >= (num_loglevel_chars-1))
775 sprintf(ptr, "%c%d", arr_loglevel_chars[num_loglevel_chars-1],
780 sprintf(ptr, "%c", arr_loglevel_chars[loglevel/10]);
783 templen = strlen(ptr);
788 if (what_to_print.print_errtype)
790 const char *logtype_string;
792 /* get string represnetation of the Log Type */
793 if (logtype<num_logtype_strings)
794 logtype_string = arr_logtype_strings[logtype];
798 if (what_to_print.print_errlevel)
800 strncat(ptr, ":", len);
804 sprintf(ptr, "%s", logtype_string);
807 message_details_buffer[message_details_buffer_length-1] = 0;
809 #ifdef DEBUG_OUTPUT_TO_SCREEN
810 printf("Message Details are %s\n", message_details_buffer);
813 #endif /* DISABLE_LOGGER */
815 int get_syslog_equivalent(enum loglevels loglevel)
819 /* The question is we know how bad it is for us,
820 but how should that translate in the syslogs? */
825 case 2: /* warning */
829 case 4: /* information */