2 * os_win32/daemon_win32.cpp
4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-8 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.
24 #define WIN32_LEAN_AND_MEAN
25 // Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
26 #define _WIN32_WINNT 0x0400
32 #include "daemon_win32.h"
34 const char *daemon_win32_c_cvsid
= "$Id: daemon_win32.cpp,v 1.12 2008/03/04 22:09:48 ballen4705 Exp $"
38 /////////////////////////////////////////////////////////////////////////////
40 #define ARGUSED(x) ((void)(x))
42 // Prevent spawning of child process if debugging
44 #define debugging() IsDebuggerPresent()
46 #define debugging() FALSE
50 #define EVT_NAME_LEN 260
52 // Internal events (must be > SIGUSRn)
53 #define EVT_RUNNING 100 // Exists when running, signaled on creation
54 #define EVT_DETACHED 101 // Signaled when child detaches from console
55 #define EVT_RESTART 102 // Signaled when child should restart
57 static void make_name(char * name
, int sig
)
60 if (!GetModuleFileNameA(NULL
, name
, EVT_NAME_LEN
-10))
61 strcpy(name
, "DaemonEvent");
62 for (i
= 0; name
[i
]; i
++) {
64 if (!( ('0' <= c
&& c
<= '9')
65 || ('A' <= c
&& c
<= 'Z')
66 || ('a' <= c
&& c
<= 'z')))
69 sprintf(name
+strlen(name
), "-%d", sig
);
73 static HANDLE
create_event(int sig
, BOOL initial
, BOOL errmsg
, BOOL
* exists
)
75 char name
[EVT_NAME_LEN
];
83 if (!(h
= CreateEventA(NULL
, FALSE
, initial
, (name
[0] ? name
: NULL
)))) {
85 fprintf(stderr
, "CreateEvent(.,\"%s\"): Error=%ld\n", name
, GetLastError());
89 if (GetLastError() == ERROR_ALREADY_EXISTS
) {
92 fprintf(stderr
, "CreateEvent(.,\"%s\"): Exists\n", name
);
102 static HANDLE
open_event(int sig
)
104 char name
[EVT_NAME_LEN
];
105 make_name(name
, sig
);
106 return OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
);
110 static int event_exists(int sig
)
112 char name
[EVT_NAME_LEN
];
114 make_name(name
, sig
);
115 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
)))
122 static int sig_event(int sig
)
124 char name
[EVT_NAME_LEN
];
126 make_name(name
, sig
);
127 if (!(h
= OpenEventA(EVENT_MODIFY_STATE
, FALSE
, name
))) {
128 make_name(name
, EVT_RUNNING
);
129 if (!(h
= OpenEvent(EVENT_MODIFY_STATE
, FALSE
, name
)))
140 static void daemon_help(FILE * f
, const char * ident
, const char * message
)
144 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
145 ident
, message
, ident
);
150 /////////////////////////////////////////////////////////////////////////////
154 static BOOL WINAPI
parent_console_handler(DWORD event
)
158 case CTRL_BREAK_EVENT
:
159 return TRUE
; // Ignore
161 return FALSE
; // continue with next handler ...
165 static int parent_main(HANDLE rev
)
171 PROCESS_INFORMATION pi
;
174 // Ignore ^C, ^BREAK in parent
175 SetConsoleCtrlHandler(parent_console_handler
, TRUE
/*add*/);
177 // Create event used by child to signal daemon_detach()
178 if (!(dev
= create_event(EVT_DETACHED
, FALSE
/*not signaled*/, TRUE
, NULL
/*must not exist*/))) {
183 // Restart process with same args
184 cmdline
= GetCommandLineA();
185 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
189 NULL
, NULL
, TRUE
/*inherit*/,
190 0, NULL
, NULL
, &si
, &pi
)) {
191 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
192 CloseHandle(rev
); CloseHandle(dev
);
195 CloseHandle(pi
.hThread
);
197 // Wait for daemon_detach() or exit()
198 ht
[0] = dev
; ht
[1] = pi
.hProcess
;
199 rc
= WaitForMultipleObjects(2, ht
, FALSE
/*or*/, INFINITE
);
200 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+2)) {
201 fprintf(stderr
, "WaitForMultipleObjects returns %lX\n", rc
);
202 TerminateProcess(pi
.hProcess
, 200);
204 CloseHandle(rev
); CloseHandle(dev
);
207 if (!GetExitCodeProcess(pi
.hProcess
, &exitcode
))
209 else if (exitcode
== STILL_ACTIVE
) // detach()ed, assume OK
212 CloseHandle(pi
.hProcess
);
217 /////////////////////////////////////////////////////////////////////////////
221 static int svc_mode
; // Running as service?
222 static int svc_paused
; // Service paused?
224 static void service_report_status(int state
, int waithint
);
227 // Tables of signal handler and corresponding events
228 typedef void (*sigfunc_t
)(int);
230 #define MAX_SIG_HANDLERS 8
232 static int num_sig_handlers
= 0;
233 static sigfunc_t sig_handlers
[MAX_SIG_HANDLERS
];
234 static int sig_numbers
[MAX_SIG_HANDLERS
];
235 static HANDLE sig_events
[MAX_SIG_HANDLERS
];
237 static HANDLE sighup_handle
, sigint_handle
, sigbreak_handle
;
238 static HANDLE sigterm_handle
, sigusr1_handle
;
240 static HANDLE running_event
;
242 static int reopen_stdin
, reopen_stdout
, reopen_stderr
;
245 // Handler for windows console events
247 static BOOL WINAPI
child_console_handler(DWORD event
)
249 // Caution: runs in a new thread
250 // TODO: Guard with a mutex
253 case CTRL_C_EVENT
: // <CONTROL-C> (SIGINT)
254 h
= sigint_handle
; break;
255 case CTRL_BREAK_EVENT
: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
256 case CTRL_CLOSE_EVENT
: // User closed console or abort via task manager
257 h
= sigbreak_handle
; break;
258 case CTRL_LOGOFF_EVENT
: // Logout/Shutdown (SIGTERM)
259 case CTRL_SHUTDOWN_EVENT
:
260 h
= sigterm_handle
; break;
263 return FALSE
; // continue with next handler
271 static void child_exit(void)
277 PROCESS_INFORMATION pi
;
279 for (i
= 0; i
< num_sig_handlers
; i
++)
280 CloseHandle(sig_events
[i
]);
281 num_sig_handlers
= 0;
282 CloseHandle(running_event
); running_event
= 0;
285 if (!(rst
= open_event(EVT_RESTART
)))
286 return; // No => normal exit
288 // Yes => Signal exit and restart process
294 cmdline
= GetCommandLineA();
295 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
296 si
.dwFlags
= STARTF_USESHOWWINDOW
; si
.wShowWindow
= SW_HIDE
;
300 NULL
, NULL
, TRUE
/*inherit*/,
301 0, NULL
, NULL
, &si
, &pi
)) {
302 fprintf(stderr
, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline
, GetLastError());
304 CloseHandle(pi
.hThread
); CloseHandle(pi
.hProcess
);
307 static int child_main(HANDLE hev
,int (*main_func
)(int, char **), int argc
, char **argv
)
309 // Keep EVT_RUNNING open until exit
312 // Install console handler
313 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
315 // Install restart handler
318 // Continue in main_func() to do the real work
319 return main_func(argc
, argv
);
325 sigfunc_t
daemon_signal(int sig
, sigfunc_t func
)
329 if (func
== SIG_DFL
|| func
== SIG_IGN
)
331 for (i
= 0; i
< num_sig_handlers
; i
++) {
332 if (sig_numbers
[i
] == sig
) {
333 sigfunc_t old
= sig_handlers
[i
];
334 sig_handlers
[i
] = func
;
338 if (num_sig_handlers
>= MAX_SIG_HANDLERS
)
340 if (!(h
= create_event((!svc_mode
? sig
: -1), FALSE
, TRUE
, NULL
)))
342 sig_events
[num_sig_handlers
] = h
;
343 sig_numbers
[num_sig_handlers
] = sig
;
344 sig_handlers
[num_sig_handlers
] = func
;
346 case SIGHUP
: sighup_handle
= h
; break;
347 case SIGINT
: sigint_handle
= h
; break;
348 case SIGTERM
: sigterm_handle
= h
; break;
349 case SIGBREAK
: sigbreak_handle
= h
; break;
350 case SIGUSR1
: sigusr1_handle
= h
; break;
359 const char * daemon_strsignal(int sig
)
362 case SIGHUP
: return "SIGHUP";
363 case SIGINT
: return "SIGINT";
364 case SIGTERM
: return "SIGTERM";
365 case SIGBREAK
:return "SIGBREAK";
366 case SIGUSR1
: return "SIGUSR1";
367 case SIGUSR2
: return "SIGUSR2";
368 default: return "*UNKNOWN*";
375 void daemon_sleep(int seconds
)
378 if (num_sig_handlers
<= 0) {
379 Sleep(seconds
*1000L);
382 // Wait for any signal or timeout
383 DWORD rc
= WaitForMultipleObjects(num_sig_handlers
, sig_events
,
384 FALSE
/*OR*/, seconds
*1000L);
385 if (rc
!= WAIT_TIMEOUT
) {
386 if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc
< WAIT_OBJECT_0
+(unsigned)num_sig_handlers
)) {
387 fprintf(stderr
,"WaitForMultipleObjects returns %lu\n", rc
);
388 Sleep(seconds
*1000L);
392 sig_handlers
[rc
-WAIT_OBJECT_0
](sig_numbers
[rc
-WAIT_OBJECT_0
]);
396 } while (svc_paused
);
400 // Disable/Enable console
402 void daemon_disable_console()
404 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
405 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
406 if (isatty(fileno(stdin
))) {
407 fclose(stdin
); reopen_stdin
= 1;
409 if (isatty(fileno(stdout
))) {
410 fclose(stdout
); reopen_stdout
= 1;
412 if (isatty(fileno(stderr
))) {
413 fclose(stderr
); reopen_stderr
= 1;
416 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
419 int daemon_enable_console(const char * title
)
422 SetConsoleCtrlHandler(child_console_handler
, FALSE
/*remove*/);
424 SetConsoleCtrlHandler(child_console_handler
, TRUE
/*add*/);
428 SetConsoleTitleA(title
);
430 freopen("conin$", "r", stdin
);
432 freopen("conout$", "w", stdout
);
434 freopen("conout$", "w", stderr
);
435 reopen_stdin
= reopen_stdout
= reopen_stderr
= 0;
440 // Detach daemon from console & parent
442 int daemon_detach(const char * ident
)
447 FILE * f
= ( isatty(fileno(stdout
)) ? stdout
448 : isatty(fileno(stderr
)) ? stderr
: NULL
);
450 daemon_help(f
, ident
, "now detaches from console into background mode");
452 // Signal detach to parent
453 if (sig_event(EVT_DETACHED
) != 1) {
457 daemon_disable_console();
460 // Signal end of initialization to service control manager
461 service_report_status(SERVICE_RUNNING
, 0);
462 reopen_stdin
= reopen_stdout
= reopen_stderr
= 1;
469 /////////////////////////////////////////////////////////////////////////////
473 //MT runtime not necessary, because mbox_thread uses no unsafe lib functions
474 //#error Program must be linked with multithreaded runtime library
477 static LONG mbox_count
; // # mbox_thread()s
478 static HANDLE mbox_mutex
; // Show 1 box at a time (not necessary for service)
480 typedef struct mbox_args_s
{
481 HANDLE taken
; const char * title
, * text
; int mode
;
485 // Thread to display one message box
487 static ULONG WINAPI
mbox_thread(LPVOID arg
)
490 mbox_args
* mb
= (mbox_args
*)arg
;
491 char title
[100]; char text
[1000]; int mode
;
492 strncpy(title
, mb
->title
, sizeof(title
)-1); title
[sizeof(title
)-1] = 0;
493 strncpy(text
, mb
->text
, sizeof(text
)-1); text
[sizeof(text
)-1] = 0;
497 // Show only one box at a time
498 WaitForSingleObject(mbox_mutex
, INFINITE
);
499 MessageBoxA(NULL
, text
, title
, mode
);
500 ReleaseMutex(mbox_mutex
);
502 InterlockedDecrement(&mbox_count
);
507 // Display a message box
508 int daemon_messagebox(int system
, const char * title
, const char * text
)
511 HANDLE ht
; DWORD tid
;
513 // Create mutex during first call
515 mbox_mutex
= CreateMutex(NULL
/*!inherit*/, FALSE
/*!owned*/, NULL
/*unnamed*/);
517 // Allow at most 10 threads
518 if (InterlockedIncrement(&mbox_count
) > 10) {
519 InterlockedDecrement(&mbox_count
);
524 mb
.taken
= CreateEvent(NULL
/*!inherit*/, FALSE
, FALSE
/*!signaled*/, NULL
/*unnamed*/);
525 mb
.mode
= MB_OK
|MB_ICONWARNING
526 |(svc_mode
?MB_SERVICE_NOTIFICATION
:0)
527 |(system
?MB_SYSTEMMODAL
:MB_APPLMODAL
);
528 mb
.title
= title
; mb
.text
= text
;
530 if (!(ht
= CreateThread(NULL
, 0, mbox_thread
, &mb
, 0, &tid
)))
533 // Wait for args taken
534 if (WaitForSingleObject(mb
.taken
, 10000) != WAIT_OBJECT_0
)
535 TerminateThread(ht
, 0);
536 CloseHandle(mb
.taken
);
542 /////////////////////////////////////////////////////////////////////////////
544 // Spawn a command and redirect <inpbuf >outbuf
545 // return command's exitcode or -1 on error
547 int daemon_spawn(const char * cmd
,
548 const char * inpbuf
, int inpsize
,
549 char * outbuf
, int outsize
)
551 HANDLE pipe_inp_r
, pipe_inp_w
, pipe_out_r
, pipe_out_w
, pipe_err_w
, h
;
552 char temp_path
[MAX_PATH
];
553 DWORD flags
, num_io
, exitcode
;
554 int use_file
, state
, i
;
555 SECURITY_ATTRIBUTES sa
;
556 STARTUPINFO si
; PROCESS_INFORMATION pi
;
557 HANDLE self
= GetCurrentProcess();
559 if (GetVersion() & 0x80000000L
) {
560 // Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or
561 // any other DOS program is redirected via a pipe. Using a temp file instead.
562 use_file
= 1; flags
= DETACHED_PROCESS
;
565 // NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window
566 // for each external command in a redirected .BAT file.
567 // Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work.
568 use_file
= 0; flags
= CREATE_NO_WINDOW
;
571 // Create stdin pipe with inheritable read side
572 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
573 sa
.bInheritHandle
= TRUE
;
574 if (!CreatePipe(&pipe_inp_r
, &h
, &sa
/*inherit*/, inpsize
*2+13))
576 if (!DuplicateHandle(self
, h
, self
, &pipe_inp_w
,
577 0, FALSE
/*!inherit*/, DUPLICATE_SAME_ACCESS
|DUPLICATE_CLOSE_SOURCE
)) {
578 CloseHandle(pipe_inp_r
);
582 memset(&sa
, 0, sizeof(sa
)); sa
.nLength
= sizeof(sa
);
583 sa
.bInheritHandle
= TRUE
;
585 // Create stdout pipe with inheritable write side
586 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
)) {
587 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
592 // Create temp file with inheritable write handle
593 char temp_dir
[MAX_PATH
];
594 if (!GetTempPathA(sizeof(temp_dir
), temp_dir
))
595 strcpy(temp_dir
, ".");
596 if (!GetTempFileNameA(temp_dir
, "out"/*prefix*/, 0/*create unique*/, temp_path
)) {
597 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
600 if ((h
= CreateFileA(temp_path
, GENERIC_READ
|GENERIC_WRITE
,
601 0/*no sharing*/, &sa
/*inherit*/, OPEN_EXISTING
, 0, NULL
)) == INVALID_HANDLE_VALUE
) {
602 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
605 if (!DuplicateHandle(self
, h
, self
, &pipe_out_w
,
606 GENERIC_WRITE
, TRUE
/*inherit*/, 0)) {
607 CloseHandle(h
); CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
612 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
613 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
614 CloseHandle(pipe_out_w
); CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
618 // Create stderr handle as dup of stdout write side
619 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
620 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
621 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
622 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
626 // Create process with pipes/file as stdio
627 memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
628 si
.hStdInput
= pipe_inp_r
;
629 si
.hStdOutput
= pipe_out_w
;
630 si
.hStdError
= pipe_err_w
;
631 si
.dwFlags
= STARTF_USESTDHANDLES
;
634 NULL
, NULL
, TRUE
/*inherit*/,
635 flags
/*DETACHED_PROCESS or CREATE_NO_WINDOW*/,
636 NULL
, NULL
, &si
, &pi
)) {
637 CloseHandle(pipe_err_w
);
638 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
639 CloseHandle(pipe_inp_r
); CloseHandle(pipe_inp_w
);
642 CloseHandle(pi
.hThread
);
643 // Close inherited handles
644 CloseHandle(pipe_inp_r
);
645 CloseHandle(pipe_out_w
);
646 CloseHandle(pipe_err_w
);
648 // Copy inpbuf to stdin
649 // convert \n => \r\n
650 for (i
= 0; i
< inpsize
; ) {
652 while (i
+len
< inpsize
&& inpbuf
[i
+len
] != '\n')
655 WriteFile(pipe_inp_w
, inpbuf
+i
, len
, &num_io
, NULL
);
658 WriteFile(pipe_inp_w
, "\r\n", 2, &num_io
, NULL
);
662 CloseHandle(pipe_inp_w
);
665 for (state
= 0; state
< 2; state
++) {
666 // stdout pipe: read pipe first
667 // stdout file: wait for process first
668 if (state
== use_file
) {
669 // Copy stdout to output buffer until full, rest to /dev/null
670 // convert \r\n => \n
672 SetFilePointer(pipe_out_r
, 0, NULL
, FILE_BEGIN
);
676 if (!ReadFile(pipe_out_r
, buf
, sizeof(buf
), &num_io
, NULL
) || num_io
== 0)
678 for (j
= 0; i
< outsize
-1 && j
< (int)num_io
; j
++) {
680 outbuf
[i
++] = buf
[j
];
684 CloseHandle(pipe_out_r
);
686 DeleteFileA(temp_path
);
689 // Wait for process exitcode
690 WaitForSingleObject(pi
.hProcess
, INFINITE
);
691 GetExitCodeProcess(pi
.hProcess
, &exitcode
);
692 CloseHandle(pi
.hProcess
);
699 /////////////////////////////////////////////////////////////////////////////
702 static int wait_signaled(HANDLE h
, int seconds
)
706 if (WaitForSingleObject(h
, 1000L) == WAIT_OBJECT_0
)
710 fputchar('.'); fflush(stdout
);
715 static int wait_evt_running(int seconds
, int exists
)
718 if (event_exists(EVT_RUNNING
) == exists
)
722 if (event_exists(EVT_RUNNING
) == exists
)
726 fputchar('.'); fflush(stdout
);
731 static int is_initd_command(char * s
)
733 if (!strcmp(s
, "status"))
735 if (!strcmp(s
, "stop"))
737 if (!strcmp(s
, "reload"))
739 if (!strcmp(s
, "sigusr1"))
741 if (!strcmp(s
, "sigusr2"))
743 if (!strcmp(s
, "restart"))
749 static int initd_main(const char * ident
, int argc
, char **argv
)
754 if ((rc
= is_initd_command(argv
[1])) < 0)
757 printf("%s: no arguments allowed for command %s\n", ident
, argv
[1]);
764 printf("Checking for %s:", ident
); fflush(stdout
);
765 rc
= event_exists(EVT_RUNNING
);
766 puts(rc
? " running" : " not running");
770 printf("Stopping %s:", ident
); fflush(stdout
);
771 rc
= sig_event(SIGTERM
);
773 puts(rc
< 0 ? " not running" : " error");
774 return (rc
< 0 ? 0 : 1);
776 rc
= wait_evt_running(10, 0);
777 puts(!rc
? " done" : " timeout");
778 return (!rc
? 0 : 1);
781 printf("Reloading %s:", ident
); fflush(stdout
);
782 rc
= sig_event(SIGHUP
);
783 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
784 return (rc
> 0 ? 0 : 1);
788 printf("Sending SIGUSR%d to %s:", (rc
-SIGUSR1
+1), ident
); fflush(stdout
);
790 puts(rc
> 0 ? " done" : rc
== 0 ? " error" : " not running");
791 return (rc
> 0 ? 0 : 1);
796 printf("Stopping %s:", ident
); fflush(stdout
);
797 if (event_exists(EVT_DETACHED
)) {
798 puts(" not detached, cannot restart");
801 if (!(rst
= create_event(EVT_RESTART
, FALSE
, FALSE
, NULL
))) {
805 rc
= sig_event(SIGTERM
);
807 puts(rc
< 0 ? " not running" : " error");
811 rc
= wait_signaled(rst
, 10);
820 printf("Starting %s:", ident
); fflush(stdout
);
821 rc
= wait_evt_running(10, 1);
822 puts(!rc
? " done" : " error");
823 return (!rc
? 0 : 1);
829 /////////////////////////////////////////////////////////////////////////////
830 // Windows Service Functions
832 int daemon_winsvc_exitcode
; // Set by app to exit(code)
834 static SERVICE_STATUS_HANDLE svc_handle
;
835 static SERVICE_STATUS svc_status
;
838 // Report status to SCM
840 static void service_report_status(int state
, int seconds
)
843 static DWORD checkpoint
= 1;
844 static DWORD accept_more
= SERVICE_ACCEPT_PARAMCHANGE
; // Win2000/XP
845 svc_status
.dwCurrentState
= state
;
846 svc_status
.dwWaitHint
= seconds
*1000;
849 svc_status
.dwCheckPoint
= checkpoint
++;
851 case SERVICE_RUNNING
:
852 case SERVICE_STOPPED
:
853 svc_status
.dwCheckPoint
= 0;
856 case SERVICE_START_PENDING
:
857 case SERVICE_STOP_PENDING
:
858 svc_status
.dwControlsAccepted
= 0;
861 svc_status
.dwControlsAccepted
=
862 SERVICE_ACCEPT_STOP
|SERVICE_ACCEPT_SHUTDOWN
|
863 SERVICE_ACCEPT_PAUSE_CONTINUE
|accept_more
;
866 if (!SetServiceStatus(svc_handle
, &svc_status
)) {
867 if (svc_status
.dwControlsAccepted
& accept_more
) {
868 // Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4)
869 svc_status
.dwControlsAccepted
&= ~accept_more
;
871 SetServiceStatus(svc_handle
, &svc_status
);
877 // Control the service, called by SCM
879 static void WINAPI
service_control(DWORD ctrlcode
)
882 case SERVICE_CONTROL_STOP
:
883 case SERVICE_CONTROL_SHUTDOWN
:
884 service_report_status(SERVICE_STOP_PENDING
, 30);
886 SetEvent(sigterm_handle
);
888 case SERVICE_CONTROL_PARAMCHANGE
: // Win2000/XP
889 service_report_status(svc_status
.dwCurrentState
, 0);
891 SetEvent(sighup_handle
); // reload
893 case SERVICE_CONTROL_PAUSE
:
894 service_report_status(SERVICE_PAUSED
, 0);
897 case SERVICE_CONTROL_CONTINUE
:
898 service_report_status(SERVICE_RUNNING
, 0);
900 int was_paused
= svc_paused
;
902 SetEvent(was_paused
? sighup_handle
: sigusr1_handle
); // reload:recheck
905 case SERVICE_CONTROL_INTERROGATE
:
907 service_report_status(svc_status
.dwCurrentState
, 0);
913 // Exit handler for service
915 static void service_exit(void)
917 // Close signal events
919 for (i
= 0; i
< num_sig_handlers
; i
++)
920 CloseHandle(sig_events
[i
]);
921 num_sig_handlers
= 0;
924 if (daemon_winsvc_exitcode
) {
925 svc_status
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
926 svc_status
.dwServiceSpecificExitCode
= daemon_winsvc_exitcode
;
929 service_report_status(SERVICE_STOPPED
, 0);
933 // Variables for passing main(argc, argv) from daemon_main to service_main()
934 static int (*svc_main_func
)(int, char **);
935 static int svc_main_argc
;
936 static char ** svc_main_argv
;
938 // Main function for service, called by service dispatcher
940 static void WINAPI
service_main(DWORD argc
, LPSTR
* argv
)
942 char path
[MAX_PATH
], *p
;
945 // Register control handler
946 svc_handle
= RegisterServiceCtrlHandler(argv
[0], service_control
);
948 // Init service status
949 svc_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
|SERVICE_INTERACTIVE_PROCESS
;
950 service_report_status(SERVICE_START_PENDING
, 10);
952 // Service started in \windows\system32, change to .exe directory
953 if (GetModuleFileNameA(NULL
, path
, sizeof(path
)) && (p
= strrchr(path
, '\\'))) {
954 *p
= 0; SetCurrentDirectoryA(path
);
957 // Install exit handler
958 atexit(service_exit
);
960 // Do the real work, service status later updated by daemon_detach()
961 daemon_winsvc_exitcode
= svc_main_func(svc_main_argc
, svc_main_argv
);
963 exit(daemon_winsvc_exitcode
);
964 // ... continued in service_exit()
968 /////////////////////////////////////////////////////////////////////////////
969 // Windows Service Admin Functions
971 // Set Service description (Win2000/XP)
973 static int svcadm_setdesc(SC_HANDLE hs
, const char * desc
)
976 BOOL (WINAPI
* ChangeServiceConfig2A_p
)(SC_HANDLE
, DWORD
, LPVOID
);
978 if (!(hdll
= LoadLibraryA("ADVAPI32.DLL")))
980 if (!((ChangeServiceConfig2A_p
= (BOOL (WINAPI
*)(SC_HANDLE
, DWORD
, LPVOID
))GetProcAddress(hdll
, "ChangeServiceConfig2A"))))
983 SERVICE_DESCRIPTIONA sd
= { (char *)desc
};
984 ret
= ChangeServiceConfig2A_p(hs
, SERVICE_CONFIG_DESCRIPTION
, &sd
);
991 // Service install/remove commands
993 static int svcadm_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
994 int argc
, char **argv
)
996 int remove
; long err
;
1001 if (!strcmp(argv
[1], "install"))
1003 else if (!strcmp(argv
[1], "remove")) {
1005 printf("%s: no arguments allowed for command remove\n", ident
);
1013 printf("%s service %s:", (!remove
?"Installing":"Removing"), ident
); fflush(stdout
);
1016 if (!(hm
= OpenSCManager(NULL
/*local*/, NULL
/*default*/, SC_MANAGER_ALL_ACCESS
))) {
1017 if ((err
= GetLastError()) == ERROR_ACCESS_DENIED
)
1018 puts(" access to SCManager denied");
1019 else if (err
== ERROR_CALL_NOT_IMPLEMENTED
)
1020 puts(" services not implemented on this version of Windows");
1022 printf(" cannot open SCManager, Error=%ld\n", err
);
1027 char path
[MAX_PATH
+100];
1030 if (!GetModuleFileNameA(NULL
, path
, MAX_PATH
)) {
1031 printf(" unknown program path, Error=%ld\n", GetLastError());
1032 CloseServiceHandle(hm
);
1036 strcat(path
, " "); strcat(path
, svc_opts
->cmd_opt
);
1037 for (i
= 2; i
< argc
; i
++) {
1038 const char * s
= argv
[i
];
1039 if (strlen(path
)+strlen(s
)+1 >= sizeof(path
))
1041 strcat(path
, " "); strcat(path
, s
);
1044 if (!(hs
= CreateService(hm
,
1045 svc_opts
->svcname
, svc_opts
->dispname
,
1047 SERVICE_WIN32_OWN_PROCESS
|SERVICE_INTERACTIVE_PROCESS
,
1048 SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, path
,
1049 NULL
/*no load ordering*/, NULL
/*no tag id*/,
1050 ""/*no depedencies*/, NULL
/*local system account*/, NULL
/*no pw*/))) {
1051 if ((err
= GetLastError()) == ERROR_SERVICE_EXISTS
)
1052 puts(" the service is already installed");
1053 else if (err
== ERROR_SERVICE_MARKED_FOR_DELETE
)
1054 puts(" service is still running and marked for deletion\n"
1055 "Stop the service and retry install");
1057 printf(" failed, Error=%ld\n", err
);
1058 CloseServiceHandle(hm
);
1061 // Set optional description
1062 if (svc_opts
->descript
)
1063 svcadm_setdesc(hs
, svc_opts
->descript
);
1067 if (!(hs
= OpenService(hm
, svc_opts
->svcname
, SERVICE_ALL_ACCESS
))) {
1069 CloseServiceHandle(hm
);
1072 // TODO: Stop service if running
1074 if (!DeleteService(hs
)) {
1075 if ((err
= GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE
)
1076 puts(" service is still running and marked for deletion\n"
1077 "Stop the service to remove it");
1079 printf(" failed, Error=%ld\n", err
);
1080 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
1085 CloseServiceHandle(hs
); CloseServiceHandle(hm
);
1090 /////////////////////////////////////////////////////////////////////////////
1093 // This function must be called from main()
1094 // main_func is the function doing the real work
1096 int daemon_main(const char * ident
, const daemon_winsvc_options
* svc_opts
,
1097 int (*main_func
)(int, char **), int argc
, char **argv
)
1101 // Enable Debug heap checks
1102 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG
)
1103 |_CRTDBG_ALLOC_MEM_DF
|_CRTDBG_CHECK_ALWAYS_DF
|_CRTDBG_LEAK_CHECK_DF
);
1106 // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
1107 if ((rc
= initd_main(ident
, argc
, argv
)) >= 0)
1109 // Check for [install|remove] parameters
1110 if (svc_opts
&& (rc
= svcadm_main(ident
, svc_opts
, argc
, argv
)) >= 0)
1113 // Run as service if svc_opts.cmd_opt is given as first(!) argument
1114 svc_mode
= (svc_opts
&& argc
>= 2 && !strcmp(argv
[1], svc_opts
->cmd_opt
));
1117 // Daemon: Try to simulate a Unix-like daemon
1121 // Create main event to detect process type:
1122 // 1. new: parent process => start child and wait for detach() or exit() of child.
1123 // 2. exists && signaled: child process => do the real work, signal detach() to parent
1124 // 3. exists && !signaled: already running => exit()
1125 if (!(rev
= create_event(EVT_RUNNING
, TRUE
/*signaled*/, TRUE
, &exists
)))
1128 if (!exists
&& !debugging()) {
1129 // Event new => parent process
1130 return parent_main(rev
);
1133 if (WaitForSingleObject(rev
, 0) == WAIT_OBJECT_0
) {
1134 // Event was signaled => In child process
1135 return child_main(rev
, main_func
, argc
, argv
);
1138 // Event no longer signaled => Already running!
1139 daemon_help(stdout
, ident
, "already running");
1144 // Service: Start service_main() via SCM
1145 SERVICE_TABLE_ENTRY service_table
[] = {
1146 { (char*)svc_opts
->svcname
, service_main
}, { NULL
, NULL
}
1149 svc_main_func
= main_func
;
1150 svc_main_argc
= argc
;
1151 svc_main_argv
= argv
;
1152 if (!StartServiceCtrlDispatcher(service_table
)) {
1153 printf("%s: cannot dispatch service, Error=%ld\n"
1154 "Option \"%s\" cannot be used to start %s as a service from console.\n"
1155 "Use \"%s install ...\" to install the service\n"
1156 "and \"net start %s\" to start it.\n",
1157 ident
, GetLastError(), svc_opts
->cmd_opt
, ident
, ident
, ident
);
1161 service_main(argc
, argv
);
1166 ExitThread(0); // Do not redo exit() processing
1173 /////////////////////////////////////////////////////////////////////////////
1178 static volatile sig_atomic_t caughtsig
= 0;
1180 static void sig_handler(int sig
)
1185 static void test_exit(void)
1187 printf("Main exit\n");
1190 int test_main(int argc
, char **argv
)
1196 printf("PID=%ld\n", GetCurrentProcessId());
1197 for (i
= 0; i
< argc
; i
++) {
1198 printf("%d: \"%s\"\n", i
, argv
[i
]);
1199 if (!strcmp(argv
[i
],"-d"))
1202 if (argc
> 1 && argv
[argc
-1][0] != '-')
1205 daemon_signal(SIGINT
, sig_handler
);
1206 daemon_signal(SIGBREAK
, sig_handler
);
1207 daemon_signal(SIGTERM
, sig_handler
);
1208 daemon_signal(SIGHUP
, sig_handler
);
1209 daemon_signal(SIGUSR1
, sig_handler
);
1210 daemon_signal(SIGUSR2
, sig_handler
);
1215 printf("Preparing to detach...\n");
1217 daemon_detach("test");
1218 printf("Detached!\n");
1223 printf("."); fflush(stdout
);
1225 if (caughtsig
== SIGUSR2
) {
1228 daemon_enable_console("Daemon[Debug]");
1230 daemon_disable_console();
1232 else if (caughtsig
== SIGUSR1
&& cmd
) {
1233 char inpbuf
[200], outbuf
[1000]; int rc
;
1234 strcpy(inpbuf
, "Hello\nWorld!\n");
1235 rc
= daemon_spawn(cmd
, inpbuf
, strlen(inpbuf
), outbuf
, sizeof(outbuf
));
1237 daemon_enable_console("Command output");
1238 printf("\"%s\" returns %d\n", cmd
, rc
);
1240 printf("output:\n%s.\n", outbuf
);
1243 Sleep(10000); daemon_disable_console();
1246 printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig
); fflush(stdout
);
1247 if (caughtsig
== SIGTERM
|| caughtsig
== SIGBREAK
)
1252 printf("\nExiting on signal %d\n", caughtsig
);
1257 int main(int argc
, char **argv
)
1259 static const daemon_winsvc_options svc_opts
= {
1260 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1263 return daemon_main("testd", &svc_opts
, test_main
, argc
, argv
);