2 * os_win32/daemon_win32.cpp
4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * You should have received a copy of the GNU General Public License
14 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
19 #define _WIN32_WINNT WINVER
21 #include "daemon_win32.h"
23 const char * daemon_win32_cpp_cvsid
= "$Id: daemon_win32.cpp 3834 2013-07-20 16:17:13Z chrfranke $"
31 #define WIN32_LEAN_AND_MEAN
37 #ifndef SERVICE_CONFIG_DELAYED_AUTO_START_INFO
38 // Missing in older MinGW headers
39 #define SERVICE_CONFIG_DELAYED_AUTO_START_INFO 3
43 /////////////////////////////////////////////////////////////////////////////
45 // Prevent spawning of child process if debugging
47 #define debugging() IsDebuggerPresent()
49 #define debugging() FALSE
53 #define EVT_NAME_LEN 260
55 // Internal events (must be > SIGUSRn)
56 #define EVT_RUNNING 100 // Exists when running, signaled on creation
57 #define EVT_DETACHED 101 // Signaled when child detaches from console
58 #define EVT_RESTART 102 // Signaled when child should restart
60 static void make_name(char * name
, int sig
)
63 if (!GetModuleFileNameA(NULL
, name
, EVT_NAME_LEN
-10))
64 strcpy(name
, "DaemonEvent");
65 for (i
= 0; name
[i
]; i
++) {
67 if (!( ('0' <= c
&& c
<= '9')
68 || ('A' <= c
&& c
<= 'Z')
69 || ('a' <= c
&& c
<= 'z')))
72 sprintf(name
+strlen(name
), "-%d", sig
);
76 static HANDLE
create_event(int sig
, BOOL initial
, BOOL errmsg
, BOOL
* exists
)
78 char name
[EVT_NAME_LEN
];
86 if (!(h
= CreateEventA(NULL
, FALSE
, initial
, (name
[0] ? name
: NULL
)))) {
88 fprintf(stderr
, "CreateEvent(.,\"%s\"): Error=%ld\n", name
, GetLastError());
92 if (GetLastError() == ERROR_ALREADY_EXISTS
) {
95 fprintf(stderr
, "CreateEvent(.,\"%s\"): Exists\n", name
);
105 static HANDLE
open_event(int sig
)
107 char name
[EVT_NAME_LEN
];
108 make_name(name
, sig
);
109 return OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
);
113 static int event_exists(int sig
)
115 char name
[EVT_NAME_LEN
];
117 make_name(name
, sig
);
118 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
)))
125 static int sig_event(int sig
)
127 char name
[EVT_NAME_LEN
];
129 make_name(name
, sig
);
130 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
))) {
131 make_name(name
, EVT_RUNNING
);
132 if (!(h
= OpenEvent(EVENT_MODIFY_STATE
, FALSE
, name
)))
143 static void daemon_help(FILE * f
, const char * ident
, const char * message
)
147 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
148 ident
, message
, ident
);
153 /////////////////////////////////////////////////////////////////////////////
157 static BOOL WINAPI
parent_console_handler(DWORD event
)
161 case CTRL_BREAK_EVENT
:
162 return TRUE
; // Ignore
164 return FALSE
; // continue with next handler ...
168 static int parent_main(HANDLE rev
)
174 PROCESS_INFORMATION pi
;
177 // Ignore ^C, ^BREAK in parent
178 SetConsoleCtrlHandler(parent_console_handler
, TRUE
/*add*/);
180 // Create event used by child to signal daemon_detach()
181 if (!(dev
= create_event(EVT_DETACHED
, FALSE
/*not signaled*/, TRUE
, NULL
/*must not exist*/))) {
186 // Restart process with same args
187 cmdline
= GetCommandLineA();
188 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
192 NULL
, NULL
, TRUE
/*inherit*/,
193 0, NULL
, NULL
, &si
, &pi
)) {
194 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
195 CloseHandle(rev
); CloseHandle(dev
);
198 CloseHandle(pi
.hThread
);
200 // Wait for daemon_detach() or exit()
201 ht
[0] = dev
; ht
[1] = pi
.hProcess
;
202 rc
= WaitForMultipleObjects(2, ht
, FALSE
/*or*/, INFINITE
);
203 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+2)) {
204 fprintf(stderr
, "WaitForMultipleObjects returns %lX\n", rc
);
205 TerminateProcess(pi
.hProcess
, 200);
207 CloseHandle(rev
); CloseHandle(dev
);
210 if (!GetExitCodeProcess(pi
.hProcess
, &exitcode
))
212 else if (exitcode
== STILL_ACTIVE
) // detach()ed, assume OK
215 CloseHandle(pi
.hProcess
);
220 /////////////////////////////////////////////////////////////////////////////
224 static int svc_mode
; // Running as service?
225 static int svc_paused
; // Service paused?
227 static void service_report_status(int state
, int waithint
);
230 // Tables of signal handler and corresponding events
231 typedef void (*sigfunc_t
)(int);
233 #define MAX_SIG_HANDLERS 8
235 static int num_sig_handlers
= 0;
236 static sigfunc_t sig_handlers
[MAX_SIG_HANDLERS
];
237 static int sig_numbers
[MAX_SIG_HANDLERS
];
238 static HANDLE sig_events
[MAX_SIG_HANDLERS
];
240 static HANDLE sighup_handle
, sigint_handle
, sigbreak_handle
;
241 static HANDLE sigterm_handle
, sigusr1_handle
;
243 static HANDLE running_event
;
245 static int reopen_stdin
, reopen_stdout
, reopen_stderr
;
248 // Handler for windows console events
250 static BOOL WINAPI
child_console_handler(DWORD event
)
252 // Caution: runs in a new thread
253 // TODO: Guard with a mutex
256 case CTRL_C_EVENT
: // <CONTROL-C> (SIGINT)
257 h
= sigint_handle
; break;
258 case CTRL_BREAK_EVENT
: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
259 case CTRL_CLOSE_EVENT
: // User closed console or abort via task manager
260 h
= sigbreak_handle
; break;
261 case CTRL_LOGOFF_EVENT
: // Logout/Shutdown (SIGTERM)
262 case CTRL_SHUTDOWN_EVENT
:
263 h
= sigterm_handle
; break;
266 return FALSE
; // continue with next handler
274 static void child_exit(void)
280 PROCESS_INFORMATION pi
;
282 for (i
= 0; i
< num_sig_handlers
; i
++)
283 CloseHandle(sig_events
[i
]);
284 num_sig_handlers
= 0;
285 CloseHandle(running_event
); running_event
= 0;
288 if (!(rst
= open_event(EVT_RESTART
)))
289 return; // No => normal exit
291 // Yes => Signal exit and restart process
297 cmdline
= GetCommandLineA();
298 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
299 si
.dwFlags
= STARTF_USESHOWWINDOW
; si
.wShowWindow
= SW_HIDE
;
303 NULL
, NULL
, TRUE
/*inherit*/,
304 0, NULL
, NULL
, &si
, &pi
)) {
305 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
307 CloseHandle(pi
.hThread
); CloseHandle(pi
.hProcess
);
310 static int child_main(HANDLE hev
,int (*main_func
)(int, char **), int argc
, char **argv
)
312 // Keep EVT_RUNNING open until exit
315 // Install console handler
316 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
318 // Install restart handler
321 // Continue in main_func() to do the real work
322 return main_func(argc
, argv
);
328 sigfunc_t
daemon_signal(int sig
, sigfunc_t func
)
332 if (func
== SIG_DFL
|| func
== SIG_IGN
)
334 for (i
= 0; i
< num_sig_handlers
; i
++) {
335 if (sig_numbers
[i
] == sig
) {
336 sigfunc_t old
= sig_handlers
[i
];
337 sig_handlers
[i
] = func
;
341 if (num_sig_handlers
>= MAX_SIG_HANDLERS
)
343 if (!(h
= create_event((!svc_mode
? sig
: -1), FALSE
, TRUE
, NULL
)))
345 sig_events
[num_sig_handlers
] = h
;
346 sig_numbers
[num_sig_handlers
] = sig
;
347 sig_handlers
[num_sig_handlers
] = func
;
349 case SIGHUP
: sighup_handle
= h
; break;
350 case SIGINT
: sigint_handle
= h
; break;
351 case SIGTERM
: sigterm_handle
= h
; break;
352 case SIGBREAK
: sigbreak_handle
= h
; break;
353 case SIGUSR1
: sigusr1_handle
= h
; break;
362 const char * daemon_strsignal(int sig
)
365 case SIGHUP
: return "SIGHUP";
366 case SIGINT
: return "SIGINT";
367 case SIGTERM
: return "SIGTERM";
368 case SIGBREAK
:return "SIGBREAK";
369 case SIGUSR1
: return "SIGUSR1";
370 case SIGUSR2
: return "SIGUSR2";
371 default: return "*UNKNOWN*";
378 void daemon_sleep(int seconds
)
381 if (num_sig_handlers
<= 0) {
382 Sleep(seconds
*1000L);
385 // Wait for any signal or timeout
386 DWORD rc
= WaitForMultipleObjects(num_sig_handlers
, sig_events
,
387 FALSE
/*OR*/, seconds
*1000L);
388 if (rc
!= WAIT_TIMEOUT
) {
389 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+(unsigned)num_sig_handlers
)) {
390 fprintf(stderr
,"WaitForMultipleObjects returns %lu\n", rc
);
391 Sleep(seconds
*1000L);
395 sig_handlers
[rc
-WAIT_OBJECT_0
](sig_numbers
[rc
-WAIT_OBJECT_0
]);
399 } while (svc_paused
);
403 // Disable/Enable console
405 void daemon_disable_console()
407 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
408 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
409 if (isatty(fileno(stdin
))) {
410 fclose(stdin
); reopen_stdin
= 1;
412 if (isatty(fileno(stdout
))) {
413 fclose(stdout
); reopen_stdout
= 1;
415 if (isatty(fileno(stderr
))) {
416 fclose(stderr
); reopen_stderr
= 1;
419 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
422 int daemon_enable_console(const char * title
)
425 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
427 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
431 SetConsoleTitleA(title
);
433 freopen("conin$", "r", stdin
);
435 freopen("conout$", "w", stdout
);
437 freopen("conout$", "w", stderr
);
438 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
443 // Detach daemon from console & parent
445 int daemon_detach(const char * ident
)
450 FILE * f
= ( isatty(fileno(stdout
)) ? stdout
451 : isatty(fileno(stderr
)) ? stderr
: NULL
);
453 daemon_help(f
, ident
, "now detaches from console into background mode");
455 // Signal detach to parent
456 if (sig_event(EVT_DETACHED
) != 1) {
460 daemon_disable_console();
463 // Signal end of initialization to service control manager
464 service_report_status(SERVICE_RUNNING
, 0);
465 reopen_stdin
= reopen_stdout
= reopen_stderr
= 1;
472 /////////////////////////////////////////////////////////////////////////////
474 // Spawn a command and redirect <inpbuf >outbuf
475 // return command's exitcode or -1 on error
477 int daemon_spawn(const char * cmd
,
478 const char * inpbuf
, int inpsize
,
479 char * outbuf
, int outsize
)
481 HANDLE self
= GetCurrentProcess();
483 // Create stdin pipe with inheritable read side
484 SECURITY_ATTRIBUTES sa
;
485 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
486 sa
.bInheritHandle
= TRUE
;
487 HANDLE pipe_inp_r
, pipe_inp_w
, h
;
488 if (!CreatePipe(&pipe_inp_r
, &h
, &sa
/*inherit*/, inpsize
*2+13))
490 if (!DuplicateHandle(self
, h
, self
, &pipe_inp_w
,
491 0, FALSE
/*!inherit*/, DUPLICATE_SAME_ACCESS
|DUPLICATE_CLOSE_SOURCE
)) {
492 CloseHandle(pipe_inp_r
);
496 // Create stdout pipe with inheritable write side
497 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
498 sa
.bInheritHandle
= TRUE
;
500 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
)) {
501 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
506 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
507 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
508 CloseHandle(pipe_out_w
); CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
512 // Create stderr handle as dup of stdout write side
514 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
515 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
516 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
517 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
521 // Create process with pipes as stdio
523 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
524 si
.hStdInput
= pipe_inp_r
;
525 si
.hStdOutput
= pipe_out_w
;
526 si
.hStdError
= pipe_err_w
;
527 si
.dwFlags
= STARTF_USESTDHANDLES
;
528 PROCESS_INFORMATION pi
;
531 NULL
, NULL
, TRUE
/*inherit*/,
532 CREATE_NO_WINDOW
, // DETACHED_PROCESS does not work
533 NULL
, NULL
, &si
, &pi
)) {
534 CloseHandle(pipe_err_w
);
535 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
536 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
539 CloseHandle(pi
.hThread
);
540 // Close inherited handles
541 CloseHandle(pipe_inp_r
);
542 CloseHandle(pipe_out_w
);
543 CloseHandle(pipe_err_w
);
545 // Copy inpbuf to stdin
546 // convert \n => \r\n
549 for (i
= 0; i
< inpsize
; ) {
551 while (i
+len
< inpsize
&& inpbuf
[i
+len
] != '\n')
554 WriteFile(pipe_inp_w
, inpbuf
+i
, len
, &num_io
, NULL
);
557 WriteFile(pipe_inp_w
, "\r\n", 2, &num_io
, NULL
);
561 CloseHandle(pipe_inp_w
);
563 // Copy stdout to output buffer until full, rest to /dev/null
564 // convert \r\n => \n
567 if (!ReadFile(pipe_out_r
, buf
, sizeof(buf
), &num_io
, NULL
) || num_io
== 0)
569 for (int j
= 0; i
< outsize
-1 && j
< (int)num_io
; j
++) {
571 outbuf
[i
++] = buf
[j
];
575 CloseHandle(pipe_out_r
);
577 // Wait for process exitcode
579 WaitForSingleObject(pi
.hProcess
, INFINITE
);
580 GetExitCodeProcess(pi
.hProcess
, &exitcode
);
581 CloseHandle(pi
.hProcess
);
586 /////////////////////////////////////////////////////////////////////////////
589 static int wait_signaled(HANDLE h
, int seconds
)
593 if (WaitForSingleObject(h
, 1000L) == WAIT_OBJECT_0
)
597 fputchar('.'); fflush(stdout
);
602 static int wait_evt_running(int seconds
, int exists
)
605 if (event_exists(EVT_RUNNING
) == exists
)
609 if (event_exists(EVT_RUNNING
) == exists
)
613 fputchar('.'); fflush(stdout
);
618 static int is_initd_command(char * s
)
620 if (!strcmp(s
, "status"))
622 if (!strcmp(s
, "stop"))
624 if (!strcmp(s
, "reload"))
626 if (!strcmp(s
, "sigusr1"))
628 if (!strcmp(s
, "sigusr2"))
630 if (!strcmp(s
, "restart"))
636 static int initd_main(const char * ident
, int argc
, char **argv
)
641 if ((rc
= is_initd_command(argv
[1])) < 0)
644 printf("%s: no arguments allowed for command %s\n", ident
, argv
[1]);
651 printf("Checking for %s:", ident
); fflush(stdout
);
652 rc
= event_exists(EVT_RUNNING
);
653 puts(rc
? " running" : " not running");
657 printf("Stopping %s:", ident
); fflush(stdout
);
658 rc
= sig_event(SIGTERM
);
660 puts(rc
< 0 ? " not running" : " error");
661 return (rc
< 0 ? 0 : 1);
663 rc
= wait_evt_running(10, 0);
664 puts(!rc
? " done" : " timeout");
665 return (!rc
? 0 : 1);
668 printf("Reloading %s:", ident
); fflush(stdout
);
669 rc
= sig_event(SIGHUP
);
670 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
671 return (rc
> 0 ? 0 : 1);
675 printf("Sending SIGUSR%d to %s:", (rc
-SIGUSR1
+1), ident
); fflush(stdout
);
677 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
678 return (rc
> 0 ? 0 : 1);
683 printf("Stopping %s:", ident
); fflush(stdout
);
684 if (event_exists(EVT_DETACHED
)) {
685 puts(" not detached, cannot restart");
688 if (!(rst
= create_event(EVT_RESTART
, FALSE
, FALSE
, NULL
))) {
692 rc
= sig_event(SIGTERM
);
694 puts(rc
< 0 ? " not running" : " error");
698 rc
= wait_signaled(rst
, 10);
707 printf("Starting %s:", ident
); fflush(stdout
);
708 rc
= wait_evt_running(10, 1);
709 puts(!rc
? " done" : " error");
710 return (!rc
? 0 : 1);
716 /////////////////////////////////////////////////////////////////////////////
717 // Windows Service Functions
719 int daemon_winsvc_exitcode
; // Set by app to exit(code)
721 static SERVICE_STATUS_HANDLE svc_handle
;
722 static SERVICE_STATUS svc_status
;
725 // Report status to SCM
727 static void service_report_status(int state
, int seconds
)
730 static DWORD checkpoint
= 1;
731 svc_status
.dwCurrentState
= state
;
732 svc_status
.dwWaitHint
= seconds
*1000;
735 svc_status
.dwCheckPoint
= checkpoint
++;
737 case SERVICE_RUNNING
:
738 case SERVICE_STOPPED
:
739 svc_status
.dwCheckPoint
= 0;
742 case SERVICE_START_PENDING
:
743 case SERVICE_STOP_PENDING
:
744 svc_status
.dwControlsAccepted
= 0;
747 svc_status
.dwControlsAccepted
=
748 SERVICE_ACCEPT_STOP
|SERVICE_ACCEPT_SHUTDOWN
|
749 SERVICE_ACCEPT_PAUSE_CONTINUE
|SERVICE_ACCEPT_PARAMCHANGE
;
752 SetServiceStatus(svc_handle
, &svc_status
);
756 // Control the service, called by SCM
758 static void WINAPI
service_control(DWORD ctrlcode
)
761 case SERVICE_CONTROL_STOP
:
762 case SERVICE_CONTROL_SHUTDOWN
:
763 service_report_status(SERVICE_STOP_PENDING
, 30);
765 SetEvent(sigterm_handle
);
767 case SERVICE_CONTROL_PARAMCHANGE
: // Win2000/XP
768 service_report_status(svc_status
.dwCurrentState
, 0);
770 SetEvent(sighup_handle
); // reload
772 case SERVICE_CONTROL_PAUSE
:
773 service_report_status(SERVICE_PAUSED
, 0);
776 case SERVICE_CONTROL_CONTINUE
:
777 service_report_status(SERVICE_RUNNING
, 0);
779 int was_paused
= svc_paused
;
781 SetEvent(was_paused
? sighup_handle
: sigusr1_handle
); // reload:recheck
784 case SERVICE_CONTROL_INTERROGATE
:
786 service_report_status(svc_status
.dwCurrentState
, 0);
792 // Exit handler for service
794 static void service_exit(void)
796 // Close signal events
798 for (i
= 0; i
< num_sig_handlers
; i
++)
799 CloseHandle(sig_events
[i
]);
800 num_sig_handlers
= 0;
803 if (daemon_winsvc_exitcode
) {
804 svc_status
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
805 svc_status
.dwServiceSpecificExitCode
= daemon_winsvc_exitcode
;
808 service_report_status(SERVICE_STOPPED
, 0);
812 // Variables for passing main(argc, argv) from daemon_main to service_main()
813 static int (*svc_main_func
)(int, char **);
814 static int svc_main_argc
;
815 static char ** svc_main_argv
;
817 // Main function for service, called by service dispatcher
819 static void WINAPI
service_main(DWORD
/*argc*/, LPSTR
* argv
)
821 char path
[MAX_PATH
], *p
;
823 // Register control handler
824 svc_handle
= RegisterServiceCtrlHandler(argv
[0], service_control
);
826 // Init service status
827 svc_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
828 service_report_status(SERVICE_START_PENDING
, 10);
830 // Service started in \windows\system32, change to .exe directory
831 if (GetModuleFileNameA(NULL
, path
, sizeof(path
)) && (p
= strrchr(path
, '\\'))) {
832 *p
= 0; SetCurrentDirectoryA(path
);
835 // Install exit handler
836 atexit(service_exit
);
838 // Do the real work, service status later updated by daemon_detach()
839 daemon_winsvc_exitcode
= svc_main_func(svc_main_argc
, svc_main_argv
);
841 exit(daemon_winsvc_exitcode
);
842 // ... continued in service_exit()
846 /////////////////////////////////////////////////////////////////////////////
847 // Windows Service Admin Functions
850 // Make registry key name for event message file
851 static bool make_evtkey(char * buf
, unsigned size
, const char * ident
)
853 static const char prefix
[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\";
854 const unsigned pfxlen
= sizeof(prefix
)-1;
855 unsigned idlen
= strlen(ident
);
856 if (pfxlen
+ idlen
>= size
) {
857 printf(" Buffer overflow\n");
860 memcpy(buf
, prefix
, pfxlen
);
861 memcpy(buf
+pfxlen
, ident
, idlen
+1);
865 // Install this exe as event message file
866 static void inst_evtmsg(const char * ident
)
868 printf("Installing event message file for %s:", ident
); fflush(stdout
);
870 char mypath
[MAX_PATH
];
871 if (!GetModuleFileNameA((HMODULE
)0, mypath
, sizeof(mypath
))) {
872 printf(" unknown program path, Error=%ld\n", GetLastError());
876 char subkey
[MAX_PATH
];
877 if (!make_evtkey(subkey
, sizeof(subkey
), ident
))
881 LONG err
= RegCreateKeyExA(HKEY_LOCAL_MACHINE
, subkey
, 0, (char *)0, 0, KEY_ALL_ACCESS
,
882 (SECURITY_ATTRIBUTES
*)0, &hk
, (DWORD
*)0);
883 if (err
!= ERROR_SUCCESS
) {
884 printf(" RegCreateKeyEx failed, error=%ld\n", err
);
888 err
= RegSetValueExA(hk
, "EventMessageFile", 0, REG_SZ
,
889 (const BYTE
*)mypath
, strlen(mypath
)+1);
890 if (err
== ERROR_SUCCESS
) {
891 DWORD val
= EVENTLOG_INFORMATION_TYPE
892 |EVENTLOG_WARNING_TYPE
893 |EVENTLOG_ERROR_TYPE
;
894 err
= RegSetValueExA(hk
, "TypesSupported", 0, REG_DWORD
,
895 (const BYTE
*)&val
, sizeof(val
));
897 if (err
!= ERROR_SUCCESS
)
898 printf(" RegSetValueEx failed, error=%ld\n", err
);
904 // Uninstall event message file
905 static void uninst_evtmsg(const char * ident
)
907 printf("Removing event message file for %s:", ident
); fflush(stdout
);
909 char subkey
[MAX_PATH
];
910 if (!make_evtkey(subkey
, sizeof(subkey
), ident
))
913 LONG err
= RegDeleteKeyA(HKEY_LOCAL_MACHINE
, subkey
);
914 if (err
!= ERROR_SUCCESS
&& err
!= ERROR_FILE_NOT_FOUND
) {
915 printf(" RegDeleteKey failed, error=%ld\n", err
);
922 // Service install/remove commands
924 static int svcadm_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
925 int argc
, char **argv
)
927 int remove
; long err
;
932 if (!strcmp(argv
[1], "install"))
934 else if (!strcmp(argv
[1], "remove")) {
936 printf("%s: no arguments allowed for command remove\n", ident
);
944 printf("%s service %s:", (!remove
?"Installing":"Removing"), ident
); fflush(stdout
);
947 if (!(hm
= OpenSCManager(NULL
/*local*/, NULL
/*default*/, SC_MANAGER_ALL_ACCESS
))) {
948 if ((err
= GetLastError()) == ERROR_ACCESS_DENIED
)
949 puts(" access to SCManager denied");
951 printf(" cannot open SCManager, Error=%ld\n", err
);
956 char path
[MAX_PATH
+100];
959 if (!GetModuleFileNameA(NULL
, path
, MAX_PATH
)) {
960 printf(" unknown program path, Error=%ld\n", GetLastError());
961 CloseServiceHandle(hm
);
964 // Add quotes if necessary
965 if (strchr(path
, ' ')) {
967 path
[i
+1] = '"'; path
[i
+2] = 0;
973 strcat(path
, " "); strcat(path
, svc_opts
->cmd_opt
);
974 for (i
= 2; i
< argc
; i
++) {
975 const char * s
= argv
[i
];
976 if (strlen(path
)+1+1+strlen(s
)+1 >= sizeof(path
))
978 // Add quotes if necessary
979 if (strchr(s
, ' ') && !strchr(s
, '"')) {
980 strcat(path
, " \""); strcat(path
, s
); strcat(path
, "\"");
983 strcat(path
, " "); strcat(path
, s
);
987 if (!(hs
= CreateService(hm
,
988 svc_opts
->svcname
, svc_opts
->dispname
,
990 SERVICE_WIN32_OWN_PROCESS
,
991 SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, path
,
992 NULL
/*no load ordering*/, NULL
/*no tag id*/,
993 ""/*no depedencies*/, NULL
/*local system account*/, NULL
/*no pw*/))) {
994 if ((err
= GetLastError()) == ERROR_SERVICE_EXISTS
)
995 puts(" the service is already installed");
996 else if (err
== ERROR_SERVICE_MARKED_FOR_DELETE
)
997 puts(" service is still running and marked for deletion\n"
998 "Stop the service and retry install");
1000 printf(" failed, Error=%ld\n", err
);
1001 CloseServiceHandle(hm
);
1004 // Set optional description
1005 if (svc_opts
->descript
) {
1006 SERVICE_DESCRIPTIONA sd
= { const_cast<char *>(svc_opts
->descript
) };
1007 ChangeServiceConfig2A(hs
, SERVICE_CONFIG_DESCRIPTION
, &sd
);
1009 // Enable delayed auto start if supported
1010 OSVERSIONINFOA ver
; ver
.dwOSVersionInfoSize
= sizeof(ver
);
1011 if ( GetVersionExA(&ver
)
1012 && ver
.dwPlatformId
== VER_PLATFORM_WIN32_NT
1013 && ver
.dwMajorVersion
>= 6 /* Vista */ ) {
1014 SERVICE_DELAYED_AUTO_START_INFO sdasi
= { TRUE
};
1015 ChangeServiceConfig2A(hs
, SERVICE_CONFIG_DELAYED_AUTO_START_INFO
, &sdasi
);
1020 if (!(hs
= OpenService(hm
, svc_opts
->svcname
, SERVICE_ALL_ACCESS
))) {
1022 CloseServiceHandle(hm
);
1025 // TODO: Stop service if running
1027 if (!DeleteService(hs
)) {
1028 if ((err
= GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE
)
1029 puts(" service is still running and marked for deletion\n"
1030 "Stop the service to remove it");
1032 printf(" failed, Error=%ld\n", err
);
1033 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
1038 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
1040 // Install/Remove event message file registry entry
1045 uninst_evtmsg(ident
);
1052 /////////////////////////////////////////////////////////////////////////////
1055 // This function must be called from main()
1056 // main_func is the function doing the real work
1058 int daemon_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
1059 int (*main_func
)(int, char **), int argc
, char **argv
)
1063 // Enable Debug heap checks
1064 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG
)
1065 |_CRTDBG_ALLOC_MEM_DF
|_CRTDBG_CHECK_ALWAYS_DF
|_CRTDBG_LEAK_CHECK_DF
);
1068 // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
1069 if ((rc
= initd_main(ident
, argc
, argv
)) >= 0)
1071 // Check for [install|remove] parameters
1072 if (svc_opts
&& (rc
= svcadm_main(ident
, svc_opts
, argc
, argv
)) >= 0)
1075 // Run as service if svc_opts.cmd_opt is given as first(!) argument
1076 svc_mode
= (svc_opts
&& argc
>= 2 && !strcmp(argv
[1], svc_opts
->cmd_opt
));
1079 // Daemon: Try to simulate a Unix-like daemon
1083 // Create main event to detect process type:
1084 // 1. new: parent process => start child and wait for detach() or exit() of child.
1085 // 2. exists && signaled: child process => do the real work, signal detach() to parent
1086 // 3. exists && !signaled: already running => exit()
1087 if (!(rev
= create_event(EVT_RUNNING
, TRUE
/*signaled*/, TRUE
, &exists
)))
1090 if (!exists
&& !debugging()) {
1091 // Event new => parent process
1092 return parent_main(rev
);
1095 if (WaitForSingleObject(rev
, 0) == WAIT_OBJECT_0
) {
1096 // Event was signaled => In child process
1097 return child_main(rev
, main_func
, argc
, argv
);
1100 // Event no longer signaled => Already running!
1101 daemon_help(stdout
, ident
, "already running");
1106 // Service: Start service_main() via SCM
1107 SERVICE_TABLE_ENTRY service_table
[] = {
1108 { (char*)svc_opts
->svcname
, service_main
}, { NULL
, NULL
}
1111 svc_main_func
= main_func
;
1112 svc_main_argc
= argc
;
1113 svc_main_argv
= argv
;
1114 if (!StartServiceCtrlDispatcher(service_table
)) {
1115 printf("%s: cannot dispatch service, Error=%ld\n"
1116 "Option \"%s\" cannot be used to start %s as a service from console.\n"
1117 "Use \"%s install ...\" to install the service\n"
1118 "and \"net start %s\" to start it.\n",
1119 ident
, GetLastError(), svc_opts
->cmd_opt
, ident
, ident
, ident
);
1123 service_main(argc
, argv
);
1128 ExitThread(0); // Do not redo exit() processing
1135 /////////////////////////////////////////////////////////////////////////////
1140 static volatile sig_atomic_t caughtsig
= 0;
1142 static void sig_handler(int sig
)
1147 static void test_exit(void)
1149 printf("Main exit\n");
1152 int test_main(int argc
, char **argv
)
1158 printf("PID=%ld\n", GetCurrentProcessId());
1159 for (i
= 0; i
< argc
; i
++) {
1160 printf("%d: \"%s\"\n", i
, argv
[i
]);
1161 if (!strcmp(argv
[i
],"-d"))
1164 if (argc
> 1 && argv
[argc
-1][0] != '-')
1167 daemon_signal(SIGINT
, sig_handler
);
1168 daemon_signal(SIGBREAK
, sig_handler
);
1169 daemon_signal(SIGTERM
, sig_handler
);
1170 daemon_signal(SIGHUP
, sig_handler
);
1171 daemon_signal(SIGUSR1
, sig_handler
);
1172 daemon_signal(SIGUSR2
, sig_handler
);
1177 printf("Preparing to detach...\n");
1179 daemon_detach("test");
1180 printf("Detached!\n");
1185 printf("."); fflush(stdout
);
1187 if (caughtsig
== SIGUSR2
) {
1190 daemon_enable_console("Daemon[Debug]");
1192 daemon_disable_console();
1194 else if (caughtsig
== SIGUSR1
&& cmd
) {
1195 char inpbuf
[200], outbuf
[1000]; int rc
;
1196 strcpy(inpbuf
, "Hello\nWorld!\n");
1197 rc
= daemon_spawn(cmd
, inpbuf
, strlen(inpbuf
), outbuf
, sizeof(outbuf
));
1199 daemon_enable_console("Command output");
1200 printf("\"%s\" returns %d\n", cmd
, rc
);
1202 printf("output:\n%s.\n", outbuf
);
1205 Sleep(10000); daemon_disable_console();
1208 printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig
); fflush(stdout
);
1209 if (caughtsig
== SIGTERM
|| caughtsig
== SIGBREAK
)
1214 printf("\nExiting on signal %d\n", caughtsig
);
1219 int main(int argc
, char **argv
)
1221 static const daemon_winsvc_options svc_opts
= {
1222 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1225 return daemon_main("testd", &svc_opts
, test_main
, argc
, argv
);