version 1.1.1.1, 2012/02/21 16:32:16
|
version 1.1.1.2, 2013/07/22 01:17:36
|
Line 3
|
Line 3
|
* |
* |
* Home page of code is: http://smartmontools.sourceforge.net |
* Home page of code is: http://smartmontools.sourceforge.net |
* |
* |
* Copyright (C) 2004-11 Christian Franke <smartmontools-support@lists.sourceforge.net> | * Copyright (C) 2004-13 Christian Franke <smartmontools-support@lists.sourceforge.net> |
* |
* |
* This program is free software; you can redistribute it and/or modify |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* it under the terms of the GNU General Public License as published by |
Line 11
|
Line 11
|
* any later version. |
* any later version. |
* |
* |
* You should have received a copy of the GNU General Public License |
* You should have received a copy of the GNU General Public License |
* (for example COPYING); if not, write to the Free | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. |
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
* |
* |
*/ |
*/ |
|
|
// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP) | #define WINVER 0x0600 |
#define WINVER 0x0400 | |
#define _WIN32_WINNT WINVER |
#define _WIN32_WINNT WINVER |
|
|
|
#include "daemon_win32.h" |
|
|
|
const char * daemon_win32_cpp_cvsid = "$Id$" |
|
DAEMON_WIN32_H_CVSID; |
|
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <signal.h> |
#include <signal.h> |
Line 31
|
Line 34
|
#include <crtdbg.h> |
#include <crtdbg.h> |
#endif |
#endif |
|
|
#include "daemon_win32.h" | #ifndef SERVICE_CONFIG_DELAYED_AUTO_START_INFO |
| // Missing in older MinGW headers |
| #define SERVICE_CONFIG_DELAYED_AUTO_START_INFO 3 |
| #endif |
|
|
const char * daemon_win32_cpp_cvsid = "$Id$" |
|
DAEMON_WIN32_H_CVSID; |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////////////////////////// |
|
|
#define ARGUSED(x) ((void)(x)) |
|
|
|
// Prevent spawning of child process if debugging |
// Prevent spawning of child process if debugging |
#ifdef _DEBUG |
#ifdef _DEBUG |
#define debugging() IsDebuggerPresent() |
#define debugging() IsDebuggerPresent() |
Line 58 const char * daemon_win32_cpp_cvsid = "$Id$"
|
Line 59 const char * daemon_win32_cpp_cvsid = "$Id$"
|
|
|
static void make_name(char * name, int sig) |
static void make_name(char * name, int sig) |
{ |
{ |
int i; | int i; |
if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10)) | if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10)) |
strcpy(name, "DaemonEvent"); | strcpy(name, "DaemonEvent"); |
for (i = 0; name[i]; i++) { | for (i = 0; name[i]; i++) { |
char c = name[i]; | char c = name[i]; |
if (!( ('0' <= c && c <= '9') | if (!( ('0' <= c && c <= '9') |
|| ('A' <= c && c <= 'Z') | || ('A' <= c && c <= 'Z') |
|| ('a' <= c && c <= 'z'))) | || ('a' <= c && c <= 'z'))) |
name[i] = '_'; | name[i] = '_'; |
} | } |
sprintf(name+strlen(name), "-%d", sig); | sprintf(name+strlen(name), "-%d", sig); |
} |
} |
|
|
|
|
static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists) |
static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists) |
{ |
{ |
char name[EVT_NAME_LEN]; | char name[EVT_NAME_LEN]; |
HANDLE h; | HANDLE h; |
if (sig >= 0) | if (sig >= 0) |
make_name(name, sig); | make_name(name, sig); |
else | else |
name[0] = 0; | name[0] = 0; |
if (exists) | if (exists) |
*exists = FALSE; | *exists = FALSE; |
if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) { | if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) { |
if (errmsg) | if (errmsg) |
fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError()); | fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError()); |
return 0; | return 0; |
} | } |
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS) { | if (GetLastError() == ERROR_ALREADY_EXISTS) { |
if (!exists) { | if (!exists) { |
if (errmsg) | if (errmsg) |
fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name); | fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name); |
CloseHandle(h); | CloseHandle(h); |
return 0; | return 0; |
} | } |
*exists = TRUE; | *exists = TRUE; |
} | } |
return h; | return h; |
} |
} |
|
|
|
|
static HANDLE open_event(int sig) |
static HANDLE open_event(int sig) |
{ |
{ |
char name[EVT_NAME_LEN]; | char name[EVT_NAME_LEN]; |
make_name(name, sig); | make_name(name, sig); |
return OpenEventA(EVENT_MODIFY_STATE, FALSE, name); | return OpenEventA(EVENT_MODIFY_STATE, FALSE, name); |
} |
} |
|
|
|
|
static int event_exists(int sig) |
static int event_exists(int sig) |
{ |
{ |
char name[EVT_NAME_LEN]; | char name[EVT_NAME_LEN]; |
HANDLE h; | HANDLE h; |
make_name(name, sig); | make_name(name, sig); |
if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) | if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) |
return 0; | return 0; |
CloseHandle(h); | CloseHandle(h); |
return 1; | return 1; |
} |
} |
|
|
|
|
static int sig_event(int sig) |
static int sig_event(int sig) |
{ |
{ |
char name[EVT_NAME_LEN]; | char name[EVT_NAME_LEN]; |
HANDLE h; | HANDLE h; |
make_name(name, sig); | make_name(name, sig); |
if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) { | if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) { |
make_name(name, EVT_RUNNING); | make_name(name, EVT_RUNNING); |
if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name))) | if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name))) |
return -1; | return -1; |
CloseHandle(h); | CloseHandle(h); |
return 0; | return 0; |
} | } |
SetEvent(h); | SetEvent(h); |
CloseHandle(h); | CloseHandle(h); |
return 1; | return 1; |
} |
} |
|
|
|
|
static void daemon_help(FILE * f, const char * ident, const char * message) |
static void daemon_help(FILE * f, const char * ident, const char * message) |
{ |
{ |
fprintf(f, | fprintf(f, |
"%s: %s.\n" | "%s: %s.\n" |
"Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n", | "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n", |
ident, message, ident); | ident, message, ident); |
fflush(f); | fflush(f); |
} |
} |
|
|
|
|
Line 155 static void daemon_help(FILE * f, const char * ident,
|
Line 156 static void daemon_help(FILE * f, const char * ident,
|
|
|
static BOOL WINAPI parent_console_handler(DWORD event) |
static BOOL WINAPI parent_console_handler(DWORD event) |
{ |
{ |
switch (event) { | switch (event) { |
case CTRL_C_EVENT: | case CTRL_C_EVENT: |
case CTRL_BREAK_EVENT: | case CTRL_BREAK_EVENT: |
return TRUE; // Ignore | return TRUE; // Ignore |
} | } |
return FALSE; // continue with next handler ... | return FALSE; // continue with next handler ... |
} |
} |
|
|
|
|
static int parent_main(HANDLE rev) |
static int parent_main(HANDLE rev) |
{ |
{ |
HANDLE dev; | HANDLE dev; |
HANDLE ht[2]; | HANDLE ht[2]; |
char * cmdline; | char * cmdline; |
STARTUPINFO si; | STARTUPINFO si; |
PROCESS_INFORMATION pi; | PROCESS_INFORMATION pi; |
DWORD rc, exitcode; | DWORD rc, exitcode; |
|
|
// Ignore ^C, ^BREAK in parent | // Ignore ^C, ^BREAK in parent |
SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/); | SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/); |
|
|
// Create event used by child to signal daemon_detach() | // Create event used by child to signal daemon_detach() |
if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) { | if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) { |
CloseHandle(rev); | CloseHandle(rev); |
return 101; | return 101; |
} | } |
|
|
// Restart process with same args | // Restart process with same args |
cmdline = GetCommandLineA(); | cmdline = GetCommandLineA(); |
memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | memset(&si, 0, sizeof(si)); si.cb = sizeof(si); |
| |
if (!CreateProcessA( | |
NULL, cmdline, | |
NULL, NULL, TRUE/*inherit*/, | |
0, NULL, NULL, &si, &pi)) { | |
fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); | |
CloseHandle(rev); CloseHandle(dev); | |
return 101; | |
} | |
CloseHandle(pi.hThread); | |
|
|
// Wait for daemon_detach() or exit() | if (!CreateProcessA( |
ht[0] = dev; ht[1] = pi.hProcess; | NULL, cmdline, |
rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE); | NULL, NULL, TRUE/*inherit*/, |
if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) { | 0, NULL, NULL, &si, &pi)) { |
fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc); | fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); |
TerminateProcess(pi.hProcess, 200); | CloseHandle(rev); CloseHandle(dev); |
} | return 101; |
CloseHandle(rev); CloseHandle(dev); | } |
| CloseHandle(pi.hThread); |
|
|
// Get exit code | // Wait for daemon_detach() or exit() |
if (!GetExitCodeProcess(pi.hProcess, &exitcode)) | ht[0] = dev; ht[1] = pi.hProcess; |
exitcode = 201; | rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE); |
else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK | if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) { |
exitcode = 0; | fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc); |
| TerminateProcess(pi.hProcess, 200); |
| } |
| CloseHandle(rev); CloseHandle(dev); |
|
|
CloseHandle(pi.hProcess); | // Get exit code |
return exitcode; | if (!GetExitCodeProcess(pi.hProcess, &exitcode)) |
| exitcode = 201; |
| else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK |
| exitcode = 0; |
| |
| CloseHandle(pi.hProcess); |
| return exitcode; |
} |
} |
|
|
|
|
Line 248 static int reopen_stdin, reopen_stdout, reopen_stderr;
|
Line 249 static int reopen_stdin, reopen_stdout, reopen_stderr;
|
|
|
static BOOL WINAPI child_console_handler(DWORD event) |
static BOOL WINAPI child_console_handler(DWORD event) |
{ |
{ |
// Caution: runs in a new thread | // Caution: runs in a new thread |
// TODO: Guard with a mutex | // TODO: Guard with a mutex |
HANDLE h = 0; | HANDLE h = 0; |
switch (event) { | switch (event) { |
case CTRL_C_EVENT: // <CONTROL-C> (SIGINT) | case CTRL_C_EVENT: // <CONTROL-C> (SIGINT) |
h = sigint_handle; break; | h = sigint_handle; break; |
case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT) | case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT) |
case CTRL_CLOSE_EVENT: // User closed console or abort via task manager | case CTRL_CLOSE_EVENT: // User closed console or abort via task manager |
h = sigbreak_handle; break; | h = sigbreak_handle; break; |
case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM) | case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM) |
case CTRL_SHUTDOWN_EVENT: | case CTRL_SHUTDOWN_EVENT: |
h = sigterm_handle; break; | h = sigterm_handle; break; |
} | } |
if (!h) | if (!h) |
return FALSE; // continue with next handler | return FALSE; // continue with next handler |
// Signal event | // Signal event |
if (!SetEvent(h)) | if (!SetEvent(h)) |
return FALSE; | return FALSE; |
return TRUE; | return TRUE; |
} |
} |
|
|
|
|
static void child_exit(void) |
static void child_exit(void) |
{ |
{ |
int i; | int i; |
char * cmdline; | char * cmdline; |
HANDLE rst; | HANDLE rst; |
STARTUPINFO si; | STARTUPINFO si; |
PROCESS_INFORMATION pi; | PROCESS_INFORMATION pi; |
|
|
for (i = 0; i < num_sig_handlers; i++) | for (i = 0; i < num_sig_handlers; i++) |
CloseHandle(sig_events[i]); | CloseHandle(sig_events[i]); |
num_sig_handlers = 0; | num_sig_handlers = 0; |
CloseHandle(running_event); running_event = 0; | CloseHandle(running_event); running_event = 0; |
|
|
// Restart? | // Restart? |
if (!(rst = open_event(EVT_RESTART))) | if (!(rst = open_event(EVT_RESTART))) |
return; // No => normal exit | return; // No => normal exit |
|
|
// Yes => Signal exit and restart process | // Yes => Signal exit and restart process |
Sleep(500); | Sleep(500); |
SetEvent(rst); | SetEvent(rst); |
CloseHandle(rst); | CloseHandle(rst); |
Sleep(500); | Sleep(500); |
|
|
cmdline = GetCommandLineA(); | cmdline = GetCommandLineA(); |
memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | memset(&si, 0, sizeof(si)); si.cb = sizeof(si); |
si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; | si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; |
|
|
if (!CreateProcessA( | if (!CreateProcessA( |
NULL, cmdline, | NULL, cmdline, |
NULL, NULL, TRUE/*inherit*/, | NULL, NULL, TRUE/*inherit*/, |
0, NULL, NULL, &si, &pi)) { | 0, NULL, NULL, &si, &pi)) { |
fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); | fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); |
} | } |
CloseHandle(pi.hThread); CloseHandle(pi.hProcess); | CloseHandle(pi.hThread); CloseHandle(pi.hProcess); |
} |
} |
|
|
static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv) |
static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv) |
{ |
{ |
// Keep EVT_RUNNING open until exit | // Keep EVT_RUNNING open until exit |
running_event = hev; | running_event = hev; |
|
|
// Install console handler | // Install console handler |
SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); | SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); |
|
|
// Install restart handler | // Install restart handler |
atexit(child_exit); | atexit(child_exit); |
|
|
// Continue in main_func() to do the real work | // Continue in main_func() to do the real work |
return main_func(argc, argv); | return main_func(argc, argv); |
} |
} |
|
|
|
|
Line 326 static int child_main(HANDLE hev,int (*main_func)(int,
|
Line 327 static int child_main(HANDLE hev,int (*main_func)(int,
|
|
|
sigfunc_t daemon_signal(int sig, sigfunc_t func) |
sigfunc_t daemon_signal(int sig, sigfunc_t func) |
{ |
{ |
int i; | int i; |
HANDLE h; | HANDLE h; |
if (func == SIG_DFL || func == SIG_IGN) | if (func == SIG_DFL || func == SIG_IGN) |
return func; // TODO | return func; // TODO |
for (i = 0; i < num_sig_handlers; i++) { | for (i = 0; i < num_sig_handlers; i++) { |
if (sig_numbers[i] == sig) { | if (sig_numbers[i] == sig) { |
sigfunc_t old = sig_handlers[i]; | sigfunc_t old = sig_handlers[i]; |
sig_handlers[i] = func; | sig_handlers[i] = func; |
return old; | return old; |
} | } |
} | } |
if (num_sig_handlers >= MAX_SIG_HANDLERS) | if (num_sig_handlers >= MAX_SIG_HANDLERS) |
return SIG_ERR; | return SIG_ERR; |
if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL))) | if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL))) |
return SIG_ERR; | return SIG_ERR; |
sig_events[num_sig_handlers] = h; | sig_events[num_sig_handlers] = h; |
sig_numbers[num_sig_handlers] = sig; | sig_numbers[num_sig_handlers] = sig; |
sig_handlers[num_sig_handlers] = func; | sig_handlers[num_sig_handlers] = func; |
switch (sig) { | switch (sig) { |
case SIGHUP: sighup_handle = h; break; | case SIGHUP: sighup_handle = h; break; |
case SIGINT: sigint_handle = h; break; | case SIGINT: sigint_handle = h; break; |
case SIGTERM: sigterm_handle = h; break; | case SIGTERM: sigterm_handle = h; break; |
case SIGBREAK: sigbreak_handle = h; break; | case SIGBREAK: sigbreak_handle = h; break; |
case SIGUSR1: sigusr1_handle = h; break; | case SIGUSR1: sigusr1_handle = h; break; |
} | } |
num_sig_handlers++; | num_sig_handlers++; |
return SIG_DFL; | return SIG_DFL; |
} |
} |
|
|
|
|
Line 360 sigfunc_t daemon_signal(int sig, sigfunc_t func)
|
Line 361 sigfunc_t daemon_signal(int sig, sigfunc_t func)
|
|
|
const char * daemon_strsignal(int sig) |
const char * daemon_strsignal(int sig) |
{ |
{ |
switch (sig) { | switch (sig) { |
case SIGHUP: return "SIGHUP"; | case SIGHUP: return "SIGHUP"; |
case SIGINT: return "SIGINT"; | case SIGINT: return "SIGINT"; |
case SIGTERM: return "SIGTERM"; | case SIGTERM: return "SIGTERM"; |
case SIGBREAK:return "SIGBREAK"; | case SIGBREAK:return "SIGBREAK"; |
case SIGUSR1: return "SIGUSR1"; | case SIGUSR1: return "SIGUSR1"; |
case SIGUSR2: return "SIGUSR2"; | case SIGUSR2: return "SIGUSR2"; |
default: return "*UNKNOWN*"; | default: return "*UNKNOWN*"; |
} | } |
} |
} |
|
|
|
|
Line 376 const char * daemon_strsignal(int sig)
|
Line 377 const char * daemon_strsignal(int sig)
|
|
|
void daemon_sleep(int seconds) |
void daemon_sleep(int seconds) |
{ |
{ |
do { | do { |
if (num_sig_handlers <= 0) { | if (num_sig_handlers <= 0) { |
Sleep(seconds*1000L); | Sleep(seconds*1000L); |
} | } |
else { | else { |
// Wait for any signal or timeout | // Wait for any signal or timeout |
DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events, | DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events, |
FALSE/*OR*/, seconds*1000L); | FALSE/*OR*/, seconds*1000L); |
if (rc != WAIT_TIMEOUT) { | if (rc != WAIT_TIMEOUT) { |
if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) { | if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) { |
fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc); | fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc); |
Sleep(seconds*1000L); | Sleep(seconds*1000L); |
return; | return; |
} | } |
// Call Handler | // Call Handler |
sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]); | sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]); |
break; | break; |
} | } |
} | } |
} while (svc_paused); | } while (svc_paused); |
} |
} |
|
|
|
|
Line 403 void daemon_sleep(int seconds)
|
Line 404 void daemon_sleep(int seconds)
|
|
|
void daemon_disable_console() |
void daemon_disable_console() |
{ |
{ |
SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); | SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); |
reopen_stdin = reopen_stdout = reopen_stderr = 0; | reopen_stdin = reopen_stdout = reopen_stderr = 0; |
if (isatty(fileno(stdin))) { | if (isatty(fileno(stdin))) { |
fclose(stdin); reopen_stdin = 1; | fclose(stdin); reopen_stdin = 1; |
} | } |
if (isatty(fileno(stdout))) { | if (isatty(fileno(stdout))) { |
fclose(stdout); reopen_stdout = 1; | fclose(stdout); reopen_stdout = 1; |
} | } |
if (isatty(fileno(stderr))) { | if (isatty(fileno(stderr))) { |
fclose(stderr); reopen_stderr = 1; | fclose(stderr); reopen_stderr = 1; |
} | } |
FreeConsole(); | FreeConsole(); |
SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); | SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); |
} |
} |
|
|
int daemon_enable_console(const char * title) |
int daemon_enable_console(const char * title) |
{ |
{ |
BOOL ok; | BOOL ok; |
SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); | SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); |
ok = AllocConsole(); | ok = AllocConsole(); |
SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); | SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); |
if (!ok) | if (!ok) |
return -1; | return -1; |
if (title) | if (title) |
SetConsoleTitleA(title); | SetConsoleTitleA(title); |
if (reopen_stdin) | if (reopen_stdin) |
freopen("conin$", "r", stdin); | freopen("conin$", "r", stdin); |
if (reopen_stdout) | if (reopen_stdout) |
freopen("conout$", "w", stdout); | freopen("conout$", "w", stdout); |
if (reopen_stderr) | if (reopen_stderr) |
freopen("conout$", "w", stderr); | freopen("conout$", "w", stderr); |
reopen_stdin = reopen_stdout = reopen_stderr = 0; | reopen_stdin = reopen_stdout = reopen_stderr = 0; |
return 0; | return 0; |
} |
} |
|
|
|
|
Line 443 int daemon_enable_console(const char * title)
|
Line 444 int daemon_enable_console(const char * title)
|
|
|
int daemon_detach(const char * ident) |
int daemon_detach(const char * ident) |
{ |
{ |
if (!svc_mode) { | if (!svc_mode) { |
if (ident) { | if (ident) { |
// Print help | // Print help |
FILE * f = ( isatty(fileno(stdout)) ? stdout | FILE * f = ( isatty(fileno(stdout)) ? stdout |
: isatty(fileno(stderr)) ? stderr : NULL); | : isatty(fileno(stderr)) ? stderr : NULL); |
if (f) | if (f) |
daemon_help(f, ident, "now detaches from console into background mode"); | daemon_help(f, ident, "now detaches from console into background mode"); |
} | } |
// Signal detach to parent | // Signal detach to parent |
if (sig_event(EVT_DETACHED) != 1) { | if (sig_event(EVT_DETACHED) != 1) { |
if (!debugging()) | if (!debugging()) |
return -1; | return -1; |
} | } |
daemon_disable_console(); | daemon_disable_console(); |
} | } |
else { | else { |
// Signal end of initialization to service control manager | // Signal end of initialization to service control manager |
service_report_status(SERVICE_RUNNING, 0); | service_report_status(SERVICE_RUNNING, 0); |
reopen_stdin = reopen_stdout = reopen_stderr = 1; | reopen_stdin = reopen_stdout = reopen_stderr = 1; |
} | } |
|
|
return 0; | return 0; |
} |
} |
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////////////////////////// |
// MessageBox |
|
|
|
#ifndef _MT |
|
//MT runtime not necessary, because mbox_thread uses no unsafe lib functions |
|
//#error Program must be linked with multithreaded runtime library |
|
#endif |
|
|
|
static LONG mbox_count; // # mbox_thread()s |
|
static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service) |
|
|
|
typedef struct mbox_args_s { |
|
HANDLE taken; const char * title, * text; int mode; |
|
} mbox_args; |
|
|
|
|
|
// Thread to display one message box |
|
|
|
static ULONG WINAPI mbox_thread(LPVOID arg) |
|
{ |
|
// Take args |
|
mbox_args * mb = (mbox_args *)arg; |
|
char title[100]; char text[1000]; int mode; |
|
strncpy(title, mb->title, sizeof(title)-1); title[sizeof(title)-1] = 0; |
|
strncpy(text , mb->text , sizeof(text )-1); text [sizeof(text )-1] = 0; |
|
mode = mb->mode; |
|
SetEvent(mb->taken); |
|
|
|
// Show only one box at a time |
|
WaitForSingleObject(mbox_mutex, INFINITE); |
|
MessageBoxA(NULL, text, title, mode); |
|
ReleaseMutex(mbox_mutex); |
|
|
|
InterlockedDecrement(&mbox_count); |
|
return 0; |
|
} |
|
|
|
|
|
// Display a message box |
|
int daemon_messagebox(int system, const char * title, const char * text) |
|
{ |
|
mbox_args mb; |
|
HANDLE ht; DWORD tid; |
|
|
|
// Create mutex during first call |
|
if (!mbox_mutex) |
|
mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/); |
|
|
|
// Allow at most 10 threads |
|
if (InterlockedIncrement(&mbox_count) > 10) { |
|
InterlockedDecrement(&mbox_count); |
|
return -1; |
|
} |
|
|
|
// Create thread |
|
mb.taken = CreateEvent(NULL/*!inherit*/, FALSE, FALSE/*!signaled*/, NULL/*unnamed*/); |
|
mb.mode = MB_OK|MB_ICONWARNING |
|
|(svc_mode?MB_SERVICE_NOTIFICATION:0) |
|
|(system?MB_SYSTEMMODAL:MB_APPLMODAL); |
|
mb.title = title; |
|
mb.text = text; |
|
if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid))) |
|
return -1; |
|
|
|
// Wait for args taken |
|
if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0) |
|
TerminateThread(ht, 0); |
|
CloseHandle(mb.taken); |
|
CloseHandle(ht); |
|
return 0; |
|
} |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
|
|
// Spawn a command and redirect <inpbuf >outbuf |
// Spawn a command and redirect <inpbuf >outbuf |
// return command's exitcode or -1 on error |
// return command's exitcode or -1 on error |
|
|
Line 550 int daemon_spawn(const char * cmd,
|
Line 478 int daemon_spawn(const char * cmd,
|
const char * inpbuf, int inpsize, |
const char * inpbuf, int inpsize, |
char * outbuf, int outsize ) |
char * outbuf, int outsize ) |
{ |
{ |
HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h; | HANDLE self = GetCurrentProcess(); |
char temp_path[MAX_PATH]; | |
DWORD flags, num_io, exitcode; | |
int use_file, state, i; | |
SECURITY_ATTRIBUTES sa; | |
STARTUPINFO si; PROCESS_INFORMATION pi; | |
HANDLE self = GetCurrentProcess(); | |
|
|
if (GetVersion() & 0x80000000L) { | // Create stdin pipe with inheritable read side |
// Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or | SECURITY_ATTRIBUTES sa; |
// any other DOS program is redirected via a pipe. Using a temp file instead. | memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); |
use_file = 1; flags = DETACHED_PROCESS; | sa.bInheritHandle = TRUE; |
} | HANDLE pipe_inp_r, pipe_inp_w, h; |
else { | if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13)) |
// NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window | return -1; |
// for each external command in a redirected .BAT file. | if (!DuplicateHandle(self, h, self, &pipe_inp_w, |
// Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work. | 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { |
use_file = 0; flags = CREATE_NO_WINDOW; | CloseHandle(pipe_inp_r); |
} | return -1; |
| } |
|
|
// Create stdin pipe with inheritable read side | // Create stdout pipe with inheritable write side |
memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); | memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); |
sa.bInheritHandle = TRUE; | sa.bInheritHandle = TRUE; |
if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13)) | HANDLE pipe_out_w; |
return -1; | if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) { |
if (!DuplicateHandle(self, h, self, &pipe_inp_w, | CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); |
0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { | return -1; |
CloseHandle(pipe_inp_r); | } |
return -1; | |
} | |
|
|
memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); | HANDLE pipe_out_r; |
sa.bInheritHandle = TRUE; | if (!DuplicateHandle(self, h, self, &pipe_out_r, |
if (!use_file) { | GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) { |
// Create stdout pipe with inheritable write side | CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); |
if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) { | return -1; |
CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | } |
return -1; | |
} | |
} | |
else { | |
// Create temp file with inheritable write handle | |
char temp_dir[MAX_PATH]; | |
if (!GetTempPathA(sizeof(temp_dir), temp_dir)) | |
strcpy(temp_dir, "."); | |
if (!GetTempFileNameA(temp_dir, "out"/*prefix*/, 0/*create unique*/, temp_path)) { | |
CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
return -1; | |
} | |
if ((h = CreateFileA(temp_path, GENERIC_READ|GENERIC_WRITE, | |
0/*no sharing*/, &sa/*inherit*/, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { | |
CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
return -1; | |
} | |
if (!DuplicateHandle(self, h, self, &pipe_out_w, | |
GENERIC_WRITE, TRUE/*inherit*/, 0)) { | |
CloseHandle(h); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
return -1; | |
} | |
} | |
|
|
if (!DuplicateHandle(self, h, self, &pipe_out_r, | // Create stderr handle as dup of stdout write side |
GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) { | HANDLE pipe_err_w; |
CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w, |
return -1; | 0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) { |
} | CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); |
| CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); |
| return -1; |
| } |
|
|
// Create stderr handle as dup of stdout write side | // Create process with pipes as stdio |
if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w, | STARTUPINFO si; |
0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) { | memset(&si, 0, sizeof(si)); si.cb = sizeof(si); |
CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); | si.hStdInput = pipe_inp_r; |
CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | si.hStdOutput = pipe_out_w; |
return -1; | si.hStdError = pipe_err_w; |
} | si.dwFlags = STARTF_USESTDHANDLES; |
| PROCESS_INFORMATION pi; |
| if (!CreateProcessA( |
| NULL, (char*)cmd, |
| NULL, NULL, TRUE/*inherit*/, |
| CREATE_NO_WINDOW, // DETACHED_PROCESS does not work |
| NULL, NULL, &si, &pi)) { |
| CloseHandle(pipe_err_w); |
| CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); |
| CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); |
| return -1; |
| } |
| CloseHandle(pi.hThread); |
| // Close inherited handles |
| CloseHandle(pipe_inp_r); |
| CloseHandle(pipe_out_w); |
| CloseHandle(pipe_err_w); |
|
|
// Create process with pipes/file as stdio | // Copy inpbuf to stdin |
memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | // convert \n => \r\n |
si.hStdInput = pipe_inp_r; | DWORD num_io; |
si.hStdOutput = pipe_out_w; | int i; |
si.hStdError = pipe_err_w; | for (i = 0; i < inpsize; ) { |
si.dwFlags = STARTF_USESTDHANDLES; | int len = 0; |
if (!CreateProcessA( | while (i+len < inpsize && inpbuf[i+len] != '\n') |
NULL, (char*)cmd, | len++; |
NULL, NULL, TRUE/*inherit*/, | if (len > 0) |
flags/*DETACHED_PROCESS or CREATE_NO_WINDOW*/, | WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL); |
NULL, NULL, &si, &pi)) { | i += len; |
CloseHandle(pipe_err_w); | if (i < inpsize) { |
CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); | WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL); |
CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | i++; |
return -1; | } |
} | } |
CloseHandle(pi.hThread); | CloseHandle(pipe_inp_w); |
// Close inherited handles | |
CloseHandle(pipe_inp_r); | |
CloseHandle(pipe_out_w); | |
CloseHandle(pipe_err_w); | |
|
|
// Copy inpbuf to stdin | // Copy stdout to output buffer until full, rest to /dev/null |
// convert \n => \r\n | // convert \r\n => \n |
for (i = 0; i < inpsize; ) { | for (i = 0; ; ) { |
int len = 0; | char buf[256]; |
while (i+len < inpsize && inpbuf[i+len] != '\n') | if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0) |
len++; | break; |
if (len > 0) | for (int j = 0; i < outsize-1 && j < (int)num_io; j++) { |
WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL); | if (buf[j] != '\r') |
i += len; | outbuf[i++] = buf[j]; |
if (i < inpsize) { | } |
WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL); | } |
i++; | outbuf[i] = 0; |
} | CloseHandle(pipe_out_r); |
} | |
CloseHandle(pipe_inp_w); | |
|
|
exitcode = 42; | // Wait for process exitcode |
for (state = 0; state < 2; state++) { | DWORD exitcode = 42; |
// stdout pipe: read pipe first | WaitForSingleObject(pi.hProcess, INFINITE); |
// stdout file: wait for process first | GetExitCodeProcess(pi.hProcess, &exitcode); |
if (state == use_file) { | CloseHandle(pi.hProcess); |
// Copy stdout to output buffer until full, rest to /dev/null | return exitcode; |
// convert \r\n => \n | |
if (use_file) | |
SetFilePointer(pipe_out_r, 0, NULL, FILE_BEGIN); | |
for (i = 0; ; ) { | |
char buf[256]; | |
int j; | |
if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0) | |
break; | |
for (j = 0; i < outsize-1 && j < (int)num_io; j++) { | |
if (buf[j] != '\r') | |
outbuf[i++] = buf[j]; | |
} | |
} | |
outbuf[i] = 0; | |
CloseHandle(pipe_out_r); | |
if (use_file) | |
DeleteFileA(temp_path); | |
} | |
else { | |
// Wait for process exitcode | |
WaitForSingleObject(pi.hProcess, INFINITE); | |
GetExitCodeProcess(pi.hProcess, &exitcode); | |
CloseHandle(pi.hProcess); | |
} | |
} | |
return exitcode; | |
} |
} |
|
|
|
|
Line 703 int daemon_spawn(const char * cmd,
|
Line 588 int daemon_spawn(const char * cmd,
|
|
|
static int wait_signaled(HANDLE h, int seconds) |
static int wait_signaled(HANDLE h, int seconds) |
{ |
{ |
int i; | int i; |
for (i = 0; ; ) { | for (i = 0; ; ) { |
if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0) | if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0) |
return 0; | return 0; |
if (++i >= seconds) | if (++i >= seconds) |
return -1; | return -1; |
fputchar('.'); fflush(stdout); | fputchar('.'); fflush(stdout); |
} | } |
} |
} |
|
|
|
|
static int wait_evt_running(int seconds, int exists) |
static int wait_evt_running(int seconds, int exists) |
{ |
{ |
int i; | int i; |
if (event_exists(EVT_RUNNING) == exists) | if (event_exists(EVT_RUNNING) == exists) |
return 0; | return 0; |
for (i = 0; ; ) { | for (i = 0; ; ) { |
Sleep(1000); | Sleep(1000); |
if (event_exists(EVT_RUNNING) == exists) | if (event_exists(EVT_RUNNING) == exists) |
return 0; | return 0; |
if (++i >= seconds) | if (++i >= seconds) |
return -1; | return -1; |
fputchar('.'); fflush(stdout); | fputchar('.'); fflush(stdout); |
} | } |
} |
} |
|
|
|
|
static int is_initd_command(char * s) |
static int is_initd_command(char * s) |
{ |
{ |
if (!strcmp(s, "status")) | if (!strcmp(s, "status")) |
return EVT_RUNNING; | return EVT_RUNNING; |
if (!strcmp(s, "stop")) | if (!strcmp(s, "stop")) |
return SIGTERM; | return SIGTERM; |
if (!strcmp(s, "reload")) | if (!strcmp(s, "reload")) |
return SIGHUP; | return SIGHUP; |
if (!strcmp(s, "sigusr1")) | if (!strcmp(s, "sigusr1")) |
return SIGUSR1; | return SIGUSR1; |
if (!strcmp(s, "sigusr2")) | if (!strcmp(s, "sigusr2")) |
return SIGUSR2; | return SIGUSR2; |
if (!strcmp(s, "restart")) | if (!strcmp(s, "restart")) |
return EVT_RESTART; | return EVT_RESTART; |
return -1; | return -1; |
} |
} |
|
|
|
|
static int initd_main(const char * ident, int argc, char **argv) |
static int initd_main(const char * ident, int argc, char **argv) |
{ |
{ |
int rc; | int rc; |
if (argc < 2) | if (argc < 2) |
return -1; | return -1; |
if ((rc = is_initd_command(argv[1])) < 0) | if ((rc = is_initd_command(argv[1])) < 0) |
return -1; | return -1; |
if (argc != 2) { | if (argc != 2) { |
printf("%s: no arguments allowed for command %s\n", ident, argv[1]); | printf("%s: no arguments allowed for command %s\n", ident, argv[1]); |
return 1; | return 1; |
} | } |
|
|
switch (rc) { | switch (rc) { |
default: | default: |
case EVT_RUNNING: | case EVT_RUNNING: |
printf("Checking for %s:", ident); fflush(stdout); | printf("Checking for %s:", ident); fflush(stdout); |
rc = event_exists(EVT_RUNNING); | rc = event_exists(EVT_RUNNING); |
puts(rc ? " running" : " not running"); | puts(rc ? " running" : " not running"); |
return (rc ? 0 : 1); | return (rc ? 0 : 1); |
|
|
case SIGTERM: | case SIGTERM: |
printf("Stopping %s:", ident); fflush(stdout); | printf("Stopping %s:", ident); fflush(stdout); |
rc = sig_event(SIGTERM); | rc = sig_event(SIGTERM); |
if (rc <= 0) { | if (rc <= 0) { |
puts(rc < 0 ? " not running" : " error"); | puts(rc < 0 ? " not running" : " error"); |
return (rc < 0 ? 0 : 1); | return (rc < 0 ? 0 : 1); |
} | } |
rc = wait_evt_running(10, 0); | rc = wait_evt_running(10, 0); |
puts(!rc ? " done" : " timeout"); | puts(!rc ? " done" : " timeout"); |
return (!rc ? 0 : 1); | return (!rc ? 0 : 1); |
|
|
case SIGHUP: | case SIGHUP: |
printf("Reloading %s:", ident); fflush(stdout); | printf("Reloading %s:", ident); fflush(stdout); |
rc = sig_event(SIGHUP); | rc = sig_event(SIGHUP); |
puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); | puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); |
return (rc > 0 ? 0 : 1); | return (rc > 0 ? 0 : 1); |
|
|
case SIGUSR1: | case SIGUSR1: |
case SIGUSR2: | case SIGUSR2: |
printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout); | printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout); |
rc = sig_event(rc); | rc = sig_event(rc); |
puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); | puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); |
return (rc > 0 ? 0 : 1); | return (rc > 0 ? 0 : 1); |
|
|
case EVT_RESTART: | case EVT_RESTART: |
{ | { |
HANDLE rst; | HANDLE rst; |
printf("Stopping %s:", ident); fflush(stdout); | printf("Stopping %s:", ident); fflush(stdout); |
if (event_exists(EVT_DETACHED)) { | if (event_exists(EVT_DETACHED)) { |
puts(" not detached, cannot restart"); | puts(" not detached, cannot restart"); |
return 1; | return 1; |
} | } |
if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) { | if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) { |
puts(" error"); | puts(" error"); |
return 1; | return 1; |
} | } |
rc = sig_event(SIGTERM); | rc = sig_event(SIGTERM); |
if (rc <= 0) { | if (rc <= 0) { |
puts(rc < 0 ? " not running" : " error"); | puts(rc < 0 ? " not running" : " error"); |
CloseHandle(rst); | CloseHandle(rst); |
return 1; | return 1; |
} | } |
rc = wait_signaled(rst, 10); | rc = wait_signaled(rst, 10); |
CloseHandle(rst); | CloseHandle(rst); |
if (rc) { | if (rc) { |
puts(" timeout"); | puts(" timeout"); |
return 1; | return 1; |
} | } |
puts(" done"); | puts(" done"); |
Sleep(100); | Sleep(100); |
|
|
printf("Starting %s:", ident); fflush(stdout); | printf("Starting %s:", ident); fflush(stdout); |
rc = wait_evt_running(10, 1); | rc = wait_evt_running(10, 1); |
puts(!rc ? " done" : " error"); | puts(!rc ? " done" : " error"); |
return (!rc ? 0 : 1); | return (!rc ? 0 : 1); |
} | } |
} | } |
} |
} |
|
|
|
|
Line 841 static SERVICE_STATUS svc_status;
|
Line 726 static SERVICE_STATUS svc_status;
|
|
|
static void service_report_status(int state, int seconds) |
static void service_report_status(int state, int seconds) |
{ |
{ |
// TODO: Avoid race | // TODO: Avoid race |
static DWORD checkpoint = 1; | static DWORD checkpoint = 1; |
static DWORD accept_more = SERVICE_ACCEPT_PARAMCHANGE; // Win2000/XP | svc_status.dwCurrentState = state; |
svc_status.dwCurrentState = state; | svc_status.dwWaitHint = seconds*1000; |
svc_status.dwWaitHint = seconds*1000; | switch (state) { |
switch (state) { | default: |
default: | svc_status.dwCheckPoint = checkpoint++; |
svc_status.dwCheckPoint = checkpoint++; | break; |
break; | case SERVICE_RUNNING: |
case SERVICE_RUNNING: | case SERVICE_STOPPED: |
case SERVICE_STOPPED: | svc_status.dwCheckPoint = 0; |
svc_status.dwCheckPoint = 0; | } |
} | switch (state) { |
switch (state) { | case SERVICE_START_PENDING: |
case SERVICE_START_PENDING: | case SERVICE_STOP_PENDING: |
case SERVICE_STOP_PENDING: | svc_status.dwControlsAccepted = 0; |
svc_status.dwControlsAccepted = 0; | break; |
break; | default: |
default: | svc_status.dwControlsAccepted = |
svc_status.dwControlsAccepted = | SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN| |
SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN| | SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE; |
SERVICE_ACCEPT_PAUSE_CONTINUE|accept_more; | break; |
break; | } |
} | SetServiceStatus(svc_handle, &svc_status); |
if (!SetServiceStatus(svc_handle, &svc_status)) { | |
if (svc_status.dwControlsAccepted & accept_more) { | |
// Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4) | |
svc_status.dwControlsAccepted &= ~accept_more; | |
accept_more = 0; | |
SetServiceStatus(svc_handle, &svc_status); | |
} | |
} | |
} |
} |
|
|
|
|
Line 880 static void service_report_status(int state, int secon
|
Line 757 static void service_report_status(int state, int secon
|
|
|
static void WINAPI service_control(DWORD ctrlcode) |
static void WINAPI service_control(DWORD ctrlcode) |
{ |
{ |
switch (ctrlcode) { | switch (ctrlcode) { |
case SERVICE_CONTROL_STOP: | case SERVICE_CONTROL_STOP: |
case SERVICE_CONTROL_SHUTDOWN: | case SERVICE_CONTROL_SHUTDOWN: |
service_report_status(SERVICE_STOP_PENDING, 30); | service_report_status(SERVICE_STOP_PENDING, 30); |
svc_paused = 0; | svc_paused = 0; |
SetEvent(sigterm_handle); | SetEvent(sigterm_handle); |
break; | break; |
case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP | case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP |
service_report_status(svc_status.dwCurrentState, 0); | service_report_status(svc_status.dwCurrentState, 0); |
svc_paused = 0; | svc_paused = 0; |
SetEvent(sighup_handle); // reload | SetEvent(sighup_handle); // reload |
break; | break; |
case SERVICE_CONTROL_PAUSE: | case SERVICE_CONTROL_PAUSE: |
service_report_status(SERVICE_PAUSED, 0); | service_report_status(SERVICE_PAUSED, 0); |
svc_paused = 1; | svc_paused = 1; |
break; | break; |
case SERVICE_CONTROL_CONTINUE: | case SERVICE_CONTROL_CONTINUE: |
service_report_status(SERVICE_RUNNING, 0); | service_report_status(SERVICE_RUNNING, 0); |
{ | { |
int was_paused = svc_paused; | int was_paused = svc_paused; |
svc_paused = 0; | svc_paused = 0; |
SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck | SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck |
} | } |
break; | break; |
case SERVICE_CONTROL_INTERROGATE: | case SERVICE_CONTROL_INTERROGATE: |
default: // unknown | default: // unknown |
service_report_status(svc_status.dwCurrentState, 0); | service_report_status(svc_status.dwCurrentState, 0); |
break; | break; |
} | } |
} |
} |
|
|
|
|
Line 916 static void WINAPI service_control(DWORD ctrlcode)
|
Line 793 static void WINAPI service_control(DWORD ctrlcode)
|
|
|
static void service_exit(void) |
static void service_exit(void) |
{ |
{ |
// Close signal events | // Close signal events |
int i; | int i; |
for (i = 0; i < num_sig_handlers; i++) | for (i = 0; i < num_sig_handlers; i++) |
CloseHandle(sig_events[i]); | CloseHandle(sig_events[i]); |
num_sig_handlers = 0; | num_sig_handlers = 0; |
|
|
// Set exitcode | // Set exitcode |
if (daemon_winsvc_exitcode) { | if (daemon_winsvc_exitcode) { |
svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; | svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; |
svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode; | svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode; |
} | } |
// Report stopped | // Report stopped |
service_report_status(SERVICE_STOPPED, 0); | service_report_status(SERVICE_STOPPED, 0); |
} |
} |
|
|
|
|
Line 939 static char ** svc_main_argv;
|
Line 816 static char ** svc_main_argv;
|
|
|
// Main function for service, called by service dispatcher |
// Main function for service, called by service dispatcher |
|
|
static void WINAPI service_main(DWORD argc, LPSTR * argv) | static void WINAPI service_main(DWORD /*argc*/, LPSTR * argv) |
{ |
{ |
char path[MAX_PATH], *p; | char path[MAX_PATH], *p; |
ARGUSED(argc); | |
|
|
// Register control handler | // Register control handler |
svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); | svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); |
|
|
// Init service status | // Init service status |
svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS; | svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
service_report_status(SERVICE_START_PENDING, 10); | service_report_status(SERVICE_START_PENDING, 10); |
|
|
// Service started in \windows\system32, change to .exe directory | // Service started in \windows\system32, change to .exe directory |
if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) { | if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) { |
*p = 0; SetCurrentDirectoryA(path); | *p = 0; SetCurrentDirectoryA(path); |
} | } |
| |
// Install exit handler | |
atexit(service_exit); | |
|
|
// Do the real work, service status later updated by daemon_detach() | // Install exit handler |
daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv); | atexit(service_exit); |
|
|
exit(daemon_winsvc_exitcode); | // Do the real work, service status later updated by daemon_detach() |
// ... continued in service_exit() | daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv); |
| |
| exit(daemon_winsvc_exitcode); |
| // ... continued in service_exit() |
} |
} |
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////////////////////////// |
// Windows Service Admin Functions |
// Windows Service Admin Functions |
|
|
// Set Service description (Win2000/XP) |
|
|
|
static int svcadm_setdesc(SC_HANDLE hs, const char * desc) | // Make registry key name for event message file |
| static bool make_evtkey(char * buf, unsigned size, const char * ident) |
{ |
{ |
HINSTANCE hdll; | static const char prefix[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\"; |
BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID); | const unsigned pfxlen = sizeof(prefix)-1; |
BOOL ret; | unsigned idlen = strlen(ident); |
if (!(hdll = LoadLibraryA("ADVAPI32.DLL"))) | if (pfxlen + idlen >= size) { |
return FALSE; | printf(" Buffer overflow\n"); |
if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A")))) | return false; |
ret = FALSE; | } |
else { | memcpy(buf, prefix, pfxlen); |
SERVICE_DESCRIPTIONA sd = { (char *)desc }; | memcpy(buf+pfxlen, ident, idlen+1); |
ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd); | return true; |
} | |
FreeLibrary(hdll); | |
return ret; | |
} |
} |
|
|
|
// Install this exe as event message file |
|
static void inst_evtmsg(const char * ident) |
|
{ |
|
printf("Installing event message file for %s:", ident); fflush(stdout); |
|
|
|
char mypath[MAX_PATH]; |
|
if (!GetModuleFileNameA((HMODULE)0, mypath, sizeof(mypath))) { |
|
printf(" unknown program path, Error=%ld\n", GetLastError()); |
|
return; |
|
} |
|
|
|
char subkey[MAX_PATH]; |
|
if (!make_evtkey(subkey, sizeof(subkey), ident)) |
|
return; |
|
|
|
HKEY hk; |
|
LONG err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, (char *)0, 0, KEY_ALL_ACCESS, |
|
(SECURITY_ATTRIBUTES *)0, &hk, (DWORD *)0); |
|
if (err != ERROR_SUCCESS) { |
|
printf(" RegCreateKeyEx failed, error=%ld\n", err); |
|
return; |
|
} |
|
|
|
err = RegSetValueExA(hk, "EventMessageFile", 0, REG_SZ, |
|
(const BYTE *)mypath, strlen(mypath)+1); |
|
if (err == ERROR_SUCCESS) { |
|
DWORD val = EVENTLOG_INFORMATION_TYPE |
|
|EVENTLOG_WARNING_TYPE |
|
|EVENTLOG_ERROR_TYPE; |
|
err = RegSetValueExA(hk, "TypesSupported", 0, REG_DWORD, |
|
(const BYTE *)&val, sizeof(val)); |
|
} |
|
if (err != ERROR_SUCCESS) |
|
printf(" RegSetValueEx failed, error=%ld\n", err); |
|
|
|
RegCloseKey(hk); |
|
puts(" done"); |
|
} |
|
|
|
// Uninstall event message file |
|
static void uninst_evtmsg(const char * ident) |
|
{ |
|
printf("Removing event message file for %s:", ident); fflush(stdout); |
|
|
|
char subkey[MAX_PATH]; |
|
if (!make_evtkey(subkey, sizeof(subkey), ident)) |
|
return; |
|
|
|
LONG err = RegDeleteKeyA(HKEY_LOCAL_MACHINE, subkey); |
|
if (err != ERROR_SUCCESS && err != ERROR_FILE_NOT_FOUND) { |
|
printf(" RegDeleteKey failed, error=%ld\n", err); |
|
return; |
|
} |
|
puts(" done"); |
|
} |
|
|
|
|
// Service install/remove commands |
// Service install/remove commands |
|
|
static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts, |
static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts, |
int argc, char **argv ) |
int argc, char **argv ) |
{ |
{ |
int remove; long err; | int remove; long err; |
SC_HANDLE hm, hs; | SC_HANDLE hm, hs; |
|
|
if (argc < 2) | if (argc < 2) |
return -1; | return -1; |
if (!strcmp(argv[1], "install")) | if (!strcmp(argv[1], "install")) |
remove = 0; | remove = 0; |
else if (!strcmp(argv[1], "remove")) { | else if (!strcmp(argv[1], "remove")) { |
if (argc != 2) { | if (argc != 2) { |
printf("%s: no arguments allowed for command remove\n", ident); | printf("%s: no arguments allowed for command remove\n", ident); |
return 1; | return 1; |
} | } |
remove = 1; | remove = 1; |
} | } |
else | else |
return -1; | return -1; |
|
|
printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout); | printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout); |
|
|
// Open SCM | // Open SCM |
if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) { | if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) { |
if ((err = GetLastError()) == ERROR_ACCESS_DENIED) | if ((err = GetLastError()) == ERROR_ACCESS_DENIED) |
puts(" access to SCManager denied"); | puts(" access to SCManager denied"); |
else if (err == ERROR_CALL_NOT_IMPLEMENTED) | else |
puts(" services not implemented on this version of Windows"); | printf(" cannot open SCManager, Error=%ld\n", err); |
else | return 1; |
printf(" cannot open SCManager, Error=%ld\n", err); | } |
return 1; | |
} | |
|
|
if (!remove) { | if (!remove) { |
char path[MAX_PATH+100]; | char path[MAX_PATH+100]; |
int i; | int i; |
// Get program path | // Get program path |
if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { | if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { |
printf(" unknown program path, Error=%ld\n", GetLastError()); | printf(" unknown program path, Error=%ld\n", GetLastError()); |
CloseServiceHandle(hm); | CloseServiceHandle(hm); |
return 1; | return 1; |
} | } |
// Add quotes if necessary | // Add quotes if necessary |
if (strchr(path, ' ')) { | if (strchr(path, ' ')) { |
i = strlen(path); | i = strlen(path); |
path[i+1] = '"'; path[i+2] = 0; | path[i+1] = '"'; path[i+2] = 0; |
while (--i >= 0) | while (--i >= 0) |
path[i+1] = path[i]; | path[i+1] = path[i]; |
path[0] = '"'; | path[0] = '"'; |
} | } |
// Append options | // Append options |
strcat(path, " "); strcat(path, svc_opts->cmd_opt); | strcat(path, " "); strcat(path, svc_opts->cmd_opt); |
for (i = 2; i < argc; i++) { | for (i = 2; i < argc; i++) { |
const char * s = argv[i]; | const char * s = argv[i]; |
if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path)) | if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path)) |
break; | break; |
// Add quotes if necessary | // Add quotes if necessary |
if (strchr(s, ' ') && !strchr(s, '"')) { | if (strchr(s, ' ') && !strchr(s, '"')) { |
strcat(path, " \""); strcat(path, s); strcat(path, "\""); | strcat(path, " \""); strcat(path, s); strcat(path, "\""); |
} | } |
else { | else { |
strcat(path, " "); strcat(path, s); | strcat(path, " "); strcat(path, s); |
} | } |
} | } |
// Create | // Create |
if (!(hs = CreateService(hm, | if (!(hs = CreateService(hm, |
svc_opts->svcname, svc_opts->dispname, | svc_opts->svcname, svc_opts->dispname, |
SERVICE_ALL_ACCESS, | SERVICE_ALL_ACCESS, |
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, | SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, |
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, | SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, |
NULL/*no load ordering*/, NULL/*no tag id*/, | NULL/*no load ordering*/, NULL/*no tag id*/, |
""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) { | ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) { |
if ((err = GetLastError()) == ERROR_SERVICE_EXISTS) | if ((err = GetLastError()) == ERROR_SERVICE_EXISTS) |
puts(" the service is already installed"); | puts(" the service is already installed"); |
else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) | else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) |
puts(" service is still running and marked for deletion\n" | puts(" service is still running and marked for deletion\n" |
"Stop the service and retry install"); | "Stop the service and retry install"); |
else | else |
printf(" failed, Error=%ld\n", err); | printf(" failed, Error=%ld\n", err); |
CloseServiceHandle(hm); | CloseServiceHandle(hm); |
return 1; | return 1; |
} | } |
// Set optional description | // Set optional description |
if (svc_opts->descript) | if (svc_opts->descript) { |
svcadm_setdesc(hs, svc_opts->descript); | SERVICE_DESCRIPTIONA sd = { const_cast<char *>(svc_opts->descript) }; |
} | ChangeServiceConfig2A(hs, SERVICE_CONFIG_DESCRIPTION, &sd); |
else { | } |
// Open | // Enable delayed auto start if supported |
if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) { | OSVERSIONINFOA ver; ver.dwOSVersionInfoSize = sizeof(ver); |
puts(" not found"); | if ( GetVersionExA(&ver) |
CloseServiceHandle(hm); | && ver.dwPlatformId == VER_PLATFORM_WIN32_NT |
return 1; | && ver.dwMajorVersion >= 6 /* Vista */ ) { |
} | SERVICE_DELAYED_AUTO_START_INFO sdasi = { TRUE }; |
// TODO: Stop service if running | ChangeServiceConfig2A(hs, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &sdasi); |
// Remove | } |
if (!DeleteService(hs)) { | } |
if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE) | else { |
puts(" service is still running and marked for deletion\n" | // Open |
"Stop the service to remove it"); | if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) { |
else | puts(" not found"); |
printf(" failed, Error=%ld\n", err); | CloseServiceHandle(hm); |
CloseServiceHandle(hs); CloseServiceHandle(hm); | return 1; |
return 1; | } |
} | // TODO: Stop service if running |
} | // Remove |
puts(" done"); | if (!DeleteService(hs)) { |
CloseServiceHandle(hs); CloseServiceHandle(hm); | if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE) |
return 0; | puts(" service is still running and marked for deletion\n" |
| "Stop the service to remove it"); |
| else |
| printf(" failed, Error=%ld\n", err); |
| CloseServiceHandle(hs); CloseServiceHandle(hm); |
| return 1; |
| } |
| } |
| puts(" done"); |
| CloseServiceHandle(hs); CloseServiceHandle(hm); |
| |
| // Install/Remove event message file registry entry |
| if (!remove) { |
| inst_evtmsg(ident); |
| } |
| else { |
| uninst_evtmsg(ident); |
| } |
| |
| return 0; |
} |
} |
|
|
|
|
Line 1112 static int svcadm_main(const char * ident, const daemo
|
Line 1058 static int svcadm_main(const char * ident, const daemo
|
int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, |
int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, |
int (*main_func)(int, char **), int argc, char **argv ) |
int (*main_func)(int, char **), int argc, char **argv ) |
{ |
{ |
int rc; | int rc; |
#ifdef _DEBUG |
#ifdef _DEBUG |
// Enable Debug heap checks | // Enable Debug heap checks |
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) |
|_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); | |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); |
#endif |
#endif |
|
|
// Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters | // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters |
if ((rc = initd_main(ident, argc, argv)) >= 0) | if ((rc = initd_main(ident, argc, argv)) >= 0) |
return rc; | return rc; |
// Check for [install|remove] parameters | // Check for [install|remove] parameters |
if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0) | if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0) |
return rc; | return rc; |
|
|
// Run as service if svc_opts.cmd_opt is given as first(!) argument | // Run as service if svc_opts.cmd_opt is given as first(!) argument |
svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt)); | svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt)); |
|
|
if (!svc_mode) { | if (!svc_mode) { |
// Daemon: Try to simulate a Unix-like daemon | // Daemon: Try to simulate a Unix-like daemon |
HANDLE rev; | HANDLE rev; |
BOOL exists; | BOOL exists; |
|
|
// Create main event to detect process type: | // Create main event to detect process type: |
// 1. new: parent process => start child and wait for detach() or exit() of child. | // 1. new: parent process => start child and wait for detach() or exit() of child. |
// 2. exists && signaled: child process => do the real work, signal detach() to parent | // 2. exists && signaled: child process => do the real work, signal detach() to parent |
// 3. exists && !signaled: already running => exit() | // 3. exists && !signaled: already running => exit() |
if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists))) | if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists))) |
return 100; | return 100; |
|
|
if (!exists && !debugging()) { | if (!exists && !debugging()) { |
// Event new => parent process | // Event new => parent process |
return parent_main(rev); | return parent_main(rev); |
} | } |
|
|
if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) { | if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) { |
// Event was signaled => In child process | // Event was signaled => In child process |
return child_main(rev, main_func, argc, argv); | return child_main(rev, main_func, argc, argv); |
} | } |
|
|
// Event no longer signaled => Already running! | // Event no longer signaled => Already running! |
daemon_help(stdout, ident, "already running"); | daemon_help(stdout, ident, "already running"); |
CloseHandle(rev); | CloseHandle(rev); |
return 1; | return 1; |
} | } |
else { | else { |
// Service: Start service_main() via SCM | // Service: Start service_main() via SCM |
SERVICE_TABLE_ENTRY service_table[] = { | SERVICE_TABLE_ENTRY service_table[] = { |
{ (char*)svc_opts->svcname, service_main }, { NULL, NULL } | { (char*)svc_opts->svcname, service_main }, { NULL, NULL } |
}; | }; |
|
|
svc_main_func = main_func; | svc_main_func = main_func; |
svc_main_argc = argc; | svc_main_argc = argc; |
svc_main_argv = argv; | svc_main_argv = argv; |
if (!StartServiceCtrlDispatcher(service_table)) { | if (!StartServiceCtrlDispatcher(service_table)) { |
printf("%s: cannot dispatch service, Error=%ld\n" | printf("%s: cannot dispatch service, Error=%ld\n" |
"Option \"%s\" cannot be used to start %s as a service from console.\n" | "Option \"%s\" cannot be used to start %s as a service from console.\n" |
"Use \"%s install ...\" to install the service\n" | "Use \"%s install ...\" to install the service\n" |
"and \"net start %s\" to start it.\n", | "and \"net start %s\" to start it.\n", |
ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident); | ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident); |
|
|
#ifdef _DEBUG |
#ifdef _DEBUG |
if (debugging()) | if (debugging()) |
service_main(argc, argv); | service_main(argc, argv); |
#endif |
#endif |
return 100; | return 100; |
} | } |
Sleep(1000); | Sleep(1000); |
ExitThread(0); // Do not redo exit() processing | ExitThread(0); // Do not redo exit() processing |
/*NOTREACHED*/ | /*NOTREACHED*/ |
return 0; | return 0; |
} | } |
} |
} |
|
|
|
|
Line 1195 static volatile sig_atomic_t caughtsig = 0;
|
Line 1141 static volatile sig_atomic_t caughtsig = 0;
|
|
|
static void sig_handler(int sig) |
static void sig_handler(int sig) |
{ |
{ |
caughtsig = sig; | caughtsig = sig; |
} |
} |
|
|
static void test_exit(void) |
static void test_exit(void) |
{ |
{ |
printf("Main exit\n"); | printf("Main exit\n"); |
} |
} |
|
|
int test_main(int argc, char **argv) |
int test_main(int argc, char **argv) |
{ |
{ |
int i; | int i; |
int debug = 0; | int debug = 0; |
char * cmd = 0; | char * cmd = 0; |
|
|
printf("PID=%ld\n", GetCurrentProcessId()); | printf("PID=%ld\n", GetCurrentProcessId()); |
for (i = 0; i < argc; i++) { | for (i = 0; i < argc; i++) { |
printf("%d: \"%s\"\n", i, argv[i]); | printf("%d: \"%s\"\n", i, argv[i]); |
if (!strcmp(argv[i],"-d")) | if (!strcmp(argv[i],"-d")) |
debug = 1; | debug = 1; |
} | } |
if (argc > 1 && argv[argc-1][0] != '-') | if (argc > 1 && argv[argc-1][0] != '-') |
cmd = argv[argc-1]; | cmd = argv[argc-1]; |
|
|
daemon_signal(SIGINT, sig_handler); | daemon_signal(SIGINT, sig_handler); |
daemon_signal(SIGBREAK, sig_handler); | daemon_signal(SIGBREAK, sig_handler); |
daemon_signal(SIGTERM, sig_handler); | daemon_signal(SIGTERM, sig_handler); |
daemon_signal(SIGHUP, sig_handler); | daemon_signal(SIGHUP, sig_handler); |
daemon_signal(SIGUSR1, sig_handler); | daemon_signal(SIGUSR1, sig_handler); |
daemon_signal(SIGUSR2, sig_handler); | daemon_signal(SIGUSR2, sig_handler); |
|
|
atexit(test_exit); | atexit(test_exit); |
|
|
if (!debug) { | if (!debug) { |
printf("Preparing to detach...\n"); | printf("Preparing to detach...\n"); |
Sleep(2000); | Sleep(2000); |
daemon_detach("test"); | daemon_detach("test"); |
printf("Detached!\n"); | printf("Detached!\n"); |
} | } |
|
|
for (;;) { | for (;;) { |
daemon_sleep(1); | daemon_sleep(1); |
printf("."); fflush(stdout); | printf("."); fflush(stdout); |
if (caughtsig) { | if (caughtsig) { |
if (caughtsig == SIGUSR2) { | if (caughtsig == SIGUSR2) { |
debug ^= 1; | debug ^= 1; |
if (debug) | if (debug) |
daemon_enable_console("Daemon[Debug]"); | daemon_enable_console("Daemon[Debug]"); |
else | else |
daemon_disable_console(); | daemon_disable_console(); |
} | } |
else if (caughtsig == SIGUSR1 && cmd) { | else if (caughtsig == SIGUSR1 && cmd) { |
char inpbuf[200], outbuf[1000]; int rc; | char inpbuf[200], outbuf[1000]; int rc; |
strcpy(inpbuf, "Hello\nWorld!\n"); | strcpy(inpbuf, "Hello\nWorld!\n"); |
rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf)); | rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf)); |
if (!debug) | if (!debug) |
daemon_enable_console("Command output"); | daemon_enable_console("Command output"); |
printf("\"%s\" returns %d\n", cmd, rc); | printf("\"%s\" returns %d\n", cmd, rc); |
if (rc >= 0) | if (rc >= 0) |
printf("output:\n%s.\n", outbuf); | printf("output:\n%s.\n", outbuf); |
fflush(stdout); | fflush(stdout); |
if (!debug) { | if (!debug) { |
Sleep(10000); daemon_disable_console(); | Sleep(10000); daemon_disable_console(); |
} | } |
} | } |
printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout); | printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout); |
if (caughtsig == SIGTERM || caughtsig == SIGBREAK) | if (caughtsig == SIGTERM || caughtsig == SIGBREAK) |
break; | break; |
caughtsig = 0; | caughtsig = 0; |
} | } |
} | } |
printf("\nExiting on signal %d\n", caughtsig); | printf("\nExiting on signal %d\n", caughtsig); |
return 0; | return 0; |
} |
} |
|
|
|
|
int main(int argc, char **argv) |
int main(int argc, char **argv) |
{ |
{ |
static const daemon_winsvc_options svc_opts = { | static const daemon_winsvc_options svc_opts = { |
"-s", "test", "Test Service", "Service to test daemon_win32.c Module" | "-s", "test", "Test Service", "Service to test daemon_win32.c Module" |
}; | }; |
|
|
return daemon_main("testd", &svc_opts, test_main, argc, argv); | return daemon_main("testd", &svc_opts, test_main, argc, argv); |
} |
} |
|
|
#endif |
#endif |