X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=libatalk%2Futil%2Flogger.c;h=c8a2a0a8acd2845c43bfbd088e81b7a496855a1b;hp=146fba4c3dc6925902ecf2cc111ba4966d46e4af;hb=4111aba41c36a99bfd7eb7e987b24314735cdd10;hpb=887089f41ffd26cedecbd0c18d01de872f350b48 diff --git a/libatalk/util/logger.c b/libatalk/util/logger.c index 146fba4c..c8a2a0a8 100644 --- a/libatalk/util/logger.c +++ b/libatalk/util/logger.c @@ -1,38 +1,17 @@ + #ifdef HAVE_CONFIG_H #include "config.h" #endif /* ========================================================================= - logger.c is part of the utils section in the libatalk library, - which is part of the netatalk project. - - logger.c was written by Simon Bazley (sibaz@sibaz.com) - - I believe libatalk is released under the L/GPL licence. - - Just incase, it is, thats the licence I'm applying to this file. - - Netatalk 2001 (c) - - ========================================================================== - - Logger.c is intended as an alternative to syslog for logging +logger.c was written by Simon Bazley (sibaz@sibaz.com) - --------------------------------------------------------------- +I believe libatalk is released under the L/GPL licence. +Just incase, it is, thats the licence I'm applying to this file. +Netatalk 2001 (c) - The initial plan is to create a structure for general information needed - to log to a file. - - Initally I'll hard code the neccesary stuff to start a log, this should - probably be moved elsewhere when some code is written to read the log - file locations from the config files. - - As a more longterm idea, I'll code this so that the data struct can be - duplicated to allow multiple concurrent log files, although this is - probably a recipe for wasted resources. - - ========================================================================= */ +========================================================================= */ #include #include @@ -42,958 +21,625 @@ #include #include #include +#include +#include #include +#include #include +#include +#include +#include -#include +#include #include +#include #define COUNT_ARRAY(array) (sizeof((array))/sizeof((array)[0])) -#define NUMOF COUNT_ARRAY -#undef KEEP_LOGFILES_OPEN -#define DO_SYSLOG -#define DO_FILELOG -#undef DEBUG_OUTPUT_TO_SCREEN -#undef CHECK_STAT_ON_NEW_FILES -#undef CHECK_ACCESS_ON_NEW_FILES +#define MAXLOGSIZE 512 + +#define LOGLEVEL_STRING_IDENTIFIERS { \ + "NOTHING", \ + "SEVERE", \ + "ERROR", \ + "WARN", \ + "NOTE", \ + "INFO", \ + "DEBUG", \ + "DEBUG6", \ + "DEBUG7", \ + "DEBUG8", \ + "DEBUG9", \ + "MAXDEBUG"} + +/* these are the string identifiers corresponding to each logtype */ +#define LOGTYPE_STRING_IDENTIFIERS { \ + "Default", \ + "Logger", \ + "CNID", \ + "AFPDaemon", \ + "DSI", \ + "ATalkDaemon", \ + "PAPDaemon", \ + "UAMS", \ + "end_of_list_marker"} \ /* ========================================================================= - External function declarations - ========================================================================= */ - -/* setup the internal variables used by the logger (called automatically) */ -void log_init(); - -/* Setup the log filename and the loglevel, and the type of log it is. */ -bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype, - int display_options); - -/* Setup the Level and type of log that will be logged to syslog. */ -void syslog_setup(enum loglevels loglevel, enum logtypes logtype, - int display_options, int facility); - -/* finish up and close the logs */ -void log_close(); - -/* This function sets up the processname */ -void set_processname(char *processname); - -/* Log a Message */ -void make_log(enum loglevels loglevel, enum logtypes logtype, - char *message, ...); -#ifndef DISABLE_LOGGER -make_log_func set_log_location(char *srcfilename, int srclinenumber); - -/* ========================================================================= - Structure definitions + Config ========================================================================= */ -/* A structure containing object level stuff */ -struct tag_log_file_data { - char log_filename[PATH_MAX]; /* Name of file */ - FILE *log_file; /* FILE pointer to file */ - enum loglevels log_level; /* Log Level to put in this file */ - int display_options; +/* Main log config container */ +log_config_t log_config = { 0 }; + +/* Default log config: log nothing to files. + 0: set ? + 0: syslog ? + -1: logfiles fd + log_none: no logging by default + 0: Display options */ +#define DEFAULT_LOG_CONFIG {0, 0, -1, log_none, 0} + +UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = { + DEFAULT_LOG_CONFIG, /* logtype_default */ + DEFAULT_LOG_CONFIG, /* logtype_logger */ + DEFAULT_LOG_CONFIG, /* logtype_cnid */ + DEFAULT_LOG_CONFIG, /* logtype_afpd */ + DEFAULT_LOG_CONFIG, /* logtype_dsi */ + DEFAULT_LOG_CONFIG, /* logtype_atalkd */ + DEFAULT_LOG_CONFIG, /* logtype_papd */ + DEFAULT_LOG_CONFIG /* logtype_uams */ }; -typedef struct tag_log_file_data log_file_data_pair[2]; +static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility); -/* A structure containg class level stuff */ -struct tag_global_log_data { - int struct_size; +/* We use this in order to track the last n log messages in order to prevent flooding */ +#define LOG_FLOODING_MINCOUNT 5 /* this controls after how many consecutive messages must be detected + before we start to hide them */ +#define LOG_FLOODING_MAXCOUNT 1000 /* this controls after how many consecutive messages we force a + "repeated x times" message */ +#define LOG_FLOODING_ARRAY_SIZE 3 /* this contols how many messages in flow we track */ - char *temp_src_filename; - int temp_src_linenumber; - char processname[16]; - - int facility; - char *log_file_directory; /* Path of directory containing log files */ - log_file_data_pair **logs; +struct log_flood_entry { + int count; + unsigned int hash; }; -struct what_to_print_array { - bool print_datetime; - bool print_processname; - bool print_pid; - bool print_srcfile; - bool print_srcline; - bool print_errlevel; - bool print_errtype; -}; - -/* ========================================================================= - Internal function declarations - ========================================================================= */ - -void generate_message_details(char *message_details_buffer, - int message_details_buffer_length, - struct tag_log_file_data *log_struct, - enum loglevels loglevel, enum logtypes logtype); - -int get_syslog_equivalent(enum loglevels loglevel); +static struct log_flood_entry log_flood_array[LOG_FLOODING_ARRAY_SIZE]; +static int log_flood_entries; -static char *get_command_name(char *commandpath); - -/* ========================================================================= - Instanciated data - ========================================================================= */ - -/* A populated instance */ - -static log_file_data_pair default_log_file_data_pair = { -{ - log_filename: "\0\0\0\0\0\0\0\0", - log_file: NULL, - log_level: log_debug, - display_options: logoption_pid -}, -{ - log_filename: LOGFILEPATH, - log_file: NULL, - log_level: log_debug, - display_options: logoption_pid -}}; - -static log_file_data_pair logger_log_file_data_pair = { -{ - log_filename: "\0\0\0\0\0\0\0\0", - log_file: NULL, - log_level: log_warning, - display_options: logoption_pid -}, -{ - log_filename: LOGFILEPATH, - log_file: NULL, - log_level: log_maxdebug, - display_options: logoption_pid -}}; - -static log_file_data_pair *log_file_data_array[logtype_end_of_list_marker] = -{&default_log_file_data_pair}; - -/* The class (populated) */ -static struct tag_global_log_data global_log_data = { - struct_size: sizeof(struct tag_global_log_data), - temp_src_filename: NULL, - temp_src_linenumber: 0, - processname: "", - facility: logfacility_daemon, - log_file_directory: "", - logs: NULL, -}; - -/* macro to get access to the array */ -#define log_file_arr (global_log_data.logs) +/* These are used by the LOG macro to store __FILE__ and __LINE__ */ +static const char *log_src_filename; +static int log_src_linenumber; /* Array to store text to list given a log type */ -static const char * arr_logtype_strings[] = LOGTYPE_STRING_IDENTIFIERS; -static const int num_logtype_strings = COUNT_ARRAY(arr_logtype_strings); +static const char *arr_logtype_strings[] = LOGTYPE_STRING_IDENTIFIERS; +static const unsigned int num_logtype_strings = COUNT_ARRAY(arr_logtype_strings); /* Array for charachters representing log severity in the log file */ -static const char arr_loglevel_chars[] = {'S', 'E', 'W', 'N', 'I', 'D'}; -static const int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars); +static const char arr_loglevel_chars[] = {'-','S', 'E', 'W', 'N', 'I', 'D'}; +static const unsigned int num_loglevel_chars = COUNT_ARRAY(arr_loglevel_chars); -static const char * arr_loglevel_strings[] = LOGLEVEL_STRING_IDENTIFIERS; -static const int num_loglevel_strings = COUNT_ARRAY(arr_loglevel_strings); +static const char *arr_loglevel_strings[] = LOGLEVEL_STRING_IDENTIFIERS; +static const unsigned int num_loglevel_strings = COUNT_ARRAY(arr_loglevel_strings); -#else /* #ifndef DISABLE_LOGGER */ - char *disabled_logger_processname=NULL; -#endif /* DISABLE_LOGGER */ /* ========================================================================= - Global function definitions + Internal function definitions ========================================================================= */ -#ifndef DISABLE_LOGGER - -/* remember I'm keeping a copy of the actual char * Filename, so you mustn't - delete it, until you've finished with the log. Also you're responsible - for deleting it when you have finished with it. */ -void log_init() +/* Hash a log message */ +static unsigned int hash_message(const char *message) { - if (global_log_data.logs==NULL) - { - /* first check default_log_file_data_pair */ + const char *p = message; + unsigned int hash = 0, i = 7; - /* next clear out the log_file_data_array */ - memset(log_file_data_array, 0, sizeof(log_file_data_array)); - /* now set default_log_file_data_pairs */ - log_file_data_array[logtype_default] = &default_log_file_data_pair; - log_file_data_array[logtype_logger] = &logger_log_file_data_pair; - - /* now setup the global_log_data struct */ - global_log_data.logs = log_file_data_array; - - /* make_log_entry(log_debug, logtype_logger, "log_init ran for the first time"); */ - } + while (*p) { + hash += *p * i; + i++; + p++; + } + return hash; } -#endif /* #ifndef DISABLE_LOGGER */ -bool log_setup(char *filename, enum loglevels loglevel, enum logtypes logtype, - int display_options) +static void generate_message_details(char *message_details_buffer, + int message_details_buffer_length, + int display_options, + enum loglevels loglevel, enum logtypes logtype) { -#ifndef DISABLE_LOGGER - - struct stat statbuf; - int firstattempt; - int retval; - gid_t gid; - uid_t uid; - int access; - char lastchar[2]; - - log_file_data_pair *logs; - - log_init(); - - logs = log_file_arr[logtype]; - - LOG(log_info, logtype_logger, "doing log_setup, type %d, level %d, filename \"%s\"", logtype, loglevel, filename); - - /* LOG(log_extradebug+10, logtype_logger, "checking array for logtype is malloc'd"); */ - /* has the given logtype already been assigned memory? */ - if (logs==NULL) - { - logs = (log_file_data_pair *)malloc(sizeof(log_file_data_pair)); - if (logs==NULL) - { - LOG(log_severe, logtype_logger, "can't calloc in log_setup"); - } - else - { - /* - memcpy(logs, log_file_arr[logtype_default], sizeof(log_file_data_pair)); - */ - log_file_arr[logtype] = logs; - (*logs)[1].log_file = NULL; - } - } - - /* I think this checks if we're logging to stdout or not. Probably unused */ - if ( ((*logs)[1].log_file == stdout) && ((*logs)[1].log_file != NULL) ) - { - fclose((*logs)[1].log_file); - (*logs)[1].log_file = NULL; - } - - /* check if we need to append the given filename to a directory */ - if (strlen(global_log_data.log_file_directory)>0) - { - lastchar[0] = global_log_data. - log_file_directory[strlen(global_log_data.log_file_directory)-1]; - - if (lastchar[0] == '/' || lastchar[0] == '\\' || lastchar[0] == ':') - lastchar[0] = 0; - else - /* this should probably be a platform specific path separator */ - lastchar[0] = '/'; - - lastchar[1] = 0; - } - else - lastchar[0] = 0; - -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("filename is %s stored at location %p\n", (*logs)[1].log_filename, - (*logs)[1].log_filename); -#endif /* DEBUG_OUTPUT_TO_SCREEN */ - if (filename == NULL) - { - strncpy((*logs)[1].log_filename, - (*(log_file_arr[0]))[1].log_filename, PATH_MAX); - } - else - { - sprintf((*logs)[1].log_filename, "%s%s%s", - global_log_data.log_file_directory, - lastchar, filename); - } - (*logs)[1].log_level = loglevel; - (*logs)[1].display_options = display_options; - -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("filename is %s stored at location %p\n", (*logs)[1].log_filename, - (*logs)[1].log_filename); -#endif /* DEBUG_OUTPUT_TO_SCREEN */ - -#ifdef CHECK_STAT_ON_NEW_FILES - uid = geteuid(); - gid = getegid(); - -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("about to stat file %s\n", (*logs)[1].log_filename); -#endif - firstattempt = stat((*logs)[1].log_filename, &statbuf); - - if (firstattempt == -1) - { -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("about to call Log with %d, %d, %s, %s\n", - log_note, logtype_logger, - "can't stat Logfile", - (*logs)[1].log_filename - ); -#endif - - /* syslog(LOG_INFO, "stat failed"); */ - LOG(log_warning, logtype_logger, "stat fails on file %s", - (*logs)[1].log_filename); + char *ptr = message_details_buffer; + int templen; + int len = message_details_buffer_length; + struct timeval tv; + pid_t pid; - if (strlen(global_log_data.log_file_directory)>0) - { - retval = stat(global_log_data.log_file_directory, &statbuf); - if (retval == -1) - { -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("can't stat dir either so I'm giving up\n"); -#endif - LOG(log_severe, logtype_logger, "can't stat directory %s either", - global_log_data.log_file_directory); - return false; - } - } - } - -#ifdef CHECK_ACCESS_ON_NEW_FILES - access = ((statbuf.st_uid == uid)?(statbuf.st_mode & S_IWUSR):0) + - ((statbuf.st_gid == gid)?(statbuf.st_mode & S_IWGRP):0) + - (statbuf.st_mode & S_IWOTH); - - if (access==0) - { -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("failing with %d, %d, %s, %s\n", log_note, logtype_logger, - "can't access Logfile %s", (*logs)[1].log_filename); -#endif - - LOG(log_note, logtype_logger, "can't access file %s", - (*logs)[1].log_filename); - return false; - } -#endif /* CHECK_ACCESS_ON_NEW_FILES */ -#endif /* CHECK_STAT_ON_NEW_FILES */ -#ifdef KEEP_LOGFILES_OPEN - if ((*logs)[1].log_file!=NULL) - fclose((*logs)[1].log_file); - - (*logs)[1].log_file = fopen((*logs)[1].log_filename, "at"); - if ((*logs)[1].log_file == NULL) - { - LOG(log_severe, logtype_logger, "can't open Logfile %s", - (*logs)[1].log_filename - ); - return false; - } -#endif - - LOG(log_debug7, logtype_logger, "log_file_arr[%d] now contains: " - "{log_filename:%s, log_file:%p, log_level: %d}", logtype, - (*logs)[1].log_filename, (*logs)[1].log_file, (*logs)[1].log_level); - LOG(log_debug, logtype_logger, "log_setup[%d] done", logtype); + *ptr = 0; -#endif /* DISABLE_LOGGER */ - return true; -} + /* Print time */ + gettimeofday(&tv, NULL); + strftime(ptr, len, "%b %d %H:%M:%S.", localtime(&tv.tv_sec)); + templen = strlen(ptr); + len -= templen; + ptr += templen; + templen = snprintf(ptr, len, "%06u ", (int)tv.tv_usec); + if (templen == -1 || templen >= len) + return; + + len -= templen; + ptr += templen; -void syslog_setup(enum loglevels loglevel, enum logtypes logtype, - int display_options, int facility) -{ -#ifndef DISABLE_LOGGER - log_file_data_pair *logs; + /* Process name && PID */ + pid = getpid(); + templen = snprintf(ptr, len, "%s[%d]", log_config.processname, pid); + if (templen == -1 || templen >= len) + return; + len -= templen; + ptr += templen; - log_init(); + /* Source info ? */ + if ( ! (display_options & logoption_nsrcinfo)) { + char *basename = strrchr(log_src_filename, '/'); + if (basename) + templen = snprintf(ptr, len, " {%s:%d}", basename + 1, log_src_linenumber); + else + templen = snprintf(ptr, len, " {%s:%d}", log_src_filename, log_src_linenumber); + if (templen == -1 || templen >= len) + return; + len -= templen; + ptr += templen; + } - logs = log_file_arr[logtype]; + /* Errorlevel */ + if (loglevel >= (num_loglevel_chars - 1)) + templen = snprintf(ptr, len, " (D%d:", loglevel - 1); + else + templen = snprintf(ptr, len, " (%c:", arr_loglevel_chars[loglevel]); - LOG(log_info, logtype_logger, "doing syslog_setup, type %d, level %d", logtype, loglevel); + if (templen == -1 || templen >= len) + return; + len -= templen; + ptr += templen; - if (logs==NULL) - { - logs = (log_file_data_pair *)malloc(sizeof(log_file_data_pair)); - if (logs==NULL) - { - LOG(log_severe, logtype_logger, "can't calloc in log_setup"); - } - else - { - memcpy(logs, log_file_arr[logtype_default], sizeof(log_file_data_pair)); - log_file_arr[logtype] = logs; + /* Errortype */ + if (logtype= len) + return; + len -= templen; + ptr += templen; } - } - - (*logs)[0].log_file = NULL; - (*logs)[0].log_filename[0] = 0; - (*logs)[0].log_level = loglevel; - (*logs)[0].display_options = display_options; - global_log_data.facility = facility; - - openlog(global_log_data.processname, (*logs)[0].display_options, - global_log_data.facility); - - LOG(log_debug7, logtype_logger, "log_file_arr[%d] now contains: " - "{log_filename:%s, log_file:%p, log_level: %d}", logtype, - (*logs)[0].log_filename, (*logs)[0].log_file, (*logs)[0].log_level); - LOG(log_debug, logtype_logger, "syslog_setup[%d] done", logtype); -#else /* DISABLE_LOGGER */ -/* behave like a normal openlog call */ - openlog(disabled_logger_processname, display_options, facility); -#endif /* DISABLE_LOGGER */ + + strncat(ptr, "): ", len); + ptr[len -1] = 0; } -void log_close() +static int get_syslog_equivalent(enum loglevels loglevel) { -#ifndef DISABLE_LOGGER - log_file_data_pair *logs; - int n; - - LOG(log_info, logtype_logger, "log_close called"); - - for(n=(sizeof(log_file_arr)-1);n>0;n--) - { - logs = log_file_arr[n]; -#ifdef KEEP_LOGFILES_OPEN - if ((*logs)[1].log_file!=NULL) - fclose((*logs)[1].log_file); -#endif /* KEEP_LOGFILES_OPEN */ - if (logs!=NULL) + switch (loglevel) { - LOG(log_debug, logtype_logger, "freeing log entry at %d", n); -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Freeing log_data %d, stored at %p\n", n, logs); - printf("\t(filename) %s\t(type) %s\n", (*logs)[1].log_filename, - ((n=loglevel) - { - int sysloglevel = get_syslog_equivalent(loglevel); + /* Is it /dev/tty ? */ + if (strcmp(filename, "/dev/tty") == 0) { + type_configs[logtype].fd = 1; /* stdout */ - generate_message_details(log_details_buffer, sizeof(log_details_buffer), - &(*logs)[0], loglevel, logtype); + /* Does it end in "XXXXXX" ? debug reguest via SIGINT */ + } else if (strcmp(filename + strlen(filename) - 6, "XXXXXX") == 0) { + char *tmp = strdup(filename); + type_configs[logtype].fd = mkstemp(tmp); + free(tmp); -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("About to log %s %s\n", log_details_buffer, log_buffer); - printf("about to do syslog\n"); - printf("done onw syslog\n"); -#endif - syslog(sysloglevel, "%s: %s", log_details_buffer, log_buffer); - /* - syslog(sysloglevel, "%s:%s: %s", log_levelString, - log_typeString, LogBuffer); - */ - } -#endif + } else { + become_root(); + type_configs[logtype].fd = open(filename, + O_CREAT | O_WRONLY | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + become_root(); + } -#ifdef DO_FILELOG -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("about to do the filelog\n"); -#endif - /* check if log_level is high enough */ - if ((*logs)[1].log_level>=loglevel) { - -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Open the Log, FILE* is %p\n", (*logs)[1].log_file); -#endif - /* if log isn't open, open it */ - if ((*logs)[1].log_file==NULL) { -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Opening the Log, filename is %s\n", (*logs)[1].log_filename); -#endif - (*logs)[1].log_file = fopen((*logs)[1].log_filename, "at"); - if ((*logs)[1].log_file == NULL) - { - (*logs)[1].log_file = stdout; - LOG(log_severe, logtype_logger, "can't open Logfile %s", - (*logs)[1].log_filename - ); + /* Check for error opening/creating logfile */ + if (type_configs[logtype].fd == -1) { + type_configs[logtype].level = -1; + type_configs[logtype].set = false; return; - } } - generate_message_details(log_details_buffer, sizeof(log_details_buffer), - &(*logs)[1], loglevel, logtype); -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Files open, lets log\n"); - printf("FILE* is %p\n", (*logs)[1].log_file); - printf("%s: %s\n", log_details_buffer, log_buffer); -#endif - - fprintf((*logs)[1].log_file, "%s: %s\n", log_details_buffer, log_buffer); - -#ifndef KEEP_LOGFILES_OPEN - if ((*logs)[1].log_file != stdout) - { -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Closing %s\n", (*logs)[1].log_filename); -#endif - fclose((*logs)[1].log_file); - (*logs)[1].log_file = NULL; -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Closed\n"); -#endif + fcntl(type_configs[logtype].fd, F_SETFD, FD_CLOEXEC); + type_configs[logtype].set = true; + log_config.inited = true; + + /* Here's how we make it possible to LOG to a logtype like "logtype_afpd" */ + /* which then uses the default logtype setup if it isn't setup itself: */ + /* we just copy the loglevel from default to all logtypes that are not setup. */ + /* In "make_log_entry" we then check for the logtypes if they arent setup */ + /* and use default then. We must provide accessible values for all logtypes */ + /* in order to make it easy and fast to check the loglevels in the LOG macro! */ + + if (logtype == logtype_default) { + int typeiter = 0; + while (typeiter != logtype_end_of_list_marker) { + if ( ! (type_configs[typeiter].set)) + type_configs[typeiter].level = loglevel; + typeiter++; + } } -#endif - } -#endif - global_log_data.temp_src_filename = NULL; - global_log_data.temp_src_linenumber = 0; -#endif /* DISABLE_LOGGER */ + LOG(log_debug, logtype_logger, "Setup file logging: type: %s, level: %s, file: %s", + arr_logtype_strings[logtype], arr_loglevel_strings[loglevel], filename); } -#ifndef DISABLE_LOGGER -void load_proccessname_from_proc() +/* Setup syslog logging */ +static void syslog_setup(int loglevel, enum logtypes logtype, int display_options, int facility) { - pid_t pid = getpid(); - char buffer[PATH_MAX]; - char procname[16]; - FILE * statfile; - char *ptr; - - sprintf(buffer, "/proc/%d/stat", pid); - statfile = fopen(buffer, "rt"); - fgets(buffer, PATH_MAX-1, statfile); - fclose(statfile); - - ptr = (char *)strrchr(buffer, ')'); - *ptr = '\0'; - memset(procname, 0, sizeof procname); - sscanf(buffer, "%d (%15c", &pid, procname); /* comm[16] in kernel */ - - set_processname(procname); -} + /* + * FIXME: + * this currently doesn't care if logtype is already logging to a file. + * Fortunately currently there's no way a user could trigger this as afpd.conf + * is not re-read on SIGHUP. + */ -/* ========================================================================= - Internal function definitions - ========================================================================= */ + type_configs[logtype].level = loglevel; + type_configs[logtype].set = true; + type_configs[logtype].syslog = true; + log_config.syslog_display_options = display_options; + log_config.syslog_facility = facility; + + /* Setting default logging? Then set all logtype not set individually */ + if (logtype == logtype_default) { + int typeiter = 0; + while (typeiter != logtype_end_of_list_marker) { + if ( ! (type_configs[typeiter].set)) { + type_configs[typeiter].level = loglevel; + type_configs[typeiter].syslog = true; + } + typeiter++; + } + } -static char *get_command_name(char *commandpath) -{ - char *ptr; -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("getting command name %s\n",commandpath); -#endif - ptr = (char *)strrchr(commandpath, '/'); - if (ptr==NULL) - ptr = commandpath; - else - ptr++; - -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Concluded %s\n", ptr); -#endif - return ptr; -} + log_config.inited = 1; -void workout_what_to_print(struct what_to_print_array *what_to_print, - struct tag_log_file_data *log_struct) -{ - /* is this a syslog entry? */ - if (log_struct->log_filename[0]==0) - { - what_to_print->print_datetime = false; - what_to_print->print_processname = false; - what_to_print->print_pid = false; - } - else - { - what_to_print->print_datetime = true; - what_to_print->print_processname = true; - - /* pid is dealt with at the syslog level if we're syslogging */ - what_to_print->print_pid = - (((log_struct->display_options & logoption_pid) == 0)?false:true); - } - - what_to_print->print_srcfile = - (((log_struct->display_options & logoption_nfile) == 0)?true:false); - what_to_print->print_srcline = - (((log_struct->display_options & logoption_nline) == 0)?true:false); - - what_to_print->print_errlevel = true; - what_to_print->print_errtype = true; + LOG(log_info, logtype_logger, "Set syslog logging to level: %s", + arr_loglevel_strings[loglevel]); } -void generate_message_details(char *message_details_buffer, - int message_details_buffer_length, - struct tag_log_file_data *log_struct, - enum loglevels loglevel, enum logtypes logtype) +/* + * If filename == NULL its for syslog logging, otherwise its for file-logging. + * "unsetuplog" calls with loglevel == NULL. + * loglevel == NULL means: + * if logtype == default + * disable logging + * else + * set to default logging + */ +static void setuplog_internal(const char *loglevel, const char *logtype, const char *filename) { - char datebuffer[32]; - char processinfo[64]; - - char *ptr = message_details_buffer; - int templen; - int len = message_details_buffer_length; - - char log_buffer[MAXLOGSIZE]; - const char *logtype_string; - - char loglevel_string[12]; /* max int size is 2 billion, or 10 digits */ - - struct what_to_print_array what_to_print; - - workout_what_to_print(&what_to_print, log_struct); - -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Making MessageDetails\n"); -#endif + unsigned int typenum, levelnum; - *ptr = 0; - /* - datebuffer[0] = 0; - ptr = datebuffer; - */ - - if (what_to_print.print_datetime) - { - time_t thetime; - time(&thetime); - - /* some people might prefer localtime() to gmtime() */ - strftime(ptr, len, "%b %d %H:%M:%S", localtime(&thetime)); -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("date is %s\n", ptr); -#endif - - templen = strlen(ptr); - len -= templen; - if (what_to_print.print_processname || what_to_print.print_pid) - strncat(ptr, " ", len); - else - strncat(ptr, ":", len); - - templen++; - len --; - ptr += templen; - } - - /* - processinfo[0] = 0; - ptr = processinfo; - */ - - if (what_to_print.print_processname) - { - strncpy(ptr, global_log_data.processname, len); - - templen = strlen(ptr); - len -= templen; - ptr += templen; - } - - if (what_to_print.print_pid) - { - pid_t pid = getpid(); - - sprintf(ptr, "[%d]", pid); - - templen = strlen(ptr); - len -= templen; - ptr += templen; - } - - if (what_to_print.print_srcfile || what_to_print.print_srcline) - { - char sprintf_buffer[8]; - char *buff_ptr; - - sprintf_buffer[0] = '['; - if (what_to_print.print_srcfile) - { - strcpy(&sprintf_buffer[1], "%s"); - buff_ptr = &sprintf_buffer[3]; - } - if (what_to_print.print_srcfile && what_to_print.print_srcline) - { - strcpy(&sprintf_buffer[3], ":"); - buff_ptr = &sprintf_buffer[4]; + /* Parse logtype */ + for( typenum=0; typenum < num_logtype_strings; typenum++) { + if (strcasecmp(logtype, arr_logtype_strings[typenum]) == 0) + break; } - if (what_to_print.print_srcline) - { - strcpy(buff_ptr, "%d"); - buff_ptr = &buff_ptr[2]; + if (typenum >= num_logtype_strings) { + return; } - strcpy(buff_ptr, "]"); - /* - ok sprintf string is ready, now is the 1st parameter src or linenumber - */ - if (what_to_print.print_srcfile) - { - sprintf(ptr, sprintf_buffer, - global_log_data.temp_src_filename, - global_log_data.temp_src_linenumber); + /* Parse loglevel */ + if (loglevel == NULL) { + levelnum = 0; + } else { + for(levelnum=1; levelnum < num_loglevel_strings; levelnum++) { + if (strcasecmp(loglevel, arr_loglevel_strings[levelnum]) == 0) + break; + } + if (levelnum >= num_loglevel_strings) { + return; + } } - else - { - sprintf(ptr, sprintf_buffer, global_log_data.temp_src_linenumber); + + /* is this a syslog setup or a filelog setup ? */ + if (filename == NULL) { + /* must be syslog */ + syslog_setup(levelnum, + typenum, + logoption_ndelay | logoption_pid, + logfacility_daemon); + } else { + /* this must be a filelog */ + log_setup(filename, levelnum, typenum); } -#ifdef DEBUG_OUTPUT_TO_SCREEN - printf("Process info is %s\n", ptr); -#endif + return; +} - templen = strlen(ptr); - len -= templen; - ptr += templen; +/* ========================================================================= + Global function definitions + ========================================================================= */ - } +/* This function sets up the processname */ +void set_processname(const char *processname) +{ + strncpy(log_config.processname, processname, 15); + log_config.processname[15] = 0; +} - if (what_to_print.print_processname || what_to_print.print_pid || - what_to_print.print_srcfile || what_to_print.print_srcline) - { - strncat(ptr, ": ", len); - len -= 2; - ptr += 2; - } +/* ------------------------------------------------------------------------- + make_log_entry has 1 main flaws: + The message in its entirity, must fit into the tempbuffer. + So it must be shorter than MAXLOGSIZE + ------------------------------------------------------------------------- */ +void make_log_entry(enum loglevels loglevel, enum logtypes logtype, + const char *file, int line, char *message, ...) +{ + /* fn is not reentrant but is used in signal handler + * with LOGGER it's a little late source name and line number + * are already changed. */ + static int inlog = 0; + int fd, len; + char temp_buffer[MAXLOGSIZE]; + char log_details_buffer[MAXLOGSIZE]; + va_list args; + struct iovec iov[2]; + + if (inlog) + return; -/* - loglevel_string[0] = 0; - ptr = loglevel_string; -*/ + inlog = 1; - if (what_to_print.print_errlevel) - { - if ((loglevel/10) >= (num_loglevel_chars-1)) - { - sprintf(ptr, "%c%d", arr_loglevel_chars[num_loglevel_chars-1], - loglevel/10); + if (!log_config.inited) { + log_init(); } - else - { - sprintf(ptr, "%c", arr_loglevel_chars[loglevel/10]); + + if (type_configs[logtype].syslog) { + if (type_configs[logtype].level >= loglevel) { + /* Initialise the Messages and send it to syslog */ + va_start(args, message); + vsnprintf(temp_buffer, MAXLOGSIZE -1, message, args); + va_end(args); + temp_buffer[MAXLOGSIZE -1] = 0; + make_syslog_entry(loglevel, logtype, temp_buffer); + } + inlog = 0; + return; } - templen = strlen(ptr); - len -= templen; - ptr += templen; - } + /* logging to a file */ - if (what_to_print.print_errtype) - { - const char *logtype_string; + log_src_filename = file; + log_src_linenumber = line; - /* get string represnetation of the Log Type */ - if (logtype []*/ - /* - This should be rewritten so that somehow logsource is assumed and everything - can be taken from default if needs be. - */ - /* const char* sources[] = {"syslog", "filelog"}; */ - const char *null = ""; - int sourcenum, typenum, levelnum; - log_file_data_pair *logs = log_file_arr[logtype_default]; - - /* - LOG(log_extradebug, logtype_logger, "Attempting setuplog: %s %s %s %s", - logsource, logtype, loglevel, filename); - */ - LOG(log_info, logtype_logger, "setuplog is parsing logtype:%s, loglevel:%s, filename:%s", - logtype, loglevel, filename); - - if (logtype==NULL) - { - LOG(log_note, logtype_logger, "no logsource given, default is assumed"); - typenum=0; - } - else - { - for(typenum=0;typenum= MAXLOGSIZE -1) { + /* vsnprintf hit the buffer size*/ + temp_buffer[MAXLOGSIZE-2] = '\n'; + temp_buffer[MAXLOGSIZE-1] = 0; } - if (typenum>=num_logtype_strings) - { - LOG(log_warning, logtype_logger, "%s is not a valid log type", logtype); + else { + temp_buffer[len] = '\n'; + temp_buffer[len+1] = 0; } - } - - if (loglevel==NULL) - { - LOG(log_note, logtype_logger, "no loglevel given, severe is assumed"); - levelnum=0; - } - else - { - for(levelnum=0;levelnum= log_debug) + goto log; /* bypass flooding checks */ + + /* Prevent flooding: hash the message and check if we got the same one recently */ + int hash = hash_message(temp_buffer) + log_src_linenumber; + + /* Search for the same message by hash */ + for (int i = log_flood_entries - 1; i >= 0; i--) { + if (log_flood_array[i].hash == hash) { + + /* found same message */ + log_flood_array[i].count++; + + /* Check if that message has reached LOG_FLOODING_MAXCOUNT */ + if (log_flood_array[i].count >= LOG_FLOODING_MAXCOUNT) { + /* yes, log it and remove from array */ + + /* reusing log_details_buffer */ + sprintf(log_details_buffer, "message repeated %i times\n", + LOG_FLOODING_MAXCOUNT - 1); + write(fd, log_details_buffer, strlen(log_details_buffer)); + + if ((i + 1) == LOG_FLOODING_ARRAY_SIZE) { + /* last array element, just decrement count */ + log_flood_entries--; + goto exit; + } + /* move array elements down */ + for (int j = i + 1; j != LOG_FLOODING_ARRAY_SIZE ; j++) + log_flood_array[j-1] = log_flood_array[j]; + log_flood_entries--; + } + + if (log_flood_array[i].count < LOG_FLOODING_MINCOUNT) + /* log it */ + goto log; + /* discard it */ + goto exit; + } /* if */ + } /* for */ + + /* No matching message found, add this message to array*/ + if (log_flood_entries == LOG_FLOODING_ARRAY_SIZE) { + /* array is full, discard oldest entry printing "message repeated..." if count > 1 */ + if (log_flood_array[0].count >= LOG_FLOODING_MINCOUNT) { + /* reusing log_details_buffer */ + sprintf(log_details_buffer, "message repeated %i times\n", + log_flood_array[0].count - LOG_FLOODING_MINCOUNT + 1); + write(fd, log_details_buffer, strlen(log_details_buffer)); + } + for (int i = 1; i < LOG_FLOODING_ARRAY_SIZE; i++) { + log_flood_array[i-1] = log_flood_array[i]; + } + log_flood_entries--; } - if (levelnum>=num_loglevel_strings) - { - LOG(log_warning, logtype_logger, "%s is not a valid log level", loglevel); + log_flood_array[log_flood_entries].count = 1; + log_flood_array[log_flood_entries].hash = hash; + log_flood_entries++; + +log: + if ( ! log_config.console) { + generate_message_details(log_details_buffer, sizeof(log_details_buffer), + type_configs[logtype].set ? + type_configs[logtype].display_options : + type_configs[logtype_default].display_options, + loglevel, logtype); + + /* If default wasnt setup its fd is -1 */ + iov[0].iov_base = log_details_buffer; + iov[0].iov_len = strlen(log_details_buffer); + iov[1].iov_base = temp_buffer; + iov[1].iov_len = strlen(temp_buffer); + writev( fd, iov, 2); + } else { + write(fd, temp_buffer, strlen(temp_buffer)); } - } - /* sanity check */ - if ((typenum>=num_logtype_strings) || (levelnum>=num_loglevel_strings)) - { - LOG(log_warning, logtype_logger, "sanity check failed: (%s:%d), (%s:%d)", - logtype, typenum, loglevel, levelnum); - return; - } - - /* now match the order of the text string with the actual enum value (10 times) */ - levelnum*=10; - - /* is this a syslog setup or a filelog setup */ - if (filename==NULL) /* must be syslog */ - { - LOG(log_debug6, logtype_logger, "calling syslog_setup(%d, %d, ...)", levelnum, typenum); - syslog_setup(levelnum, typenum, - (*logs)[0].display_options, - global_log_data.facility); - } - else /* this must be a filelog */ - { - LOG(log_debug6, logtype_logger, "calling log_setup(%s, %d, %d, ...)", filename, levelnum, typenum); - log_setup(filename, levelnum, typenum, - (*logs)[0].display_options); - }; - return; -#endif /* DISABLE_LOGGER */ +exit: + inlog = 0; } +void setuplog(const char *logstr, const char *logfile) +{ + char *ptr, *save; + char *logtype, *loglevel; + char c; + + save = ptr = strdup(logstr); + + ptr = strtok(ptr, ", "); + + while (ptr) { + while (*ptr) { + while (*ptr && isspace(*ptr)) + ptr++; + + logtype = ptr; + ptr = strpbrk(ptr, ":"); + if (!ptr) + break; + *ptr = 0; + + ptr++; + loglevel = ptr; + while (*ptr && !isspace(*ptr)) + ptr++; + c = *ptr; + *ptr = 0; + setuplog_internal(loglevel, logtype, logfile); + *ptr = c; + } + ptr = strtok(NULL, ", "); + } - - - - + free(save); +}