/* * ebusd - daemon for communication with eBUS heating systems. * Copyright (C) 2014-2022 John Baier * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include "lib/utils/log.h" #include #include #include #include #include #include #include #include "lib/utils/clock.h" namespace ebusd { /** the name of each @a LogFacility. */ static const char *s_facilityNames[] = { "main", "network", "bus", "update", "other", "all", nullptr }; /** the name of each @a LogLevel. */ static const char* s_levelNames[] = { "none", "error", "notice", "info", "debug", nullptr }; /** the syslog level of each @a LogLevel. */ static const int s_syslogLevels[] = { LOG_INFO, LOG_ERR, LOG_NOTICE, LOG_INFO, LOG_DEBUG, 0 }; /** the current log level by log facility. */ static LogLevel s_facilityLogLevel[] = { ll_notice, ll_notice, ll_notice, ll_notice, ll_notice, }; /** the current log FILE, or nullptr if closed or syslog is used. */ static FILE* s_logFile = stdout; /** whether to log to syslog. */ static bool s_useSyslog = false; LogFacility parseLogFacility(const char* facility) { if (!facility) { return lf_COUNT; } char *input = strdup(facility); char *opt = reinterpret_cast(input), *value = nullptr; int val = getsubopt(&opt, (char *const *)s_facilityNames, &value); if (val < 0 || val >= lf_COUNT || value || *opt) { free(input); return lf_COUNT; } free(input); return (LogFacility)val; } int parseLogFacilities(const char* facilities) { char *input = strdup(facilities); char *opt = reinterpret_cast(input), *value = nullptr; int newFacilites = 0; while (*opt) { int val = getsubopt(&opt, (char *const *)s_facilityNames, &value); if (val < 0 || val > lf_COUNT || value) { free(input); return -1; } if (val == lf_COUNT) { newFacilites = LF_ALL; } else { newFacilites |= 1 << val; } } free(input); return newFacilites; } LogLevel parseLogLevel(const char* level) { if (!level) { return ll_COUNT; } char *input = strdup(level); char *opt = reinterpret_cast(input), *value = nullptr; int val = getsubopt(&opt, (char *const *)s_levelNames, &value); if (val < 0 || val >= ll_COUNT || value || *opt) { free(input); return ll_COUNT; } free(input); return (LogLevel)val; } const char* getLogFacilityStr(LogFacility facility) { return s_facilityNames[facility]; } const char* getLogLevelStr(LogLevel level) { return s_levelNames[level]; } bool setFacilitiesLogLevel(int facilities, LogLevel level) { bool changed = false; for (int val = 0; val < lf_COUNT && facilities != 0; val++) { if ((facilities & (1 << val)) != 0 && s_facilityLogLevel[(LogFacility)val] != level) { s_facilityLogLevel[(LogFacility)val] = level; changed = true; } } return changed; } LogLevel getFacilityLogLevel(LogFacility facility) { return s_facilityLogLevel[facility]; } bool setLogFile(const char* filename) { if (filename[0] == 0) { closeLogFile(); openlog("ebusd", LOG_NDELAY|LOG_PID, LOG_USER); s_useSyslog = true; return true; } FILE* newFile = fopen(filename, "a"); if (newFile == nullptr) { return false; } closeLogFile(); s_logFile = newFile; return true; } void closeLogFile() { if (s_logFile != nullptr) { if (s_logFile != stdout) { fclose(s_logFile); } s_logFile = nullptr; } if (s_useSyslog) { closelog(); s_useSyslog = false; } } bool needsLog(const LogFacility facility, const LogLevel level) { if (s_logFile == nullptr && !s_useSyslog) { return false; } return s_facilityLogLevel[facility] >= level; } void logWrite(const char* facility, const LogLevel level, const char* message, va_list ap) { if (s_logFile == nullptr && !s_useSyslog) { return; } char* buf; if (vasprintf(&buf, message, ap) >= 0 && buf) { if (s_useSyslog) { syslog(s_syslogLevels[level], "[%s %s] %s", facility, s_levelNames[level], buf); } else { struct timespec ts; struct tm td; clockGettime(&ts); localtime_r(&ts.tv_sec, &td); fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n", td.tm_year+1900, td.tm_mon+1, td.tm_mday, td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, facility, s_levelNames[level], buf); fflush(s_logFile); } } if (buf) { free(buf); } } void logWrite(const LogFacility facility, const LogLevel level, const char* message, ...) { va_list ap; va_start(ap, message); logWrite(s_facilityNames[facility], level, message, ap); va_end(ap); } void logWrite(const char* facility, const LogLevel level, const char* message, ...) { va_list ap; va_start(ap, message); logWrite(facility, level, message, ap); va_end(ap); } } // namespace ebusd