00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "daemon/daemon.h"
00029 #include "ossock.h"
00030 #include "base/string.hpp"
00031 #include "base/memory.hpp"
00032 #include "base/common.hpp"
00033 #include "base/vector.hpp"
00034 #include "base/map.hpp"
00035 #include "base/list.hpp"
00036 #include "base/date.h"
00037 #include "base/xmlconfig.h"
00038 #include "xml/sxs.hpp"
00039
00040 #if OS_TYPE == OS_WIN32
00042 // WinMain entry point
00043 extern "C" int WINAPI WinMain(HINSTANCE , HINSTANCE , char* , int )
00044 {
00045 int argc = 0;
00046
00047 LPWSTR wcmd = ::GetCommandLineW();
00048
00049 LPWSTR* wargv = CommandLineToArgvW(wcmd, &argc);
00050
00051
00052 TERIMBER::vector< TERIMBER::string_t > strargv;
00053 strargv.resize(argc);
00054
00055 TERIMBER::vector< char* > argv;
00056 argv.resize(argc, 0);
00057
00058
00059 for (int i = 0; i < argc; ++ i)
00060 argv[i] = (char*)TERIMBER::str_template::unicode_to_multibyte(strargv[i], wargv[i], os_minus_one);
00061
00062
00063 ::GlobalFree(wargv);
00064
00065
00066 return TERIMBER::daemon::s_daemon_main(argc, &argv[0]);
00067 }
00069 #else
00070
00071 #include <syslog.h>
00073 int main(int argc, char* argv[])
00074 {
00075
00076 return TERIMBER::daemon::s_daemon_main(argc, argv);
00077 }
00079 #endif
00080
00081 BEGIN_TERIMBER_NAMESPACE
00082 #pragma pack(4)
00083
00084
00085 daemon* daemon::g_daemon = 0;
00086
00087
00088
00089 daemon::daemon()
00090 {
00091
00092 assert(!g_daemon);
00093
00094
00095 g_daemon = this;
00096
00097 _isDaemon = false;
00098
00099 _severity = 0;
00100
00101 #if OS_TYPE == OS_WIN32
00102
00103 _hStatus = 0;
00104 memset(&_pStatus, 0, sizeof(_pStatus));
00105 _pStatus.dwServiceType = SERVICE_WIN32;
00106 _pStatus.dwCurrentState = SERVICE_START_PENDING;
00107 _pStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
00108 #endif
00109 }
00110
00111 daemon::~daemon()
00112 {
00113
00114 g_daemon = 0;
00115 }
00116
00118
00119
00120 int
00121 daemon::s_daemon_main(unsigned long argc, char* argv[])
00122 {
00123
00124 assert(g_daemon);
00125
00126 return g_daemon->daemon_main(argc, argv);
00127 }
00128
00129 int daemon::daemon_main(unsigned long argc, char* argv[])
00130 {
00131
00132 if (argc > 1 && argv && argv[1])
00133 {
00134 if (!str_template::strcmp((const char*)argv[1], "uninstall", os_minus_one))
00135 {
00136 uninstall();
00137 return 0;
00138 }
00139 else if (!str_template::strcmp((const char*)argv[1], "install", os_minus_one))
00140 {
00141 uninstall();
00142 install();
00143 return 0;
00144 }
00145 else if (!str_template::strcmp((const char*)argv[1], "noservice", os_minus_one))
00146 {
00147 _isDaemon = false;
00148 }
00149 else
00150 _isDaemon = is_installed();
00151 }
00152 else
00153 _isDaemon = is_installed();
00154
00155 #if OS_TYPE == OS_WIN32
00156 if (_isDaemon)
00157 {
00158 SERVICE_TABLE_ENTRY pTable[] = { { (char*)(const char*)_daemonName, s_daemon_start }, { 0, 0 } };
00159
00160 if (!StartServiceCtrlDispatcher(pTable))
00161 _pStatus.dwWin32ExitCode = GetLastError();
00162
00163
00164 return _pStatus.dwWin32ExitCode;
00165 }
00166
00167 #else
00168
00169 umask(0);
00170
00171
00172 if (!_isDaemon)
00173 {
00174
00175 log_message(false, "starting as a cmd line program...");
00176 for (int i = 1; i < argc; ++i)
00177 log_message(false, "cmd parameter %d: %s", i, argv[i]);
00178 }
00179 else
00180 {
00181
00182 pid_t pid = fork();
00183
00184 if (pid < 0)
00185 exit(EXIT_FAILURE);
00186 else if (pid > 0)
00187 exit(EXIT_SUCCESS);
00188
00189
00190
00191 pid_t sid = setsid();
00192
00193 if (sid < 0)
00194 exit(EXIT_FAILURE);
00195
00196 close(STDIN_FILENO);
00197 close(STDOUT_FILENO);
00198 close(STDERR_FILENO);
00199
00200
00201 TERIMBER::string_t lockfilename("./");
00202 lockfilename += _daemonName;
00203 lockfilename += ".pid";
00204
00205 int fd = ::open(lockfilename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
00206 if (fd < 0)
00207 {
00208 log_message(true, "can not open: %s, error: %s", (const char*)lockfilename, strerror( errno ));
00209 exit(EXIT_FAILURE);
00210 }
00211
00212
00213 struct flock fl;
00214 fl.l_type = F_WRLCK;
00215 fl.l_start = 0;
00216 fl.l_whence = SEEK_SET;
00217 fl.l_len = 0;
00218
00219 if (::fcntl(fd, F_SETLK, &fl) < 0)
00220 {
00221 ::close(fd);
00222 if (errno == EACCES || errno == EAGAIN)
00223 log_message(true, "service %s is alreay running", (const char*)lockfilename);
00224 else
00225 log_message(true, "can not lock file %s", (const char*)lockfilename);
00226
00227 exit(EXIT_FAILURE);
00228 }
00229
00230 char fbuf[32];
00231 if (-1 == ftruncate(fd, 0))
00232 exit(EXIT_FAILURE);
00233
00234 str_template::strprint(fbuf, sizeof(fbuf), "%ld", (long)getpid());
00235 if (-1 == ::write(fd, fbuf, strlen(fbuf) + 1))
00236 exit(EXIT_FAILURE);
00237
00238
00239 struct sigaction sa;
00240 sa.sa_handler = SIG_IGN;
00241 sigemptyset(&sa.sa_mask);
00242 sa.sa_flags = 0;
00243 if (sigaction(SIGHUP, &sa, 0) < 0)
00244 log_message(true, "sigaction: %s", strerror( errno ));
00245
00246
00247 if ((pid = fork()) < 0)
00248 exit(EXIT_FAILURE);
00249 else if (pid > 0)
00250 exit(EXIT_SUCCESS);
00251 }
00252
00253 static const int signal_list[] =
00254 {
00255 SIGALRM,
00256 SIGCHLD,
00257 SIGHUP, SIGUSR1, SIGUSR2,
00258 SIGINT, SIGTERM, SIGQUIT,
00259 SIGABRT,
00260 SIGBUS, SIGFPE, SIGILL, SIGSEGV,
00261 SIGPIPE, SIGTSTP, SIGTTIN, SIGTTOU,
00262 0
00263 };
00264
00265
00266 for (const int *signal_ptr = signal_list; *signal_ptr; ++signal_ptr)
00267 {
00268
00269 struct sigaction sa;
00270 sa.sa_handler = s_daemon_handler;
00271 sigemptyset(&sa.sa_mask);
00272 sigaddset(&sa.sa_mask, *signal_ptr);
00273 sa.sa_flags = 0;
00274 if (sigaction(*signal_ptr, &sa, 0) < 0)
00275 log_message(true, "sigaction: %s", strerror( errno ));
00276 }
00277
00278 #endif
00279
00280
00281 daemon_start(argc, argv);
00282
00283 #if OS_TYPE == OS_WIN32
00284 return _pStatus.dwWin32ExitCode;
00285 #else
00286 exit(EXIT_SUCCESS);
00287 #endif
00288 }
00289
00291 #if OS_TYPE == OS_WIN32
00292
00293 bool daemon::set_state(DWORD nState)
00294 {
00295 _pStatus.dwCurrentState = nState;
00296 return _hStatus && SetServiceStatus(_hStatus, &_pStatus);
00297 }
00298 #endif
00300 // winservice service installation functions
00301 bool daemon::is_installed()
00302 {
00303 if (!_daemonName.length())
00304 {
00305 log_message(true, "daemon name is not assigned");
00306 return false;
00307 }
00308
00309 #if OS_TYPE == OS_WIN32
00310 if (SC_HANDLE hSCM = ::OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS))
00311 {
00312 if (SC_HANDLE hService = ::OpenService(hSCM, _daemonName, SERVICE_QUERY_CONFIG))
00313 {
00314 ::CloseServiceHandle(hService);
00315 ::CloseServiceHandle(hSCM);
00316 return true;
00317 }
00318 else
00319 {
00320 log_message(true, "can not open service for name: %s", (const char*)_daemonName);
00321 _pStatus.dwWin32ExitCode = GetLastError();
00322 }
00323
00324 ::CloseServiceHandle(hSCM);
00325 }
00326 else
00327 {
00328 log_message(true, "can not open service manager");
00329 _pStatus.dwWin32ExitCode = GetLastError();
00330 }
00331
00332
00333 return false;
00334 #else
00335 #ifdef _NDEBUG
00336 return true;
00337 #else
00338 return false;
00339 #endif
00340 #endif
00341 }
00342
00343 bool daemon::install()
00344 {
00345 if (is_installed())
00346 return true;
00347 if (!_daemonName.length())
00348 return false;
00349
00350 #if OS_TYPE == OS_WIN32
00351 char path[MAX_PATH + 2];
00352 DWORD len = ::GetModuleFileName(NULL, path + 1, MAX_PATH);
00353 if (!len || len == MAX_PATH)
00354 return false;
00355
00356 path[0] = '\"';
00357 path[len + 1] = '\"';
00358 path[len + 2] = 0;
00359
00360 if (SC_HANDLE hSCM = ::OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS))
00361 {
00362 if (SC_HANDLE hService = ::CreateService(hSCM, _daemonName, _daemonName,
00363 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
00364 SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
00365 path, 0, 0, "RPCSS\0", 0, 0))
00366 {
00367 SERVICE_DESCRIPTION d;
00368 d.lpDescription = (LPSTR)(const char*)_daemonDesc;
00369 ::ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &d);
00370 ::CloseServiceHandle(hService);
00371 ::CloseServiceHandle(hSCM);
00372 return true;
00373 }
00374 else
00375 {
00376 log_message(true, "can not open service for name: %s", (const char*)_daemonName);
00377 _pStatus.dwWin32ExitCode = GetLastError();
00378 }
00379
00380
00381 ::CloseServiceHandle(hSCM);
00382 }
00383 else
00384 {
00385 log_message(true, "can not open service manager");
00386 _pStatus.dwWin32ExitCode = GetLastError();
00387 }
00388
00389
00390 return false;
00391 #else
00392 return true;
00393 #endif
00394 }
00395
00396 bool daemon::uninstall()
00397 {
00398 if (!is_installed())
00399 return true;
00400
00401 #if OS_TYPE == OS_WIN32
00402 if (SC_HANDLE hSCM = ::OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS))
00403 {
00404 if (SC_HANDLE hService = ::OpenService(hSCM, _daemonName, SERVICE_STOP | DELETE))
00405 {
00406 bool okay = true;
00407 SERVICE_STATUS pStatus;
00408
00409 if (!::ControlService(hService, SERVICE_CONTROL_STOP, &pStatus))
00410 {
00411 DWORD err = ::GetLastError();
00412
00413 if (err != ERROR_SERVICE_NOT_ACTIVE
00414 && (err != ERROR_SERVICE_CANNOT_ACCEPT_CTRL
00415 || pStatus.dwCurrentState != SERVICE_STOP_PENDING)
00416 )
00417 okay = false;
00418 }
00419
00420 okay = okay && ::DeleteService(hService) == TRUE;
00421 ::CloseServiceHandle(hService);
00422 ::CloseServiceHandle(hSCM);
00423 return okay;
00424 }
00425 else
00426 {
00427 log_message(true, "can not open service for name: %s", (const char*)_daemonName);
00428 _pStatus.dwWin32ExitCode = GetLastError();
00429 }
00430
00431
00432 ::CloseServiceHandle(hSCM);
00433 }
00434 else
00435 {
00436 log_message(true, "can not open service manager");
00437 _pStatus.dwWin32ExitCode = GetLastError();
00438 }
00439
00440 return false;
00441 #else
00442 return true;
00443 #endif
00444 }
00445
00447
00448 void STATIC_FUNCTION_DECL daemon::s_daemon_start(unsigned long argc, char* argv[])
00449 {
00450
00451 assert(g_daemon);
00452
00453 g_daemon->daemon_start(argc, argv);
00454 }
00455
00456 void daemon::daemon_start(unsigned long argc, char* argv[])
00457 {
00458 #if OS_TYPE == OS_WIN32
00459 _pStatus.dwWin32ExitCode = ERROR_SUCCESS;
00460
00461 if (_isDaemon)
00462 {
00463
00464 _hStatus = ::RegisterServiceCtrlHandler(_daemonName, s_daemon_handler);
00465 if (!_hStatus)
00466 {
00467 log_message(true, "can not register service handlers for %s", (const char*)_daemonName);
00468 _pStatus.dwWin32ExitCode = ERROR_ACCESS_DENIED;
00469 set_state(SERVICE_STOPPED);
00470 return;
00471 }
00472 }
00473
00474
00475 set_state(SERVICE_START_PENDING);
00476 #endif
00477
00478
00479 _log_timer.activate(this, 0, 15000);
00480
00481
00482 if (on_startup(argc, argv))
00483 {
00484 log_message(false, "Service started");
00485
00486 #if OS_TYPE == OS_WIN32
00487 set_state(SERVICE_RUNNING);
00488 #endif
00489
00490 on_run();
00491
00492 #if OS_TYPE == OS_WIN32
00493 set_state(SERVICE_STOP_PENDING);
00494 #endif
00495 }
00496 else
00497 {
00498 log_message(true, "Can not start daemon");
00499 #if OS_TYPE == OS_WIN32
00500 _pStatus.dwWin32ExitCode = ERROR_ACCESS_DENIED;
00501 #endif
00502 }
00503
00504
00505 _log_timer.deactivate();
00506
00507 on_shutdown();
00508 }
00509
00511
00512
00513 void STATIC_FUNCTION_DECL daemon::s_daemon_handler(DAEMON_HANDLE_FUNCTION_ARG cmd)
00514 {
00515
00516 assert(g_daemon);
00517
00518 g_daemon->daemon_handler(cmd);
00519 }
00520
00521 void daemon::daemon_handler(DAEMON_HANDLE_FUNCTION_ARG cmd)
00522 {
00523 switch (cmd)
00524 {
00525 #if OS_TYPE == OS_WIN32
00526 case SERVICE_CONTROL_STOP:
00527 case SERVICE_CONTROL_SHUTDOWN:
00528 #else
00529 case SIGABRT:
00530 case SIGBUS:
00531 case SIGFPE:
00532 case SIGILL:
00533 case SIGSEGV:
00534 case SIGINT:
00535 case SIGTERM:
00536 case SIGQUIT:
00537 #endif
00538 log_message(false, "Stop signal: %d received", cmd);
00539
00540 on_stop();
00541 break;
00542 #if OS_TYPE == OS_WIN32
00543 case SERVICE_CONTROL_PAUSE:
00544 case SERVICE_CONTROL_CONTINUE:
00545 case SERVICE_CONTROL_INTERROGATE:
00546 #else
00547 case SIGALRM:
00548 case SIGCHLD:
00549 case SIGHUP:
00550 case SIGUSR1:
00551 case SIGUSR2:
00552 case SIGPIPE:
00553 case SIGTSTP:
00554 case SIGTTIN:
00555 case SIGTTOU:
00556 #endif
00557 log_message(false, "Handler signal: %d received", cmd);
00558
00559 v_on_handler(cmd);
00560 break;
00561 }
00562 }
00563
00565 bool daemon::on_startup(unsigned long argc, char* argv[])
00566 {
00567
00568 if (_sockStartup())
00569 return false;
00570
00571 string_t cfg_file, err;
00572
00573 if (v_find_cfg_file(cfg_file))
00574 {
00575
00576 xmlconfig settings(cfg_file, 0, 0);
00577 int severity = 0;
00578 if (settings.get(0, "severity", severity))
00579 {
00580 _severity = severity;
00581 }
00582 }
00583
00584
00585 if (!v_on_init(argc, argv, cfg_file, err))
00586 {
00587 log_message(true, "init failed: %s", (const char*)err);
00588 return false;
00589 }
00590
00591 return true;
00592 }
00593
00595 void daemon::on_run()
00596 {
00597
00598 _hShutdown.wait(INFINITE);
00599 }
00600
00601 void daemon::on_stop()
00602 {
00603
00604 _hShutdown.signal();
00605 }
00606
00607 void daemon::on_shutdown()
00608 {
00609
00610 v_on_uninit();
00611
00612 _sockCleanup();
00613
00614 log_message(false, "Service stopped");
00615
00616 #if OS_TYPE == OS_WIN32
00617 set_state(SERVICE_STOPPED);
00618 #endif
00619 }
00620
00622
00623 void
00624 daemon::notify(size_t ident,
00625 size_t interval,
00626 size_t multiplier
00627 )
00628 {
00629 string_t cfg_file;
00630
00631 if (v_find_cfg_file(cfg_file))
00632 {
00633
00634 xmlconfig settings(cfg_file, 0, 0);
00635 int severity = 0;
00636 if (settings.get(0, "severity", severity))
00637 {
00638 _severity = severity;
00639 }
00640 }
00641 }
00642
00643
00644 bool
00645 daemon::v_is_logging(size_t module, const char* file, size_t line, terimber_log_severity severity) const
00646 {
00647 return (severity & _severity) != 0;
00648 }
00649
00650
00651 void
00652 daemon::v_do_logging(size_t module, const char* file, size_t line, terimber_log_severity severity, const char* msg) const
00653 {
00654 log_message(severity == en_log_error, "file: %s, line: %d, msg: %s", file, line, msg);
00655 }
00656
00657 void
00658 daemon::log_message(bool err, const char* format, ...) const
00659 {
00660 if (!_severity)
00661 return;
00662
00663 char buffer[4096];
00664 va_list pArg;
00665
00666 va_start(pArg, format);
00667 #if OS_TYPE == OS_WIN32
00668 _vsnprintf(buffer, sizeof(buffer), format, pArg);
00669 #else
00670 vsnprintf(buffer, sizeof(buffer), format, pArg);
00671 #endif
00672
00673 va_end(pArg);
00674
00675 char dbuf[64];
00676 date now;
00677 now.get_date(dbuf);
00678
00679 if (!_isDaemon)
00680 {
00681
00682 char logName[1024];
00683 char* start = 0;
00684
00685 size_t len = sizeof(logName) - 5;
00686 find_full_path(logName, start, len);
00687 strcat(start, ".log");
00688
00689 FILE* f = fopen(start, "at+");
00690 if (f)
00691 {
00692 fprintf(f, err ? "%s: error: %s\n" : "%s: info: %s\n", dbuf, buffer);
00693 fclose(f);
00694 }
00695 }
00696 else
00697 {
00698 #if OS_TYPE == OS_WIN32
00699
00700 if (HANDLE hEventSource = RegisterEventSource(0, _daemonName))
00701 {
00702 char* arr[2] = {buffer, dbuf};
00703 ReportEvent(hEventSource, err ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, 0, 0, 0, 2, 0, (const char**)arr, 0);
00704 DeregisterEventSource(hEventSource);
00705 }
00706 #else
00707
00708 openlog((const char*)_daemonName, LOG_PID, LOG_USER);
00709 syslog(LOG_USER | (err ? LOG_ERR : LOG_INFO), err ? "%s: error: %s\n" : "%s: info: %s\n", dbuf, buffer);
00710 closelog();
00711 #endif
00712 }
00713 }
00714
00715
00716 bool
00717 daemon::v_find_cfg_file(string_t& cfg_file)
00718 {
00719 char path[2048];
00720 char* start = 0;
00721
00722 size_t len = sizeof(path) - 5;
00723 find_full_path(path, start, len);
00724
00725 strcat(start, ".cfg");
00726 cfg_file = start;
00727 format_logging(0, __FILE__, __LINE__, en_log_info, "config file detected at the location: %s", start);
00728 return true;
00729 }
00730
00731
00732 void
00733 daemon::find_full_path( char* path,
00734 char*& prefix,
00735 size_t& len
00736 )
00737 {
00738 assert(len > 5);
00739 #if OS_TYPE == OS_WIN32
00740
00741 static const char* unc_prefix = "\\\\?\\";
00742 ::GetModuleFileName(0, path, (DWORD)len);
00743 prefix = path + ((strstr(path, unc_prefix) == path) ? strlen(unc_prefix) : 0);
00744 #else
00745 ssize_t written = 0;
00746 if (-1 == (written = readlink("/proc/self/exe", path, len - 1)))
00747 return;
00748
00749 path[written] = 0;
00750 prefix = path;
00751 #endif
00752 char* posfix = 0;
00753 if ((posfix = strrchr(path, '.')) != 0
00754 && __max(strrchr(path, '/'), strrchr(path, '\\')) < posfix
00755 )
00756 {
00757 *posfix = 0;
00758 }
00759
00760 len = str_template::strlen(prefix);
00761 }
00762
00763
00764 END_TERIMBER_NAMESPACE