2 * os_win32/daemon_win32.cpp
4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-18 Christian Franke
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #define _WIN32_WINNT WINVER
14 #include "daemon_win32.h"
16 const char * daemon_win32_cpp_cvsid
= "$Id: daemon_win32.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
24 #define WIN32_LEAN_AND_MEAN
31 /////////////////////////////////////////////////////////////////////////////
33 // Prevent spawning of child process if debugging
35 #define debugging() IsDebuggerPresent()
37 #define debugging() FALSE
41 #define EVT_NAME_LEN 260
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
48 static void make_name(char * name
, int sig
)
51 if (!GetModuleFileNameA(NULL
, name
, EVT_NAME_LEN
-10))
52 strcpy(name
, "DaemonEvent");
53 for (i
= 0; name
[i
]; i
++) {
55 if (!( ('0' <= c
&& c
<= '9')
56 || ('A' <= c
&& c
<= 'Z')
57 || ('a' <= c
&& c
<= 'z')))
60 sprintf(name
+strlen(name
), "-%d", sig
);
64 static HANDLE
create_event(int sig
, BOOL initial
, BOOL errmsg
, BOOL
* exists
)
66 char name
[EVT_NAME_LEN
];
74 if (!(h
= CreateEventA(NULL
, FALSE
, initial
, (name
[0] ? name
: NULL
)))) {
76 fprintf(stderr
, "CreateEvent(.,\"%s\"): Error=%ld\n", name
, GetLastError());
80 if (GetLastError() == ERROR_ALREADY_EXISTS
) {
83 fprintf(stderr
, "CreateEvent(.,\"%s\"): Exists\n", name
);
93 static HANDLE
open_event(int sig
)
95 char name
[EVT_NAME_LEN
];
97 return OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
);
101 static int event_exists(int sig
)
103 char name
[EVT_NAME_LEN
];
105 make_name(name
, sig
);
106 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
)))
113 static int sig_event(int sig
)
115 char name
[EVT_NAME_LEN
];
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
)))
131 static void daemon_help(FILE * f
, const char * ident
, const char * message
)
135 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
136 ident
, message
, ident
);
141 /////////////////////////////////////////////////////////////////////////////
145 static BOOL WINAPI
parent_console_handler(DWORD event
)
149 case CTRL_BREAK_EVENT
:
150 return TRUE
; // Ignore
152 return FALSE
; // continue with next handler ...
156 static int parent_main(HANDLE rev
)
162 PROCESS_INFORMATION pi
;
165 // Ignore ^C, ^BREAK in parent
166 SetConsoleCtrlHandler(parent_console_handler
, TRUE
/*add*/);
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*/))) {
174 // Restart process with same args
175 cmdline
= GetCommandLineA();
176 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
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
);
186 CloseHandle(pi
.hThread
);
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);
195 CloseHandle(rev
); CloseHandle(dev
);
198 if (!GetExitCodeProcess(pi
.hProcess
, &exitcode
))
200 else if (exitcode
== STILL_ACTIVE
) // detach()ed, assume OK
203 CloseHandle(pi
.hProcess
);
208 /////////////////////////////////////////////////////////////////////////////
212 static int svc_mode
; // Running as service?
213 static int svc_paused
; // Service paused?
215 static void service_report_status(int state
, int waithint
);
218 // Tables of signal handler and corresponding events
219 typedef void (*sigfunc_t
)(int);
221 #define MAX_SIG_HANDLERS 8
223 static int num_sig_handlers
= 0;
224 static sigfunc_t sig_handlers
[MAX_SIG_HANDLERS
];
225 static int sig_numbers
[MAX_SIG_HANDLERS
];
226 static HANDLE sig_events
[MAX_SIG_HANDLERS
];
228 static HANDLE sighup_handle
, sigint_handle
, sigbreak_handle
;
229 static HANDLE sigterm_handle
, sigusr1_handle
;
231 static HANDLE running_event
;
233 static int reopen_stdin
, reopen_stdout
, reopen_stderr
;
236 // Handler for windows console events
238 static BOOL WINAPI
child_console_handler(DWORD event
)
240 // Caution: runs in a new thread
241 // TODO: Guard with a mutex
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;
254 return FALSE
; // continue with next handler
262 static void child_exit(void)
268 PROCESS_INFORMATION pi
;
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;
276 if (!(rst
= open_event(EVT_RESTART
)))
277 return; // No => normal exit
279 // Yes => Signal exit and restart process
285 cmdline
= GetCommandLineA();
286 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
287 si
.dwFlags
= STARTF_USESHOWWINDOW
; si
.wShowWindow
= SW_HIDE
;
291 NULL
, NULL
, TRUE
/*inherit*/,
292 0, NULL
, NULL
, &si
, &pi
)) {
293 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
295 CloseHandle(pi
.hThread
); CloseHandle(pi
.hProcess
);
298 static int child_main(HANDLE hev
,int (*main_func
)(int, char **), int argc
, char **argv
)
300 // Keep EVT_RUNNING open until exit
303 // Install console handler
304 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
306 // Install restart handler
309 // Continue in main_func() to do the real work
310 return main_func(argc
, argv
);
316 sigfunc_t
daemon_signal(int sig
, sigfunc_t func
)
320 if (func
== SIG_DFL
|| func
== SIG_IGN
)
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
;
329 if (num_sig_handlers
>= MAX_SIG_HANDLERS
)
331 if (!(h
= create_event((!svc_mode
? sig
: -1), FALSE
, TRUE
, NULL
)))
333 sig_events
[num_sig_handlers
] = h
;
334 sig_numbers
[num_sig_handlers
] = sig
;
335 sig_handlers
[num_sig_handlers
] = func
;
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;
350 const char * daemon_strsignal(int 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*";
366 void daemon_sleep(int seconds
)
369 if (num_sig_handlers
<= 0) {
370 Sleep(seconds
*1000L);
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);
383 sig_handlers
[rc
-WAIT_OBJECT_0
](sig_numbers
[rc
-WAIT_OBJECT_0
]);
387 } while (svc_paused
);
391 // Disable/Enable console
393 void daemon_disable_console()
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;
400 if (isatty(fileno(stdout
))) {
401 fclose(stdout
); reopen_stdout
= 1;
403 if (isatty(fileno(stderr
))) {
404 fclose(stderr
); reopen_stderr
= 1;
407 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
410 int daemon_enable_console(const char * title
)
413 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
415 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
419 SetConsoleTitleA(title
);
421 freopen("conin$", "r", stdin
);
423 freopen("conout$", "w", stdout
);
425 freopen("conout$", "w", stderr
);
426 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
431 // Detach daemon from console & parent
433 int daemon_detach(const char * ident
)
438 FILE * f
= ( isatty(fileno(stdout
)) ? stdout
439 : isatty(fileno(stderr
)) ? stderr
: NULL
);
441 daemon_help(f
, ident
, "now detaches from console into background mode");
443 // Signal detach to parent
444 if (sig_event(EVT_DETACHED
) != 1) {
448 daemon_disable_console();
451 // Signal end of initialization to service control manager
452 service_report_status(SERVICE_RUNNING
, 0);
453 reopen_stdin
= reopen_stdout
= reopen_stderr
= 1;
460 /////////////////////////////////////////////////////////////////////////////
463 static int wait_signaled(HANDLE h
, int seconds
)
467 if (WaitForSingleObject(h
, 1000L) == WAIT_OBJECT_0
)
471 fputchar('.'); fflush(stdout
);
476 static int wait_evt_running(int seconds
, int exists
)
479 if (event_exists(EVT_RUNNING
) == exists
)
483 if (event_exists(EVT_RUNNING
) == exists
)
487 fputchar('.'); fflush(stdout
);
492 static int is_initd_command(char * s
)
494 if (!strcmp(s
, "status"))
496 if (!strcmp(s
, "stop"))
498 if (!strcmp(s
, "reload"))
500 if (!strcmp(s
, "sigusr1"))
502 if (!strcmp(s
, "sigusr2"))
504 if (!strcmp(s
, "restart"))
510 static int initd_main(const char * ident
, int argc
, char **argv
)
515 if ((rc
= is_initd_command(argv
[1])) < 0)
518 printf("%s: no arguments allowed for command %s\n", ident
, argv
[1]);
525 printf("Checking for %s:", ident
); fflush(stdout
);
526 rc
= event_exists(EVT_RUNNING
);
527 puts(rc
? " running" : " not running");
531 printf("Stopping %s:", ident
); fflush(stdout
);
532 rc
= sig_event(SIGTERM
);
534 puts(rc
< 0 ? " not running" : " error");
535 return (rc
< 0 ? 0 : 1);
537 rc
= wait_evt_running(10, 0);
538 puts(!rc
? " done" : " timeout");
539 return (!rc
? 0 : 1);
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);
549 printf("Sending SIGUSR%d to %s:", (rc
-SIGUSR1
+1), ident
); fflush(stdout
);
551 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
552 return (rc
> 0 ? 0 : 1);
557 printf("Stopping %s:", ident
); fflush(stdout
);
558 if (event_exists(EVT_DETACHED
)) {
559 puts(" not detached, cannot restart");
562 if (!(rst
= create_event(EVT_RESTART
, FALSE
, FALSE
, NULL
))) {
566 rc
= sig_event(SIGTERM
);
568 puts(rc
< 0 ? " not running" : " error");
572 rc
= wait_signaled(rst
, 10);
581 printf("Starting %s:", ident
); fflush(stdout
);
582 rc
= wait_evt_running(10, 1);
583 puts(!rc
? " done" : " error");
584 return (!rc
? 0 : 1);
590 /////////////////////////////////////////////////////////////////////////////
591 // Windows Service Functions
593 int daemon_winsvc_exitcode
; // Set by app to exit(code)
595 static SERVICE_STATUS_HANDLE svc_handle
;
596 static SERVICE_STATUS svc_status
;
599 // Report status to SCM
601 static void service_report_status(int state
, int seconds
)
604 static DWORD checkpoint
= 1;
605 svc_status
.dwCurrentState
= state
;
606 svc_status
.dwWaitHint
= seconds
*1000;
609 svc_status
.dwCheckPoint
= checkpoint
++;
611 case SERVICE_RUNNING
:
612 case SERVICE_STOPPED
:
613 svc_status
.dwCheckPoint
= 0;
616 case SERVICE_START_PENDING
:
617 case SERVICE_STOP_PENDING
:
618 svc_status
.dwControlsAccepted
= 0;
621 svc_status
.dwControlsAccepted
=
622 SERVICE_ACCEPT_STOP
|SERVICE_ACCEPT_SHUTDOWN
|
623 SERVICE_ACCEPT_PAUSE_CONTINUE
|SERVICE_ACCEPT_PARAMCHANGE
;
626 SetServiceStatus(svc_handle
, &svc_status
);
630 // Control the service, called by SCM
632 static void WINAPI
service_control(DWORD ctrlcode
)
635 case SERVICE_CONTROL_STOP
:
636 case SERVICE_CONTROL_SHUTDOWN
:
637 service_report_status(SERVICE_STOP_PENDING
, 30);
639 SetEvent(sigterm_handle
);
641 case SERVICE_CONTROL_PARAMCHANGE
: // Win2000/XP
642 service_report_status(svc_status
.dwCurrentState
, 0);
644 SetEvent(sighup_handle
); // reload
646 case SERVICE_CONTROL_PAUSE
:
647 service_report_status(SERVICE_PAUSED
, 0);
650 case SERVICE_CONTROL_CONTINUE
:
651 service_report_status(SERVICE_RUNNING
, 0);
653 int was_paused
= svc_paused
;
655 SetEvent(was_paused
? sighup_handle
: sigusr1_handle
); // reload:recheck
658 case SERVICE_CONTROL_INTERROGATE
:
660 service_report_status(svc_status
.dwCurrentState
, 0);
666 // Exit handler for service
668 static void service_exit(void)
670 // Close signal events
672 for (i
= 0; i
< num_sig_handlers
; i
++)
673 CloseHandle(sig_events
[i
]);
674 num_sig_handlers
= 0;
677 if (daemon_winsvc_exitcode
) {
678 svc_status
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
679 svc_status
.dwServiceSpecificExitCode
= daemon_winsvc_exitcode
;
682 service_report_status(SERVICE_STOPPED
, 0);
686 // Variables for passing main(argc, argv) from daemon_main to service_main()
687 static int (*svc_main_func
)(int, char **);
688 static int svc_main_argc
;
689 static char ** svc_main_argv
;
691 // Main function for service, called by service dispatcher
693 static void WINAPI
service_main(DWORD
/*argc*/, LPSTR
* argv
)
695 char path
[MAX_PATH
], *p
;
697 // Register control handler
698 svc_handle
= RegisterServiceCtrlHandler(argv
[0], service_control
);
700 // Init service status
701 svc_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
702 service_report_status(SERVICE_START_PENDING
, 10);
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
);
709 // Install exit handler
710 atexit(service_exit
);
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
);
715 exit(daemon_winsvc_exitcode
);
716 // ... continued in service_exit()
720 /////////////////////////////////////////////////////////////////////////////
721 // Windows Service Admin Functions
724 // Make registry key name for event message file
725 static bool make_evtkey(char * buf
, unsigned size
, const char * ident
)
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");
734 memcpy(buf
, prefix
, pfxlen
);
735 memcpy(buf
+pfxlen
, ident
, idlen
+1);
739 // Install this exe as event message file
740 static void inst_evtmsg(const char * ident
)
742 printf("Installing event message file for %s:", ident
); fflush(stdout
);
744 char mypath
[MAX_PATH
];
745 if (!GetModuleFileNameA((HMODULE
)0, mypath
, sizeof(mypath
))) {
746 printf(" unknown program path, Error=%ld\n", GetLastError());
750 char subkey
[MAX_PATH
];
751 if (!make_evtkey(subkey
, sizeof(subkey
), ident
))
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
);
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
));
771 if (err
!= ERROR_SUCCESS
)
772 printf(" RegSetValueEx failed, error=%ld\n", err
);
778 // Uninstall event message file
779 static void uninst_evtmsg(const char * ident
)
781 printf("Removing event message file for %s:", ident
); fflush(stdout
);
783 char subkey
[MAX_PATH
];
784 if (!make_evtkey(subkey
, sizeof(subkey
), ident
))
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
);
796 // Service install/remove commands
798 static int svcadm_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
799 int argc
, char **argv
)
801 int remove
; long err
;
806 if (!strcmp(argv
[1], "install"))
808 else if (!strcmp(argv
[1], "remove")) {
810 printf("%s: no arguments allowed for command remove\n", ident
);
818 printf("%s service %s:", (!remove
?"Installing":"Removing"), ident
); fflush(stdout
);
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");
825 printf(" cannot open SCManager, Error=%ld\n", err
);
830 char path
[MAX_PATH
+100];
833 if (!GetModuleFileNameA(NULL
, path
, MAX_PATH
)) {
834 printf(" unknown program path, Error=%ld\n", GetLastError());
835 CloseServiceHandle(hm
);
838 // Add quotes if necessary
839 if (strchr(path
, ' ')) {
841 path
[i
+1] = '"'; path
[i
+2] = 0;
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
))
852 // Add quotes if necessary
853 if (strchr(s
, ' ') && !strchr(s
, '"')) {
854 strcat(path
, " \""); strcat(path
, s
); strcat(path
, "\"");
857 strcat(path
, " "); strcat(path
, s
);
861 if (!(hs
= CreateService(hm
,
862 svc_opts
->svcname
, svc_opts
->dispname
,
864 SERVICE_WIN32_OWN_PROCESS
,
865 SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, path
,
866 NULL
/*no load ordering*/, NULL
/*no tag id*/,
867 ""/*no dependencies*/, NULL
/*local system account*/, NULL
/*no pw*/))) {
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");
874 printf(" failed, Error=%ld\n", err
);
875 CloseServiceHandle(hm
);
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
);
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 */ ) {
888 // SERVICE_{,CONFIG_}DELAYED_AUTO_START_INFO are missing in older MinGW headers
889 struct /* SERVICE_DELAYED_AUTO_START_INFO */ {
890 BOOL fDelayedAutostart
;
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
);
899 if (!(hs
= OpenService(hm
, svc_opts
->svcname
, SERVICE_ALL_ACCESS
))) {
901 CloseServiceHandle(hm
);
904 // TODO: Stop service if running
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");
911 printf(" failed, Error=%ld\n", err
);
912 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
917 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
919 // Install/Remove event message file registry entry
924 uninst_evtmsg(ident
);
931 /////////////////////////////////////////////////////////////////////////////
934 // This function must be called from main()
935 // main_func is the function doing the real work
937 int daemon_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
938 int (*main_func
)(int, char **), int argc
, char **argv
)
942 // Enable Debug heap checks
943 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG
)
944 |_CRTDBG_ALLOC_MEM_DF
|_CRTDBG_CHECK_ALWAYS_DF
|_CRTDBG_LEAK_CHECK_DF
);
947 // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
948 if ((rc
= initd_main(ident
, argc
, argv
)) >= 0)
950 // Check for [install|remove] parameters
951 if (svc_opts
&& (rc
= svcadm_main(ident
, svc_opts
, argc
, argv
)) >= 0)
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
));
958 // Daemon: Try to simulate a Unix-like daemon
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
)))
969 if (!exists
&& !debugging()) {
970 // Event new => parent process
971 return parent_main(rev
);
974 if (WaitForSingleObject(rev
, 0) == WAIT_OBJECT_0
) {
975 // Event was signaled => In child process
976 return child_main(rev
, main_func
, argc
, argv
);
979 // Event no longer signaled => Already running!
980 daemon_help(stdout
, ident
, "already running");
985 // Service: Start service_main() via SCM
986 SERVICE_TABLE_ENTRY service_table
[] = {
987 { (char*)svc_opts
->svcname
, service_main
}, { NULL
, NULL
}
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
);
1002 service_main(argc
, argv
);
1007 ExitThread(0); // Do not redo exit() processing
1014 /////////////////////////////////////////////////////////////////////////////
1019 static volatile sig_atomic_t caughtsig
= 0;
1021 static void sig_handler(int sig
)
1026 static void test_exit(void)
1028 printf("Main exit\n");
1031 int test_main(int argc
, char **argv
)
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"))
1043 if (argc
> 1 && argv
[argc
-1][0] != '-')
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
);
1056 printf("Preparing to detach...\n");
1058 daemon_detach("test");
1059 printf("Detached!\n");
1064 printf("."); fflush(stdout
);
1066 if (caughtsig
== SIGUSR2
) {
1069 daemon_enable_console("Daemon[Debug]");
1071 daemon_disable_console();
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
));
1078 daemon_enable_console("Command output");
1079 printf("\"%s\" returns %d\n", cmd
, rc
);
1081 printf("output:\n%s.\n", outbuf
);
1084 Sleep(10000); daemon_disable_console();
1087 printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig
); fflush(stdout
);
1088 if (caughtsig
== SIGTERM
|| caughtsig
== SIGBREAK
)
1093 printf("\nExiting on signal %d\n", caughtsig
);
1098 int main(int argc
, char **argv
)
1100 static const daemon_winsvc_options svc_opts
= {
1101 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1104 return daemon_main("testd", &svc_opts
, test_main
, argc
, argv
);