2 * os_win32/daemon_win32.cpp
4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-14 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 3959 2014-07-18 19:22:18Z chrfranke $"
31 #define WIN32_LEAN_AND_MEAN
38 /////////////////////////////////////////////////////////////////////////////
40 // Prevent spawning of child process if debugging
42 #define debugging() IsDebuggerPresent()
44 #define debugging() FALSE
48 #define EVT_NAME_LEN 260
50 // Internal events (must be > SIGUSRn)
51 #define EVT_RUNNING 100 // Exists when running, signaled on creation
52 #define EVT_DETACHED 101 // Signaled when child detaches from console
53 #define EVT_RESTART 102 // Signaled when child should restart
55 static void make_name(char * name
, int sig
)
58 if (!GetModuleFileNameA(NULL
, name
, EVT_NAME_LEN
-10))
59 strcpy(name
, "DaemonEvent");
60 for (i
= 0; name
[i
]; i
++) {
62 if (!( ('0' <= c
&& c
<= '9')
63 || ('A' <= c
&& c
<= 'Z')
64 || ('a' <= c
&& c
<= 'z')))
67 sprintf(name
+strlen(name
), "-%d", sig
);
71 static HANDLE
create_event(int sig
, BOOL initial
, BOOL errmsg
, BOOL
* exists
)
73 char name
[EVT_NAME_LEN
];
81 if (!(h
= CreateEventA(NULL
, FALSE
, initial
, (name
[0] ? name
: NULL
)))) {
83 fprintf(stderr
, "CreateEvent(.,\"%s\"): Error=%ld\n", name
, GetLastError());
87 if (GetLastError() == ERROR_ALREADY_EXISTS
) {
90 fprintf(stderr
, "CreateEvent(.,\"%s\"): Exists\n", name
);
100 static HANDLE
open_event(int sig
)
102 char name
[EVT_NAME_LEN
];
103 make_name(name
, sig
);
104 return OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
);
108 static int event_exists(int sig
)
110 char name
[EVT_NAME_LEN
];
112 make_name(name
, sig
);
113 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
)))
120 static int sig_event(int sig
)
122 char name
[EVT_NAME_LEN
];
124 make_name(name
, sig
);
125 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
))) {
126 make_name(name
, EVT_RUNNING
);
127 if (!(h
= OpenEvent(EVENT_MODIFY_STATE
, FALSE
, name
)))
138 static void daemon_help(FILE * f
, const char * ident
, const char * message
)
142 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
143 ident
, message
, ident
);
148 /////////////////////////////////////////////////////////////////////////////
152 static BOOL WINAPI
parent_console_handler(DWORD event
)
156 case CTRL_BREAK_EVENT
:
157 return TRUE
; // Ignore
159 return FALSE
; // continue with next handler ...
163 static int parent_main(HANDLE rev
)
169 PROCESS_INFORMATION pi
;
172 // Ignore ^C, ^BREAK in parent
173 SetConsoleCtrlHandler(parent_console_handler
, TRUE
/*add*/);
175 // Create event used by child to signal daemon_detach()
176 if (!(dev
= create_event(EVT_DETACHED
, FALSE
/*not signaled*/, TRUE
, NULL
/*must not exist*/))) {
181 // Restart process with same args
182 cmdline
= GetCommandLineA();
183 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
187 NULL
, NULL
, TRUE
/*inherit*/,
188 0, NULL
, NULL
, &si
, &pi
)) {
189 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
190 CloseHandle(rev
); CloseHandle(dev
);
193 CloseHandle(pi
.hThread
);
195 // Wait for daemon_detach() or exit()
196 ht
[0] = dev
; ht
[1] = pi
.hProcess
;
197 rc
= WaitForMultipleObjects(2, ht
, FALSE
/*or*/, INFINITE
);
198 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+2)) {
199 fprintf(stderr
, "WaitForMultipleObjects returns %lX\n", rc
);
200 TerminateProcess(pi
.hProcess
, 200);
202 CloseHandle(rev
); CloseHandle(dev
);
205 if (!GetExitCodeProcess(pi
.hProcess
, &exitcode
))
207 else if (exitcode
== STILL_ACTIVE
) // detach()ed, assume OK
210 CloseHandle(pi
.hProcess
);
215 /////////////////////////////////////////////////////////////////////////////
219 static int svc_mode
; // Running as service?
220 static int svc_paused
; // Service paused?
222 static void service_report_status(int state
, int waithint
);
225 // Tables of signal handler and corresponding events
226 typedef void (*sigfunc_t
)(int);
228 #define MAX_SIG_HANDLERS 8
230 static int num_sig_handlers
= 0;
231 static sigfunc_t sig_handlers
[MAX_SIG_HANDLERS
];
232 static int sig_numbers
[MAX_SIG_HANDLERS
];
233 static HANDLE sig_events
[MAX_SIG_HANDLERS
];
235 static HANDLE sighup_handle
, sigint_handle
, sigbreak_handle
;
236 static HANDLE sigterm_handle
, sigusr1_handle
;
238 static HANDLE running_event
;
240 static int reopen_stdin
, reopen_stdout
, reopen_stderr
;
243 // Handler for windows console events
245 static BOOL WINAPI
child_console_handler(DWORD event
)
247 // Caution: runs in a new thread
248 // TODO: Guard with a mutex
251 case CTRL_C_EVENT
: // <CONTROL-C> (SIGINT)
252 h
= sigint_handle
; break;
253 case CTRL_BREAK_EVENT
: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
254 case CTRL_CLOSE_EVENT
: // User closed console or abort via task manager
255 h
= sigbreak_handle
; break;
256 case CTRL_LOGOFF_EVENT
: // Logout/Shutdown (SIGTERM)
257 case CTRL_SHUTDOWN_EVENT
:
258 h
= sigterm_handle
; break;
261 return FALSE
; // continue with next handler
269 static void child_exit(void)
275 PROCESS_INFORMATION pi
;
277 for (i
= 0; i
< num_sig_handlers
; i
++)
278 CloseHandle(sig_events
[i
]);
279 num_sig_handlers
= 0;
280 CloseHandle(running_event
); running_event
= 0;
283 if (!(rst
= open_event(EVT_RESTART
)))
284 return; // No => normal exit
286 // Yes => Signal exit and restart process
292 cmdline
= GetCommandLineA();
293 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
294 si
.dwFlags
= STARTF_USESHOWWINDOW
; si
.wShowWindow
= SW_HIDE
;
298 NULL
, NULL
, TRUE
/*inherit*/,
299 0, NULL
, NULL
, &si
, &pi
)) {
300 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
302 CloseHandle(pi
.hThread
); CloseHandle(pi
.hProcess
);
305 static int child_main(HANDLE hev
,int (*main_func
)(int, char **), int argc
, char **argv
)
307 // Keep EVT_RUNNING open until exit
310 // Install console handler
311 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
313 // Install restart handler
316 // Continue in main_func() to do the real work
317 return main_func(argc
, argv
);
323 sigfunc_t
daemon_signal(int sig
, sigfunc_t func
)
327 if (func
== SIG_DFL
|| func
== SIG_IGN
)
329 for (i
= 0; i
< num_sig_handlers
; i
++) {
330 if (sig_numbers
[i
] == sig
) {
331 sigfunc_t old
= sig_handlers
[i
];
332 sig_handlers
[i
] = func
;
336 if (num_sig_handlers
>= MAX_SIG_HANDLERS
)
338 if (!(h
= create_event((!svc_mode
? sig
: -1), FALSE
, TRUE
, NULL
)))
340 sig_events
[num_sig_handlers
] = h
;
341 sig_numbers
[num_sig_handlers
] = sig
;
342 sig_handlers
[num_sig_handlers
] = func
;
344 case SIGHUP
: sighup_handle
= h
; break;
345 case SIGINT
: sigint_handle
= h
; break;
346 case SIGTERM
: sigterm_handle
= h
; break;
347 case SIGBREAK
: sigbreak_handle
= h
; break;
348 case SIGUSR1
: sigusr1_handle
= h
; break;
357 const char * daemon_strsignal(int sig
)
360 case SIGHUP
: return "SIGHUP";
361 case SIGINT
: return "SIGINT";
362 case SIGTERM
: return "SIGTERM";
363 case SIGBREAK
:return "SIGBREAK";
364 case SIGUSR1
: return "SIGUSR1";
365 case SIGUSR2
: return "SIGUSR2";
366 default: return "*UNKNOWN*";
373 void daemon_sleep(int seconds
)
376 if (num_sig_handlers
<= 0) {
377 Sleep(seconds
*1000L);
380 // Wait for any signal or timeout
381 DWORD rc
= WaitForMultipleObjects(num_sig_handlers
, sig_events
,
382 FALSE
/*OR*/, seconds
*1000L);
383 if (rc
!= WAIT_TIMEOUT
) {
384 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+(unsigned)num_sig_handlers
)) {
385 fprintf(stderr
,"WaitForMultipleObjects returns %lu\n", rc
);
386 Sleep(seconds
*1000L);
390 sig_handlers
[rc
-WAIT_OBJECT_0
](sig_numbers
[rc
-WAIT_OBJECT_0
]);
394 } while (svc_paused
);
398 // Disable/Enable console
400 void daemon_disable_console()
402 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
403 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
404 if (isatty(fileno(stdin
))) {
405 fclose(stdin
); reopen_stdin
= 1;
407 if (isatty(fileno(stdout
))) {
408 fclose(stdout
); reopen_stdout
= 1;
410 if (isatty(fileno(stderr
))) {
411 fclose(stderr
); reopen_stderr
= 1;
414 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
417 int daemon_enable_console(const char * title
)
420 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
422 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
426 SetConsoleTitleA(title
);
428 freopen("conin$", "r", stdin
);
430 freopen("conout$", "w", stdout
);
432 freopen("conout$", "w", stderr
);
433 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
438 // Detach daemon from console & parent
440 int daemon_detach(const char * ident
)
445 FILE * f
= ( isatty(fileno(stdout
)) ? stdout
446 : isatty(fileno(stderr
)) ? stderr
: NULL
);
448 daemon_help(f
, ident
, "now detaches from console into background mode");
450 // Signal detach to parent
451 if (sig_event(EVT_DETACHED
) != 1) {
455 daemon_disable_console();
458 // Signal end of initialization to service control manager
459 service_report_status(SERVICE_RUNNING
, 0);
460 reopen_stdin
= reopen_stdout
= reopen_stderr
= 1;
467 /////////////////////////////////////////////////////////////////////////////
469 // Spawn a command and redirect <inpbuf >outbuf
470 // return command's exitcode or -1 on error
472 int daemon_spawn(const char * cmd
,
473 const char * inpbuf
, int inpsize
,
474 char * outbuf
, int outsize
)
476 HANDLE self
= GetCurrentProcess();
478 // Create stdin pipe with inheritable read side
479 SECURITY_ATTRIBUTES sa
;
480 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
481 sa
.bInheritHandle
= TRUE
;
482 HANDLE pipe_inp_r
, pipe_inp_w
, h
;
483 if (!CreatePipe(&pipe_inp_r
, &h
, &sa
/*inherit*/, inpsize
*2+13))
485 if (!DuplicateHandle(self
, h
, self
, &pipe_inp_w
,
486 0, FALSE
/*!inherit*/, DUPLICATE_SAME_ACCESS
|DUPLICATE_CLOSE_SOURCE
)) {
487 CloseHandle(pipe_inp_r
);
491 // Create stdout pipe with inheritable write side
492 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
493 sa
.bInheritHandle
= TRUE
;
495 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
)) {
496 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
501 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
502 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
503 CloseHandle(pipe_out_w
); CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
507 // Create stderr handle as dup of stdout write side
509 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
510 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
511 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
512 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
516 // Create process with pipes as stdio
518 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
519 si
.hStdInput
= pipe_inp_r
;
520 si
.hStdOutput
= pipe_out_w
;
521 si
.hStdError
= pipe_err_w
;
522 si
.dwFlags
= STARTF_USESTDHANDLES
;
523 PROCESS_INFORMATION pi
;
526 NULL
, NULL
, TRUE
/*inherit*/,
527 CREATE_NO_WINDOW
, // DETACHED_PROCESS does not work
528 NULL
, NULL
, &si
, &pi
)) {
529 CloseHandle(pipe_err_w
);
530 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
531 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
534 CloseHandle(pi
.hThread
);
535 // Close inherited handles
536 CloseHandle(pipe_inp_r
);
537 CloseHandle(pipe_out_w
);
538 CloseHandle(pipe_err_w
);
540 // Copy inpbuf to stdin
541 // convert \n => \r\n
544 for (i
= 0; i
< inpsize
; ) {
546 while (i
+len
< inpsize
&& inpbuf
[i
+len
] != '\n')
549 WriteFile(pipe_inp_w
, inpbuf
+i
, len
, &num_io
, NULL
);
552 WriteFile(pipe_inp_w
, "\r\n", 2, &num_io
, NULL
);
556 CloseHandle(pipe_inp_w
);
558 // Copy stdout to output buffer until full, rest to /dev/null
559 // convert \r\n => \n
562 if (!ReadFile(pipe_out_r
, buf
, sizeof(buf
), &num_io
, NULL
) || num_io
== 0)
564 for (int j
= 0; i
< outsize
-1 && j
< (int)num_io
; j
++) {
566 outbuf
[i
++] = buf
[j
];
570 CloseHandle(pipe_out_r
);
572 // Wait for process exitcode
574 WaitForSingleObject(pi
.hProcess
, INFINITE
);
575 GetExitCodeProcess(pi
.hProcess
, &exitcode
);
576 CloseHandle(pi
.hProcess
);
581 /////////////////////////////////////////////////////////////////////////////
584 static int wait_signaled(HANDLE h
, int seconds
)
588 if (WaitForSingleObject(h
, 1000L) == WAIT_OBJECT_0
)
592 fputchar('.'); fflush(stdout
);
597 static int wait_evt_running(int seconds
, int exists
)
600 if (event_exists(EVT_RUNNING
) == exists
)
604 if (event_exists(EVT_RUNNING
) == exists
)
608 fputchar('.'); fflush(stdout
);
613 static int is_initd_command(char * s
)
615 if (!strcmp(s
, "status"))
617 if (!strcmp(s
, "stop"))
619 if (!strcmp(s
, "reload"))
621 if (!strcmp(s
, "sigusr1"))
623 if (!strcmp(s
, "sigusr2"))
625 if (!strcmp(s
, "restart"))
631 static int initd_main(const char * ident
, int argc
, char **argv
)
636 if ((rc
= is_initd_command(argv
[1])) < 0)
639 printf("%s: no arguments allowed for command %s\n", ident
, argv
[1]);
646 printf("Checking for %s:", ident
); fflush(stdout
);
647 rc
= event_exists(EVT_RUNNING
);
648 puts(rc
? " running" : " not running");
652 printf("Stopping %s:", ident
); fflush(stdout
);
653 rc
= sig_event(SIGTERM
);
655 puts(rc
< 0 ? " not running" : " error");
656 return (rc
< 0 ? 0 : 1);
658 rc
= wait_evt_running(10, 0);
659 puts(!rc
? " done" : " timeout");
660 return (!rc
? 0 : 1);
663 printf("Reloading %s:", ident
); fflush(stdout
);
664 rc
= sig_event(SIGHUP
);
665 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
666 return (rc
> 0 ? 0 : 1);
670 printf("Sending SIGUSR%d to %s:", (rc
-SIGUSR1
+1), ident
); fflush(stdout
);
672 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
673 return (rc
> 0 ? 0 : 1);
678 printf("Stopping %s:", ident
); fflush(stdout
);
679 if (event_exists(EVT_DETACHED
)) {
680 puts(" not detached, cannot restart");
683 if (!(rst
= create_event(EVT_RESTART
, FALSE
, FALSE
, NULL
))) {
687 rc
= sig_event(SIGTERM
);
689 puts(rc
< 0 ? " not running" : " error");
693 rc
= wait_signaled(rst
, 10);
702 printf("Starting %s:", ident
); fflush(stdout
);
703 rc
= wait_evt_running(10, 1);
704 puts(!rc
? " done" : " error");
705 return (!rc
? 0 : 1);
711 /////////////////////////////////////////////////////////////////////////////
712 // Windows Service Functions
714 int daemon_winsvc_exitcode
; // Set by app to exit(code)
716 static SERVICE_STATUS_HANDLE svc_handle
;
717 static SERVICE_STATUS svc_status
;
720 // Report status to SCM
722 static void service_report_status(int state
, int seconds
)
725 static DWORD checkpoint
= 1;
726 svc_status
.dwCurrentState
= state
;
727 svc_status
.dwWaitHint
= seconds
*1000;
730 svc_status
.dwCheckPoint
= checkpoint
++;
732 case SERVICE_RUNNING
:
733 case SERVICE_STOPPED
:
734 svc_status
.dwCheckPoint
= 0;
737 case SERVICE_START_PENDING
:
738 case SERVICE_STOP_PENDING
:
739 svc_status
.dwControlsAccepted
= 0;
742 svc_status
.dwControlsAccepted
=
743 SERVICE_ACCEPT_STOP
|SERVICE_ACCEPT_SHUTDOWN
|
744 SERVICE_ACCEPT_PAUSE_CONTINUE
|SERVICE_ACCEPT_PARAMCHANGE
;
747 SetServiceStatus(svc_handle
, &svc_status
);
751 // Control the service, called by SCM
753 static void WINAPI
service_control(DWORD ctrlcode
)
756 case SERVICE_CONTROL_STOP
:
757 case SERVICE_CONTROL_SHUTDOWN
:
758 service_report_status(SERVICE_STOP_PENDING
, 30);
760 SetEvent(sigterm_handle
);
762 case SERVICE_CONTROL_PARAMCHANGE
: // Win2000/XP
763 service_report_status(svc_status
.dwCurrentState
, 0);
765 SetEvent(sighup_handle
); // reload
767 case SERVICE_CONTROL_PAUSE
:
768 service_report_status(SERVICE_PAUSED
, 0);
771 case SERVICE_CONTROL_CONTINUE
:
772 service_report_status(SERVICE_RUNNING
, 0);
774 int was_paused
= svc_paused
;
776 SetEvent(was_paused
? sighup_handle
: sigusr1_handle
); // reload:recheck
779 case SERVICE_CONTROL_INTERROGATE
:
781 service_report_status(svc_status
.dwCurrentState
, 0);
787 // Exit handler for service
789 static void service_exit(void)
791 // Close signal events
793 for (i
= 0; i
< num_sig_handlers
; i
++)
794 CloseHandle(sig_events
[i
]);
795 num_sig_handlers
= 0;
798 if (daemon_winsvc_exitcode
) {
799 svc_status
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
800 svc_status
.dwServiceSpecificExitCode
= daemon_winsvc_exitcode
;
803 service_report_status(SERVICE_STOPPED
, 0);
807 // Variables for passing main(argc, argv) from daemon_main to service_main()
808 static int (*svc_main_func
)(int, char **);
809 static int svc_main_argc
;
810 static char ** svc_main_argv
;
812 // Main function for service, called by service dispatcher
814 static void WINAPI
service_main(DWORD
/*argc*/, LPSTR
* argv
)
816 char path
[MAX_PATH
], *p
;
818 // Register control handler
819 svc_handle
= RegisterServiceCtrlHandler(argv
[0], service_control
);
821 // Init service status
822 svc_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
823 service_report_status(SERVICE_START_PENDING
, 10);
825 // Service started in \windows\system32, change to .exe directory
826 if (GetModuleFileNameA(NULL
, path
, sizeof(path
)) && (p
= strrchr(path
, '\\'))) {
827 *p
= 0; SetCurrentDirectoryA(path
);
830 // Install exit handler
831 atexit(service_exit
);
833 // Do the real work, service status later updated by daemon_detach()
834 daemon_winsvc_exitcode
= svc_main_func(svc_main_argc
, svc_main_argv
);
836 exit(daemon_winsvc_exitcode
);
837 // ... continued in service_exit()
841 /////////////////////////////////////////////////////////////////////////////
842 // Windows Service Admin Functions
845 // Make registry key name for event message file
846 static bool make_evtkey(char * buf
, unsigned size
, const char * ident
)
848 static const char prefix
[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\";
849 const unsigned pfxlen
= sizeof(prefix
)-1;
850 unsigned idlen
= strlen(ident
);
851 if (pfxlen
+ idlen
>= size
) {
852 printf(" Buffer overflow\n");
855 memcpy(buf
, prefix
, pfxlen
);
856 memcpy(buf
+pfxlen
, ident
, idlen
+1);
860 // Install this exe as event message file
861 static void inst_evtmsg(const char * ident
)
863 printf("Installing event message file for %s:", ident
); fflush(stdout
);
865 char mypath
[MAX_PATH
];
866 if (!GetModuleFileNameA((HMODULE
)0, mypath
, sizeof(mypath
))) {
867 printf(" unknown program path, Error=%ld\n", GetLastError());
871 char subkey
[MAX_PATH
];
872 if (!make_evtkey(subkey
, sizeof(subkey
), ident
))
876 LONG err
= RegCreateKeyExA(HKEY_LOCAL_MACHINE
, subkey
, 0, (char *)0, 0, KEY_ALL_ACCESS
,
877 (SECURITY_ATTRIBUTES
*)0, &hk
, (DWORD
*)0);
878 if (err
!= ERROR_SUCCESS
) {
879 printf(" RegCreateKeyEx failed, error=%ld\n", err
);
883 err
= RegSetValueExA(hk
, "EventMessageFile", 0, REG_SZ
,
884 (const BYTE
*)mypath
, strlen(mypath
)+1);
885 if (err
== ERROR_SUCCESS
) {
886 DWORD val
= EVENTLOG_INFORMATION_TYPE
887 |EVENTLOG_WARNING_TYPE
888 |EVENTLOG_ERROR_TYPE
;
889 err
= RegSetValueExA(hk
, "TypesSupported", 0, REG_DWORD
,
890 (const BYTE
*)&val
, sizeof(val
));
892 if (err
!= ERROR_SUCCESS
)
893 printf(" RegSetValueEx failed, error=%ld\n", err
);
899 // Uninstall event message file
900 static void uninst_evtmsg(const char * ident
)
902 printf("Removing event message file for %s:", ident
); fflush(stdout
);
904 char subkey
[MAX_PATH
];
905 if (!make_evtkey(subkey
, sizeof(subkey
), ident
))
908 LONG err
= RegDeleteKeyA(HKEY_LOCAL_MACHINE
, subkey
);
909 if (err
!= ERROR_SUCCESS
&& err
!= ERROR_FILE_NOT_FOUND
) {
910 printf(" RegDeleteKey failed, error=%ld\n", err
);
917 // Service install/remove commands
919 static int svcadm_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
920 int argc
, char **argv
)
922 int remove
; long err
;
927 if (!strcmp(argv
[1], "install"))
929 else if (!strcmp(argv
[1], "remove")) {
931 printf("%s: no arguments allowed for command remove\n", ident
);
939 printf("%s service %s:", (!remove
?"Installing":"Removing"), ident
); fflush(stdout
);
942 if (!(hm
= OpenSCManager(NULL
/*local*/, NULL
/*default*/, SC_MANAGER_ALL_ACCESS
))) {
943 if ((err
= GetLastError()) == ERROR_ACCESS_DENIED
)
944 puts(" access to SCManager denied");
946 printf(" cannot open SCManager, Error=%ld\n", err
);
951 char path
[MAX_PATH
+100];
954 if (!GetModuleFileNameA(NULL
, path
, MAX_PATH
)) {
955 printf(" unknown program path, Error=%ld\n", GetLastError());
956 CloseServiceHandle(hm
);
959 // Add quotes if necessary
960 if (strchr(path
, ' ')) {
962 path
[i
+1] = '"'; path
[i
+2] = 0;
968 strcat(path
, " "); strcat(path
, svc_opts
->cmd_opt
);
969 for (i
= 2; i
< argc
; i
++) {
970 const char * s
= argv
[i
];
971 if (strlen(path
)+1+1+strlen(s
)+1 >= sizeof(path
))
973 // Add quotes if necessary
974 if (strchr(s
, ' ') && !strchr(s
, '"')) {
975 strcat(path
, " \""); strcat(path
, s
); strcat(path
, "\"");
978 strcat(path
, " "); strcat(path
, s
);
982 if (!(hs
= CreateService(hm
,
983 svc_opts
->svcname
, svc_opts
->dispname
,
985 SERVICE_WIN32_OWN_PROCESS
,
986 SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, path
,
987 NULL
/*no load ordering*/, NULL
/*no tag id*/,
988 ""/*no depedencies*/, NULL
/*local system account*/, NULL
/*no pw*/))) {
989 if ((err
= GetLastError()) == ERROR_SERVICE_EXISTS
)
990 puts(" the service is already installed");
991 else if (err
== ERROR_SERVICE_MARKED_FOR_DELETE
)
992 puts(" service is still running and marked for deletion\n"
993 "Stop the service and retry install");
995 printf(" failed, Error=%ld\n", err
);
996 CloseServiceHandle(hm
);
999 // Set optional description
1000 if (svc_opts
->descript
) {
1001 SERVICE_DESCRIPTIONA sd
= { const_cast<char *>(svc_opts
->descript
) };
1002 ChangeServiceConfig2A(hs
, SERVICE_CONFIG_DESCRIPTION
, &sd
);
1004 // Enable delayed auto start if supported
1005 OSVERSIONINFOA ver
; ver
.dwOSVersionInfoSize
= sizeof(ver
);
1006 if ( GetVersionExA(&ver
)
1007 && ver
.dwPlatformId
== VER_PLATFORM_WIN32_NT
1008 && ver
.dwMajorVersion
>= 6 /* Vista */ ) {
1009 // SERVICE_{,CONFIG_}DELAYED_AUTO_START_INFO are missing in older MinGW headers
1010 struct /* SERVICE_DELAYED_AUTO_START_INFO */ {
1011 BOOL fDelayedAutostart
;
1013 // typedef char ASSERT_sizeof_sdasi[sizeof(sdasi) == sizeof(SERVICE_DELAYED_AUTO_START_INFO) ? 1 : -1];
1014 // typedef char ASSERT_const_scdasi[SERVICE_CONFIG_DELAYED_AUTO_START_INFO == 3 ? 1 : -1];
1015 ChangeServiceConfig2A(hs
, 3 /* 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
);