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