/** * xrdp: A Remote Desktop Protocol server. * * Copyright (C) Jay Sorg 2004-2014 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include "list.h" #include "file.h" #include "os_calls.h" #include "thread_calls.h" #include "string_calls.h" /* Add a define here so that the log.h will hold more information * when compiled from this C file. * When compiled normally the log.h file only contain the public parts * of the operators in this file. */ #define LOGINTERNALSTUFF #include "log.h" /* Here we store the current state and configuration of the log */ static struct log_config *g_staticLogConfig = NULL; /* This file first start with all private functions. In the end of the file the public functions is defined */ /** * * @brief Opens log file * @param fname log file name * @return see open(2) return values * */ int internal_log_file_open(const char *fname) { int ret = -1; if (fname != NULL) { if (g_strcmp(fname, "") != 0) { ret = open(fname, O_WRONLY | O_CREAT | O_APPEND | O_SYNC, S_IRUSR | S_IWUSR); } else { ret = dup(1); } } if (ret != -1) { g_file_set_cloexec(ret, 1); } return ret; } /** * * @brief Converts xrdp log level to syslog logging level * @param xrdp logging level * @return syslog equivalent logging level * */ int internal_log_xrdp2syslog(const enum logLevels lvl) { switch (lvl) { case LOG_LEVEL_ALWAYS: return LOG_CRIT; case LOG_LEVEL_ERROR: return LOG_ERR; case LOG_LEVEL_WARNING: return LOG_WARNING; case LOG_LEVEL_INFO: return LOG_INFO; case LOG_LEVEL_DEBUG: case LOG_LEVEL_TRACE: return LOG_DEBUG; default: g_writeln("Undefined log level - programming error"); return LOG_DEBUG; } } /** * @brief Converts xrdp log levels to textual logging levels * @param lvl logging level * @param str pointer to a string, must be allocated before * @return The log string in str pointer. * */ void internal_log_lvl2str(const enum logLevels lvl, char *str) { switch (lvl) { case LOG_LEVEL_ALWAYS: snprintf(str, 9, "%s", "[CORE ] "); break; case LOG_LEVEL_ERROR: snprintf(str, 9, "%s", "[ERROR] "); break; case LOG_LEVEL_WARNING: snprintf(str, 9, "%s", "[WARN ] "); break; case LOG_LEVEL_INFO: snprintf(str, 9, "%s", "[INFO ] "); break; case LOG_LEVEL_DEBUG: snprintf(str, 9, "%s", "[DEBUG] "); break; case LOG_LEVEL_TRACE: snprintf(str, 9, "%s", "[TRACE] "); break; default: snprintf(str, 9, "%s", "PRG ERR!"); g_writeln("Programming error - undefined log level!!!"); } } /******************************************************************************/ enum logReturns internal_log_start(struct log_config *l_cfg) { enum logReturns ret = LOG_GENERAL_ERROR; if (0 == l_cfg) { ret = LOG_ERROR_MALLOC; return ret; } /* if progname is NULL, we return error */ if (0 == l_cfg->program_name) { g_writeln("program_name not properly assigned"); return ret; } if (l_cfg->dump_on_start) { internal_log_config_dump(l_cfg); } /* open file */ if (l_cfg->log_file != NULL) { l_cfg->fd = internal_log_file_open(l_cfg->log_file); if (-1 == l_cfg->fd) { return LOG_ERROR_FILE_OPEN; } } /* if syslog is enabled, open it */ if (l_cfg->enable_syslog) { openlog(l_cfg->program_name, LOG_CONS | LOG_PID, LOG_DAEMON); } #ifdef LOG_ENABLE_THREAD pthread_mutexattr_init(&(l_cfg->log_lock_attr)); pthread_mutex_init(&(l_cfg->log_lock), &(l_cfg->log_lock_attr)); #endif return LOG_STARTUP_OK; } /******************************************************************************/ enum logReturns internal_log_end(struct log_config *l_cfg) { enum logReturns ret = LOG_GENERAL_ERROR; /* if log is closed, quit silently */ if (0 == l_cfg) { return ret; } if (-1 != l_cfg->fd) { /* closing logfile... */ g_file_close(l_cfg->fd); } /* if syslog is enabled, close it */ if (l_cfg->enable_syslog) { closelog(); } /* freeing allocated memory */ if (0 != l_cfg->log_file) { g_free(l_cfg->log_file); l_cfg->log_file = 0; } ret = LOG_STARTUP_OK; return ret; } /** * Converts a string representing the log level to a value * @param buf * @return */ enum logLevels internal_log_text2level(const char *buf) { if (0 == g_strcasecmp(buf, "0") || 0 == g_strcasecmp(buf, "core")) { return LOG_LEVEL_ALWAYS; } else if (0 == g_strcasecmp(buf, "1") || 0 == g_strcasecmp(buf, "error")) { return LOG_LEVEL_ERROR; } else if (0 == g_strcasecmp(buf, "2") || 0 == g_strcasecmp(buf, "warn") || 0 == g_strcasecmp(buf, "warning")) { return LOG_LEVEL_WARNING; } else if (0 == g_strcasecmp(buf, "3") || 0 == g_strcasecmp(buf, "info")) { return LOG_LEVEL_INFO; } else if (0 == g_strcasecmp(buf, "4") || 0 == g_strcasecmp(buf, "debug")) { return LOG_LEVEL_DEBUG; } else if (0 == g_strcasecmp(buf, "5") || 0 == g_strcasecmp(buf, "trace")) { return LOG_LEVEL_TRACE; } g_writeln("Your configured log level is corrupt - we use debug log level"); return LOG_LEVEL_DEBUG; } /******************************************************************************/ struct log_config * internal_config_read_logging(int file, const char *applicationName, const char *section_prefix) { int i; char *buf; char *temp_buf; char section_name[512]; struct log_config *lc; struct list *param_n; struct list *param_v; lc = internalInitAndAllocStruct(); if (lc == NULL) { return NULL; } param_n = list_create(); param_n->auto_free = 1; param_v = list_create(); param_v->auto_free = 1; list_clear(param_v); list_clear(param_n); /* setting defaults */ lc->program_name = applicationName; lc->log_file = 0; lc->fd = -1; lc->log_level = LOG_LEVEL_INFO; lc->enable_console = 0; lc->console_level = LOG_LEVEL_INFO; lc->enable_syslog = 0; lc->syslog_level = LOG_LEVEL_INFO; lc->dump_on_start = 0; lc->enable_pid = 0; g_snprintf(section_name, 511, "%s%s", section_prefix, SESMAN_CFG_LOGGING); file_read_section(file, section_name, param_n, param_v); for (i = 0; i < param_n->count; i++) { buf = (char *)list_get_item(param_n, i); if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_FILE)) { lc->log_file = g_strdup((char *)list_get_item(param_v, i)); if (lc->log_file != NULL) { if (lc->log_file[0] != '/' && g_strcmp(lc->log_file, "") != 0) { temp_buf = (char *)g_malloc(512, 0); g_snprintf(temp_buf, 511, "%s/%s", XRDP_LOG_PATH, lc->log_file); g_free(lc->log_file); lc->log_file = temp_buf; } } } if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_LEVEL)) { lc->log_level = internal_log_text2level((char *)list_get_item(param_v, i)); } if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_ENABLE_SYSLOG)) { lc->enable_syslog = g_text2bool((char *)list_get_item(param_v, i)); } if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_SYSLOG_LEVEL)) { lc->syslog_level = internal_log_text2level((char *)list_get_item(param_v, i)); } if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_ENABLE_CONSOLE)) { lc->enable_console = g_text2bool((char *)list_get_item(param_v, i)); } if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_CONSOLE_LEVEL)) { lc->console_level = internal_log_text2level((char *)list_get_item(param_v, i)); } if (0 == g_strcasecmp(buf, SESMAN_CFG_LOG_ENABLE_PID)) { lc->enable_pid = g_text2bool((char *)list_get_item(param_v, i)); } } if (0 == lc->log_file) { lc->log_file = g_strdup("./sesman.log"); } /* try to create path if not exist */ g_create_path(lc->log_file); #ifdef LOG_PER_LOGGER_LEVEL int len; struct log_logger_level *logger; list_clear(param_v); list_clear(param_n); g_snprintf(section_name, 511, "%s%s", section_prefix, SESMAN_CFG_LOGGING_LOGGER); file_read_section(file, section_name, param_n, param_v); for (i = 0; i < param_n->count; i++) { logger = (struct log_logger_level *)g_malloc(sizeof(struct log_logger_level), 1); list_add_item(lc->per_logger_level, (tbus) logger); logger->log_level = internal_log_text2level((char *)list_get_item(param_v, i)); g_strncpy(logger->logger_name, (char *)list_get_item(param_n, i), LOGGER_NAME_SIZE); logger->logger_name[LOGGER_NAME_SIZE] = '\0'; len = g_strlen(logger->logger_name); if (len >= 2 && logger->logger_name[len - 2] == '(' && logger->logger_name[len - 1] == ')' ) { logger->logger_type = LOG_TYPE_FUNCTION; logger->logger_name[len - 2] = '\0'; } else { logger->logger_type = LOG_TYPE_FILE; } } #endif list_delete(param_v); list_delete(param_n); return lc; } void internal_log_config_dump(struct log_config *config) { char str_level[20]; #ifdef LOG_PER_LOGGER_LEVEL struct log_logger_level *logger; int i; #endif g_printf("logging configuration:\r\n"); if (config->log_file) { internal_log_lvl2str(config->log_level, str_level); g_printf("\tLogFile: %s\r\n", config->log_file); g_printf("\tLogLevel: %s\r\n", str_level); } else { g_printf("\tLogFile: %s\r\n", ""); } if (config->enable_console) { internal_log_lvl2str(config->console_level, str_level); } else { g_strcpy(str_level, ""); } g_printf("\tConsoleLevel: %s\r\n", str_level); if (config->enable_syslog) { internal_log_lvl2str(config->syslog_level, str_level); } else { g_strcpy(str_level, ""); } g_printf("\tSyslogLevel: %s\r\n", str_level); #ifdef LOG_PER_LOGGER_LEVEL g_printf("per logger configuration:\r\n"); for (i = 0; i < config->per_logger_level->count; i++) { logger = (struct log_logger_level *)list_get_item(config->per_logger_level, i); internal_log_lvl2str(logger->log_level, str_level); g_printf("\t%-*s: %s\r\n", LOGGER_NAME_SIZE, logger->logger_name, str_level); } if (config->per_logger_level->count == 0) { g_printf("\tNone\r\n"); } #endif } struct log_config * internalInitAndAllocStruct(void) { struct log_config *ret = g_new0(struct log_config, 1); if (ret != NULL) { ret->fd = -1; ret->enable_syslog = 0; #ifdef LOG_PER_LOGGER_LEVEL ret->per_logger_level = list_create(); if (ret->per_logger_level != NULL) { ret->per_logger_level->auto_free = 1; } else { g_writeln("could not allocate memory for log struct"); g_free(ret); ret = NULL; } #endif } else { g_writeln("could not allocate memory for log struct"); } return ret; } /** * Copies logging levels only from one log_config structure to another **/ static void internal_log_config_copy_levels(struct log_config *dest, const struct log_config *src) { dest->log_level = src->log_level; dest->enable_syslog = src->enable_syslog; dest->syslog_level = src->syslog_level; dest->enable_console = src->enable_console; dest->console_level = src->console_level; #ifdef LOG_PER_LOGGER_LEVEL if (dest->per_logger_level == NULL) { dest->per_logger_level = list_create(); if (dest->per_logger_level == NULL) { return; } dest->per_logger_level->auto_free = 1; } else { list_clear(dest->per_logger_level); } if (src->per_logger_level == NULL) { return; } int i; for (i = 0; i < src->per_logger_level->count; ++i) { struct log_logger_level *dst_logger = (struct log_logger_level *)g_malloc(sizeof(struct log_logger_level), 1); *dst_logger = *(struct log_logger_level *) list_get_item(src->per_logger_level, i), list_add_item(dest->per_logger_level, (tbus) dst_logger); } #endif } void internal_log_config_copy(struct log_config *dest, const struct log_config *src) { if (src != NULL && dest != NULL) { dest->fd = src->fd; g_free(dest->log_file); dest->log_file = g_strdup(src->log_file); #ifdef LOG_ENABLE_THREAD dest->log_lock = src->log_lock; dest->log_lock_attr = src->log_lock_attr; #endif dest->program_name = src->program_name; dest->enable_pid = src->enable_pid; dest->dump_on_start = src->dump_on_start; internal_log_config_copy_levels(dest, src); } } bool_t internal_log_is_enabled_for_level(const enum logLevels log_level, const bool_t override_destination_level, const enum logLevels override_log_level) { /* Is log initialized? */ if (g_staticLogConfig == NULL) { return 0; } else if (g_staticLogConfig->fd < 0 && !g_staticLogConfig->enable_syslog && !g_staticLogConfig->enable_console) { /* all logging outputs are disabled */ return 0; } else if (override_destination_level) { /* Override is enabled - should the message should be logged? */ return log_level <= override_log_level; } /* Override is disabled - Is there at least one log destination * which will accept the message based on the log level? */ else if (g_staticLogConfig->fd >= 0 && log_level <= g_staticLogConfig->log_level) { return 1; } else if (g_staticLogConfig->enable_syslog && log_level <= g_staticLogConfig->syslog_level) { return 1; } else if (g_staticLogConfig->enable_console && log_level <= g_staticLogConfig->console_level) { return 1; } else { return 0; } } bool_t internal_log_location_overrides_level(const char *function_name, const char *file_name, enum logLevels *log_level_return) { #ifdef LOG_PER_LOGGER_LEVEL struct log_logger_level *logger = NULL; int i; if (g_staticLogConfig == NULL) { return 0; } for (i = 0; i < g_staticLogConfig->per_logger_level->count; i++) { logger = (struct log_logger_level *)list_get_item(g_staticLogConfig->per_logger_level, i); if ((logger->logger_type == LOG_TYPE_FILE && 0 == g_strncmp(logger->logger_name, file_name, LOGGER_NAME_SIZE)) || (logger->logger_type == LOG_TYPE_FUNCTION && 0 == g_strncmp(logger->logger_name, function_name, LOGGER_NAME_SIZE))) { *log_level_return = logger->log_level; return 1; } } #endif return 0; } /* * Here below the public functions */ struct log_config * log_config_init_for_console(enum logLevels lvl, const char *override_name) { struct log_config *config = internalInitAndAllocStruct(); if (config != NULL) { config->program_name = ""; config->enable_console = 1; if (override_name != NULL && override_name[0] != '\0') { config->console_level = internal_log_text2level(override_name); } else { config->console_level = lvl; } } return config; } struct log_config * log_config_init_from_config(const char *iniFilename, const char *applicationName, const char *section_prefix) { int fd; struct log_config *config; if (applicationName == NULL) { g_writeln("Programming error your application name cannot be null"); return NULL; } if (iniFilename == NULL) { g_writeln("The inifile is null to log_config_init_from_config!"); return NULL; } fd = g_file_open_ro(iniFilename); if (-1 == fd) { g_writeln("We could not open the configuration file to read log parameters"); return NULL; } /* read logging config */ config = internal_config_read_logging(fd, applicationName, section_prefix); /* cleanup */ g_file_close(fd); return config; } enum logReturns log_config_free(struct log_config *config) { if (config != NULL) { #ifdef LOG_PER_LOGGER_LEVEL if (config->per_logger_level != NULL) { list_delete(config->per_logger_level); config->per_logger_level = NULL; } #endif if (0 != config->log_file) { g_free(config->log_file); config->log_file = 0; } g_free(config); } return LOG_STARTUP_OK; } /** * Restarts the logging. * * The logging file is never changed, as it is common in xrdp to share a * log file between parents and children. The end result would be * confusing for the user. */ static enum logReturns log_restart_from_param(const struct log_config *lc) { enum logReturns rv = LOG_GENERAL_ERROR; if (g_staticLogConfig == NULL) { log_message(LOG_LEVEL_ALWAYS, "Log not already initialized"); } else if (lc == NULL) { g_writeln("lc to log_start_from_param is NULL"); } else { if (g_staticLogConfig->fd >= 0 && g_strcmp(g_staticLogConfig->log_file, lc->log_file) != 0) { log_message(LOG_LEVEL_WARNING, "Unable to change log file name from %s to %s", g_staticLogConfig->log_file, lc->log_file); } /* Reconfigure syslog logging, allowing for a program_name change */ if (g_staticLogConfig->enable_syslog) { closelog(); } if (lc->enable_syslog) { openlog(lc->program_name, LOG_CONS | LOG_PID, LOG_DAEMON); } /* Copy over simple values... */ #ifdef LOG_ENABLE_THREAD g_staticLogConfig->log_lock = lc->log_lock; g_staticLogConfig->log_lock_attr = lc->log_lock_attr; #endif g_staticLogConfig->program_name = lc->program_name; g_staticLogConfig->enable_pid = lc->enable_pid; g_staticLogConfig->dump_on_start = lc->dump_on_start; /* ... and the log levels */ internal_log_config_copy_levels(g_staticLogConfig, lc); rv = LOG_STARTUP_OK; } return rv; } enum logReturns log_start_from_param(const struct log_config *src_log_config) { enum logReturns ret = LOG_GENERAL_ERROR; if (g_staticLogConfig != NULL) { log_message(LOG_LEVEL_ALWAYS, "Log already initialized"); return ret; } if (src_log_config == NULL) { g_writeln("src_log_config to log_start_from_param is NULL"); return ret; } else { g_staticLogConfig = internalInitAndAllocStruct(); if (g_staticLogConfig == NULL) { g_writeln("internalInitAndAllocStruct failed"); return LOG_ERROR_MALLOC; } internal_log_config_copy(g_staticLogConfig, src_log_config); ret = internal_log_start(g_staticLogConfig); if (ret != LOG_STARTUP_OK) { g_writeln("Could not start log"); log_config_free(g_staticLogConfig); g_staticLogConfig = NULL; } } return ret; } /** * This function initialize the log facilities according to the configuration * file, that is described by the in parameter. * @param iniFile * @param applicationName, the name that is used in the log for the running application * @return 0 on success */ enum logReturns log_start(const char *iniFile, const char *applicationName, unsigned int flags) { enum logReturns ret = LOG_GENERAL_ERROR; struct log_config *config; config = log_config_init_from_config(iniFile, applicationName, ""); if (config != NULL) { config->dump_on_start = (flags & LOG_START_DUMP_CONFIG) ? 1 : 0; if (flags & LOG_START_RESTART) { ret = log_restart_from_param(config); if (ret != LOG_STARTUP_OK) { g_writeln("Could not restart log"); } } else { ret = log_start_from_param(config); if (ret != LOG_STARTUP_OK) { g_writeln("Could not start log"); } } log_config_free(config); } else { g_writeln("Error reading configuration for log based on config: %s", iniFile); } return ret; } /** * Function that terminates all logging * @return */ enum logReturns log_end(void) { enum logReturns ret = LOG_GENERAL_ERROR; ret = internal_log_end(g_staticLogConfig); log_config_free(g_staticLogConfig); g_staticLogConfig = NULL; return ret; } /*****************************************************************************/ /* log a hex dump */ enum logReturns log_hexdump(const enum logLevels log_level, const char *message, const char *src, int len) { return log_hexdump_with_location("", "", 0, log_level, message, src, len); } /*****************************************************************************/ /* log a hex dump */ enum logReturns log_hexdump_with_location(const char *function_name, const char *file_name, const int line_number, const enum logLevels log_level, const char *message, const char *src, int len) { char *dump_buffer; enum logReturns rv = LOG_STARTUP_OK; enum logLevels override_log_level = LOG_LEVEL_NEVER; bool_t override_destination_level = 0; override_destination_level = internal_log_location_overrides_level( function_name, file_name, &override_log_level); if (!internal_log_is_enabled_for_level(log_level, override_destination_level, override_log_level)) { return LOG_STARTUP_OK; } /* Start the dump on a new line so that the first line of the dump is aligned to the first column instead of to after the log message preamble (eg. time, log level, ...) */ #ifdef _WIN32 #define HEX_DUMP_HEADER ("Hex Dump:\r\n") #else #ifdef _MACOS #define HEX_DUMP_HEADER ("Hex Dump:\r") #else #define HEX_DUMP_HEADER ("Hex Dump:\n") #endif #endif dump_buffer = g_bytes_to_hexdump(src, len); if (dump_buffer != NULL) { if (g_strlen(file_name) > 0) { rv = log_message_with_location(function_name, file_name, line_number, log_level, "%s %s%s", message, HEX_DUMP_HEADER, dump_buffer); } else { rv = log_message(log_level, "%s %s%s", message, HEX_DUMP_HEADER, dump_buffer); } g_free(dump_buffer); } return rv; } enum logReturns log_message_with_location(const char *function_name, const char *file_name, const int line_number, const enum logLevels level, const char *msg, ...) { va_list ap; enum logReturns rv; char buff[LOG_BUFFER_SIZE]; enum logLevels override_log_level = LOG_LEVEL_NEVER; bool_t override_destination_level = 0; if (g_staticLogConfig == NULL) { g_writeln("The log reference is NULL - log not initialized properly " "when called from [%s(%s:%d)]", (function_name != NULL ? function_name : "unknown_function"), (file_name != NULL ? file_name : "unknown_file"), line_number); return LOG_ERROR_NO_CFG; } override_destination_level = internal_log_location_overrides_level( function_name, file_name, &override_log_level); if (!internal_log_is_enabled_for_level(level, override_destination_level, override_log_level)) { return LOG_STARTUP_OK; } g_snprintf(buff, LOG_BUFFER_SIZE, "[%s(%s:%d)] %s", function_name, file_name, line_number, msg); va_start(ap, msg); rv = internal_log_message(level, override_destination_level, override_log_level, buff, ap); va_end(ap); return rv; } enum logReturns log_message(const enum logLevels lvl, const char *msg, ...) { va_list ap; enum logReturns rv; va_start(ap, msg); rv = internal_log_message(lvl, 0, LOG_LEVEL_NEVER, msg, ap); va_end(ap); return rv; } enum logReturns internal_log_message(const enum logLevels lvl, const bool_t override_destination_level, const enum logLevels override_log_level, const char *msg, va_list ap) { char buff[LOG_BUFFER_SIZE + 43]; /* 31 ("[2022-10-07T19:58:33.065+0900] ") + 8 (log level) + 4 (space+cr+lf+\0) */ int len = 0; enum logReturns rv = LOG_STARTUP_OK; int writereply = 0; if (g_staticLogConfig == NULL) { g_writeln("The log reference is NULL - log not initialized properly"); return LOG_ERROR_NO_CFG; } if (0 > g_staticLogConfig->fd && g_staticLogConfig->enable_syslog == 0 && g_staticLogConfig->enable_console == 0) { return LOG_ERROR_FILE_NOT_OPEN; } if (!internal_log_is_enabled_for_level(lvl, override_destination_level, override_log_level)) { return LOG_STARTUP_OK; } getFormattedDateTime(buff, 32); internal_log_lvl2str(lvl, buff + 31); if (g_staticLogConfig->enable_pid) { /* 31 (datetime) + 8 (log level) = 39 */ g_snprintf(buff + 39, LOG_BUFFER_SIZE, "[pid:%d tid:%lld] ", g_getpid(), (long long) tc_get_threadid()); len = g_strlen(buff + 39); } len += vsnprintf(buff + 39 + len, LOG_BUFFER_SIZE - len, msg, ap); /* checking for truncated messages */ if (len > LOG_BUFFER_SIZE) { log_message(LOG_LEVEL_WARNING, "next message will be truncated"); len = LOG_BUFFER_SIZE; } /* forcing the end of message string */ /* 31 (datetime) + 8 (log level) = 39 */ #ifdef _WIN32 buff[len + 39] = '\r'; buff[len + 40] = '\n'; buff[len + 41] = '\0'; #else #ifdef _MACOS buff[len + 39] = '\r'; buff[len + 40] = '\0'; #else buff[len + 39] = '\n'; buff[len + 40] = '\0'; #endif #endif if (g_staticLogConfig->enable_syslog && ((override_destination_level && lvl <= override_log_level) || (!override_destination_level && lvl <= g_staticLogConfig->syslog_level))) { /* log to syslog*/ /* %s fix compiler warning 'not a string literal' */ syslog(internal_log_xrdp2syslog(lvl), "%s", buff + 31); } if (g_staticLogConfig->enable_console && ((override_destination_level && lvl <= override_log_level) || (!override_destination_level && lvl <= g_staticLogConfig->console_level))) { /* log to console */ g_printf("%s", buff); } if ((override_destination_level && lvl <= override_log_level) || (!override_destination_level && lvl <= g_staticLogConfig->log_level)) { /* log to application logfile */ if (g_staticLogConfig->fd >= 0) { #ifdef LOG_ENABLE_THREAD pthread_mutex_lock(&(g_staticLogConfig->log_lock)); #endif writereply = g_file_write(g_staticLogConfig->fd, buff, g_strlen(buff)); if (writereply <= 0) { rv = LOG_ERROR_NULL_FILE; } #ifdef LOG_ENABLE_THREAD pthread_mutex_unlock(&(g_staticLogConfig->log_lock)); #endif } } return rv; } /** * Return the configured log file name * @return */ char * getLogFile(char *replybuf, int bufsize) { if (g_staticLogConfig) { if (g_staticLogConfig->log_file) { g_strncpy(replybuf, g_staticLogConfig->log_file, bufsize); } else { g_sprintf(replybuf, "The log_file name is NULL"); } } else { g_snprintf(replybuf, bufsize, "The log is not properly started"); } return replybuf; } /** * Returns formatted datetime for log * @return */ char * getFormattedDateTime(char *replybuf, int bufsize) { char buf_datetime[21]; /* 2022-10-07T16:36:04 + . */ char buf_millisec[4]; /* 357 */ char buf_timezone[6]; /* +0900 */ struct tm *now; struct timeval tv; int millisec; gettimeofday(&tv, NULL); now = localtime(&tv.tv_sec); millisec = (tv.tv_usec + 500 / 1000); g_snprintf(buf_millisec, sizeof(buf_millisec), "%03d", millisec); strftime(buf_datetime, sizeof(buf_datetime), "%FT%T.", now); strftime(buf_timezone, sizeof(buf_timezone), "%z", now); g_snprintf(replybuf, bufsize, "[%s%s%s] ", buf_datetime, buf_millisec, buf_timezone); return replybuf; } /*****************************************************************************/ #ifdef USE_DEVEL_LOGGING void log_devel_leaking_fds(const char *exe, int min, int max) { struct list *fd_list = g_get_open_fds(min, max); if (fd_list != NULL) { int i; for (i = 0 ; i < fd_list->count ; ++i) { int fd = (int)fd_list->items[i]; if (g_file_get_cloexec(fd) == 0) { LOG_DEVEL(LOG_LEVEL_WARNING, "File descriptor %d is not CLOEXEC when running %s", fd, exe); } } } } #endif // USE_DEVEL_LOGGING