00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "CArchDaemonWindows.h"
00016 #include "CArch.h"
00017 #include "CArchMiscWindows.h"
00018 #include "XArchWindows.h"
00019 #include "stdvector.h"
00020
00021
00022
00023
00024
00025 CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL;
00026
00027 CArchDaemonWindows::CArchDaemonWindows()
00028 {
00029 m_quitMessage = RegisterWindowMessage("SynergyDaemonExit");
00030 }
00031
00032 CArchDaemonWindows::~CArchDaemonWindows()
00033 {
00034
00035 }
00036
00037 int
00038 CArchDaemonWindows::runDaemon(RunFunc runFunc)
00039 {
00040 assert(s_daemon != NULL);
00041
00042 return s_daemon->doRunDaemon(runFunc);
00043 }
00044
00045 void
00046 CArchDaemonWindows::daemonRunning(bool running)
00047 {
00048
00049
00050
00051
00052 if (s_daemon != NULL) {
00053 s_daemon->doDaemonRunning(running);
00054 }
00055 }
00056
00057 UINT
00058 CArchDaemonWindows::getDaemonQuitMessage()
00059 {
00060 if (s_daemon != NULL) {
00061 return s_daemon->doGetDaemonQuitMessage();
00062 }
00063 else {
00064 return 0;
00065 }
00066 }
00067
00068 void
00069 CArchDaemonWindows::daemonFailed(int result)
00070 {
00071
00072
00073
00074
00075 if (s_daemon != NULL) {
00076 throw XArchDaemonRunFailed(result);
00077 }
00078 }
00079
00080 void
00081 CArchDaemonWindows::installDaemon(const char* name,
00082 const char* description,
00083 const char* pathname,
00084 const char* commandLine,
00085 const char* dependencies,
00086 bool allUsers)
00087 {
00088
00089
00090 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
00091
00092 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
00093 open95ServicesKey() : openUserStartupKey();
00094 if (key == NULL) {
00095
00096 throw XArchDaemonInstallFailed(new XArchEvalWindows);
00097 }
00098
00099
00100 std::string value;
00101 value += "\"";
00102 value += pathname;
00103 value += "\" ";
00104 value += commandLine;
00105
00106
00107 CArchMiscWindows::setValue(key, name, value);
00108
00109
00110 CArchMiscWindows::closeKey(key);
00111 }
00112
00113
00114 else {
00115
00116 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
00117 if (mgr == NULL) {
00118
00119 throw XArchDaemonInstallFailed(new XArchEvalWindows);
00120 }
00121
00122
00123 SC_HANDLE service = CreateService(mgr,
00124 name,
00125 name,
00126 0,
00127 SERVICE_WIN32_OWN_PROCESS |
00128 SERVICE_INTERACTIVE_PROCESS,
00129 SERVICE_AUTO_START,
00130 SERVICE_ERROR_NORMAL,
00131 pathname,
00132 NULL,
00133 NULL,
00134 dependencies,
00135 NULL,
00136 NULL);
00137 if (service == NULL) {
00138
00139 DWORD err = GetLastError();
00140 if (err != ERROR_SERVICE_EXISTS) {
00141 CloseServiceHandle(mgr);
00142 throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
00143 }
00144 }
00145
00146
00147 CloseServiceHandle(service);
00148 CloseServiceHandle(mgr);
00149
00150
00151 HKEY key = openNTServicesKey();
00152 key = CArchMiscWindows::addKey(key, name);
00153 if (key == NULL) {
00154
00155 DWORD err = GetLastError();
00156 try {
00157 uninstallDaemon(name, allUsers);
00158 }
00159 catch (...) {
00160
00161 }
00162 throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
00163 }
00164
00165
00166 CArchMiscWindows::setValue(key, _T("Description"), description);
00167
00168
00169 key = CArchMiscWindows::addKey(key, _T("Parameters"));
00170 if (key == NULL) {
00171
00172 DWORD err = GetLastError();
00173 CArchMiscWindows::closeKey(key);
00174 try {
00175 uninstallDaemon(name, allUsers);
00176 }
00177 catch (...) {
00178
00179 }
00180 throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
00181 }
00182 CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
00183
00184
00185 CArchMiscWindows::closeKey(key);
00186 }
00187 }
00188
00189 void
00190 CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers)
00191 {
00192
00193
00194 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
00195
00196 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
00197 open95ServicesKey() : openUserStartupKey();
00198 if (key == NULL) {
00199
00200 throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows);
00201 }
00202
00203
00204 CArchMiscWindows::deleteValue(key, name);
00205
00206
00207 CArchMiscWindows::closeKey(key);
00208 }
00209
00210
00211 else {
00212
00213 HKEY key = openNTServicesKey();
00214 key = CArchMiscWindows::openKey(key, name);
00215 if (key != NULL) {
00216 CArchMiscWindows::deleteKey(key, _T("Parameters"));
00217 CArchMiscWindows::closeKey(key);
00218 }
00219
00220
00221 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
00222 if (mgr == NULL) {
00223
00224 throw XArchDaemonUninstallFailed(new XArchEvalWindows);
00225 }
00226
00227
00228 SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
00229 if (service == NULL) {
00230 DWORD err = GetLastError();
00231 CloseServiceHandle(mgr);
00232 if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
00233 throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
00234 }
00235 throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
00236 }
00237
00238
00239 SERVICE_STATUS status;
00240 ControlService(service, SERVICE_CONTROL_STOP, &status);
00241
00242
00243 const bool okay = (DeleteService(service) == 0);
00244 const DWORD err = GetLastError();
00245
00246
00247 CloseServiceHandle(service);
00248 CloseServiceHandle(mgr);
00249
00250
00251 if (!okay && isDaemonInstalled(name, allUsers)) {
00252 if (err == ERROR_IO_PENDING) {
00253
00254 return;
00255 }
00256 if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
00257 throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
00258 }
00259 throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
00260 }
00261 }
00262 }
00263
00264 int
00265 CArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
00266 {
00267 assert(name != NULL);
00268 assert(func != NULL);
00269
00270
00271 if (CArchMiscWindows::isWindows95Family()) {
00272 typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
00273
00274
00275
00276 HINSTANCE kernel = LoadLibrary("kernel32.dll");
00277 if (kernel == NULL) {
00278 throw XArchDaemonFailed(new XArchEvalWindows);
00279 }
00280 RegisterServiceProcessT RegisterServiceProcess =
00281 reinterpret_cast<RegisterServiceProcessT>(
00282 GetProcAddress(kernel,
00283 "RegisterServiceProcess"));
00284 if (RegisterServiceProcess == NULL) {
00285
00286 DWORD err = GetLastError();
00287 FreeLibrary(kernel);
00288 throw XArchDaemonFailed(new XArchEvalWindows(err));
00289 }
00290 if (RegisterServiceProcess(0, 1) == 0) {
00291
00292 DWORD err = GetLastError();
00293 FreeLibrary(kernel);
00294 throw XArchDaemonFailed(new XArchEvalWindows(err));
00295 }
00296 FreeLibrary(kernel);
00297
00298
00299 return func(1, &name);
00300 }
00301
00302
00303 else {
00304
00305 m_daemonFunc = func;
00306
00307
00308 SERVICE_TABLE_ENTRY entry[2];
00309 entry[0].lpServiceName = const_cast<char*>(name);
00310 entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry;
00311 entry[1].lpServiceName = NULL;
00312 entry[1].lpServiceProc = NULL;
00313
00314
00315
00316 s_daemon = this;
00317 if (StartServiceCtrlDispatcher(entry) == 0) {
00318
00319 s_daemon = NULL;
00320 throw XArchDaemonFailed(new XArchEvalWindows);
00321 }
00322
00323 s_daemon = NULL;
00324 return m_daemonResult;
00325 }
00326 }
00327
00328 bool
00329 CArchDaemonWindows::canInstallDaemon(const char* , bool allUsers)
00330 {
00331
00332
00333 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
00334
00335 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
00336 open95ServicesKey() : openUserStartupKey();
00337 CArchMiscWindows::closeKey(key);
00338 return (key != NULL);
00339 }
00340
00341
00342 else {
00343
00344 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
00345 if (mgr == NULL) {
00346 return false;
00347 }
00348 CloseServiceHandle(mgr);
00349
00350
00351 HKEY key = openNTServicesKey();
00352
00353
00354 CArchMiscWindows::closeKey(key);
00355
00356 return (key != NULL);
00357 }
00358 }
00359
00360 bool
00361 CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers)
00362 {
00363
00364
00365 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
00366
00367 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
00368 open95ServicesKey() : openUserStartupKey();
00369 if (key == NULL) {
00370 return false;
00371 }
00372
00373
00374 const bool installed = !CArchMiscWindows::readValueString(key,
00375 name).empty();
00376
00377
00378 CArchMiscWindows::closeKey(key);
00379
00380 return installed;
00381 }
00382
00383
00384 else {
00385
00386 HKEY key = openNTServicesKey();
00387 key = CArchMiscWindows::openKey(key, name);
00388 key = CArchMiscWindows::openKey(key, _T("Parameters"));
00389 if (key != NULL) {
00390 const bool installed = !CArchMiscWindows::readValueString(key,
00391 _T("CommandLine")).empty();
00392 CArchMiscWindows::closeKey(key);
00393 if (!installed) {
00394 return false;
00395 }
00396 }
00397
00398
00399 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
00400 if (mgr == NULL) {
00401 return false;
00402 }
00403
00404
00405 SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
00406
00407
00408 if (service != NULL) {
00409 CloseServiceHandle(service);
00410 }
00411 CloseServiceHandle(mgr);
00412
00413 return (service != NULL);
00414 }
00415 }
00416
00417 HKEY
00418 CArchDaemonWindows::openNTServicesKey()
00419 {
00420 static const char* s_keyNames[] = {
00421 _T("SYSTEM"),
00422 _T("CurrentControlSet"),
00423 _T("Services"),
00424 NULL
00425 };
00426
00427 return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
00428 }
00429
00430 HKEY
00431 CArchDaemonWindows::open95ServicesKey()
00432 {
00433 static const char* s_keyNames[] = {
00434 _T("Software"),
00435 _T("Microsoft"),
00436 _T("Windows"),
00437 _T("CurrentVersion"),
00438 _T("RunServices"),
00439 NULL
00440 };
00441
00442 return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
00443 }
00444
00445 HKEY
00446 CArchDaemonWindows::openUserStartupKey()
00447 {
00448 static const char* s_keyNames[] = {
00449 _T("Software"),
00450 _T("Microsoft"),
00451 _T("Windows"),
00452 _T("CurrentVersion"),
00453 _T("Run"),
00454 NULL
00455 };
00456
00457 return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames);
00458 }
00459
00460 bool
00461 CArchDaemonWindows::isRunState(DWORD state)
00462 {
00463 switch (state) {
00464 case SERVICE_START_PENDING:
00465 case SERVICE_CONTINUE_PENDING:
00466 case SERVICE_RUNNING:
00467 return true;
00468
00469 default:
00470 return false;
00471 }
00472 }
00473
00474 int
00475 CArchDaemonWindows::doRunDaemon(RunFunc run)
00476 {
00477
00478 assert(m_serviceMutex != NULL);
00479 assert(run != NULL);
00480
00481
00482 MSG dummy;
00483 PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
00484
00485 int result = 0;
00486 ARCH->lockMutex(m_serviceMutex);
00487 m_daemonThreadID = GetCurrentThreadId();
00488 while (m_serviceState != SERVICE_STOPPED) {
00489
00490 while (!isRunState(m_serviceState) &&
00491 m_serviceState != SERVICE_STOP_PENDING) {
00492 ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
00493 }
00494
00495
00496 if (m_serviceState != SERVICE_STOP_PENDING) {
00497 ARCH->unlockMutex(m_serviceMutex);
00498 try {
00499 result = run();
00500 }
00501 catch (...) {
00502 ARCH->lockMutex(m_serviceMutex);
00503 setStatusError(0);
00504 m_serviceState = SERVICE_STOPPED;
00505 setStatus(m_serviceState);
00506 ARCH->broadcastCondVar(m_serviceCondVar);
00507 ARCH->unlockMutex(m_serviceMutex);
00508 throw;
00509 }
00510 ARCH->lockMutex(m_serviceMutex);
00511 }
00512
00513
00514 if (m_serviceState == SERVICE_PAUSE_PENDING) {
00515 m_serviceState = SERVICE_PAUSED;
00516 }
00517 else {
00518 m_serviceState = SERVICE_STOPPED;
00519 }
00520 setStatus(m_serviceState);
00521 ARCH->broadcastCondVar(m_serviceCondVar);
00522 }
00523 ARCH->unlockMutex(m_serviceMutex);
00524 return result;
00525 }
00526
00527 void
00528 CArchDaemonWindows::doDaemonRunning(bool running)
00529 {
00530 ARCH->lockMutex(m_serviceMutex);
00531 if (running) {
00532 m_serviceState = SERVICE_RUNNING;
00533 setStatus(m_serviceState);
00534 ARCH->broadcastCondVar(m_serviceCondVar);
00535 }
00536 ARCH->unlockMutex(m_serviceMutex);
00537 }
00538
00539 UINT
00540 CArchDaemonWindows::doGetDaemonQuitMessage()
00541 {
00542 return m_quitMessage;
00543 }
00544
00545 void
00546 CArchDaemonWindows::setStatus(DWORD state)
00547 {
00548 setStatus(state, 0, 0);
00549 }
00550
00551 void
00552 CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
00553 {
00554 assert(s_daemon != NULL);
00555
00556 SERVICE_STATUS status;
00557 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
00558 SERVICE_INTERACTIVE_PROCESS;
00559 status.dwCurrentState = state;
00560 status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
00561 SERVICE_ACCEPT_PAUSE_CONTINUE |
00562 SERVICE_ACCEPT_SHUTDOWN;
00563 status.dwWin32ExitCode = NO_ERROR;
00564 status.dwServiceSpecificExitCode = 0;
00565 status.dwCheckPoint = step;
00566 status.dwWaitHint = waitHint;
00567 SetServiceStatus(s_daemon->m_statusHandle, &status);
00568 }
00569
00570 void
00571 CArchDaemonWindows::setStatusError(DWORD error)
00572 {
00573 assert(s_daemon != NULL);
00574
00575 SERVICE_STATUS status;
00576 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
00577 SERVICE_INTERACTIVE_PROCESS;
00578 status.dwCurrentState = SERVICE_STOPPED;
00579 status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
00580 SERVICE_ACCEPT_PAUSE_CONTINUE |
00581 SERVICE_ACCEPT_SHUTDOWN;
00582 status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
00583 status.dwServiceSpecificExitCode = error;
00584 status.dwCheckPoint = 0;
00585 status.dwWaitHint = 0;
00586 SetServiceStatus(s_daemon->m_statusHandle, &status);
00587 }
00588
00589 void
00590 CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
00591 {
00592 typedef std::vector<LPCTSTR> ArgList;
00593 typedef std::vector<std::string> Arguments;
00594 const char** argv = const_cast<const char**>(argvIn);
00595
00596
00597 m_serviceMutex = ARCH->newMutex();
00598 m_serviceCondVar = ARCH->newCondVar();
00599
00600
00601 m_statusHandle = RegisterServiceCtrlHandler(argv[0],
00602 &CArchDaemonWindows::serviceHandlerEntry);
00603 if (m_statusHandle == 0) {
00604
00605 m_daemonResult = -1;
00606 ARCH->closeCondVar(m_serviceCondVar);
00607 ARCH->closeMutex(m_serviceMutex);
00608 return;
00609 }
00610
00611
00612 m_serviceState = SERVICE_START_PENDING;
00613 setStatus(m_serviceState, 0, 10000);
00614
00615
00616
00617 Arguments args;
00618 ArgList myArgv;
00619 if (argc <= 1) {
00620
00621 std::string commandLine;
00622 HKEY key = openNTServicesKey();
00623 key = CArchMiscWindows::openKey(key, argvIn[0]);
00624 key = CArchMiscWindows::openKey(key, _T("Parameters"));
00625 if (key != NULL) {
00626 commandLine = CArchMiscWindows::readValueString(key,
00627 _T("CommandLine"));
00628 }
00629
00630
00631 if (!commandLine.empty()) {
00632
00633 std::string::size_type i = commandLine.find_first_not_of(" \t");
00634 while (i != std::string::npos && i != commandLine.size()) {
00635
00636 std::string::size_type e;
00637 if (commandLine[i] == '\"') {
00638
00639 ++i;
00640 e = commandLine.find("\"", i);
00641
00642
00643 if (e == std::string::npos ||
00644 (e + 1 != commandLine.size() &&
00645 commandLine[e + 1] != ' ' &&
00646 commandLine[e + 1] != '\t')) {
00647 args.clear();
00648 break;
00649 }
00650
00651
00652 args.push_back(commandLine.substr(i, e - i));
00653 i = e + 1;
00654 }
00655 else {
00656
00657 e = commandLine.find_first_of(" \t", i);
00658 if (e == std::string::npos) {
00659 e = commandLine.size();
00660 }
00661
00662
00663 args.push_back(commandLine.substr(i, e - i));
00664 i = e + 1;
00665 }
00666
00667
00668 i = commandLine.find_first_not_of(" \t", i);
00669 }
00670
00671
00672 myArgv.push_back(argv[0]);
00673
00674
00675 for (size_t i = 0; i < args.size(); ++i) {
00676 myArgv.push_back(args[i].c_str());
00677 }
00678
00679
00680 argc = myArgv.size();
00681 argv = &myArgv[0];
00682 }
00683 }
00684
00685 try {
00686
00687 m_daemonResult = m_daemonFunc(static_cast<int>(argc), argv);
00688 }
00689 catch (XArchDaemonRunFailed& e) {
00690 setStatusError(e.m_result);
00691 m_daemonResult = -1;
00692 }
00693 catch (...) {
00694 setStatusError(1);
00695 m_daemonResult = -1;
00696 }
00697
00698
00699 ARCH->closeCondVar(m_serviceCondVar);
00700 ARCH->closeMutex(m_serviceMutex);
00701 }
00702
00703 void WINAPI
00704 CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
00705 {
00706 s_daemon->serviceMain(argc, argv);
00707 }
00708
00709 void
00710 CArchDaemonWindows::serviceHandler(DWORD ctrl)
00711 {
00712 assert(m_serviceMutex != NULL);
00713 assert(m_serviceCondVar != NULL);
00714
00715 ARCH->lockMutex(m_serviceMutex);
00716
00717
00718 if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
00719 if (s_daemon != NULL) {
00720 setStatus(m_serviceState);
00721 }
00722 ARCH->unlockMutex(m_serviceMutex);
00723 return;
00724 }
00725
00726 switch (ctrl) {
00727 case SERVICE_CONTROL_PAUSE:
00728 m_serviceState = SERVICE_PAUSE_PENDING;
00729 setStatus(m_serviceState, 0, 5000);
00730 PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
00731 while (isRunState(m_serviceState)) {
00732 ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
00733 }
00734 break;
00735
00736 case SERVICE_CONTROL_CONTINUE:
00737
00738 m_serviceState = SERVICE_CONTINUE_PENDING;
00739 setStatus(m_serviceState, 0, 5000);
00740 ARCH->broadcastCondVar(m_serviceCondVar);
00741 break;
00742
00743 case SERVICE_CONTROL_STOP:
00744 case SERVICE_CONTROL_SHUTDOWN:
00745 m_serviceState = SERVICE_STOP_PENDING;
00746 setStatus(m_serviceState, 0, 5000);
00747 PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
00748 ARCH->broadcastCondVar(m_serviceCondVar);
00749 while (isRunState(m_serviceState)) {
00750 ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
00751 }
00752 break;
00753
00754 default:
00755
00756
00757
00758 case SERVICE_CONTROL_INTERROGATE:
00759 setStatus(m_serviceState);
00760 break;
00761 }
00762
00763 ARCH->unlockMutex(m_serviceMutex);
00764 }
00765
00766 void WINAPI
00767 CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
00768 {
00769 s_daemon->serviceHandler(ctrl);
00770 }