📄 ntmain.c
字号:
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
#define MAIN_PRIVATE
#include "or.h"
const char ntmain_c_id[] =
"$Id$";
#include <tchar.h>
#define GENSRV_SERVICENAME TEXT("tor")
#define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service")
#define GENSRV_DESCRIPTION \
TEXT("Provides an anonymous Internet communication system")
#define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService")
// Cheating: using the pre-defined error codes, tricks Windows into displaying
// a semi-related human-readable error message if startup fails as
// opposed to simply scaring people with Error: 0xffffffff
#define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
static SERVICE_STATUS service_status;
static SERVICE_STATUS_HANDLE hStatus;
/* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
* is a job for arguments, not globals. Alas, some of the functions that
* use them use them need to have fixed signatures, so they can be passed
* to the NT service functions. */
static char **backup_argv;
static int backup_argc;
static char* nt_strerror(uint32_t errnum);
static void nt_service_control(DWORD request);
static void nt_service_body(int argc, char **argv);
static void nt_service_main(void);
static SC_HANDLE nt_service_open_scm(void);
static SC_HANDLE nt_service_open(SC_HANDLE hSCManager);
static int nt_service_start(SC_HANDLE hService);
static int nt_service_stop(SC_HANDLE hService);
static int nt_service_install(int argc, char **argv);
static int nt_service_remove(void);
static int nt_service_cmd_start(void);
static int nt_service_cmd_stop(void);
/** Struct to hold dynamically loaded NT-service related function pointers.
*/
struct service_fns {
int loaded;
BOOL (WINAPI *ChangeServiceConfig2A_fn)(
SC_HANDLE hService,
DWORD dwInfoLevel,
LPVOID lpInfo);
BOOL (WINAPI *CloseServiceHandle_fn)(
SC_HANDLE hSCObject);
BOOL (WINAPI *ControlService_fn)(
SC_HANDLE hService,
DWORD dwControl,
LPSERVICE_STATUS lpServiceStatus);
SC_HANDLE (WINAPI *CreateServiceA_fn)(
SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
LPCTSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCTSTR lpBinaryPathName,
LPCTSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCTSTR lpDependencies,
LPCTSTR lpServiceStartName,
LPCTSTR lpPassword);
BOOL (WINAPI *DeleteService_fn)(
SC_HANDLE hService);
SC_HANDLE (WINAPI *OpenSCManagerA_fn)(
LPCTSTR lpMachineName,
LPCTSTR lpDatabaseName,
DWORD dwDesiredAccess);
SC_HANDLE (WINAPI *OpenServiceA_fn)(
SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
DWORD dwDesiredAccess);
BOOL (WINAPI *QueryServiceStatus_fn)(
SC_HANDLE hService,
LPSERVICE_STATUS lpServiceStatus);
SERVICE_STATUS_HANDLE (WINAPI *RegisterServiceCtrlHandlerA_fn)(
LPCTSTR lpServiceName,
LPHANDLER_FUNCTION lpHandlerProc);
BOOL (WINAPI *SetServiceStatus_fn)(SERVICE_STATUS_HANDLE,
LPSERVICE_STATUS);
BOOL (WINAPI *StartServiceCtrlDispatcherA_fn)(
const SERVICE_TABLE_ENTRY* lpServiceTable);
BOOL (WINAPI *StartServiceA_fn)(
SC_HANDLE hService,
DWORD dwNumServiceArgs,
LPCTSTR* lpServiceArgVectors);
BOOL (WINAPI *LookupAccountNameA_fn)(
LPCTSTR lpSystemName,
LPCTSTR lpAccountName,
PSID Sid,
LPDWORD cbSid,
LPTSTR ReferencedDomainName,
LPDWORD cchReferencedDomainName,
PSID_NAME_USE peUse);
} service_fns = { 0,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL};
/** Loads functions used by NT services. Returns on success, or prints a
* complaint to stdout and exits on error. */
static void
nt_service_loadlibrary(void)
{
HMODULE library = 0;
void *fn;
if (service_fns.loaded)
return;
/* XXXX Possibly, we should hardcode the location of this DLL. */
if (!(library = LoadLibrary("advapi32.dll"))) {
log_err(LD_GENERAL, "Couldn't open advapi32.dll. Are you trying to use "
"NT services on Windows 98? That doesn't work.");
goto err;
}
#define LOAD(f) STMT_BEGIN \
if (!(fn = GetProcAddress(library, #f))) { \
log_err(LD_BUG, \
"Couldn't find %s in advapi32.dll! We probably got the " \
"name wrong.", #f); \
goto err; \
} else { \
service_fns.f ## _fn = fn; \
} \
STMT_END
LOAD(ChangeServiceConfig2A);
LOAD(CloseServiceHandle);
LOAD(ControlService);
LOAD(CreateServiceA);
LOAD(DeleteService);
LOAD(OpenSCManagerA);
LOAD(OpenServiceA);
LOAD(QueryServiceStatus);
LOAD(RegisterServiceCtrlHandlerA);
LOAD(SetServiceStatus);
LOAD(StartServiceCtrlDispatcherA);
LOAD(StartServiceA);
LOAD(LookupAccountNameA);
service_fns.loaded = 1;
return;
err:
printf("Unable to load library support for NT services: exiting.\n");
exit(1);
}
/** If we're compiled to run as an NT service, and the service wants to
* shut down, then change our current status and return 1. Else
* return 0.
*/
int
nt_service_is_stopping(void)
/* XXXX this function would probably _love_ to be inline, in 0.2.0. */
{
/* If we haven't loaded the function pointers, we can't possibly be an NT
* service trying to shut down. */
if (!service_fns.loaded)
return 0;
if (service_status.dwCurrentState == SERVICE_STOP_PENDING) {
service_status.dwWin32ExitCode = 0;
service_status.dwCurrentState = SERVICE_STOPPED;
service_fns.SetServiceStatus_fn(hStatus, &service_status);
return 1;
} else if (service_status.dwCurrentState == SERVICE_STOPPED) {
return 1;
}
return 0;
}
/** Set the dwCurrentState field for our service to <b>state</b>. */
void
nt_service_set_state(DWORD state)
{
service_status.dwCurrentState = state;
}
/** Handles service control requests, such as stopping or starting the
* Tor service. */
static void
nt_service_control(DWORD request)
{
static struct timeval exit_now;
exit_now.tv_sec = 0;
exit_now.tv_usec = 0;
nt_service_loadlibrary();
switch (request) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
log_notice(LD_GENERAL,
"Got stop/shutdown request; shutting down cleanly.");
service_status.dwCurrentState = SERVICE_STOP_PENDING;
event_loopexit(&exit_now);
return;
}
service_fns.SetServiceStatus_fn(hStatus, &service_status);
}
/** Called when the service is started via the system's service control
* manager. This calls tor_init() and starts the main event loop. If
* tor_init() fails, the service will be stopped and exit code set to
* NT_SERVICE_ERROR_TORINIT_FAILED. */
static void
nt_service_body(int argc, char **argv)
{
int r;
(void) argc; /* unused */
(void) argv; /* unused */
nt_service_loadlibrary();
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
service_status.dwCurrentState = SERVICE_START_PENDING;
service_status.dwControlsAccepted =
SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
service_status.dwWin32ExitCode = 0;
service_status.dwServiceSpecificExitCode = 0;
service_status.dwCheckPoint = 0;
service_status.dwWaitHint = 1000;
hStatus = service_fns.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME,
(LPHANDLER_FUNCTION) nt_service_control);
if (hStatus == 0) {
/* Failed to register the service control handler function */
return;
}
r = tor_init(backup_argc, backup_argv);
if (r) {
/* Failed to start the Tor service */
r = NT_SERVICE_ERROR_TORINIT_FAILED;
service_status.dwCurrentState = SERVICE_STOPPED;
service_status.dwWin32ExitCode = r;
service_status.dwServiceSpecificExitCode = r;
service_fns.SetServiceStatus_fn(hStatus, &service_status);
return;
}
/* Set the service's status to SERVICE_RUNNING and start the main
* event loop */
service_status.dwCurrentState = SERVICE_RUNNING;
service_fns.SetServiceStatus_fn(hStatus, &service_status);
do_main_loop();
tor_cleanup();
}
/** Main service entry point. Starts the service control dispatcher and waits
* until the service status is set to SERVICE_STOPPED. */
static void
nt_service_main(void)
{
SERVICE_TABLE_ENTRY table[2];
DWORD result = 0;
char *errmsg;
nt_service_loadlibrary();
table[0].lpServiceName = (char*)GENSRV_SERVICENAME;
table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)nt_service_body;
table[1].lpServiceName = NULL;
table[1].lpServiceProc = NULL;
if (!service_fns.StartServiceCtrlDispatcherA_fn(table)) {
result = GetLastError();
errmsg = nt_strerror(result);
printf("Service error %d : %s\n", (int) result, errmsg);
LocalFree(errmsg);
if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
if (tor_init(backup_argc, backup_argv) < 0)
return;
switch (get_options()->command) {
case CMD_RUN_TOR:
do_main_loop();
break;
case CMD_LIST_FINGERPRINT:
case CMD_HASH_PASSWORD:
case CMD_VERIFY_CONFIG:
log_err(LD_CONFIG, "Unsupported command (--list-fingerint, "
"--hash-password, or --verify-config) in NT service.");
break;
case CMD_RUN_UNITTESTS:
default:
log_err(LD_CONFIG, "Illegal command number %d: internal error.",
get_options()->command);
}
tor_cleanup();
}
}
}
/** Return a handle to the service control manager on success, or NULL on
* failure. */
static SC_HANDLE
nt_service_open_scm(void)
{
SC_HANDLE hSCManager;
char *errmsg = NULL;
nt_service_loadlibrary();
if ((hSCManager = service_fns.OpenSCManagerA_fn(
NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) {
errmsg = nt_strerror(GetLastError());
printf("OpenSCManager() failed : %s\n", errmsg);
LocalFree(errmsg);
}
return hSCManager;
}
/** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
* on failure. */
static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager)
{
SC_HANDLE hService;
char *errmsg = NULL;
nt_service_loadlibrary();
if ((hService = service_fns.OpenServiceA_fn(hSCManager, GENSRV_SERVICENAME,
SERVICE_ALL_ACCESS)) == NULL) {
errmsg = nt_strerror(GetLastError());
printf("OpenService() failed : %s\n", errmsg);
LocalFree(errmsg);
}
return hService;
}
/** Start the Tor service. Return 0 if the service is started or was
* previously running. Return -1 on error. */
static int
nt_service_start(SC_HANDLE hService)
{
char *errmsg = NULL;
nt_service_loadlibrary();
service_fns.QueryServiceStatus_fn(hService, &service_status);
if (service_status.dwCurrentState == SERVICE_RUNNING) {
printf("Service is already running\n");
return 0;
}
if (service_fns.StartServiceA_fn(hService, 0, NULL)) {
/* Loop until the service has finished attempting to start */
while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
(service_status.dwCurrentState == SERVICE_START_PENDING)) {
Sleep(500);
}
/* Check if it started successfully or not */
if (service_status.dwCurrentState == SERVICE_RUNNING) {
printf("Service started successfully\n");
return 0;
} else {
errmsg = nt_strerror(service_status.dwWin32ExitCode);
printf("Service failed to start : %s\n", errmsg);
LocalFree(errmsg);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -