]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_win32/daemon_win32.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / os_win32 / daemon_win32.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * os_win32/daemon_win32.cpp
832b75ed 3 *
a86ec89e 4 * Home page of code is: http://www.smartmontools.org
832b75ed 5 *
ff28b140 6 * Copyright (C) 2004-18 Christian Franke
832b75ed 7 *
ff28b140 8 * SPDX-License-Identifier: GPL-2.0-or-later
832b75ed
GG
9 */
10
ee38a438 11#define WINVER 0x0600
cfbba5b9
GI
12#define _WIN32_WINNT WINVER
13
ee38a438
GI
14#include "daemon_win32.h"
15
ff28b140 16const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
ee38a438
GI
17 DAEMON_WIN32_H_CVSID;
18
832b75ed
GG
19#include <stdio.h>
20#include <stdlib.h>
21#include <signal.h>
22#include <io.h>
23
24#define WIN32_LEAN_AND_MEAN
832b75ed
GG
25#include <windows.h>
26#ifdef _DEBUG
27#include <crtdbg.h>
28#endif
29
832b75ed
GG
30
31/////////////////////////////////////////////////////////////////////////////
32
832b75ed
GG
33// Prevent spawning of child process if debugging
34#ifdef _DEBUG
35#define debugging() IsDebuggerPresent()
36#else
37#define debugging() FALSE
38#endif
39
40
41#define EVT_NAME_LEN 260
42
43// Internal events (must be > SIGUSRn)
44#define EVT_RUNNING 100 // Exists when running, signaled on creation
45#define EVT_DETACHED 101 // Signaled when child detaches from console
46#define EVT_RESTART 102 // Signaled when child should restart
47
48static void make_name(char * name, int sig)
49{
ee38a438
GI
50 int i;
51 if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10))
52 strcpy(name, "DaemonEvent");
53 for (i = 0; name[i]; i++) {
54 char c = name[i];
55 if (!( ('0' <= c && c <= '9')
56 || ('A' <= c && c <= 'Z')
57 || ('a' <= c && c <= 'z')))
58 name[i] = '_';
59 }
60 sprintf(name+strlen(name), "-%d", sig);
832b75ed
GG
61}
62
63
64static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
65{
ee38a438
GI
66 char name[EVT_NAME_LEN];
67 HANDLE h;
68 if (sig >= 0)
69 make_name(name, sig);
70 else
71 name[0] = 0;
72 if (exists)
73 *exists = FALSE;
74 if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) {
75 if (errmsg)
76 fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError());
77 return 0;
78 }
79
80 if (GetLastError() == ERROR_ALREADY_EXISTS) {
81 if (!exists) {
82 if (errmsg)
83 fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name);
84 CloseHandle(h);
85 return 0;
86 }
87 *exists = TRUE;
88 }
89 return h;
832b75ed
GG
90}
91
92
93static HANDLE open_event(int sig)
94{
ee38a438
GI
95 char name[EVT_NAME_LEN];
96 make_name(name, sig);
97 return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
832b75ed
GG
98}
99
100
101static int event_exists(int sig)
102{
ee38a438
GI
103 char name[EVT_NAME_LEN];
104 HANDLE h;
105 make_name(name, sig);
106 if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name)))
107 return 0;
108 CloseHandle(h);
109 return 1;
832b75ed
GG
110}
111
112
113static int sig_event(int sig)
114{
ee38a438
GI
115 char name[EVT_NAME_LEN];
116 HANDLE h;
117 make_name(name, sig);
118 if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) {
119 make_name(name, EVT_RUNNING);
120 if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name)))
121 return -1;
122 CloseHandle(h);
123 return 0;
124 }
125 SetEvent(h);
126 CloseHandle(h);
127 return 1;
832b75ed
GG
128}
129
130
131static void daemon_help(FILE * f, const char * ident, const char * message)
132{
ee38a438
GI
133 fprintf(f,
134 "%s: %s.\n"
135 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
136 ident, message, ident);
137 fflush(f);
832b75ed
GG
138}
139
140
141/////////////////////////////////////////////////////////////////////////////
142// Parent Process
143
144
145static BOOL WINAPI parent_console_handler(DWORD event)
146{
ee38a438
GI
147 switch (event) {
148 case CTRL_C_EVENT:
149 case CTRL_BREAK_EVENT:
150 return TRUE; // Ignore
151 }
152 return FALSE; // continue with next handler ...
832b75ed
GG
153}
154
155
156static int parent_main(HANDLE rev)
157{
ee38a438
GI
158 HANDLE dev;
159 HANDLE ht[2];
160 char * cmdline;
161 STARTUPINFO si;
162 PROCESS_INFORMATION pi;
163 DWORD rc, exitcode;
164
165 // Ignore ^C, ^BREAK in parent
166 SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/);
167
168 // Create event used by child to signal daemon_detach()
169 if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) {
170 CloseHandle(rev);
171 return 101;
172 }
173
174 // Restart process with same args
175 cmdline = GetCommandLineA();
176 memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
177
178 if (!CreateProcessA(
179 NULL, cmdline,
180 NULL, NULL, TRUE/*inherit*/,
181 0, NULL, NULL, &si, &pi)) {
182 fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
183 CloseHandle(rev); CloseHandle(dev);
184 return 101;
185 }
186 CloseHandle(pi.hThread);
187
188 // Wait for daemon_detach() or exit()
189 ht[0] = dev; ht[1] = pi.hProcess;
190 rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE);
191 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) {
192 fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc);
193 TerminateProcess(pi.hProcess, 200);
194 }
195 CloseHandle(rev); CloseHandle(dev);
196
197 // Get exit code
198 if (!GetExitCodeProcess(pi.hProcess, &exitcode))
199 exitcode = 201;
200 else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK
201 exitcode = 0;
202
203 CloseHandle(pi.hProcess);
204 return exitcode;
832b75ed
GG
205}
206
207
208/////////////////////////////////////////////////////////////////////////////
209// Child Process
210
211
212static int svc_mode; // Running as service?
213static int svc_paused; // Service paused?
214
215static void service_report_status(int state, int waithint);
216
217
218// Tables of signal handler and corresponding events
219typedef void (*sigfunc_t)(int);
220
221#define MAX_SIG_HANDLERS 8
222
223static int num_sig_handlers = 0;
224static sigfunc_t sig_handlers[MAX_SIG_HANDLERS];
225static int sig_numbers[MAX_SIG_HANDLERS];
226static HANDLE sig_events[MAX_SIG_HANDLERS];
227
228static HANDLE sighup_handle, sigint_handle, sigbreak_handle;
229static HANDLE sigterm_handle, sigusr1_handle;
230
231static HANDLE running_event;
232
233static int reopen_stdin, reopen_stdout, reopen_stderr;
234
235
236// Handler for windows console events
237
238static BOOL WINAPI child_console_handler(DWORD event)
239{
ee38a438
GI
240 // Caution: runs in a new thread
241 // TODO: Guard with a mutex
242 HANDLE h = 0;
243 switch (event) {
244 case CTRL_C_EVENT: // <CONTROL-C> (SIGINT)
245 h = sigint_handle; break;
246 case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
247 case CTRL_CLOSE_EVENT: // User closed console or abort via task manager
248 h = sigbreak_handle; break;
249 case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM)
250 case CTRL_SHUTDOWN_EVENT:
251 h = sigterm_handle; break;
252 }
253 if (!h)
254 return FALSE; // continue with next handler
255 // Signal event
256 if (!SetEvent(h))
257 return FALSE;
258 return TRUE;
832b75ed
GG
259}
260
261
262static void child_exit(void)
263{
ee38a438
GI
264 int i;
265 char * cmdline;
266 HANDLE rst;
267 STARTUPINFO si;
268 PROCESS_INFORMATION pi;
269
270 for (i = 0; i < num_sig_handlers; i++)
271 CloseHandle(sig_events[i]);
272 num_sig_handlers = 0;
273 CloseHandle(running_event); running_event = 0;
274
275 // Restart?
276 if (!(rst = open_event(EVT_RESTART)))
277 return; // No => normal exit
278
279 // Yes => Signal exit and restart process
280 Sleep(500);
281 SetEvent(rst);
282 CloseHandle(rst);
283 Sleep(500);
284
285 cmdline = GetCommandLineA();
286 memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
287 si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
288
289 if (!CreateProcessA(
290 NULL, cmdline,
291 NULL, NULL, TRUE/*inherit*/,
292 0, NULL, NULL, &si, &pi)) {
293 fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
294 }
295 CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
832b75ed
GG
296}
297
298static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
299{
ee38a438
GI
300 // Keep EVT_RUNNING open until exit
301 running_event = hev;
832b75ed 302
ee38a438
GI
303 // Install console handler
304 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
832b75ed 305
ee38a438
GI
306 // Install restart handler
307 atexit(child_exit);
832b75ed 308
ee38a438
GI
309 // Continue in main_func() to do the real work
310 return main_func(argc, argv);
832b75ed
GG
311}
312
313
314// Simulate signal()
315
316sigfunc_t daemon_signal(int sig, sigfunc_t func)
317{
ee38a438
GI
318 int i;
319 HANDLE h;
320 if (func == SIG_DFL || func == SIG_IGN)
321 return func; // TODO
322 for (i = 0; i < num_sig_handlers; i++) {
323 if (sig_numbers[i] == sig) {
324 sigfunc_t old = sig_handlers[i];
325 sig_handlers[i] = func;
326 return old;
327 }
328 }
329 if (num_sig_handlers >= MAX_SIG_HANDLERS)
330 return SIG_ERR;
331 if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL)))
332 return SIG_ERR;
333 sig_events[num_sig_handlers] = h;
334 sig_numbers[num_sig_handlers] = sig;
335 sig_handlers[num_sig_handlers] = func;
336 switch (sig) {
337 case SIGHUP: sighup_handle = h; break;
338 case SIGINT: sigint_handle = h; break;
339 case SIGTERM: sigterm_handle = h; break;
340 case SIGBREAK: sigbreak_handle = h; break;
341 case SIGUSR1: sigusr1_handle = h; break;
342 }
343 num_sig_handlers++;
344 return SIG_DFL;
832b75ed
GG
345}
346
347
348// strsignal()
349
350const char * daemon_strsignal(int sig)
351{
ee38a438
GI
352 switch (sig) {
353 case SIGHUP: return "SIGHUP";
354 case SIGINT: return "SIGINT";
355 case SIGTERM: return "SIGTERM";
356 case SIGBREAK:return "SIGBREAK";
357 case SIGUSR1: return "SIGUSR1";
358 case SIGUSR2: return "SIGUSR2";
359 default: return "*UNKNOWN*";
360 }
832b75ed
GG
361}
362
363
364// Simulate sleep()
365
366void daemon_sleep(int seconds)
367{
ee38a438
GI
368 do {
369 if (num_sig_handlers <= 0) {
370 Sleep(seconds*1000L);
371 }
372 else {
373 // Wait for any signal or timeout
374 DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events,
375 FALSE/*OR*/, seconds*1000L);
376 if (rc != WAIT_TIMEOUT) {
377 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) {
378 fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc);
379 Sleep(seconds*1000L);
380 return;
381 }
382 // Call Handler
383 sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]);
384 break;
385 }
386 }
387 } while (svc_paused);
832b75ed
GG
388}
389
390
391// Disable/Enable console
392
393void daemon_disable_console()
394{
ee38a438
GI
395 SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
396 reopen_stdin = reopen_stdout = reopen_stderr = 0;
397 if (isatty(fileno(stdin))) {
398 fclose(stdin); reopen_stdin = 1;
399 }
400 if (isatty(fileno(stdout))) {
401 fclose(stdout); reopen_stdout = 1;
402 }
403 if (isatty(fileno(stderr))) {
404 fclose(stderr); reopen_stderr = 1;
405 }
406 FreeConsole();
407 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
832b75ed
GG
408}
409
410int daemon_enable_console(const char * title)
411{
ee38a438
GI
412 BOOL ok;
413 SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
414 ok = AllocConsole();
415 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
416 if (!ok)
417 return -1;
418 if (title)
419 SetConsoleTitleA(title);
420 if (reopen_stdin)
421 freopen("conin$", "r", stdin);
422 if (reopen_stdout)
423 freopen("conout$", "w", stdout);
424 if (reopen_stderr)
425 freopen("conout$", "w", stderr);
426 reopen_stdin = reopen_stdout = reopen_stderr = 0;
427 return 0;
832b75ed
GG
428}
429
430
431// Detach daemon from console & parent
432
433int daemon_detach(const char * ident)
434{
ee38a438
GI
435 if (!svc_mode) {
436 if (ident) {
437 // Print help
438 FILE * f = ( isatty(fileno(stdout)) ? stdout
439 : isatty(fileno(stderr)) ? stderr : NULL);
440 if (f)
441 daemon_help(f, ident, "now detaches from console into background mode");
442 }
443 // Signal detach to parent
444 if (sig_event(EVT_DETACHED) != 1) {
445 if (!debugging())
446 return -1;
447 }
448 daemon_disable_console();
449 }
450 else {
451 // Signal end of initialization to service control manager
452 service_report_status(SERVICE_RUNNING, 0);
453 reopen_stdin = reopen_stdout = reopen_stderr = 1;
454 }
455
456 return 0;
832b75ed
GG
457}
458
459
832b75ed
GG
460/////////////////////////////////////////////////////////////////////////////
461// Initd Functions
462
463static int wait_signaled(HANDLE h, int seconds)
464{
ee38a438
GI
465 int i;
466 for (i = 0; ; ) {
467 if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0)
468 return 0;
469 if (++i >= seconds)
470 return -1;
471 fputchar('.'); fflush(stdout);
472 }
832b75ed
GG
473}
474
475
476static int wait_evt_running(int seconds, int exists)
477{
ee38a438
GI
478 int i;
479 if (event_exists(EVT_RUNNING) == exists)
480 return 0;
481 for (i = 0; ; ) {
482 Sleep(1000);
483 if (event_exists(EVT_RUNNING) == exists)
484 return 0;
485 if (++i >= seconds)
486 return -1;
487 fputchar('.'); fflush(stdout);
488 }
832b75ed
GG
489}
490
491
492static int is_initd_command(char * s)
493{
ee38a438
GI
494 if (!strcmp(s, "status"))
495 return EVT_RUNNING;
496 if (!strcmp(s, "stop"))
497 return SIGTERM;
498 if (!strcmp(s, "reload"))
499 return SIGHUP;
500 if (!strcmp(s, "sigusr1"))
501 return SIGUSR1;
502 if (!strcmp(s, "sigusr2"))
503 return SIGUSR2;
504 if (!strcmp(s, "restart"))
505 return EVT_RESTART;
506 return -1;
832b75ed
GG
507}
508
509
510static int initd_main(const char * ident, int argc, char **argv)
511{
ee38a438
GI
512 int rc;
513 if (argc < 2)
514 return -1;
515 if ((rc = is_initd_command(argv[1])) < 0)
516 return -1;
517 if (argc != 2) {
518 printf("%s: no arguments allowed for command %s\n", ident, argv[1]);
519 return 1;
520 }
521
522 switch (rc) {
523 default:
524 case EVT_RUNNING:
525 printf("Checking for %s:", ident); fflush(stdout);
526 rc = event_exists(EVT_RUNNING);
527 puts(rc ? " running" : " not running");
528 return (rc ? 0 : 1);
529
530 case SIGTERM:
531 printf("Stopping %s:", ident); fflush(stdout);
532 rc = sig_event(SIGTERM);
533 if (rc <= 0) {
534 puts(rc < 0 ? " not running" : " error");
535 return (rc < 0 ? 0 : 1);
536 }
537 rc = wait_evt_running(10, 0);
538 puts(!rc ? " done" : " timeout");
539 return (!rc ? 0 : 1);
540
541 case SIGHUP:
542 printf("Reloading %s:", ident); fflush(stdout);
543 rc = sig_event(SIGHUP);
544 puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
545 return (rc > 0 ? 0 : 1);
546
547 case SIGUSR1:
548 case SIGUSR2:
549 printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout);
550 rc = sig_event(rc);
551 puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
552 return (rc > 0 ? 0 : 1);
553
554 case EVT_RESTART:
555 {
556 HANDLE rst;
557 printf("Stopping %s:", ident); fflush(stdout);
558 if (event_exists(EVT_DETACHED)) {
559 puts(" not detached, cannot restart");
560 return 1;
561 }
562 if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) {
563 puts(" error");
564 return 1;
565 }
566 rc = sig_event(SIGTERM);
567 if (rc <= 0) {
568 puts(rc < 0 ? " not running" : " error");
569 CloseHandle(rst);
570 return 1;
571 }
572 rc = wait_signaled(rst, 10);
573 CloseHandle(rst);
574 if (rc) {
575 puts(" timeout");
576 return 1;
577 }
578 puts(" done");
579 Sleep(100);
580
581 printf("Starting %s:", ident); fflush(stdout);
582 rc = wait_evt_running(10, 1);
583 puts(!rc ? " done" : " error");
584 return (!rc ? 0 : 1);
585 }
586 }
832b75ed
GG
587}
588
589
590/////////////////////////////////////////////////////////////////////////////
591// Windows Service Functions
592
593int daemon_winsvc_exitcode; // Set by app to exit(code)
594
595static SERVICE_STATUS_HANDLE svc_handle;
596static SERVICE_STATUS svc_status;
597
598
599// Report status to SCM
600
601static void service_report_status(int state, int seconds)
602{
ee38a438
GI
603 // TODO: Avoid race
604 static DWORD checkpoint = 1;
605 svc_status.dwCurrentState = state;
606 svc_status.dwWaitHint = seconds*1000;
607 switch (state) {
608 default:
609 svc_status.dwCheckPoint = checkpoint++;
610 break;
611 case SERVICE_RUNNING:
612 case SERVICE_STOPPED:
613 svc_status.dwCheckPoint = 0;
614 }
615 switch (state) {
616 case SERVICE_START_PENDING:
617 case SERVICE_STOP_PENDING:
618 svc_status.dwControlsAccepted = 0;
619 break;
620 default:
621 svc_status.dwControlsAccepted =
622 SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
623 SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE;
624 break;
625 }
626 SetServiceStatus(svc_handle, &svc_status);
832b75ed
GG
627}
628
629
630// Control the service, called by SCM
631
632static void WINAPI service_control(DWORD ctrlcode)
633{
ee38a438
GI
634 switch (ctrlcode) {
635 case SERVICE_CONTROL_STOP:
636 case SERVICE_CONTROL_SHUTDOWN:
637 service_report_status(SERVICE_STOP_PENDING, 30);
638 svc_paused = 0;
639 SetEvent(sigterm_handle);
640 break;
641 case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP
642 service_report_status(svc_status.dwCurrentState, 0);
643 svc_paused = 0;
644 SetEvent(sighup_handle); // reload
645 break;
646 case SERVICE_CONTROL_PAUSE:
647 service_report_status(SERVICE_PAUSED, 0);
648 svc_paused = 1;
649 break;
650 case SERVICE_CONTROL_CONTINUE:
651 service_report_status(SERVICE_RUNNING, 0);
652 {
653 int was_paused = svc_paused;
654 svc_paused = 0;
655 SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck
656 }
657 break;
658 case SERVICE_CONTROL_INTERROGATE:
659 default: // unknown
660 service_report_status(svc_status.dwCurrentState, 0);
661 break;
662 }
832b75ed
GG
663}
664
665
666// Exit handler for service
667
668static void service_exit(void)
669{
ee38a438
GI
670 // Close signal events
671 int i;
672 for (i = 0; i < num_sig_handlers; i++)
673 CloseHandle(sig_events[i]);
674 num_sig_handlers = 0;
675
676 // Set exitcode
677 if (daemon_winsvc_exitcode) {
678 svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
679 svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode;
680 }
681 // Report stopped
682 service_report_status(SERVICE_STOPPED, 0);
832b75ed
GG
683}
684
685
686// Variables for passing main(argc, argv) from daemon_main to service_main()
687static int (*svc_main_func)(int, char **);
688static int svc_main_argc;
689static char ** svc_main_argv;
690
691// Main function for service, called by service dispatcher
692
ee38a438 693static void WINAPI service_main(DWORD /*argc*/, LPSTR * argv)
832b75ed 694{
ee38a438
GI
695 char path[MAX_PATH], *p;
696
697 // Register control handler
698 svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
832b75ed 699
ee38a438
GI
700 // Init service status
701 svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
702 service_report_status(SERVICE_START_PENDING, 10);
832b75ed 703
ee38a438
GI
704 // Service started in \windows\system32, change to .exe directory
705 if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) {
706 *p = 0; SetCurrentDirectoryA(path);
707 }
832b75ed 708
ee38a438
GI
709 // Install exit handler
710 atexit(service_exit);
832b75ed 711
ee38a438
GI
712 // Do the real work, service status later updated by daemon_detach()
713 daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv);
832b75ed 714
ee38a438
GI
715 exit(daemon_winsvc_exitcode);
716 // ... continued in service_exit()
832b75ed
GG
717}
718
719
720/////////////////////////////////////////////////////////////////////////////
721// Windows Service Admin Functions
722
832b75ed 723
ee38a438
GI
724// Make registry key name for event message file
725static bool make_evtkey(char * buf, unsigned size, const char * ident)
726{
727 static const char prefix[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\";
728 const unsigned pfxlen = sizeof(prefix)-1;
729 unsigned idlen = strlen(ident);
730 if (pfxlen + idlen >= size) {
731 printf(" Buffer overflow\n");
732 return false;
733 }
734 memcpy(buf, prefix, pfxlen);
735 memcpy(buf+pfxlen, ident, idlen+1);
736 return true;
737}
738
739// Install this exe as event message file
740static void inst_evtmsg(const char * ident)
741{
742 printf("Installing event message file for %s:", ident); fflush(stdout);
743
744 char mypath[MAX_PATH];
745 if (!GetModuleFileNameA((HMODULE)0, mypath, sizeof(mypath))) {
746 printf(" unknown program path, Error=%ld\n", GetLastError());
747 return;
748 }
749
750 char subkey[MAX_PATH];
751 if (!make_evtkey(subkey, sizeof(subkey), ident))
752 return;
753
754 HKEY hk;
755 LONG err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, (char *)0, 0, KEY_ALL_ACCESS,
756 (SECURITY_ATTRIBUTES *)0, &hk, (DWORD *)0);
757 if (err != ERROR_SUCCESS) {
758 printf(" RegCreateKeyEx failed, error=%ld\n", err);
759 return;
760 }
761
762 err = RegSetValueExA(hk, "EventMessageFile", 0, REG_SZ,
763 (const BYTE *)mypath, strlen(mypath)+1);
764 if (err == ERROR_SUCCESS) {
765 DWORD val = EVENTLOG_INFORMATION_TYPE
766 |EVENTLOG_WARNING_TYPE
767 |EVENTLOG_ERROR_TYPE;
768 err = RegSetValueExA(hk, "TypesSupported", 0, REG_DWORD,
769 (const BYTE *)&val, sizeof(val));
770 }
771 if (err != ERROR_SUCCESS)
772 printf(" RegSetValueEx failed, error=%ld\n", err);
773
774 RegCloseKey(hk);
775 puts(" done");
776}
777
778// Uninstall event message file
779static void uninst_evtmsg(const char * ident)
832b75ed 780{
ee38a438
GI
781 printf("Removing event message file for %s:", ident); fflush(stdout);
782
783 char subkey[MAX_PATH];
784 if (!make_evtkey(subkey, sizeof(subkey), ident))
785 return;
786
787 LONG err = RegDeleteKeyA(HKEY_LOCAL_MACHINE, subkey);
788 if (err != ERROR_SUCCESS && err != ERROR_FILE_NOT_FOUND) {
789 printf(" RegDeleteKey failed, error=%ld\n", err);
790 return;
791 }
792 puts(" done");
832b75ed
GG
793}
794
795
796// Service install/remove commands
797
798static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts,
799 int argc, char **argv )
800{
ee38a438
GI
801 int remove; long err;
802 SC_HANDLE hm, hs;
803
804 if (argc < 2)
805 return -1;
806 if (!strcmp(argv[1], "install"))
807 remove = 0;
808 else if (!strcmp(argv[1], "remove")) {
809 if (argc != 2) {
810 printf("%s: no arguments allowed for command remove\n", ident);
811 return 1;
812 }
813 remove = 1;
814 }
815 else
816 return -1;
817
818 printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout);
819
820 // Open SCM
821 if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) {
822 if ((err = GetLastError()) == ERROR_ACCESS_DENIED)
823 puts(" access to SCManager denied");
824 else
825 printf(" cannot open SCManager, Error=%ld\n", err);
826 return 1;
827 }
828
829 if (!remove) {
830 char path[MAX_PATH+100];
831 int i;
832 // Get program path
833 if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
834 printf(" unknown program path, Error=%ld\n", GetLastError());
835 CloseServiceHandle(hm);
836 return 1;
837 }
838 // Add quotes if necessary
839 if (strchr(path, ' ')) {
840 i = strlen(path);
841 path[i+1] = '"'; path[i+2] = 0;
842 while (--i >= 0)
843 path[i+1] = path[i];
844 path[0] = '"';
845 }
846 // Append options
847 strcat(path, " "); strcat(path, svc_opts->cmd_opt);
848 for (i = 2; i < argc; i++) {
849 const char * s = argv[i];
850 if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path))
851 break;
852 // Add quotes if necessary
853 if (strchr(s, ' ') && !strchr(s, '"')) {
854 strcat(path, " \""); strcat(path, s); strcat(path, "\"");
855 }
856 else {
857 strcat(path, " "); strcat(path, s);
858 }
859 }
860 // Create
861 if (!(hs = CreateService(hm,
862 svc_opts->svcname, svc_opts->dispname,
863 SERVICE_ALL_ACCESS,
3d17a85c 864 SERVICE_WIN32_OWN_PROCESS,
ee38a438
GI
865 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
866 NULL/*no load ordering*/, NULL/*no tag id*/,
ff28b140 867 ""/*no dependencies*/, NULL/*local system account*/, NULL/*no pw*/))) {
ee38a438
GI
868 if ((err = GetLastError()) == ERROR_SERVICE_EXISTS)
869 puts(" the service is already installed");
870 else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
871 puts(" service is still running and marked for deletion\n"
872 "Stop the service and retry install");
873 else
874 printf(" failed, Error=%ld\n", err);
875 CloseServiceHandle(hm);
876 return 1;
877 }
878 // Set optional description
879 if (svc_opts->descript) {
880 SERVICE_DESCRIPTIONA sd = { const_cast<char *>(svc_opts->descript) };
881 ChangeServiceConfig2A(hs, SERVICE_CONFIG_DESCRIPTION, &sd);
882 }
883 // Enable delayed auto start if supported
884 OSVERSIONINFOA ver; ver.dwOSVersionInfoSize = sizeof(ver);
885 if ( GetVersionExA(&ver)
886 && ver.dwPlatformId == VER_PLATFORM_WIN32_NT
887 && ver.dwMajorVersion >= 6 /* Vista */ ) {
d2e702cf
GI
888 // SERVICE_{,CONFIG_}DELAYED_AUTO_START_INFO are missing in older MinGW headers
889 struct /* SERVICE_DELAYED_AUTO_START_INFO */ {
890 BOOL fDelayedAutostart;
891 } sdasi = { TRUE };
892 // typedef char ASSERT_sizeof_sdasi[sizeof(sdasi) == sizeof(SERVICE_DELAYED_AUTO_START_INFO) ? 1 : -1];
893 // typedef char ASSERT_const_scdasi[SERVICE_CONFIG_DELAYED_AUTO_START_INFO == 3 ? 1 : -1];
894 ChangeServiceConfig2A(hs, 3 /* SERVICE_CONFIG_DELAYED_AUTO_START_INFO */, &sdasi);
ee38a438
GI
895 }
896 }
897 else {
898 // Open
899 if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) {
900 puts(" not found");
901 CloseServiceHandle(hm);
902 return 1;
903 }
904 // TODO: Stop service if running
905 // Remove
906 if (!DeleteService(hs)) {
907 if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE)
908 puts(" service is still running and marked for deletion\n"
909 "Stop the service to remove it");
910 else
911 printf(" failed, Error=%ld\n", err);
912 CloseServiceHandle(hs); CloseServiceHandle(hm);
913 return 1;
914 }
915 }
916 puts(" done");
917 CloseServiceHandle(hs); CloseServiceHandle(hm);
918
919 // Install/Remove event message file registry entry
920 if (!remove) {
921 inst_evtmsg(ident);
922 }
923 else {
924 uninst_evtmsg(ident);
925 }
926
927 return 0;
832b75ed
GG
928}
929
930
931/////////////////////////////////////////////////////////////////////////////
932// Main Function
933
934// This function must be called from main()
935// main_func is the function doing the real work
936
937int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
938 int (*main_func)(int, char **), int argc, char **argv )
939{
ee38a438 940 int rc;
832b75ed 941#ifdef _DEBUG
ee38a438
GI
942 // Enable Debug heap checks
943 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
944 |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
832b75ed
GG
945#endif
946
ee38a438
GI
947 // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
948 if ((rc = initd_main(ident, argc, argv)) >= 0)
949 return rc;
950 // Check for [install|remove] parameters
951 if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0)
952 return rc;
953
954 // Run as service if svc_opts.cmd_opt is given as first(!) argument
955 svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt));
956
957 if (!svc_mode) {
958 // Daemon: Try to simulate a Unix-like daemon
959 HANDLE rev;
960 BOOL exists;
961
962 // Create main event to detect process type:
963 // 1. new: parent process => start child and wait for detach() or exit() of child.
964 // 2. exists && signaled: child process => do the real work, signal detach() to parent
965 // 3. exists && !signaled: already running => exit()
966 if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists)))
967 return 100;
968
969 if (!exists && !debugging()) {
970 // Event new => parent process
971 return parent_main(rev);
972 }
973
974 if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) {
975 // Event was signaled => In child process
976 return child_main(rev, main_func, argc, argv);
977 }
978
979 // Event no longer signaled => Already running!
980 daemon_help(stdout, ident, "already running");
981 CloseHandle(rev);
982 return 1;
983 }
984 else {
985 // Service: Start service_main() via SCM
986 SERVICE_TABLE_ENTRY service_table[] = {
987 { (char*)svc_opts->svcname, service_main }, { NULL, NULL }
988 };
989
990 svc_main_func = main_func;
991 svc_main_argc = argc;
992 svc_main_argv = argv;
993 if (!StartServiceCtrlDispatcher(service_table)) {
994 printf("%s: cannot dispatch service, Error=%ld\n"
995 "Option \"%s\" cannot be used to start %s as a service from console.\n"
996 "Use \"%s install ...\" to install the service\n"
997 "and \"net start %s\" to start it.\n",
998 ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident);
832b75ed
GG
999
1000#ifdef _DEBUG
ee38a438
GI
1001 if (debugging())
1002 service_main(argc, argv);
832b75ed 1003#endif
ee38a438
GI
1004 return 100;
1005 }
1006 Sleep(1000);
1007 ExitThread(0); // Do not redo exit() processing
1008 /*NOTREACHED*/
1009 return 0;
1010 }
832b75ed
GG
1011}
1012
1013
1014/////////////////////////////////////////////////////////////////////////////
1015// Test Program
1016
1017#ifdef TEST
1018
1019static volatile sig_atomic_t caughtsig = 0;
1020
1021static void sig_handler(int sig)
1022{
ee38a438 1023 caughtsig = sig;
832b75ed
GG
1024}
1025
1026static void test_exit(void)
1027{
ee38a438 1028 printf("Main exit\n");
832b75ed
GG
1029}
1030
1031int test_main(int argc, char **argv)
1032{
ee38a438
GI
1033 int i;
1034 int debug = 0;
1035 char * cmd = 0;
1036
1037 printf("PID=%ld\n", GetCurrentProcessId());
1038 for (i = 0; i < argc; i++) {
1039 printf("%d: \"%s\"\n", i, argv[i]);
1040 if (!strcmp(argv[i],"-d"))
1041 debug = 1;
1042 }
1043 if (argc > 1 && argv[argc-1][0] != '-')
1044 cmd = argv[argc-1];
1045
1046 daemon_signal(SIGINT, sig_handler);
1047 daemon_signal(SIGBREAK, sig_handler);
1048 daemon_signal(SIGTERM, sig_handler);
1049 daemon_signal(SIGHUP, sig_handler);
1050 daemon_signal(SIGUSR1, sig_handler);
1051 daemon_signal(SIGUSR2, sig_handler);
1052
1053 atexit(test_exit);
1054
1055 if (!debug) {
1056 printf("Preparing to detach...\n");
1057 Sleep(2000);
1058 daemon_detach("test");
1059 printf("Detached!\n");
1060 }
1061
1062 for (;;) {
1063 daemon_sleep(1);
1064 printf("."); fflush(stdout);
1065 if (caughtsig) {
1066 if (caughtsig == SIGUSR2) {
1067 debug ^= 1;
1068 if (debug)
1069 daemon_enable_console("Daemon[Debug]");
1070 else
1071 daemon_disable_console();
1072 }
1073 else if (caughtsig == SIGUSR1 && cmd) {
1074 char inpbuf[200], outbuf[1000]; int rc;
1075 strcpy(inpbuf, "Hello\nWorld!\n");
1076 rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf));
1077 if (!debug)
1078 daemon_enable_console("Command output");
1079 printf("\"%s\" returns %d\n", cmd, rc);
1080 if (rc >= 0)
1081 printf("output:\n%s.\n", outbuf);
1082 fflush(stdout);
1083 if (!debug) {
1084 Sleep(10000); daemon_disable_console();
1085 }
1086 }
1087 printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout);
1088 if (caughtsig == SIGTERM || caughtsig == SIGBREAK)
1089 break;
1090 caughtsig = 0;
1091 }
1092 }
1093 printf("\nExiting on signal %d\n", caughtsig);
1094 return 0;
832b75ed
GG
1095}
1096
1097
1098int main(int argc, char **argv)
1099{
ee38a438
GI
1100 static const daemon_winsvc_options svc_opts = {
1101 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1102 };
832b75ed 1103
ee38a438 1104 return daemon_main("testd", &svc_opts, test_main, argc, argv);
832b75ed
GG
1105}
1106
1107#endif