root/trunk/smartmontools/os_win32/daemon_win32.cpp @ 3576

Revision 3576, 30.2 KB (checked in by chrfranke, 21 months ago)

Windows: Drop backward compatibility with WinNT4.

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