2 * os_win32/daemon_win32.cpp
4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-10 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, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 // Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
21 #define _WIN32_WINNT WINVER
28 #define WIN32_LEAN_AND_MEAN
34 #include "daemon_win32.h"
36 const char * daemon_win32_cpp_cvsid
= "$Id: daemon_win32.cpp 3267 2011-02-21 17:14:25Z chrfranke $"
40 /////////////////////////////////////////////////////////////////////////////
42 #define ARGUSED(x) ((void)(x))
44 // Prevent spawning of child process if debugging
46 #define debugging() IsDebuggerPresent()
48 #define debugging() FALSE
52 #define EVT_NAME_LEN 260
54 // Internal events (must be > SIGUSRn)
55 #define EVT_RUNNING 100 // Exists when running, signaled on creation
56 #define EVT_DETACHED 101 // Signaled when child detaches from console
57 #define EVT_RESTART 102 // Signaled when child should restart
59 static void make_name(char * name
, int sig
)
62 if (!GetModuleFileNameA(NULL
, name
, EVT_NAME_LEN
-10))
63 strcpy(name
, "DaemonEvent");
64 for (i
= 0; name
[i
]; i
++) {
66 if (!( ('0' <= c
&& c
<= '9')
67 || ('A' <= c
&& c
<= 'Z')
68 || ('a' <= c
&& c
<= 'z')))
71 sprintf(name
+strlen(name
), "-%d", sig
);
75 static HANDLE
create_event(int sig
, BOOL initial
, BOOL errmsg
, BOOL
* exists
)
77 char name
[EVT_NAME_LEN
];
85 if (!(h
= CreateEventA(NULL
, FALSE
, initial
, (name
[0] ? name
: NULL
)))) {
87 fprintf(stderr
, "CreateEvent(.,\"%s\"): Error=%ld\n", name
, GetLastError());
91 if (GetLastError() == ERROR_ALREADY_EXISTS
) {
94 fprintf(stderr
, "CreateEvent(.,\"%s\"): Exists\n", name
);
104 static HANDLE
open_event(int sig
)
106 char name
[EVT_NAME_LEN
];
107 make_name(name
, sig
);
108 return OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
);
112 static int event_exists(int sig
)
114 char name
[EVT_NAME_LEN
];
116 make_name(name
, sig
);
117 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
)))
124 static int sig_event(int sig
)
126 char name
[EVT_NAME_LEN
];
128 make_name(name
, sig
);
129 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
))) {
130 make_name(name
, EVT_RUNNING
);
131 if (!(h
= OpenEvent(EVENT_MODIFY_STATE
, FALSE
, name
)))
142 static void daemon_help(FILE * f
, const char * ident
, const char * message
)
146 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
147 ident
, message
, ident
);
152 /////////////////////////////////////////////////////////////////////////////
156 static BOOL WINAPI
parent_console_handler(DWORD event
)
160 case CTRL_BREAK_EVENT
:
161 return TRUE
; // Ignore
163 return FALSE
; // continue with next handler ...
167 static int parent_main(HANDLE rev
)
173 PROCESS_INFORMATION pi
;
176 // Ignore ^C, ^BREAK in parent
177 SetConsoleCtrlHandler(parent_console_handler
, TRUE
/*add*/);
179 // Create event used by child to signal daemon_detach()
180 if (!(dev
= create_event(EVT_DETACHED
, FALSE
/*not signaled*/, TRUE
, NULL
/*must not exist*/))) {
185 // Restart process with same args
186 cmdline
= GetCommandLineA();
187 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
191 NULL
, NULL
, TRUE
/*inherit*/,
192 0, NULL
, NULL
, &si
, &pi
)) {
193 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
194 CloseHandle(rev
); CloseHandle(dev
);
197 CloseHandle(pi
.hThread
);
199 // Wait for daemon_detach() or exit()
200 ht
[0] = dev
; ht
[1] = pi
.hProcess
;
201 rc
= WaitForMultipleObjects(2, ht
, FALSE
/*or*/, INFINITE
);
202 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+2)) {
203 fprintf(stderr
, "WaitForMultipleObjects returns %lX\n", rc
);
204 TerminateProcess(pi
.hProcess
, 200);
206 CloseHandle(rev
); CloseHandle(dev
);
209 if (!GetExitCodeProcess(pi
.hProcess
, &exitcode
))
211 else if (exitcode
== STILL_ACTIVE
) // detach()ed, assume OK
214 CloseHandle(pi
.hProcess
);
219 /////////////////////////////////////////////////////////////////////////////
223 static int svc_mode
; // Running as service?
224 static int svc_paused
; // Service paused?
226 static void service_report_status(int state
, int waithint
);
229 // Tables of signal handler and corresponding events
230 typedef void (*sigfunc_t
)(int);
232 #define MAX_SIG_HANDLERS 8
234 static int num_sig_handlers
= 0;
235 static sigfunc_t sig_handlers
[MAX_SIG_HANDLERS
];
236 static int sig_numbers
[MAX_SIG_HANDLERS
];
237 static HANDLE sig_events
[MAX_SIG_HANDLERS
];
239 static HANDLE sighup_handle
, sigint_handle
, sigbreak_handle
;
240 static HANDLE sigterm_handle
, sigusr1_handle
;
242 static HANDLE running_event
;
244 static int reopen_stdin
, reopen_stdout
, reopen_stderr
;
247 // Handler for windows console events
249 static BOOL WINAPI
child_console_handler(DWORD event
)
251 // Caution: runs in a new thread
252 // TODO: Guard with a mutex
255 case CTRL_C_EVENT
: // <CONTROL-C> (SIGINT)
256 h
= sigint_handle
; break;
257 case CTRL_BREAK_EVENT
: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
258 case CTRL_CLOSE_EVENT
: // User closed console or abort via task manager
259 h
= sigbreak_handle
; break;
260 case CTRL_LOGOFF_EVENT
: // Logout/Shutdown (SIGTERM)
261 case CTRL_SHUTDOWN_EVENT
:
262 h
= sigterm_handle
; break;
265 return FALSE
; // continue with next handler
273 static void child_exit(void)
279 PROCESS_INFORMATION pi
;
281 for (i
= 0; i
< num_sig_handlers
; i
++)
282 CloseHandle(sig_events
[i
]);
283 num_sig_handlers
= 0;
284 CloseHandle(running_event
); running_event
= 0;
287 if (!(rst
= open_event(EVT_RESTART
)))
288 return; // No => normal exit
290 // Yes => Signal exit and restart process
296 cmdline
= GetCommandLineA();
297 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
298 si
.dwFlags
= STARTF_USESHOWWINDOW
; si
.wShowWindow
= SW_HIDE
;
302 NULL
, NULL
, TRUE
/*inherit*/,
303 0, NULL
, NULL
, &si
, &pi
)) {
304 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
306 CloseHandle(pi
.hThread
); CloseHandle(pi
.hProcess
);
309 static int child_main(HANDLE hev
,int (*main_func
)(int, char **), int argc
, char **argv
)
311 // Keep EVT_RUNNING open until exit
314 // Install console handler
315 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
317 // Install restart handler
320 // Continue in main_func() to do the real work
321 return main_func(argc
, argv
);
327 sigfunc_t
daemon_signal(int sig
, sigfunc_t func
)
331 if (func
== SIG_DFL
|| func
== SIG_IGN
)
333 for (i
= 0; i
< num_sig_handlers
; i
++) {
334 if (sig_numbers
[i
] == sig
) {
335 sigfunc_t old
= sig_handlers
[i
];
336 sig_handlers
[i
] = func
;
340 if (num_sig_handlers
>= MAX_SIG_HANDLERS
)
342 if (!(h
= create_event((!svc_mode
? sig
: -1), FALSE
, TRUE
, NULL
)))
344 sig_events
[num_sig_handlers
] = h
;
345 sig_numbers
[num_sig_handlers
] = sig
;
346 sig_handlers
[num_sig_handlers
] = func
;
348 case SIGHUP
: sighup_handle
= h
; break;
349 case SIGINT
: sigint_handle
= h
; break;
350 case SIGTERM
: sigterm_handle
= h
; break;
351 case SIGBREAK
: sigbreak_handle
= h
; break;
352 case SIGUSR1
: sigusr1_handle
= h
; break;
361 const char * daemon_strsignal(int sig
)
364 case SIGHUP
: return "SIGHUP";
365 case SIGINT
: return "SIGINT";
366 case SIGTERM
: return "SIGTERM";
367 case SIGBREAK
:return "SIGBREAK";
368 case SIGUSR1
: return "SIGUSR1";
369 case SIGUSR2
: return "SIGUSR2";
370 default: return "*UNKNOWN*";
377 void daemon_sleep(int seconds
)
380 if (num_sig_handlers
<= 0) {
381 Sleep(seconds
*1000L);
384 // Wait for any signal or timeout
385 DWORD rc
= WaitForMultipleObjects(num_sig_handlers
, sig_events
,
386 FALSE
/*OR*/, seconds
*1000L);
387 if (rc
!= WAIT_TIMEOUT
) {
388 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+(unsigned)num_sig_handlers
)) {
389 fprintf(stderr
,"WaitForMultipleObjects returns %lu\n", rc
);
390 Sleep(seconds
*1000L);
394 sig_handlers
[rc
-WAIT_OBJECT_0
](sig_numbers
[rc
-WAIT_OBJECT_0
]);
398 } while (svc_paused
);
402 // Disable/Enable console
404 void daemon_disable_console()
406 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
407 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
408 if (isatty(fileno(stdin
))) {
409 fclose(stdin
); reopen_stdin
= 1;
411 if (isatty(fileno(stdout
))) {
412 fclose(stdout
); reopen_stdout
= 1;
414 if (isatty(fileno(stderr
))) {
415 fclose(stderr
); reopen_stderr
= 1;
418 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
421 int daemon_enable_console(const char * title
)
424 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
426 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
430 SetConsoleTitleA(title
);
432 freopen("conin$", "r", stdin
);
434 freopen("conout$", "w", stdout
);
436 freopen("conout$", "w", stderr
);
437 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
442 // Detach daemon from console & parent
444 int daemon_detach(const char * ident
)
449 FILE * f
= ( isatty(fileno(stdout
)) ? stdout
450 : isatty(fileno(stderr
)) ? stderr
: NULL
);
452 daemon_help(f
, ident
, "now detaches from console into background mode");
454 // Signal detach to parent
455 if (sig_event(EVT_DETACHED
) != 1) {
459 daemon_disable_console();
462 // Signal end of initialization to service control manager
463 service_report_status(SERVICE_RUNNING
, 0);
464 reopen_stdin
= reopen_stdout
= reopen_stderr
= 1;
471 /////////////////////////////////////////////////////////////////////////////
475 //MT runtime not necessary, because mbox_thread uses no unsafe lib functions
476 //#error Program must be linked with multithreaded runtime library
479 static LONG mbox_count
; // # mbox_thread()s
480 static HANDLE mbox_mutex
; // Show 1 box at a time (not necessary for service)
482 typedef struct mbox_args_s
{
483 HANDLE taken
; const char * title
, * text
; int mode
;
487 // Thread to display one message box
489 static ULONG WINAPI
mbox_thread(LPVOID arg
)
492 mbox_args
* mb
= (mbox_args
*)arg
;
493 char title
[100]; char text
[1000]; int mode
;
494 strncpy(title
, mb
->title
, sizeof(title
)-1); title
[sizeof(title
)-1] = 0;
495 strncpy(text
, mb
->text
, sizeof(text
)-1); text
[sizeof(text
)-1] = 0;
499 // Show only one box at a time
500 WaitForSingleObject(mbox_mutex
, INFINITE
);
501 MessageBoxA(NULL
, text
, title
, mode
);
502 ReleaseMutex(mbox_mutex
);
504 InterlockedDecrement(&mbox_count
);
509 // Display a message box
510 int daemon_messagebox(int system
, const char * title
, const char * text
)
513 HANDLE ht
; DWORD tid
;
515 // Create mutex during first call
517 mbox_mutex
= CreateMutex(NULL
/*!inherit*/, FALSE
/*!owned*/, NULL
/*unnamed*/);
519 // Allow at most 10 threads
520 if (InterlockedIncrement(&mbox_count
) > 10) {
521 InterlockedDecrement(&mbox_count
);
526 mb
.taken
= CreateEvent(NULL
/*!inherit*/, FALSE
, FALSE
/*!signaled*/, NULL
/*unnamed*/);
527 mb
.mode
= MB_OK
|MB_ICONWARNING
528 |(svc_mode
?MB_SERVICE_NOTIFICATION
:0)
529 |(system
?MB_SYSTEMMODAL
:MB_APPLMODAL
);
532 if (!(ht
= CreateThread(NULL
, 0, mbox_thread
, &mb
, 0, &tid
)))
535 // Wait for args taken
536 if (WaitForSingleObject(mb
.taken
, 10000) != WAIT_OBJECT_0
)
537 TerminateThread(ht
, 0);
538 CloseHandle(mb
.taken
);
544 /////////////////////////////////////////////////////////////////////////////
546 // Spawn a command and redirect <inpbuf >outbuf
547 // return command's exitcode or -1 on error
549 int daemon_spawn(const char * cmd
,
550 const char * inpbuf
, int inpsize
,
551 char * outbuf
, int outsize
)
553 HANDLE pipe_inp_r
, pipe_inp_w
, pipe_out_r
, pipe_out_w
, pipe_err_w
, h
;
554 char temp_path
[MAX_PATH
];
555 DWORD flags
, num_io
, exitcode
;
556 int use_file
, state
, i
;
557 SECURITY_ATTRIBUTES sa
;
558 STARTUPINFO si
; PROCESS_INFORMATION pi
;
559 HANDLE self
= GetCurrentProcess();
561 if (GetVersion() & 0x80000000L
) {
562 // Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or
563 // any other DOS program is redirected via a pipe. Using a temp file instead.
564 use_file
= 1; flags
= DETACHED_PROCESS
;
567 // NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window
568 // for each external command in a redirected .BAT file.
569 // Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work.
570 use_file
= 0; flags
= CREATE_NO_WINDOW
;
573 // Create stdin pipe with inheritable read side
574 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
575 sa
.bInheritHandle
= TRUE
;
576 if (!CreatePipe(&pipe_inp_r
, &h
, &sa
/*inherit*/, inpsize
*2+13))
578 if (!DuplicateHandle(self
, h
, self
, &pipe_inp_w
,
579 0, FALSE
/*!inherit*/, DUPLICATE_SAME_ACCESS
|DUPLICATE_CLOSE_SOURCE
)) {
580 CloseHandle(pipe_inp_r
);
584 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
585 sa
.bInheritHandle
= TRUE
;
587 // Create stdout pipe with inheritable write side
588 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
)) {
589 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
594 // Create temp file with inheritable write handle
595 char temp_dir
[MAX_PATH
];
596 if (!GetTempPathA(sizeof(temp_dir
), temp_dir
))
597 strcpy(temp_dir
, ".");
598 if (!GetTempFileNameA(temp_dir
, "out"/*prefix*/, 0/*create unique*/, temp_path
)) {
599 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
602 if ((h
= CreateFileA(temp_path
, GENERIC_READ
|GENERIC_WRITE
,
603 0/*no sharing*/, &sa
/*inherit*/, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) {
604 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
607 if (!DuplicateHandle(self
, h
, self
, &pipe_out_w
,
608 GENERIC_WRITE
, TRUE
/*inherit*/, 0)) {
609 CloseHandle(h
); CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
614 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
615 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
616 CloseHandle(pipe_out_w
); CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
620 // Create stderr handle as dup of stdout write side
621 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
622 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
623 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
624 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
628 // Create process with pipes/file as stdio
629 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
630 si
.hStdInput
= pipe_inp_r
;
631 si
.hStdOutput
= pipe_out_w
;
632 si
.hStdError
= pipe_err_w
;
633 si
.dwFlags
= STARTF_USESTDHANDLES
;
636 NULL
, NULL
, TRUE
/*inherit*/,
637 flags
/*DETACHED_PROCESS or CREATE_NO_WINDOW*/,
638 NULL
, NULL
, &si
, &pi
)) {
639 CloseHandle(pipe_err_w
);
640 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
641 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
644 CloseHandle(pi
.hThread
);
645 // Close inherited handles
646 CloseHandle(pipe_inp_r
);
647 CloseHandle(pipe_out_w
);
648 CloseHandle(pipe_err_w
);
650 // Copy inpbuf to stdin
651 // convert \n => \r\n
652 for (i
= 0; i
< inpsize
; ) {
654 while (i
+len
< inpsize
&& inpbuf
[i
+len
] != '\n')
657 WriteFile(pipe_inp_w
, inpbuf
+i
, len
, &num_io
, NULL
);
660 WriteFile(pipe_inp_w
, "\r\n", 2, &num_io
, NULL
);
664 CloseHandle(pipe_inp_w
);
667 for (state
= 0; state
< 2; state
++) {
668 // stdout pipe: read pipe first
669 // stdout file: wait for process first
670 if (state
== use_file
) {
671 // Copy stdout to output buffer until full, rest to /dev/null
672 // convert \r\n => \n
674 SetFilePointer(pipe_out_r
, 0, NULL
, FILE_BEGIN
);
678 if (!ReadFile(pipe_out_r
, buf
, sizeof(buf
), &num_io
, NULL
) || num_io
== 0)
680 for (j
= 0; i
< outsize
-1 && j
< (int)num_io
; j
++) {
682 outbuf
[i
++] = buf
[j
];
686 CloseHandle(pipe_out_r
);
688 DeleteFileA(temp_path
);
691 // Wait for process exitcode
692 WaitForSingleObject(pi
.hProcess
, INFINITE
);
693 GetExitCodeProcess(pi
.hProcess
, &exitcode
);
694 CloseHandle(pi
.hProcess
);
701 /////////////////////////////////////////////////////////////////////////////
704 static int wait_signaled(HANDLE h
, int seconds
)
708 if (WaitForSingleObject(h
, 1000L) == WAIT_OBJECT_0
)
712 fputchar('.'); fflush(stdout
);
717 static int wait_evt_running(int seconds
, int exists
)
720 if (event_exists(EVT_RUNNING
) == exists
)
724 if (event_exists(EVT_RUNNING
) == exists
)
728 fputchar('.'); fflush(stdout
);
733 static int is_initd_command(char * s
)
735 if (!strcmp(s
, "status"))
737 if (!strcmp(s
, "stop"))
739 if (!strcmp(s
, "reload"))
741 if (!strcmp(s
, "sigusr1"))
743 if (!strcmp(s
, "sigusr2"))
745 if (!strcmp(s
, "restart"))
751 static int initd_main(const char * ident
, int argc
, char **argv
)
756 if ((rc
= is_initd_command(argv
[1])) < 0)
759 printf("%s: no arguments allowed for command %s\n", ident
, argv
[1]);
766 printf("Checking for %s:", ident
); fflush(stdout
);
767 rc
= event_exists(EVT_RUNNING
);
768 puts(rc
? " running" : " not running");
772 printf("Stopping %s:", ident
); fflush(stdout
);
773 rc
= sig_event(SIGTERM
);
775 puts(rc
< 0 ? " not running" : " error");
776 return (rc
< 0 ? 0 : 1);
778 rc
= wait_evt_running(10, 0);
779 puts(!rc
? " done" : " timeout");
780 return (!rc
? 0 : 1);
783 printf("Reloading %s:", ident
); fflush(stdout
);
784 rc
= sig_event(SIGHUP
);
785 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
786 return (rc
> 0 ? 0 : 1);
790 printf("Sending SIGUSR%d to %s:", (rc
-SIGUSR1
+1), ident
); fflush(stdout
);
792 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
793 return (rc
> 0 ? 0 : 1);
798 printf("Stopping %s:", ident
); fflush(stdout
);
799 if (event_exists(EVT_DETACHED
)) {
800 puts(" not detached, cannot restart");
803 if (!(rst
= create_event(EVT_RESTART
, FALSE
, FALSE
, NULL
))) {
807 rc
= sig_event(SIGTERM
);
809 puts(rc
< 0 ? " not running" : " error");
813 rc
= wait_signaled(rst
, 10);
822 printf("Starting %s:", ident
); fflush(stdout
);
823 rc
= wait_evt_running(10, 1);
824 puts(!rc
? " done" : " error");
825 return (!rc
? 0 : 1);
831 /////////////////////////////////////////////////////////////////////////////
832 // Windows Service Functions
834 int daemon_winsvc_exitcode
; // Set by app to exit(code)
836 static SERVICE_STATUS_HANDLE svc_handle
;
837 static SERVICE_STATUS svc_status
;
840 // Report status to SCM
842 static void service_report_status(int state
, int seconds
)
845 static DWORD checkpoint
= 1;
846 static DWORD accept_more
= SERVICE_ACCEPT_PARAMCHANGE
; // Win2000/XP
847 svc_status
.dwCurrentState
= state
;
848 svc_status
.dwWaitHint
= seconds
*1000;
851 svc_status
.dwCheckPoint
= checkpoint
++;
853 case SERVICE_RUNNING
:
854 case SERVICE_STOPPED
:
855 svc_status
.dwCheckPoint
= 0;
858 case SERVICE_START_PENDING
:
859 case SERVICE_STOP_PENDING
:
860 svc_status
.dwControlsAccepted
= 0;
863 svc_status
.dwControlsAccepted
=
864 SERVICE_ACCEPT_STOP
|SERVICE_ACCEPT_SHUTDOWN
|
865 SERVICE_ACCEPT_PAUSE_CONTINUE
|accept_more
;
868 if (!SetServiceStatus(svc_handle
, &svc_status
)) {
869 if (svc_status
.dwControlsAccepted
& accept_more
) {
870 // Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4)
871 svc_status
.dwControlsAccepted
&= ~accept_more
;
873 SetServiceStatus(svc_handle
, &svc_status
);
879 // Control the service, called by SCM
881 static void WINAPI
service_control(DWORD ctrlcode
)
884 case SERVICE_CONTROL_STOP
:
885 case SERVICE_CONTROL_SHUTDOWN
:
886 service_report_status(SERVICE_STOP_PENDING
, 30);
888 SetEvent(sigterm_handle
);
890 case SERVICE_CONTROL_PARAMCHANGE
: // Win2000/XP
891 service_report_status(svc_status
.dwCurrentState
, 0);
893 SetEvent(sighup_handle
); // reload
895 case SERVICE_CONTROL_PAUSE
:
896 service_report_status(SERVICE_PAUSED
, 0);
899 case SERVICE_CONTROL_CONTINUE
:
900 service_report_status(SERVICE_RUNNING
, 0);
902 int was_paused
= svc_paused
;
904 SetEvent(was_paused
? sighup_handle
: sigusr1_handle
); // reload:recheck
907 case SERVICE_CONTROL_INTERROGATE
:
909 service_report_status(svc_status
.dwCurrentState
, 0);
915 // Exit handler for service
917 static void service_exit(void)
919 // Close signal events
921 for (i
= 0; i
< num_sig_handlers
; i
++)
922 CloseHandle(sig_events
[i
]);
923 num_sig_handlers
= 0;
926 if (daemon_winsvc_exitcode
) {
927 svc_status
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
928 svc_status
.dwServiceSpecificExitCode
= daemon_winsvc_exitcode
;
931 service_report_status(SERVICE_STOPPED
, 0);
935 // Variables for passing main(argc, argv) from daemon_main to service_main()
936 static int (*svc_main_func
)(int, char **);
937 static int svc_main_argc
;
938 static char ** svc_main_argv
;
940 // Main function for service, called by service dispatcher
942 static void WINAPI
service_main(DWORD argc
, LPSTR
* argv
)
944 char path
[MAX_PATH
], *p
;
947 // Register control handler
948 svc_handle
= RegisterServiceCtrlHandler(argv
[0], service_control
);
950 // Init service status
951 svc_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
|SERVICE_INTERACTIVE_PROCESS
;
952 service_report_status(SERVICE_START_PENDING
, 10);
954 // Service started in \windows\system32, change to .exe directory
955 if (GetModuleFileNameA(NULL
, path
, sizeof(path
)) && (p
= strrchr(path
, '\\'))) {
956 *p
= 0; SetCurrentDirectoryA(path
);
959 // Install exit handler
960 atexit(service_exit
);
962 // Do the real work, service status later updated by daemon_detach()
963 daemon_winsvc_exitcode
= svc_main_func(svc_main_argc
, svc_main_argv
);
965 exit(daemon_winsvc_exitcode
);
966 // ... continued in service_exit()
970 /////////////////////////////////////////////////////////////////////////////
971 // Windows Service Admin Functions
973 // Set Service description (Win2000/XP)
975 static int svcadm_setdesc(SC_HANDLE hs
, const char * desc
)
978 BOOL (WINAPI
* ChangeServiceConfig2A_p
)(SC_HANDLE
, DWORD
, LPVOID
);
980 if (!(hdll
= LoadLibraryA("ADVAPI32.DLL")))
982 if (!((ChangeServiceConfig2A_p
= (BOOL (WINAPI
*)(SC_HANDLE
, DWORD
, LPVOID
))GetProcAddress(hdll
, "ChangeServiceConfig2A"))))
985 SERVICE_DESCRIPTIONA sd
= { (char *)desc
};
986 ret
= ChangeServiceConfig2A_p(hs
, SERVICE_CONFIG_DESCRIPTION
, &sd
);
993 // Service install/remove commands
995 static int svcadm_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
996 int argc
, char **argv
)
998 int remove
; long err
;
1003 if (!strcmp(argv
[1], "install"))
1005 else if (!strcmp(argv
[1], "remove")) {
1007 printf("%s: no arguments allowed for command remove\n", ident
);
1015 printf("%s service %s:", (!remove
?"Installing":"Removing"), ident
); fflush(stdout
);
1018 if (!(hm
= OpenSCManager(NULL
/*local*/, NULL
/*default*/, SC_MANAGER_ALL_ACCESS
))) {
1019 if ((err
= GetLastError()) == ERROR_ACCESS_DENIED
)
1020 puts(" access to SCManager denied");
1021 else if (err
== ERROR_CALL_NOT_IMPLEMENTED
)
1022 puts(" services not implemented on this version of Windows");
1024 printf(" cannot open SCManager, Error=%ld\n", err
);
1029 char path
[MAX_PATH
+100];
1032 if (!GetModuleFileNameA(NULL
, path
, MAX_PATH
)) {
1033 printf(" unknown program path, Error=%ld\n", GetLastError());
1034 CloseServiceHandle(hm
);
1038 strcat(path
, " "); strcat(path
, svc_opts
->cmd_opt
);
1039 for (i
= 2; i
< argc
; i
++) {
1040 const char * s
= argv
[i
];
1041 if (strlen(path
)+strlen(s
)+1 >= sizeof(path
))
1043 strcat(path
, " "); strcat(path
, s
);
1046 if (!(hs
= CreateService(hm
,
1047 svc_opts
->svcname
, svc_opts
->dispname
,
1049 SERVICE_WIN32_OWN_PROCESS
|SERVICE_INTERACTIVE_PROCESS
,
1050 SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, path
,
1051 NULL
/*no load ordering*/, NULL
/*no tag id*/,
1052 ""/*no depedencies*/, NULL
/*local system account*/, NULL
/*no pw*/))) {
1053 if ((err
= GetLastError()) == ERROR_SERVICE_EXISTS
)
1054 puts(" the service is already installed");
1055 else if (err
== ERROR_SERVICE_MARKED_FOR_DELETE
)
1056 puts(" service is still running and marked for deletion\n"
1057 "Stop the service and retry install");
1059 printf(" failed, Error=%ld\n", err
);
1060 CloseServiceHandle(hm
);
1063 // Set optional description
1064 if (svc_opts
->descript
)
1065 svcadm_setdesc(hs
, svc_opts
->descript
);
1069 if (!(hs
= OpenService(hm
, svc_opts
->svcname
, SERVICE_ALL_ACCESS
))) {
1071 CloseServiceHandle(hm
);
1074 // TODO: Stop service if running
1076 if (!DeleteService(hs
)) {
1077 if ((err
= GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE
)
1078 puts(" service is still running and marked for deletion\n"
1079 "Stop the service to remove it");
1081 printf(" failed, Error=%ld\n", err
);
1082 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
1087 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
1092 /////////////////////////////////////////////////////////////////////////////
1095 // This function must be called from main()
1096 // main_func is the function doing the real work
1098 int daemon_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
1099 int (*main_func
)(int, char **), int argc
, char **argv
)
1103 // Enable Debug heap checks
1104 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG
)
1105 |_CRTDBG_ALLOC_MEM_DF
|_CRTDBG_CHECK_ALWAYS_DF
|_CRTDBG_LEAK_CHECK_DF
);
1108 // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
1109 if ((rc
= initd_main(ident
, argc
, argv
)) >= 0)
1111 // Check for [install|remove] parameters
1112 if (svc_opts
&& (rc
= svcadm_main(ident
, svc_opts
, argc
, argv
)) >= 0)
1115 // Run as service if svc_opts.cmd_opt is given as first(!) argument
1116 svc_mode
= (svc_opts
&& argc
>= 2 && !strcmp(argv
[1], svc_opts
->cmd_opt
));
1119 // Daemon: Try to simulate a Unix-like daemon
1123 // Create main event to detect process type:
1124 // 1. new: parent process => start child and wait for detach() or exit() of child.
1125 // 2. exists && signaled: child process => do the real work, signal detach() to parent
1126 // 3. exists && !signaled: already running => exit()
1127 if (!(rev
= create_event(EVT_RUNNING
, TRUE
/*signaled*/, TRUE
, &exists
)))
1130 if (!exists
&& !debugging()) {
1131 // Event new => parent process
1132 return parent_main(rev
);
1135 if (WaitForSingleObject(rev
, 0) == WAIT_OBJECT_0
) {
1136 // Event was signaled => In child process
1137 return child_main(rev
, main_func
, argc
, argv
);
1140 // Event no longer signaled => Already running!
1141 daemon_help(stdout
, ident
, "already running");
1146 // Service: Start service_main() via SCM
1147 SERVICE_TABLE_ENTRY service_table
[] = {
1148 { (char*)svc_opts
->svcname
, service_main
}, { NULL
, NULL
}
1151 svc_main_func
= main_func
;
1152 svc_main_argc
= argc
;
1153 svc_main_argv
= argv
;
1154 if (!StartServiceCtrlDispatcher(service_table
)) {
1155 printf("%s: cannot dispatch service, Error=%ld\n"
1156 "Option \"%s\" cannot be used to start %s as a service from console.\n"
1157 "Use \"%s install ...\" to install the service\n"
1158 "and \"net start %s\" to start it.\n",
1159 ident
, GetLastError(), svc_opts
->cmd_opt
, ident
, ident
, ident
);
1163 service_main(argc
, argv
);
1168 ExitThread(0); // Do not redo exit() processing
1175 /////////////////////////////////////////////////////////////////////////////
1180 static volatile sig_atomic_t caughtsig
= 0;
1182 static void sig_handler(int sig
)
1187 static void test_exit(void)
1189 printf("Main exit\n");
1192 int test_main(int argc
, char **argv
)
1198 printf("PID=%ld\n", GetCurrentProcessId());
1199 for (i
= 0; i
< argc
; i
++) {
1200 printf("%d: \"%s\"\n", i
, argv
[i
]);
1201 if (!strcmp(argv
[i
],"-d"))
1204 if (argc
> 1 && argv
[argc
-1][0] != '-')
1207 daemon_signal(SIGINT
, sig_handler
);
1208 daemon_signal(SIGBREAK
, sig_handler
);
1209 daemon_signal(SIGTERM
, sig_handler
);
1210 daemon_signal(SIGHUP
, sig_handler
);
1211 daemon_signal(SIGUSR1
, sig_handler
);
1212 daemon_signal(SIGUSR2
, sig_handler
);
1217 printf("Preparing to detach...\n");
1219 daemon_detach("test");
1220 printf("Detached!\n");
1225 printf("."); fflush(stdout
);
1227 if (caughtsig
== SIGUSR2
) {
1230 daemon_enable_console("Daemon[Debug]");
1232 daemon_disable_console();
1234 else if (caughtsig
== SIGUSR1
&& cmd
) {
1235 char inpbuf
[200], outbuf
[1000]; int rc
;
1236 strcpy(inpbuf
, "Hello\nWorld!\n");
1237 rc
= daemon_spawn(cmd
, inpbuf
, strlen(inpbuf
), outbuf
, sizeof(outbuf
));
1239 daemon_enable_console("Command output");
1240 printf("\"%s\" returns %d\n", cmd
, rc
);
1242 printf("output:\n%s.\n", outbuf
);
1245 Sleep(10000); daemon_disable_console();
1248 printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig
); fflush(stdout
);
1249 if (caughtsig
== SIGTERM
|| caughtsig
== SIGBREAK
)
1254 printf("\nExiting on signal %d\n", caughtsig
);
1259 int main(int argc
, char **argv
)
1261 static const daemon_winsvc_options svc_opts
= {
1262 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1265 return daemon_main("testd", &svc_opts
, test_main
, argc
, argv
);