source: vendor/synergy/current/lib/arch/CArchDaemonWindows.cpp

Last change on this file was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

File size: 19.3 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2002 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CArchDaemonWindows.h"
16#include "CArch.h"
17#include "CArchMiscWindows.h"
18#include "XArchWindows.h"
19#include "stdvector.h"
20
21//
22// CArchDaemonWindows
23//
24
25CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL;
26
27CArchDaemonWindows::CArchDaemonWindows()
28{
29 m_quitMessage = RegisterWindowMessage("SynergyDaemonExit");
30}
31
32CArchDaemonWindows::~CArchDaemonWindows()
33{
34 // do nothing
35}
36
37int
38CArchDaemonWindows::runDaemon(RunFunc runFunc)
39{
40 assert(s_daemon != NULL);
41
42 return s_daemon->doRunDaemon(runFunc);
43}
44
45void
46CArchDaemonWindows::daemonRunning(bool running)
47{
48 // if s_daemon is NULL we assume we're running on the windows
49 // 95 family and we just ignore this call so the caller doesn't
50 // have to go through the trouble of not calling it on the
51 // windows 95 family.
52 if (s_daemon != NULL) {
53 s_daemon->doDaemonRunning(running);
54 }
55}
56
57UINT
58CArchDaemonWindows::getDaemonQuitMessage()
59{
60 if (s_daemon != NULL) {
61 return s_daemon->doGetDaemonQuitMessage();
62 }
63 else {
64 return 0;
65 }
66}
67
68void
69CArchDaemonWindows::daemonFailed(int result)
70{
71 // if s_daemon is NULL we assume we're running on the windows
72 // 95 family and we just ignore this call so the caller doesn't
73 // have to go through the trouble of not calling it on the
74 // windows 95 family.
75 if (s_daemon != NULL) {
76 throw XArchDaemonRunFailed(result);
77 }
78}
79
80void
81CArchDaemonWindows::installDaemon(const char* name,
82 const char* description,
83 const char* pathname,
84 const char* commandLine,
85 const char* dependencies,
86 bool allUsers)
87{
88 // if not for all users then use the user's autostart registry.
89 // key. if windows 95 family then use windows 95 services key.
90 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
91 // open registry
92 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
93 open95ServicesKey() : openUserStartupKey();
94 if (key == NULL) {
95 // can't open key
96 throw XArchDaemonInstallFailed(new XArchEvalWindows);
97 }
98
99 // construct entry
100 std::string value;
101 value += "\"";
102 value += pathname;
103 value += "\" ";
104 value += commandLine;
105
106 // install entry
107 CArchMiscWindows::setValue(key, name, value);
108
109 // clean up
110 CArchMiscWindows::closeKey(key);
111 }
112
113 // windows NT family services
114 else {
115 // open service manager
116 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
117 if (mgr == NULL) {
118 // can't open service manager
119 throw XArchDaemonInstallFailed(new XArchEvalWindows);
120 }
121
122 // create the service
123 SC_HANDLE service = CreateService(mgr,
124 name,
125 name,
126 0,
127 SERVICE_WIN32_OWN_PROCESS |
128 SERVICE_INTERACTIVE_PROCESS,
129 SERVICE_AUTO_START,
130 SERVICE_ERROR_NORMAL,
131 pathname,
132 NULL,
133 NULL,
134 dependencies,
135 NULL,
136 NULL);
137 if (service == NULL) {
138 // can't create service
139 DWORD err = GetLastError();
140 if (err != ERROR_SERVICE_EXISTS) {
141 CloseServiceHandle(mgr);
142 throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
143 }
144 }
145
146 // done with service and manager
147 CloseServiceHandle(service);
148 CloseServiceHandle(mgr);
149
150 // open the registry key for this service
151 HKEY key = openNTServicesKey();
152 key = CArchMiscWindows::addKey(key, name);
153 if (key == NULL) {
154 // can't open key
155 DWORD err = GetLastError();
156 try {
157 uninstallDaemon(name, allUsers);
158 }
159 catch (...) {
160 // ignore
161 }
162 throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
163 }
164
165 // set the description
166 CArchMiscWindows::setValue(key, _T("Description"), description);
167
168 // set command line
169 key = CArchMiscWindows::addKey(key, _T("Parameters"));
170 if (key == NULL) {
171 // can't open key
172 DWORD err = GetLastError();
173 CArchMiscWindows::closeKey(key);
174 try {
175 uninstallDaemon(name, allUsers);
176 }
177 catch (...) {
178 // ignore
179 }
180 throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
181 }
182 CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
183
184 // done with registry
185 CArchMiscWindows::closeKey(key);
186 }
187}
188
189void
190CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers)
191{
192 // if not for all users then use the user's autostart registry.
193 // key. if windows 95 family then use windows 95 services key.
194 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
195 // open registry
196 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
197 open95ServicesKey() : openUserStartupKey();
198 if (key == NULL) {
199 // can't open key. daemon is probably not installed.
200 throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows);
201 }
202
203 // remove entry
204 CArchMiscWindows::deleteValue(key, name);
205
206 // clean up
207 CArchMiscWindows::closeKey(key);
208 }
209
210 // windows NT family services
211 else {
212 // remove parameters for this service. ignore failures.
213 HKEY key = openNTServicesKey();
214 key = CArchMiscWindows::openKey(key, name);
215 if (key != NULL) {
216 CArchMiscWindows::deleteKey(key, _T("Parameters"));
217 CArchMiscWindows::closeKey(key);
218 }
219
220 // open service manager
221 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
222 if (mgr == NULL) {
223 // can't open service manager
224 throw XArchDaemonUninstallFailed(new XArchEvalWindows);
225 }
226
227 // open the service. oddly, you must open a service to delete it.
228 SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
229 if (service == NULL) {
230 DWORD err = GetLastError();
231 CloseServiceHandle(mgr);
232 if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
233 throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
234 }
235 throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
236 }
237
238 // stop the service. we don't care if we fail.
239 SERVICE_STATUS status;
240 ControlService(service, SERVICE_CONTROL_STOP, &status);
241
242 // delete the service
243 const bool okay = (DeleteService(service) == 0);
244 const DWORD err = GetLastError();
245
246 // clean up
247 CloseServiceHandle(service);
248 CloseServiceHandle(mgr);
249
250 // handle failure. ignore error if service isn't installed anymore.
251 if (!okay && isDaemonInstalled(name, allUsers)) {
252 if (err == ERROR_IO_PENDING) {
253 // this seems to be a spurious error
254 return;
255 }
256 if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
257 throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
258 }
259 throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
260 }
261 }
262}
263
264int
265CArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
266{
267 assert(name != NULL);
268 assert(func != NULL);
269
270 // windows 95 family services
271 if (CArchMiscWindows::isWindows95Family()) {
272 typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
273
274 // mark this process as a service so it's not killed when the
275 // user logs off.
276 HINSTANCE kernel = LoadLibrary("kernel32.dll");
277 if (kernel == NULL) {
278 throw XArchDaemonFailed(new XArchEvalWindows);
279 }
280 RegisterServiceProcessT RegisterServiceProcess =
281 reinterpret_cast<RegisterServiceProcessT>(
282 GetProcAddress(kernel,
283 "RegisterServiceProcess"));
284 if (RegisterServiceProcess == NULL) {
285 // missing RegisterServiceProcess function
286 DWORD err = GetLastError();
287 FreeLibrary(kernel);
288 throw XArchDaemonFailed(new XArchEvalWindows(err));
289 }
290 if (RegisterServiceProcess(0, 1) == 0) {
291 // RegisterServiceProcess failed
292 DWORD err = GetLastError();
293 FreeLibrary(kernel);
294 throw XArchDaemonFailed(new XArchEvalWindows(err));
295 }
296 FreeLibrary(kernel);
297
298 // now simply call the daemon function
299 return func(1, &name);
300 }
301
302 // windows NT family services
303 else {
304 // save daemon function
305 m_daemonFunc = func;
306
307 // construct the service entry
308 SERVICE_TABLE_ENTRY entry[2];
309 entry[0].lpServiceName = const_cast<char*>(name);
310 entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry;
311 entry[1].lpServiceName = NULL;
312 entry[1].lpServiceProc = NULL;
313
314 // hook us up to the service control manager. this won't return
315 // (if successful) until the processes have terminated.
316 s_daemon = this;
317 if (StartServiceCtrlDispatcher(entry) == 0) {
318 // StartServiceCtrlDispatcher failed
319 s_daemon = NULL;
320 throw XArchDaemonFailed(new XArchEvalWindows);
321 }
322
323 s_daemon = NULL;
324 return m_daemonResult;
325 }
326}
327
328bool
329CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers)
330{
331 // if not for all users then use the user's autostart registry.
332 // key. if windows 95 family then use windows 95 services key.
333 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
334 // check if we can open the registry key
335 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
336 open95ServicesKey() : openUserStartupKey();
337 CArchMiscWindows::closeKey(key);
338 return (key != NULL);
339 }
340
341 // windows NT family services
342 else {
343 // check if we can open service manager for write
344 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
345 if (mgr == NULL) {
346 return false;
347 }
348 CloseServiceHandle(mgr);
349
350 // check if we can open the registry key
351 HKEY key = openNTServicesKey();
352// key = CArchMiscWindows::addKey(key, name);
353// key = CArchMiscWindows::addKey(key, _T("Parameters"));
354 CArchMiscWindows::closeKey(key);
355
356 return (key != NULL);
357 }
358}
359
360bool
361CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers)
362{
363 // if not for all users then use the user's autostart registry.
364 // key. if windows 95 family then use windows 95 services key.
365 if (!allUsers || CArchMiscWindows::isWindows95Family()) {
366 // check if we can open the registry key
367 HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
368 open95ServicesKey() : openUserStartupKey();
369 if (key == NULL) {
370 return false;
371 }
372
373 // check for entry
374 const bool installed = !CArchMiscWindows::readValueString(key,
375 name).empty();
376
377 // clean up
378 CArchMiscWindows::closeKey(key);
379
380 return installed;
381 }
382
383 // windows NT family services
384 else {
385 // check parameters for this service
386 HKEY key = openNTServicesKey();
387 key = CArchMiscWindows::openKey(key, name);
388 key = CArchMiscWindows::openKey(key, _T("Parameters"));
389 if (key != NULL) {
390 const bool installed = !CArchMiscWindows::readValueString(key,
391 _T("CommandLine")).empty();
392 CArchMiscWindows::closeKey(key);
393 if (!installed) {
394 return false;
395 }
396 }
397
398 // open service manager
399 SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
400 if (mgr == NULL) {
401 return false;
402 }
403
404 // open the service
405 SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
406
407 // clean up
408 if (service != NULL) {
409 CloseServiceHandle(service);
410 }
411 CloseServiceHandle(mgr);
412
413 return (service != NULL);
414 }
415}
416
417HKEY
418CArchDaemonWindows::openNTServicesKey()
419{
420 static const char* s_keyNames[] = {
421 _T("SYSTEM"),
422 _T("CurrentControlSet"),
423 _T("Services"),
424 NULL
425 };
426
427 return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
428}
429
430HKEY
431CArchDaemonWindows::open95ServicesKey()
432{
433 static const char* s_keyNames[] = {
434 _T("Software"),
435 _T("Microsoft"),
436 _T("Windows"),
437 _T("CurrentVersion"),
438 _T("RunServices"),
439 NULL
440 };
441
442 return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
443}
444
445HKEY
446CArchDaemonWindows::openUserStartupKey()
447{
448 static const char* s_keyNames[] = {
449 _T("Software"),
450 _T("Microsoft"),
451 _T("Windows"),
452 _T("CurrentVersion"),
453 _T("Run"),
454 NULL
455 };
456
457 return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames);
458}
459
460bool
461CArchDaemonWindows::isRunState(DWORD state)
462{
463 switch (state) {
464 case SERVICE_START_PENDING:
465 case SERVICE_CONTINUE_PENDING:
466 case SERVICE_RUNNING:
467 return true;
468
469 default:
470 return false;
471 }
472}
473
474int
475CArchDaemonWindows::doRunDaemon(RunFunc run)
476{
477 // should only be called from DaemonFunc
478 assert(m_serviceMutex != NULL);
479 assert(run != NULL);
480
481 // create message queue for this thread
482 MSG dummy;
483 PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
484
485 int result = 0;
486 ARCH->lockMutex(m_serviceMutex);
487 m_daemonThreadID = GetCurrentThreadId();
488 while (m_serviceState != SERVICE_STOPPED) {
489 // wait until we're told to start
490 while (!isRunState(m_serviceState) &&
491 m_serviceState != SERVICE_STOP_PENDING) {
492 ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
493 }
494
495 // run unless told to stop
496 if (m_serviceState != SERVICE_STOP_PENDING) {
497 ARCH->unlockMutex(m_serviceMutex);
498 try {
499 result = run();
500 }
501 catch (...) {
502 ARCH->lockMutex(m_serviceMutex);
503 setStatusError(0);
504 m_serviceState = SERVICE_STOPPED;
505 setStatus(m_serviceState);
506 ARCH->broadcastCondVar(m_serviceCondVar);
507 ARCH->unlockMutex(m_serviceMutex);
508 throw;
509 }
510 ARCH->lockMutex(m_serviceMutex);
511 }
512
513 // notify of new state
514 if (m_serviceState == SERVICE_PAUSE_PENDING) {
515 m_serviceState = SERVICE_PAUSED;
516 }
517 else {
518 m_serviceState = SERVICE_STOPPED;
519 }
520 setStatus(m_serviceState);
521 ARCH->broadcastCondVar(m_serviceCondVar);
522 }
523 ARCH->unlockMutex(m_serviceMutex);
524 return result;
525}
526
527void
528CArchDaemonWindows::doDaemonRunning(bool running)
529{
530 ARCH->lockMutex(m_serviceMutex);
531 if (running) {
532 m_serviceState = SERVICE_RUNNING;
533 setStatus(m_serviceState);
534 ARCH->broadcastCondVar(m_serviceCondVar);
535 }
536 ARCH->unlockMutex(m_serviceMutex);
537}
538
539UINT
540CArchDaemonWindows::doGetDaemonQuitMessage()
541{
542 return m_quitMessage;
543}
544
545void
546CArchDaemonWindows::setStatus(DWORD state)
547{
548 setStatus(state, 0, 0);
549}
550
551void
552CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
553{
554 assert(s_daemon != NULL);
555
556 SERVICE_STATUS status;
557 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
558 SERVICE_INTERACTIVE_PROCESS;
559 status.dwCurrentState = state;
560 status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
561 SERVICE_ACCEPT_PAUSE_CONTINUE |
562 SERVICE_ACCEPT_SHUTDOWN;
563 status.dwWin32ExitCode = NO_ERROR;
564 status.dwServiceSpecificExitCode = 0;
565 status.dwCheckPoint = step;
566 status.dwWaitHint = waitHint;
567 SetServiceStatus(s_daemon->m_statusHandle, &status);
568}
569
570void
571CArchDaemonWindows::setStatusError(DWORD error)
572{
573 assert(s_daemon != NULL);
574
575 SERVICE_STATUS status;
576 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
577 SERVICE_INTERACTIVE_PROCESS;
578 status.dwCurrentState = SERVICE_STOPPED;
579 status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
580 SERVICE_ACCEPT_PAUSE_CONTINUE |
581 SERVICE_ACCEPT_SHUTDOWN;
582 status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
583 status.dwServiceSpecificExitCode = error;
584 status.dwCheckPoint = 0;
585 status.dwWaitHint = 0;
586 SetServiceStatus(s_daemon->m_statusHandle, &status);
587}
588
589void
590CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
591{
592 typedef std::vector<LPCTSTR> ArgList;
593 typedef std::vector<std::string> Arguments;
594 const char** argv = const_cast<const char**>(argvIn);
595
596 // create synchronization objects
597 m_serviceMutex = ARCH->newMutex();
598 m_serviceCondVar = ARCH->newCondVar();
599
600 // register our service handler function
601 m_statusHandle = RegisterServiceCtrlHandler(argv[0],
602 &CArchDaemonWindows::serviceHandlerEntry);
603 if (m_statusHandle == 0) {
604 // cannot start as service
605 m_daemonResult = -1;
606 ARCH->closeCondVar(m_serviceCondVar);
607 ARCH->closeMutex(m_serviceMutex);
608 return;
609 }
610
611 // tell service control manager that we're starting
612 m_serviceState = SERVICE_START_PENDING;
613 setStatus(m_serviceState, 0, 10000);
614
615 // if no arguments supplied then try getting them from the registry.
616 // the first argument doesn't count because it's the service name.
617 Arguments args;
618 ArgList myArgv;
619 if (argc <= 1) {
620 // read command line
621 std::string commandLine;
622 HKEY key = openNTServicesKey();
623 key = CArchMiscWindows::openKey(key, argvIn[0]);
624 key = CArchMiscWindows::openKey(key, _T("Parameters"));
625 if (key != NULL) {
626 commandLine = CArchMiscWindows::readValueString(key,
627 _T("CommandLine"));
628 }
629
630 // if the command line isn't empty then parse and use it
631 if (!commandLine.empty()) {
632 // parse, honoring double quoted substrings
633 std::string::size_type i = commandLine.find_first_not_of(" \t");
634 while (i != std::string::npos && i != commandLine.size()) {
635 // find end of string
636 std::string::size_type e;
637 if (commandLine[i] == '\"') {
638 // quoted. find closing quote.
639 ++i;
640 e = commandLine.find("\"", i);
641
642 // whitespace must follow closing quote
643 if (e == std::string::npos ||
644 (e + 1 != commandLine.size() &&
645 commandLine[e + 1] != ' ' &&
646 commandLine[e + 1] != '\t')) {
647 args.clear();
648 break;
649 }
650
651 // extract
652 args.push_back(commandLine.substr(i, e - i));
653 i = e + 1;
654 }
655 else {
656 // unquoted. find next whitespace.
657 e = commandLine.find_first_of(" \t", i);
658 if (e == std::string::npos) {
659 e = commandLine.size();
660 }
661
662 // extract
663 args.push_back(commandLine.substr(i, e - i));
664 i = e + 1;
665 }
666
667 // next argument
668 i = commandLine.find_first_not_of(" \t", i);
669 }
670
671 // service name goes first
672 myArgv.push_back(argv[0]);
673
674 // get pointers
675 for (size_t i = 0; i < args.size(); ++i) {
676 myArgv.push_back(args[i].c_str());
677 }
678
679 // adjust argc/argv
680 argc = myArgv.size();
681 argv = &myArgv[0];
682 }
683 }
684
685 try {
686 // invoke daemon function
687 m_daemonResult = m_daemonFunc(static_cast<int>(argc), argv);
688 }
689 catch (XArchDaemonRunFailed& e) {
690 setStatusError(e.m_result);
691 m_daemonResult = -1;
692 }
693 catch (...) {
694 setStatusError(1);
695 m_daemonResult = -1;
696 }
697
698 // clean up
699 ARCH->closeCondVar(m_serviceCondVar);
700 ARCH->closeMutex(m_serviceMutex);
701}
702
703void WINAPI
704CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
705{
706 s_daemon->serviceMain(argc, argv);
707}
708
709void
710CArchDaemonWindows::serviceHandler(DWORD ctrl)
711{
712 assert(m_serviceMutex != NULL);
713 assert(m_serviceCondVar != NULL);
714
715 ARCH->lockMutex(m_serviceMutex);
716
717 // ignore request if service is already stopped
718 if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
719 if (s_daemon != NULL) {
720 setStatus(m_serviceState);
721 }
722 ARCH->unlockMutex(m_serviceMutex);
723 return;
724 }
725
726 switch (ctrl) {
727 case SERVICE_CONTROL_PAUSE:
728 m_serviceState = SERVICE_PAUSE_PENDING;
729 setStatus(m_serviceState, 0, 5000);
730 PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
731 while (isRunState(m_serviceState)) {
732 ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
733 }
734 break;
735
736 case SERVICE_CONTROL_CONTINUE:
737 // FIXME -- maybe should flush quit messages from queue
738 m_serviceState = SERVICE_CONTINUE_PENDING;
739 setStatus(m_serviceState, 0, 5000);
740 ARCH->broadcastCondVar(m_serviceCondVar);
741 break;
742
743 case SERVICE_CONTROL_STOP:
744 case SERVICE_CONTROL_SHUTDOWN:
745 m_serviceState = SERVICE_STOP_PENDING;
746 setStatus(m_serviceState, 0, 5000);
747 PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
748 ARCH->broadcastCondVar(m_serviceCondVar);
749 while (isRunState(m_serviceState)) {
750 ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
751 }
752 break;
753
754 default:
755 // unknown service command
756 // fall through
757
758 case SERVICE_CONTROL_INTERROGATE:
759 setStatus(m_serviceState);
760 break;
761 }
762
763 ARCH->unlockMutex(m_serviceMutex);
764}
765
766void WINAPI
767CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
768{
769 s_daemon->serviceHandler(ctrl);
770}
Note: See TracBrowser for help on using the repository browser.