diff --git a/telldus-core/common/Strings.cpp b/telldus-core/common/Strings.cpp index 83c1384f..720e56f8 100644 --- a/telldus-core/common/Strings.cpp +++ b/telldus-core/common/Strings.cpp @@ -158,3 +158,48 @@ std::string TelldusCore::wideToString(const std::wstring &input) { return retval; #endif } + +std::string TelldusCore::formatf(const char *format, ...) { + va_list ap; + va_start(ap, format); + std::string retval = sformatf(format, ap); + va_end(ap); + return retval; +} + +std::string TelldusCore::sformatf(const char *format, va_list ap) { + //This code is based on code from the Linux man-pages project (man vsprintf) + int n; + int size = 100; /* Guess we need no more than 100 bytes. */ + char *p, *np; + + if ((p = (char*)malloc(size)) == NULL) { + return ""; + } + + while (1) { + /* Try to print in the allocated space. */ + n = vsnprintf(p, size, format, ap); + + /* If that worked, return the string. */ + if (n > -1 && n < size) { + std::string retval(p); + free(p); + return retval; + } + + /* Else try again with more space. */ + + if (n > -1) { /* glibc 2.1 */ + size = n+1; /* precisely what is needed */ + } else { /* glibc 2.0 */ + size *= 2; /* twice the old size */ + } + if ((np = (char *)realloc (p, size)) == NULL) { + free(p); + return ""; + } else { + p = np; + } + } +} diff --git a/telldus-core/common/Strings.h b/telldus-core/common/Strings.h index aca16060..bdc44263 100644 --- a/telldus-core/common/Strings.h +++ b/telldus-core/common/Strings.h @@ -14,6 +14,9 @@ namespace TelldusCore { std::string wideToString(const std::wstring &input); int wideToInteger(const std::wstring &input); + + std::string formatf(const char *format, ...); + std::string sformatf(const char *format, va_list ap); } #endif //STRING_H diff --git a/telldus-core/service/CMakeLists.txt b/telldus-core/service/CMakeLists.txt index f50c8334..8fc6125c 100644 --- a/telldus-core/service/CMakeLists.txt +++ b/telldus-core/service/CMakeLists.txt @@ -19,6 +19,7 @@ SET( telldus-service_SRCS Device.cpp DeviceManager.cpp Event.cpp + Log.cpp Sensor.cpp Settings.cpp TelldusMain.cpp @@ -78,6 +79,7 @@ SET( telldus-service_HDRS DeviceManager.h Event.h EventHandler.h + Log.h Sensor.h Settings.h TelldusMain.h @@ -135,15 +137,27 @@ ELSEIF (WIN32) #### Windows #### main_win.cpp SettingsWinRegistry.cpp TelldusWinService_win.cpp + Messages.mc + ${CMAKE_CURRENT_BINARY_DIR}/Messages.rc + ${CMAKE_CURRENT_BINARY_DIR}/Messages.h ) LIST(APPEND telldus-service_HDRS TelldusWinService_win.h ) + ADD_CUSTOM_COMMAND( + OUTPUT Messages.rc Messages.h + COMMAND mc.exe -u -r ${CMAKE_CURRENT_BINARY_DIR} -h ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Messages.mc + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Messages.mc + DEPENDS Messages.rc + COMMENT "Compiling Messages Resource" + ) + INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) ELSE (APPLE) #### Linux #### SET(DEFAULT_FTDI_ENGINE "libftdi") FIND_LIBRARY(CONFUSE_LIBRARY confuse) ADD_DEFINITIONS( -D_CONFUSE ) + ADD_DEFINITIONS( -D_LINUX ) SET( telldus-service_TARGET telldusd ) LIST(APPEND telldus-service_SRCS diff --git a/telldus-core/service/Log.cpp b/telldus-core/service/Log.cpp new file mode 100644 index 00000000..f555378a --- /dev/null +++ b/telldus-core/service/Log.cpp @@ -0,0 +1,180 @@ +#include "Log.h" +#include + +#if defined(_LINUX) +#include +#elif defined(_WINDOWS) +#include +#include "Strings.h" +#include "Messages.h" +#endif + +class Log::PrivateData { +public: + PrivateData() : logOutput(Log::System), debug(false) {} + + Log::LogOutput logOutput; + bool debug; + + static Log *instance; +#ifdef _WINDOWS + HANDLE eventSource; +#endif +}; + +Log *Log::PrivateData::instance = 0; + +Log::Log() + :d(new PrivateData) +{ +#if defined(_LINUX) + setlogmask(LOG_UPTO(LOG_INFO)); + openlog("telldusd", LOG_CONS, LOG_USER); +#elif defined(_MACOSX) + d->logOutput = Log::StdOut; +#elif defined(_WINDOWS) + //Add ourselves to the registy + HKEY hRegKey = NULL; + DWORD dwError = 0; + TCHAR filePath[MAX_PATH]; + + std::wstring path(L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\TelldusService"); + dwError = RegCreateKey( HKEY_LOCAL_MACHINE, path.c_str(), &hRegKey ); + + GetModuleFileName( NULL, filePath, MAX_PATH ); + dwError = RegSetValueEx( hRegKey, L"EventMessageFile", 0, + REG_EXPAND_SZ, (PBYTE) filePath, + (DWORD)(wcslen(filePath) + 1) * sizeof TCHAR ); + + DWORD dwTypes = LOG_DEBUG | LOG_NOTICE | LOG_WARNING | LOG_ERR; + dwError = RegSetValueEx( hRegKey, L"TypesSupported", + 0, REG_DWORD, (LPBYTE) &dwTypes, sizeof dwTypes ); + + RegCloseKey(hRegKey); + + d->eventSource = RegisterEventSource(NULL, L"TelldusService"); +#endif +} + +Log::~Log() { +#if defined(_LINUX) + closelog(); +#elif defined(_WINDOWS) + if (d->eventSource != NULL) { + DeregisterEventSource(d->eventSource); + } +#endif + delete d; +} + +void Log::destroy() { + if (PrivateData::instance == 0) { + return; + } + delete PrivateData::instance; + PrivateData::instance = 0; +} + +void Log::debug(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Debug, fmt, ap); + va_end(ap); +} + +void Log::notice(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Notice, fmt, ap); + va_end(ap); +} + +void Log::warning(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Warning, fmt, ap); + va_end(ap); +} + +void Log::error(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Error, fmt, ap); + va_end(ap); +} + +void Log::setDebug() { + Log *log = Log::instance(); + log->d->debug = true; +} + +void Log::setLogOutput(LogOutput logOutput) { +#ifdef _MACOSX + //Always stdout + return; +#endif + Log *log = Log::instance(); + log->d->logOutput = logOutput; +} + +void Log::message(Log::LogLevel logLevel, const char *format, va_list ap) const { + if (logLevel == Debug && d->debug == false) { + return; + } + if (d->logOutput == StdOut) { + FILE *stream = stdout; + if (logLevel == Warning || logLevel == Error) { + stream = stderr; + } + vfprintf(stream, format, ap); + fprintf(stream, "\n"); + fflush(stream); + } else { +#if defined(_LINUX) + switch (logLevel) { + case Debug: + vsyslog(LOG_DEBUG, format, ap); + break; + case Notice: + vsyslog(LOG_NOTICE, format, ap); + break; + case Warning: + vsyslog(LOG_WARNING, format, ap); + break; + case Error: + vsyslog(LOG_ERR, format, ap); + break; + } +#elif defined(_WINDOWS) + LPWSTR pInsertStrings[2] = {NULL, NULL}; + std::wstring str = TelldusCore::charToWstring(TelldusCore::sformatf(format, ap).c_str()); + pInsertStrings[0] = (LPWSTR)str.c_str(); + + switch (logLevel) { + case Debug: + ReportEvent(d->eventSource, EVENTLOG_SUCCESS, NULL, LOG_DEBUG, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL); + break; + case Notice: + ReportEvent(d->eventSource, EVENTLOG_INFORMATION_TYPE, NULL, LOG_NOTICE, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL); + break; + case Warning: + ReportEvent(d->eventSource, EVENTLOG_WARNING_TYPE, NULL, LOG_WARNING, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL); + break; + case Error: + ReportEvent(d->eventSource, EVENTLOG_ERROR_TYPE, NULL, LOG_ERR, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL); + break; + } +#endif + } +} + +Log *Log::instance() { + if (PrivateData::instance == 0) { + PrivateData::instance = new Log(); + } + return PrivateData::instance; +} diff --git a/telldus-core/service/Log.h b/telldus-core/service/Log.h new file mode 100644 index 00000000..a2f547c0 --- /dev/null +++ b/telldus-core/service/Log.h @@ -0,0 +1,33 @@ +#ifndef LOG_H +#define LOG_H + +#include + +class Log { +public: + enum LogLevel { Debug, Notice, Warning, Error }; + enum LogOutput { StdOut, System }; + virtual ~Log(); + + static void destroy(); + + static void debug(const char *fmt, ...); + static void notice(const char *fmt, ...); + static void warning(const char *fmt, ...); + static void error(const char *fmt, ...); + + static void setDebug(); + static void setLogOutput(LogOutput logOutput); + +protected: + Log(); + void message(LogLevel logLevel, const char *format, va_list ap) const; + static Log *instance(); + +private: + class PrivateData; + PrivateData *d; +}; + + +#endif //LOG_H diff --git a/telldus-core/service/Messages.mc b/telldus-core/service/Messages.mc new file mode 100644 index 00000000..b7f81b41 Binary files /dev/null and b/telldus-core/service/Messages.mc differ diff --git a/telldus-core/service/TellStick_ftd2xx.cpp b/telldus-core/service/TellStick_ftd2xx.cpp index 8769cd30..288aff42 100644 --- a/telldus-core/service/TellStick_ftd2xx.cpp +++ b/telldus-core/service/TellStick_ftd2xx.cpp @@ -12,6 +12,7 @@ #include "TellStick.h" #include "Mutex.h" #include "Strings.h" +#include "Log.h" #include "../client/telldus-core.h" #include #include @@ -61,6 +62,7 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor & strcpy(tempSerial, td.serial.c_str()); FT_SetVIDPID(td.vid, td.pid); #endif + Log::notice("Connecting to TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); FT_STATUS ftStatus = FT_OpenEx(tempSerial, FT_OPEN_BY_SERIAL_NUMBER, &d->ftHandle); delete tempSerial; if (ftStatus == FT_OK) { @@ -76,10 +78,13 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor & setBaud(4800); } this->start(); + } else { + Log::warning("Failed to open TellStick"); } } TellStick::~TellStick() { + Log::warning("Disconnected TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); if (d->running) { TelldusCore::MutexLocker locker(&d->mutex); d->running = false; diff --git a/telldus-core/service/TellStick_libftdi.cpp b/telldus-core/service/TellStick_libftdi.cpp index c5e6c3ae..4b850bbb 100644 --- a/telldus-core/service/TellStick_libftdi.cpp +++ b/telldus-core/service/TellStick_libftdi.cpp @@ -54,6 +54,7 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor & ftdi_init(&d->ftHandle); ftdi_set_interface(&d->ftHandle, INTERFACE_ANY); + Log::notice("Connecting to TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); int ret = ftdi_usb_open_desc(&d->ftHandle, td.vid, td.pid, NULL, td.serial.c_str()); if (ret < 0) { ftdi_deinit(&d->ftHandle); @@ -71,10 +72,13 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor & this->setBaud(4800); } this->start(); + } else { + Log::warning("Failed to open TellStick"); } } TellStick::~TellStick() { + Log::warning("Disconnected TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); if (d->running) { stop(); } diff --git a/telldus-core/service/TelldusWinService_win.cpp b/telldus-core/service/TelldusWinService_win.cpp index a7c48322..6a415120 100644 --- a/telldus-core/service/TelldusWinService_win.cpp +++ b/telldus-core/service/TelldusWinService_win.cpp @@ -1,5 +1,6 @@ #include "TelldusWinService_win.h" #include "TelldusMain.h" +#include "Log.h" #include #include @@ -111,6 +112,13 @@ DWORD WINAPI TelldusWinService::serviceControlHandler( DWORD controlCode, DWORD void WINAPI TelldusWinService::serviceMain( DWORD argc, TCHAR* argv[] ) { TelldusWinService instance; + //Enable debug if we hade this supplied + for(unsigned int i = 1; i < argc; ++i) { + if (wcscmp(argv[i], L"--debug") == 0) { + Log::setDebug(); + } + } + // initialise service status instance.serviceStatus.dwServiceType = SERVICE_WIN32; instance.serviceStatus.dwCurrentState = SERVICE_STOPPED; @@ -140,8 +148,13 @@ void WINAPI TelldusWinService::serviceMain( DWORD argc, TCHAR* argv[] ) { devInterface.dbcc_classguid = GUID_DEVINTERFACE_USBRAW; HDEVNOTIFY deviceNotificationHandle = RegisterDeviceNotificationW(instance.serviceStatusHandle, &devInterface, DEVICE_NOTIFY_SERVICE_HANDLE); + Log::notice("TelldusService started"); + //Start our main-loop instance.tm->start(); + + Log::notice("TelldusService stopping"); + Log::destroy(); // service was stopped instance.serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; diff --git a/telldus-core/service/main_mac.cpp b/telldus-core/service/main_mac.cpp index 4527225e..f87b718a 100644 --- a/telldus-core/service/main_mac.cpp +++ b/telldus-core/service/main_mac.cpp @@ -1,16 +1,16 @@ #include "TelldusMain.h" +#include "Log.h" #include -#include TelldusMain tm; void shutdownHandler(int onSignal) { - printf("Shutting down\n"); + Log::notice("Shutting down"); tm.stop(); } void sigpipeHandler(int onSignal) { - printf("SIGPIPE received\n"); + Log::notice("SIGPIPE received"); } int main(int argc, char **argv) { @@ -19,7 +19,10 @@ int main(int argc, char **argv) { signal(SIGINT, shutdownHandler); signal(SIGPIPE, sigpipeHandler); + Log::notice("telldusd started"); tm.start(); - printf("telldusd stopped gracefully\n"); + Log::notice("telldusd stopped gracefully"); + + Log::destroy(); return 0; } diff --git a/telldus-core/service/main_unix.cpp b/telldus-core/service/main_unix.cpp index 7bfc36c5..b68d13b7 100644 --- a/telldus-core/service/main_unix.cpp +++ b/telldus-core/service/main_unix.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include "Settings.h" #include "Strings.h" +#include "Log.h" #define DAEMON_NAME "telldusd" #define PID_FILE "/var/run/" DAEMON_NAME ".pid" @@ -21,19 +21,19 @@ TelldusMain tm; void signalHandler(int sig) { switch(sig) { case SIGHUP: - syslog(LOG_WARNING, "Received SIGHUP signal."); + Log::warning("Received SIGHUP signal."); break; case SIGTERM: case SIGINT: - syslog(LOG_WARNING, "Received SIGTERM or SIGINT signal."); - syslog(LOG_WARNING, "Shutting down"); + Log::warning("Received SIGTERM or SIGINT signal."); + Log::warning("Shutting down"); tm.stop(); break; case SIGPIPE: - syslog(LOG_WARNING, "Received SIGPIPE signal."); + Log::warning("Received SIGPIPE signal."); break; default: - syslog(LOG_WARNING, "Unhandled signal (%d) %s", sig, strsignal(sig)); + Log::warning("Unhandled signal (%d) %s", sig, strsignal(sig)); break; } } @@ -46,17 +46,19 @@ int main(int argc, char **argv) { for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "--nodaemon") == 0) { deamonize = false; - } - if (strcmp(argv[i], "--help") == 0) { + Log::setLogOutput(Log::StdOut); + } else if (strcmp(argv[i], "--help") == 0) { printf("Telldus TellStick background service\n\nStart with --nodaemon to not run as daemon\n\n"); printf("Report bugs to \n"); exit(EXIT_SUCCESS); - } - if (strcmp(argv[i], "--version") == 0) { + } else if (strcmp(argv[i], "--version") == 0) { printf("telldusd " VERSION "\n\n"); printf("Copyright (C) 2011 Telldus Technologies AB\n\n"); printf("Written by Micke Prag \n"); exit(EXIT_SUCCESS); + } else { + printf("Unknown option %s\n", argv[i]); + exit(EXIT_FAILURE); } } @@ -76,17 +78,14 @@ int main(int argc, char **argv) { fprintf(fd,"%d\n",pid); fclose(fd); } else { - syslog(LOG_ERR, "Could not open pid file %s: %s", PID_FILE, strerror(errno)); + Log::error("Could not open pid file %s: %s", PID_FILE, strerror(errno)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } } - setlogmask(LOG_UPTO(LOG_INFO)); - openlog(DAEMON_NAME, LOG_CONS, LOG_USER); - - syslog(LOG_NOTICE, "%s daemon starting up", DAEMON_NAME); + Log::notice("%s daemon starting up", DAEMON_NAME); if (deamonize) { /* Change the file mode mask */ @@ -115,14 +114,14 @@ int main(int argc, char **argv) { if (grp) { setgid(grp->gr_gid); } else { - syslog(LOG_WARNING, "Group %s could not be found", group.c_str()); + Log::warning("Group %s could not be found", group.c_str()); exit(EXIT_FAILURE); } struct passwd *pw = getpwnam(user.c_str()); if (pw) { setuid( pw->pw_uid ); } else { - syslog(LOG_WARNING, "User %s could not be found", user.c_str()); + Log::warning("User %s could not be found", user.c_str()); exit(EXIT_FAILURE); } } @@ -139,6 +138,6 @@ int main(int argc, char **argv) { tm.start(); - syslog(LOG_NOTICE, "%s daemon exited", DAEMON_NAME); + Log::notice("%s daemon exited", DAEMON_NAME); exit(EXIT_SUCCESS); }