]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - os_win32/daemon_win32.cpp
Merge tag 'upstream/5.42+svn3521'
[mirror_smartmontools-debian.git] / os_win32 / daemon_win32.cpp
1 /*
2 * os_win32/daemon_win32.cpp
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2004-11 Christian Franke <smartmontools-support@lists.sourceforge.net>
7 *
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)
11 * any later version.
12 *
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.
16 *
17 */
18
19 // Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
20 #define WINVER 0x0400
21 #define _WIN32_WINNT WINVER
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <io.h>
27
28 #define WIN32_LEAN_AND_MEAN
29 #include <windows.h>
30 #ifdef _DEBUG
31 #include <crtdbg.h>
32 #endif
33
34 #include "daemon_win32.h"
35
36 const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 3426 2011-10-06 18:23:15Z chrfranke $"
37 DAEMON_WIN32_H_CVSID;
38
39
40 /////////////////////////////////////////////////////////////////////////////
41
42 #define ARGUSED(x) ((void)(x))
43
44 // Prevent spawning of child process if debugging
45 #ifdef _DEBUG
46 #define debugging() IsDebuggerPresent()
47 #else
48 #define debugging() FALSE
49 #endif
50
51
52 #define EVT_NAME_LEN 260
53
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
58
59 static void make_name(char * name, int sig)
60 {
61 int i;
62 if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10))
63 strcpy(name, "DaemonEvent");
64 for (i = 0; name[i]; i++) {
65 char c = name[i];
66 if (!( ('0' <= c && c <= '9')
67 || ('A' <= c && c <= 'Z')
68 || ('a' <= c && c <= 'z')))
69 name[i] = '_';
70 }
71 sprintf(name+strlen(name), "-%d", sig);
72 }
73
74
75 static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
76 {
77 char name[EVT_NAME_LEN];
78 HANDLE h;
79 if (sig >= 0)
80 make_name(name, sig);
81 else
82 name[0] = 0;
83 if (exists)
84 *exists = FALSE;
85 if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) {
86 if (errmsg)
87 fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError());
88 return 0;
89 }
90
91 if (GetLastError() == ERROR_ALREADY_EXISTS) {
92 if (!exists) {
93 if (errmsg)
94 fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name);
95 CloseHandle(h);
96 return 0;
97 }
98 *exists = TRUE;
99 }
100 return h;
101 }
102
103
104 static HANDLE open_event(int sig)
105 {
106 char name[EVT_NAME_LEN];
107 make_name(name, sig);
108 return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
109 }
110
111
112 static int event_exists(int sig)
113 {
114 char name[EVT_NAME_LEN];
115 HANDLE h;
116 make_name(name, sig);
117 if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name)))
118 return 0;
119 CloseHandle(h);
120 return 1;
121 }
122
123
124 static int sig_event(int sig)
125 {
126 char name[EVT_NAME_LEN];
127 HANDLE h;
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)))
132 return -1;
133 CloseHandle(h);
134 return 0;
135 }
136 SetEvent(h);
137 CloseHandle(h);
138 return 1;
139 }
140
141
142 static void daemon_help(FILE * f, const char * ident, const char * message)
143 {
144 fprintf(f,
145 "%s: %s.\n"
146 "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
147 ident, message, ident);
148 fflush(f);
149 }
150
151
152 /////////////////////////////////////////////////////////////////////////////
153 // Parent Process
154
155
156 static BOOL WINAPI parent_console_handler(DWORD event)
157 {
158 switch (event) {
159 case CTRL_C_EVENT:
160 case CTRL_BREAK_EVENT:
161 return TRUE; // Ignore
162 }
163 return FALSE; // continue with next handler ...
164 }
165
166
167 static int parent_main(HANDLE rev)
168 {
169 HANDLE dev;
170 HANDLE ht[2];
171 char * cmdline;
172 STARTUPINFO si;
173 PROCESS_INFORMATION pi;
174 DWORD rc, exitcode;
175
176 // Ignore ^C, ^BREAK in parent
177 SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/);
178
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*/))) {
181 CloseHandle(rev);
182 return 101;
183 }
184
185 // Restart process with same args
186 cmdline = GetCommandLineA();
187 memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
188
189 if (!CreateProcessA(
190 NULL, cmdline,
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);
195 return 101;
196 }
197 CloseHandle(pi.hThread);
198
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);
205 }
206 CloseHandle(rev); CloseHandle(dev);
207
208 // Get exit code
209 if (!GetExitCodeProcess(pi.hProcess, &exitcode))
210 exitcode = 201;
211 else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK
212 exitcode = 0;
213
214 CloseHandle(pi.hProcess);
215 return exitcode;
216 }
217
218
219 /////////////////////////////////////////////////////////////////////////////
220 // Child Process
221
222
223 static int svc_mode; // Running as service?
224 static int svc_paused; // Service paused?
225
226 static void service_report_status(int state, int waithint);
227
228
229 // Tables of signal handler and corresponding events
230 typedef void (*sigfunc_t)(int);
231
232 #define MAX_SIG_HANDLERS 8
233
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];
238
239 static HANDLE sighup_handle, sigint_handle, sigbreak_handle;
240 static HANDLE sigterm_handle, sigusr1_handle;
241
242 static HANDLE running_event;
243
244 static int reopen_stdin, reopen_stdout, reopen_stderr;
245
246
247 // Handler for windows console events
248
249 static BOOL WINAPI child_console_handler(DWORD event)
250 {
251 // Caution: runs in a new thread
252 // TODO: Guard with a mutex
253 HANDLE h = 0;
254 switch (event) {
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;
263 }
264 if (!h)
265 return FALSE; // continue with next handler
266 // Signal event
267 if (!SetEvent(h))
268 return FALSE;
269 return TRUE;
270 }
271
272
273 static void child_exit(void)
274 {
275 int i;
276 char * cmdline;
277 HANDLE rst;
278 STARTUPINFO si;
279 PROCESS_INFORMATION pi;
280
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;
285
286 // Restart?
287 if (!(rst = open_event(EVT_RESTART)))
288 return; // No => normal exit
289
290 // Yes => Signal exit and restart process
291 Sleep(500);
292 SetEvent(rst);
293 CloseHandle(rst);
294 Sleep(500);
295
296 cmdline = GetCommandLineA();
297 memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
298 si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
299
300 if (!CreateProcessA(
301 NULL, cmdline,
302 NULL, NULL, TRUE/*inherit*/,
303 0, NULL, NULL, &si, &pi)) {
304 fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
305 }
306 CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
307 }
308
309 static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
310 {
311 // Keep EVT_RUNNING open until exit
312 running_event = hev;
313
314 // Install console handler
315 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
316
317 // Install restart handler
318 atexit(child_exit);
319
320 // Continue in main_func() to do the real work
321 return main_func(argc, argv);
322 }
323
324
325 // Simulate signal()
326
327 sigfunc_t daemon_signal(int sig, sigfunc_t func)
328 {
329 int i;
330 HANDLE h;
331 if (func == SIG_DFL || func == SIG_IGN)
332 return func; // TODO
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;
337 return old;
338 }
339 }
340 if (num_sig_handlers >= MAX_SIG_HANDLERS)
341 return SIG_ERR;
342 if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL)))
343 return SIG_ERR;
344 sig_events[num_sig_handlers] = h;
345 sig_numbers[num_sig_handlers] = sig;
346 sig_handlers[num_sig_handlers] = func;
347 switch (sig) {
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;
353 }
354 num_sig_handlers++;
355 return SIG_DFL;
356 }
357
358
359 // strsignal()
360
361 const char * daemon_strsignal(int sig)
362 {
363 switch (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*";
371 }
372 }
373
374
375 // Simulate sleep()
376
377 void daemon_sleep(int seconds)
378 {
379 do {
380 if (num_sig_handlers <= 0) {
381 Sleep(seconds*1000L);
382 }
383 else {
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);
391 return;
392 }
393 // Call Handler
394 sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]);
395 break;
396 }
397 }
398 } while (svc_paused);
399 }
400
401
402 // Disable/Enable console
403
404 void daemon_disable_console()
405 {
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;
410 }
411 if (isatty(fileno(stdout))) {
412 fclose(stdout); reopen_stdout = 1;
413 }
414 if (isatty(fileno(stderr))) {
415 fclose(stderr); reopen_stderr = 1;
416 }
417 FreeConsole();
418 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
419 }
420
421 int daemon_enable_console(const char * title)
422 {
423 BOOL ok;
424 SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
425 ok = AllocConsole();
426 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
427 if (!ok)
428 return -1;
429 if (title)
430 SetConsoleTitleA(title);
431 if (reopen_stdin)
432 freopen("conin$", "r", stdin);
433 if (reopen_stdout)
434 freopen("conout$", "w", stdout);
435 if (reopen_stderr)
436 freopen("conout$", "w", stderr);
437 reopen_stdin = reopen_stdout = reopen_stderr = 0;
438 return 0;
439 }
440
441
442 // Detach daemon from console & parent
443
444 int daemon_detach(const char * ident)
445 {
446 if (!svc_mode) {
447 if (ident) {
448 // Print help
449 FILE * f = ( isatty(fileno(stdout)) ? stdout
450 : isatty(fileno(stderr)) ? stderr : NULL);
451 if (f)
452 daemon_help(f, ident, "now detaches from console into background mode");
453 }
454 // Signal detach to parent
455 if (sig_event(EVT_DETACHED) != 1) {
456 if (!debugging())
457 return -1;
458 }
459 daemon_disable_console();
460 }
461 else {
462 // Signal end of initialization to service control manager
463 service_report_status(SERVICE_RUNNING, 0);
464 reopen_stdin = reopen_stdout = reopen_stderr = 1;
465 }
466
467 return 0;
468 }
469
470
471 /////////////////////////////////////////////////////////////////////////////
472 // MessageBox
473
474 #ifndef _MT
475 //MT runtime not necessary, because mbox_thread uses no unsafe lib functions
476 //#error Program must be linked with multithreaded runtime library
477 #endif
478
479 static LONG mbox_count; // # mbox_thread()s
480 static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service)
481
482 typedef struct mbox_args_s {
483 HANDLE taken; const char * title, * text; int mode;
484 } mbox_args;
485
486
487 // Thread to display one message box
488
489 static ULONG WINAPI mbox_thread(LPVOID arg)
490 {
491 // Take args
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;
496 mode = mb->mode;
497 SetEvent(mb->taken);
498
499 // Show only one box at a time
500 WaitForSingleObject(mbox_mutex, INFINITE);
501 MessageBoxA(NULL, text, title, mode);
502 ReleaseMutex(mbox_mutex);
503
504 InterlockedDecrement(&mbox_count);
505 return 0;
506 }
507
508
509 // Display a message box
510 int daemon_messagebox(int system, const char * title, const char * text)
511 {
512 mbox_args mb;
513 HANDLE ht; DWORD tid;
514
515 // Create mutex during first call
516 if (!mbox_mutex)
517 mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/);
518
519 // Allow at most 10 threads
520 if (InterlockedIncrement(&mbox_count) > 10) {
521 InterlockedDecrement(&mbox_count);
522 return -1;
523 }
524
525 // Create thread
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);
530 mb.title = title;
531 mb.text = text;
532 if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid)))
533 return -1;
534
535 // Wait for args taken
536 if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0)
537 TerminateThread(ht, 0);
538 CloseHandle(mb.taken);
539 CloseHandle(ht);
540 return 0;
541 }
542
543
544 /////////////////////////////////////////////////////////////////////////////
545
546 // Spawn a command and redirect <inpbuf >outbuf
547 // return command's exitcode or -1 on error
548
549 int daemon_spawn(const char * cmd,
550 const char * inpbuf, int inpsize,
551 char * outbuf, int outsize )
552 {
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();
560
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;
565 }
566 else {
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;
571 }
572
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))
577 return -1;
578 if (!DuplicateHandle(self, h, self, &pipe_inp_w,
579 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
580 CloseHandle(pipe_inp_r);
581 return -1;
582 }
583
584 memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
585 sa.bInheritHandle = TRUE;
586 if (!use_file) {
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);
590 return -1;
591 }
592 }
593 else {
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);
600 return -1;
601 }
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);
605 return -1;
606 }
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);
610 return -1;
611 }
612 }
613
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);
617 return -1;
618 }
619
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);
625 return -1;
626 }
627
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;
634 if (!CreateProcessA(
635 NULL, (char*)cmd,
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);
642 return -1;
643 }
644 CloseHandle(pi.hThread);
645 // Close inherited handles
646 CloseHandle(pipe_inp_r);
647 CloseHandle(pipe_out_w);
648 CloseHandle(pipe_err_w);
649
650 // Copy inpbuf to stdin
651 // convert \n => \r\n
652 for (i = 0; i < inpsize; ) {
653 int len = 0;
654 while (i+len < inpsize && inpbuf[i+len] != '\n')
655 len++;
656 if (len > 0)
657 WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL);
658 i += len;
659 if (i < inpsize) {
660 WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL);
661 i++;
662 }
663 }
664 CloseHandle(pipe_inp_w);
665
666 exitcode = 42;
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
673 if (use_file)
674 SetFilePointer(pipe_out_r, 0, NULL, FILE_BEGIN);
675 for (i = 0; ; ) {
676 char buf[256];
677 int j;
678 if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0)
679 break;
680 for (j = 0; i < outsize-1 && j < (int)num_io; j++) {
681 if (buf[j] != '\r')
682 outbuf[i++] = buf[j];
683 }
684 }
685 outbuf[i] = 0;
686 CloseHandle(pipe_out_r);
687 if (use_file)
688 DeleteFileA(temp_path);
689 }
690 else {
691 // Wait for process exitcode
692 WaitForSingleObject(pi.hProcess, INFINITE);
693 GetExitCodeProcess(pi.hProcess, &exitcode);
694 CloseHandle(pi.hProcess);
695 }
696 }
697 return exitcode;
698 }
699
700
701 /////////////////////////////////////////////////////////////////////////////
702 // Initd Functions
703
704 static int wait_signaled(HANDLE h, int seconds)
705 {
706 int i;
707 for (i = 0; ; ) {
708 if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0)
709 return 0;
710 if (++i >= seconds)
711 return -1;
712 fputchar('.'); fflush(stdout);
713 }
714 }
715
716
717 static int wait_evt_running(int seconds, int exists)
718 {
719 int i;
720 if (event_exists(EVT_RUNNING) == exists)
721 return 0;
722 for (i = 0; ; ) {
723 Sleep(1000);
724 if (event_exists(EVT_RUNNING) == exists)
725 return 0;
726 if (++i >= seconds)
727 return -1;
728 fputchar('.'); fflush(stdout);
729 }
730 }
731
732
733 static int is_initd_command(char * s)
734 {
735 if (!strcmp(s, "status"))
736 return EVT_RUNNING;
737 if (!strcmp(s, "stop"))
738 return SIGTERM;
739 if (!strcmp(s, "reload"))
740 return SIGHUP;
741 if (!strcmp(s, "sigusr1"))
742 return SIGUSR1;
743 if (!strcmp(s, "sigusr2"))
744 return SIGUSR2;
745 if (!strcmp(s, "restart"))
746 return EVT_RESTART;
747 return -1;
748 }
749
750
751 static int initd_main(const char * ident, int argc, char **argv)
752 {
753 int rc;
754 if (argc < 2)
755 return -1;
756 if ((rc = is_initd_command(argv[1])) < 0)
757 return -1;
758 if (argc != 2) {
759 printf("%s: no arguments allowed for command %s\n", ident, argv[1]);
760 return 1;
761 }
762
763 switch (rc) {
764 default:
765 case EVT_RUNNING:
766 printf("Checking for %s:", ident); fflush(stdout);
767 rc = event_exists(EVT_RUNNING);
768 puts(rc ? " running" : " not running");
769 return (rc ? 0 : 1);
770
771 case SIGTERM:
772 printf("Stopping %s:", ident); fflush(stdout);
773 rc = sig_event(SIGTERM);
774 if (rc <= 0) {
775 puts(rc < 0 ? " not running" : " error");
776 return (rc < 0 ? 0 : 1);
777 }
778 rc = wait_evt_running(10, 0);
779 puts(!rc ? " done" : " timeout");
780 return (!rc ? 0 : 1);
781
782 case SIGHUP:
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);
787
788 case SIGUSR1:
789 case SIGUSR2:
790 printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout);
791 rc = sig_event(rc);
792 puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
793 return (rc > 0 ? 0 : 1);
794
795 case EVT_RESTART:
796 {
797 HANDLE rst;
798 printf("Stopping %s:", ident); fflush(stdout);
799 if (event_exists(EVT_DETACHED)) {
800 puts(" not detached, cannot restart");
801 return 1;
802 }
803 if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) {
804 puts(" error");
805 return 1;
806 }
807 rc = sig_event(SIGTERM);
808 if (rc <= 0) {
809 puts(rc < 0 ? " not running" : " error");
810 CloseHandle(rst);
811 return 1;
812 }
813 rc = wait_signaled(rst, 10);
814 CloseHandle(rst);
815 if (rc) {
816 puts(" timeout");
817 return 1;
818 }
819 puts(" done");
820 Sleep(100);
821
822 printf("Starting %s:", ident); fflush(stdout);
823 rc = wait_evt_running(10, 1);
824 puts(!rc ? " done" : " error");
825 return (!rc ? 0 : 1);
826 }
827 }
828 }
829
830
831 /////////////////////////////////////////////////////////////////////////////
832 // Windows Service Functions
833
834 int daemon_winsvc_exitcode; // Set by app to exit(code)
835
836 static SERVICE_STATUS_HANDLE svc_handle;
837 static SERVICE_STATUS svc_status;
838
839
840 // Report status to SCM
841
842 static void service_report_status(int state, int seconds)
843 {
844 // TODO: Avoid race
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;
849 switch (state) {
850 default:
851 svc_status.dwCheckPoint = checkpoint++;
852 break;
853 case SERVICE_RUNNING:
854 case SERVICE_STOPPED:
855 svc_status.dwCheckPoint = 0;
856 }
857 switch (state) {
858 case SERVICE_START_PENDING:
859 case SERVICE_STOP_PENDING:
860 svc_status.dwControlsAccepted = 0;
861 break;
862 default:
863 svc_status.dwControlsAccepted =
864 SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
865 SERVICE_ACCEPT_PAUSE_CONTINUE|accept_more;
866 break;
867 }
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;
872 accept_more = 0;
873 SetServiceStatus(svc_handle, &svc_status);
874 }
875 }
876 }
877
878
879 // Control the service, called by SCM
880
881 static void WINAPI service_control(DWORD ctrlcode)
882 {
883 switch (ctrlcode) {
884 case SERVICE_CONTROL_STOP:
885 case SERVICE_CONTROL_SHUTDOWN:
886 service_report_status(SERVICE_STOP_PENDING, 30);
887 svc_paused = 0;
888 SetEvent(sigterm_handle);
889 break;
890 case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP
891 service_report_status(svc_status.dwCurrentState, 0);
892 svc_paused = 0;
893 SetEvent(sighup_handle); // reload
894 break;
895 case SERVICE_CONTROL_PAUSE:
896 service_report_status(SERVICE_PAUSED, 0);
897 svc_paused = 1;
898 break;
899 case SERVICE_CONTROL_CONTINUE:
900 service_report_status(SERVICE_RUNNING, 0);
901 {
902 int was_paused = svc_paused;
903 svc_paused = 0;
904 SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck
905 }
906 break;
907 case SERVICE_CONTROL_INTERROGATE:
908 default: // unknown
909 service_report_status(svc_status.dwCurrentState, 0);
910 break;
911 }
912 }
913
914
915 // Exit handler for service
916
917 static void service_exit(void)
918 {
919 // Close signal events
920 int i;
921 for (i = 0; i < num_sig_handlers; i++)
922 CloseHandle(sig_events[i]);
923 num_sig_handlers = 0;
924
925 // Set exitcode
926 if (daemon_winsvc_exitcode) {
927 svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
928 svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode;
929 }
930 // Report stopped
931 service_report_status(SERVICE_STOPPED, 0);
932 }
933
934
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;
939
940 // Main function for service, called by service dispatcher
941
942 static void WINAPI service_main(DWORD argc, LPSTR * argv)
943 {
944 char path[MAX_PATH], *p;
945 ARGUSED(argc);
946
947 // Register control handler
948 svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
949
950 // Init service status
951 svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
952 service_report_status(SERVICE_START_PENDING, 10);
953
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);
957 }
958
959 // Install exit handler
960 atexit(service_exit);
961
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);
964
965 exit(daemon_winsvc_exitcode);
966 // ... continued in service_exit()
967 }
968
969
970 /////////////////////////////////////////////////////////////////////////////
971 // Windows Service Admin Functions
972
973 // Set Service description (Win2000/XP)
974
975 static int svcadm_setdesc(SC_HANDLE hs, const char * desc)
976 {
977 HINSTANCE hdll;
978 BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID);
979 BOOL ret;
980 if (!(hdll = LoadLibraryA("ADVAPI32.DLL")))
981 return FALSE;
982 if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A"))))
983 ret = FALSE;
984 else {
985 SERVICE_DESCRIPTIONA sd = { (char *)desc };
986 ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd);
987 }
988 FreeLibrary(hdll);
989 return ret;
990 }
991
992
993 // Service install/remove commands
994
995 static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts,
996 int argc, char **argv )
997 {
998 int remove; long err;
999 SC_HANDLE hm, hs;
1000
1001 if (argc < 2)
1002 return -1;
1003 if (!strcmp(argv[1], "install"))
1004 remove = 0;
1005 else if (!strcmp(argv[1], "remove")) {
1006 if (argc != 2) {
1007 printf("%s: no arguments allowed for command remove\n", ident);
1008 return 1;
1009 }
1010 remove = 1;
1011 }
1012 else
1013 return -1;
1014
1015 printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout);
1016
1017 // Open SCM
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");
1023 else
1024 printf(" cannot open SCManager, Error=%ld\n", err);
1025 return 1;
1026 }
1027
1028 if (!remove) {
1029 char path[MAX_PATH+100];
1030 int i;
1031 // Get program path
1032 if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
1033 printf(" unknown program path, Error=%ld\n", GetLastError());
1034 CloseServiceHandle(hm);
1035 return 1;
1036 }
1037 // Add quotes if necessary
1038 if (strchr(path, ' ')) {
1039 i = strlen(path);
1040 path[i+1] = '"'; path[i+2] = 0;
1041 while (--i >= 0)
1042 path[i+1] = path[i];
1043 path[0] = '"';
1044 }
1045 // Append options
1046 strcat(path, " "); strcat(path, svc_opts->cmd_opt);
1047 for (i = 2; i < argc; i++) {
1048 const char * s = argv[i];
1049 if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path))
1050 break;
1051 // Add quotes if necessary
1052 if (strchr(s, ' ') && !strchr(s, '"')) {
1053 strcat(path, " \""); strcat(path, s); strcat(path, "\"");
1054 }
1055 else {
1056 strcat(path, " "); strcat(path, s);
1057 }
1058 }
1059 // Create
1060 if (!(hs = CreateService(hm,
1061 svc_opts->svcname, svc_opts->dispname,
1062 SERVICE_ALL_ACCESS,
1063 SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
1064 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
1065 NULL/*no load ordering*/, NULL/*no tag id*/,
1066 ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) {
1067 if ((err = GetLastError()) == ERROR_SERVICE_EXISTS)
1068 puts(" the service is already installed");
1069 else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
1070 puts(" service is still running and marked for deletion\n"
1071 "Stop the service and retry install");
1072 else
1073 printf(" failed, Error=%ld\n", err);
1074 CloseServiceHandle(hm);
1075 return 1;
1076 }
1077 // Set optional description
1078 if (svc_opts->descript)
1079 svcadm_setdesc(hs, svc_opts->descript);
1080 }
1081 else {
1082 // Open
1083 if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) {
1084 puts(" not found");
1085 CloseServiceHandle(hm);
1086 return 1;
1087 }
1088 // TODO: Stop service if running
1089 // Remove
1090 if (!DeleteService(hs)) {
1091 if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE)
1092 puts(" service is still running and marked for deletion\n"
1093 "Stop the service to remove it");
1094 else
1095 printf(" failed, Error=%ld\n", err);
1096 CloseServiceHandle(hs); CloseServiceHandle(hm);
1097 return 1;
1098 }
1099 }
1100 puts(" done");
1101 CloseServiceHandle(hs); CloseServiceHandle(hm);
1102 return 0;
1103 }
1104
1105
1106 /////////////////////////////////////////////////////////////////////////////
1107 // Main Function
1108
1109 // This function must be called from main()
1110 // main_func is the function doing the real work
1111
1112 int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
1113 int (*main_func)(int, char **), int argc, char **argv )
1114 {
1115 int rc;
1116 #ifdef _DEBUG
1117 // Enable Debug heap checks
1118 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
1119 |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
1120 #endif
1121
1122 // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
1123 if ((rc = initd_main(ident, argc, argv)) >= 0)
1124 return rc;
1125 // Check for [install|remove] parameters
1126 if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0)
1127 return rc;
1128
1129 // Run as service if svc_opts.cmd_opt is given as first(!) argument
1130 svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt));
1131
1132 if (!svc_mode) {
1133 // Daemon: Try to simulate a Unix-like daemon
1134 HANDLE rev;
1135 BOOL exists;
1136
1137 // Create main event to detect process type:
1138 // 1. new: parent process => start child and wait for detach() or exit() of child.
1139 // 2. exists && signaled: child process => do the real work, signal detach() to parent
1140 // 3. exists && !signaled: already running => exit()
1141 if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists)))
1142 return 100;
1143
1144 if (!exists && !debugging()) {
1145 // Event new => parent process
1146 return parent_main(rev);
1147 }
1148
1149 if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) {
1150 // Event was signaled => In child process
1151 return child_main(rev, main_func, argc, argv);
1152 }
1153
1154 // Event no longer signaled => Already running!
1155 daemon_help(stdout, ident, "already running");
1156 CloseHandle(rev);
1157 return 1;
1158 }
1159 else {
1160 // Service: Start service_main() via SCM
1161 SERVICE_TABLE_ENTRY service_table[] = {
1162 { (char*)svc_opts->svcname, service_main }, { NULL, NULL }
1163 };
1164
1165 svc_main_func = main_func;
1166 svc_main_argc = argc;
1167 svc_main_argv = argv;
1168 if (!StartServiceCtrlDispatcher(service_table)) {
1169 printf("%s: cannot dispatch service, Error=%ld\n"
1170 "Option \"%s\" cannot be used to start %s as a service from console.\n"
1171 "Use \"%s install ...\" to install the service\n"
1172 "and \"net start %s\" to start it.\n",
1173 ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident);
1174
1175 #ifdef _DEBUG
1176 if (debugging())
1177 service_main(argc, argv);
1178 #endif
1179 return 100;
1180 }
1181 Sleep(1000);
1182 ExitThread(0); // Do not redo exit() processing
1183 /*NOTREACHED*/
1184 return 0;
1185 }
1186 }
1187
1188
1189 /////////////////////////////////////////////////////////////////////////////
1190 // Test Program
1191
1192 #ifdef TEST
1193
1194 static volatile sig_atomic_t caughtsig = 0;
1195
1196 static void sig_handler(int sig)
1197 {
1198 caughtsig = sig;
1199 }
1200
1201 static void test_exit(void)
1202 {
1203 printf("Main exit\n");
1204 }
1205
1206 int test_main(int argc, char **argv)
1207 {
1208 int i;
1209 int debug = 0;
1210 char * cmd = 0;
1211
1212 printf("PID=%ld\n", GetCurrentProcessId());
1213 for (i = 0; i < argc; i++) {
1214 printf("%d: \"%s\"\n", i, argv[i]);
1215 if (!strcmp(argv[i],"-d"))
1216 debug = 1;
1217 }
1218 if (argc > 1 && argv[argc-1][0] != '-')
1219 cmd = argv[argc-1];
1220
1221 daemon_signal(SIGINT, sig_handler);
1222 daemon_signal(SIGBREAK, sig_handler);
1223 daemon_signal(SIGTERM, sig_handler);
1224 daemon_signal(SIGHUP, sig_handler);
1225 daemon_signal(SIGUSR1, sig_handler);
1226 daemon_signal(SIGUSR2, sig_handler);
1227
1228 atexit(test_exit);
1229
1230 if (!debug) {
1231 printf("Preparing to detach...\n");
1232 Sleep(2000);
1233 daemon_detach("test");
1234 printf("Detached!\n");
1235 }
1236
1237 for (;;) {
1238 daemon_sleep(1);
1239 printf("."); fflush(stdout);
1240 if (caughtsig) {
1241 if (caughtsig == SIGUSR2) {
1242 debug ^= 1;
1243 if (debug)
1244 daemon_enable_console("Daemon[Debug]");
1245 else
1246 daemon_disable_console();
1247 }
1248 else if (caughtsig == SIGUSR1 && cmd) {
1249 char inpbuf[200], outbuf[1000]; int rc;
1250 strcpy(inpbuf, "Hello\nWorld!\n");
1251 rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf));
1252 if (!debug)
1253 daemon_enable_console("Command output");
1254 printf("\"%s\" returns %d\n", cmd, rc);
1255 if (rc >= 0)
1256 printf("output:\n%s.\n", outbuf);
1257 fflush(stdout);
1258 if (!debug) {
1259 Sleep(10000); daemon_disable_console();
1260 }
1261 }
1262 printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout);
1263 if (caughtsig == SIGTERM || caughtsig == SIGBREAK)
1264 break;
1265 caughtsig = 0;
1266 }
1267 }
1268 printf("\nExiting on signal %d\n", caughtsig);
1269 return 0;
1270 }
1271
1272
1273 int main(int argc, char **argv)
1274 {
1275 static const daemon_winsvc_options svc_opts = {
1276 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1277 };
1278
1279 return daemon_main("testd", &svc_opts, test_main, argc, argv);
1280 }
1281
1282 #endif