[or-cvs] r11388: Move NT services code into its own C file. Probably will not (in tor/trunk: . src/or)

nickm at seul.org nickm at seul.org
Thu Sep 6 17:42:05 UTC 2007


Author: nickm
Date: 2007-09-06 13:42:05 -0400 (Thu, 06 Sep 2007)
New Revision: 11388

Added:
   tor/trunk/src/or/ntmain.c
Modified:
   tor/trunk/
   tor/trunk/ChangeLog
   tor/trunk/configure.in
   tor/trunk/src/or/Makefile.am
   tor/trunk/src/or/config.c
   tor/trunk/src/or/main.c
   tor/trunk/src/or/or.h
Log:
 r14940 at catbus:  nickm | 2007-09-06 13:22:26 -0400
 Move NT services code into its own C file.  Probably will not build happily on win32 yet; more hacking needed.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r14940] on 8246c3cf-6607-4228-993b-4d95d33730f1

Modified: tor/trunk/ChangeLog
===================================================================
--- tor/trunk/ChangeLog	2007-09-06 16:27:49 UTC (rev 11387)
+++ tor/trunk/ChangeLog	2007-09-06 17:42:05 UTC (rev 11388)
@@ -32,6 +32,7 @@
     - Revamp file-writing logic so we don't need to have the entire contents
       of a file in memory at once before we write to disk.  Tor, meet stdio.
     - Turn "descriptor store" into a full-fledged type.
+    - Move all NT services code into a separate source file.
 
 
 Changes in version 0.1.2.17 - 2007-08-30

Modified: tor/trunk/configure.in
===================================================================
--- tor/trunk/configure.in	2007-09-06 16:27:49 UTC (rev 11387)
+++ tor/trunk/configure.in	2007-09-06 17:42:05 UTC (rev 11388)
@@ -126,6 +126,7 @@
 if test $bwin32 = true; then
 AC_DEFINE(MS_WINDOWS, 1, [Define to 1 if we are building for Windows.])
 fi
+AM_CONDITIONAL(BUILD_NT_SERVICES, test x$bwin32 = xtrue)
 
 dnl Enable C99 when compiling with MIPSpro
 AC_MSG_CHECKING([for MIPSpro compiler])

Modified: tor/trunk/src/or/Makefile.am
===================================================================
--- tor/trunk/src/or/Makefile.am	2007-09-06 16:27:49 UTC (rev 11387)
+++ tor/trunk/src/or/Makefile.am	2007-09-06 17:42:05 UTC (rev 11388)
@@ -4,11 +4,19 @@
 
 bin_PROGRAMS = tor
 
+if BUILD_NT_SERVICES
+PLATFORM_SOURCES=ntmain.c
+else
+PLATFORM_SOURCES=
+endif
+
+EXTRA_DIST=ntmain.c
+
 tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	circuituse.c command.c config.c \
 	connection.c connection_edge.c connection_or.c control.c \
 	cpuworker.c directory.c dirserv.c dirvote.c \
-	dns.c dnsserv.c hibernate.c main.c \
+	dns.c dnsserv.c hibernate.c main.c $(PLATFORM_SOURCES) \
 	onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
 	eventdns.c \
@@ -25,7 +33,7 @@
 	circuituse.c command.c config.c \
 	connection.c connection_edge.c connection_or.c control.c \
 	cpuworker.c directory.c dirserv.c dirvote.c \
-	dns.c dnsserv.c hibernate.c main.c \
+	dns.c dnsserv.c hibernate.c main.c $(PLATFORM_SOURCES) \
 	onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
 	eventdns.c \

Modified: tor/trunk/src/or/config.c
===================================================================
--- tor/trunk/src/or/config.c	2007-09-06 16:27:49 UTC (rev 11387)
+++ tor/trunk/src/or/config.c	2007-09-06 17:42:05 UTC (rev 11388)
@@ -4610,6 +4610,9 @@
 extern const char dns_c_id[];
 extern const char hibernate_c_id[];
 extern const char main_c_id[];
+#ifdef NT_SERVICE
+extern const char ntmain_c_id[];
+#endif
 extern const char onion_c_id[];
 extern const char policies_c_id[];
 extern const char relay_c_id[];
@@ -4663,6 +4666,9 @@
   puts(dns_c_id);
   puts(hibernate_c_id);
   puts(main_c_id);
+#ifdef NT_SERVICE
+  puts(ntmain_c_id);
+#endif
   puts(onion_c_id);
   puts(policies_c_id);
   puts(relay_c_id);

Modified: tor/trunk/src/or/main.c
===================================================================
--- tor/trunk/src/or/main.c	2007-09-06 16:27:49 UTC (rev 11387)
+++ tor/trunk/src/or/main.c	2007-09-06 17:42:05 UTC (rev 11388)
@@ -86,35 +86,6 @@
  * entry to inform the user that Tor is working. */
 int has_completed_circuit=0;
 
-#ifdef MS_WINDOWS
-#define MS_WINDOWS_SERVICE
-#endif
-
-#ifdef MS_WINDOWS_SERVICE
-#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
-
-SERVICE_STATUS service_status;
-SERVICE_STATUS_HANDLE hStatus;
-/* XXXX020 This 'backup argv' and 'backup argc' business is an ugly hack. This
- * is a job for arguments, not globals. */
-static char **backup_argv;
-static int backup_argc;
-static int nt_service_is_stopping(void);
-static char* nt_strerror(uint32_t errnum);
-#else
-#define nt_service_is_stopping() (0)
-#endif
-
 /** If our router descriptor ever goes this long without being regenerated
  * because something changed, we force an immediate regenerate-and-upload. */
 #define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60)
@@ -1909,685 +1880,6 @@
   printf("16:%s\n",output);
 }
 
-#ifdef MS_WINDOWS_SERVICE
-
-/* XXXX can some/all these functions become static? without breaking NT
- * services? -NM */
-/* XXXX I'd also like to move much of the NT service stuff into its own
- * file. -RD */
-void nt_service_control(DWORD request);
-void nt_service_body(int argc, char **argv);
-void nt_service_main(void);
-SC_HANDLE nt_service_open_scm(void);
-SC_HANDLE nt_service_open(SC_HANDLE hSCManager);
-int nt_service_start(SC_HANDLE hService);
-int nt_service_stop(SC_HANDLE hService);
-int nt_service_install(int argc, char **argv);
-int nt_service_remove(void);
-int nt_service_cmd_start(void);
-int nt_service_cmd_stop(void);
-
-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.
- */
-static 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;
-}
-
-/** Handles service control requests, such as stopping or starting the
- * Tor service. */
-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. */
-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. */
-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:
-        do_list_fingerprint();
-        break;
-      case CMD_HASH_PASSWORD:
-        do_hash_password();
-        break;
-      case CMD_VERIFY_CONFIG:
-        printf("Configuration was valid\n");
-        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. */
-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. */
-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. */
-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);
-    }
-  } else {
-    errmsg = nt_strerror(GetLastError());
-    printf("StartService() failed : %s\n", errmsg);
-    LocalFree(errmsg);
-  }
-  return -1;
-}
-
-/** Stop the Tor service. Return 0 if the service is stopped or was not
- * previously running. Return -1 on error. */
-int
-nt_service_stop(SC_HANDLE hService)
-{
-/** Wait at most 10 seconds for the service to stop. */
-#define MAX_SERVICE_WAIT_TIME 10
-  int wait_time;
-  char *errmsg = NULL;
-  nt_service_loadlibrary();
-
-  service_fns.QueryServiceStatus_fn(hService, &service_status);
-  if (service_status.dwCurrentState == SERVICE_STOPPED) {
-    printf("Service is already stopped\n");
-    return 0;
-  }
-
-  if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP,
-                                    &service_status)) {
-    wait_time = 0;
-    while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
-           (service_status.dwCurrentState != SERVICE_STOPPED) &&
-           (wait_time < MAX_SERVICE_WAIT_TIME)) {
-      Sleep(1000);
-      wait_time++;
-    }
-    if (service_status.dwCurrentState == SERVICE_STOPPED) {
-      printf("Service stopped successfully\n");
-      return 0;
-    } else if (wait_time == MAX_SERVICE_WAIT_TIME) {
-      printf("Service did not stop within %d seconds.\n", wait_time);
-    } else {
-      errmsg = nt_strerror(GetLastError());
-      printf("QueryServiceStatus() failed : %s\n",errmsg);
-      LocalFree(errmsg);
-    }
-  } else {
-    errmsg = nt_strerror(GetLastError());
-    printf("ControlService() failed : %s\n", errmsg);
-    LocalFree(errmsg);
-  }
-  return -1;
-}
-
-/** Build a formatted command line used for the NT service. Return a
- * pointer to the formatted string on success, or NULL on failure.  Set
- * *<b>using_default_torrc</b> to true if we're going to use the default
- * location to torrc, or 1 if an option was specified on the command line.
- */
-static char *
-nt_service_command_line(int *using_default_torrc)
-{
-  TCHAR tor_exe[MAX_PATH+1];
-  char *command, *options=NULL;
-  smartlist_t *sl;
-  int i, cmdlen;
-  *using_default_torrc = 1;
-
-  /* Get the location of tor.exe */
-  if (0 == GetModuleFileName(NULL, tor_exe, MAX_PATH))
-    return NULL;
-
-  /* Get the service arguments */
-  sl = smartlist_create();
-  for (i = 1; i < backup_argc; ++i) {
-    if (!strcmp(backup_argv[i], "--options") ||
-        !strcmp(backup_argv[i], "-options")) {
-      while (++i < backup_argc) {
-        if (!strcmp(backup_argv[i], "-f"))
-          *using_default_torrc = 0;
-        smartlist_add(sl, backup_argv[i]);
-      }
-    }
-  }
-  if (smartlist_len(sl))
-    options = smartlist_join_strings(sl,"\" \"",0,NULL);
-  smartlist_free(sl);
-
-  /* Allocate a string for the NT service command line */
-  cmdlen = strlen(tor_exe) + (options?strlen(options):0) + 32;
-  command = tor_malloc(cmdlen);
-
-  /* Format the service command */
-  if (options) {
-    if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service \"%s\"",
-                     tor_exe, options)<0) {
-      tor_free(command); /* sets command to NULL. */
-    }
-  } else { /* ! options */
-    if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service", tor_exe)<0) {
-      tor_free(command); /* sets command to NULL. */
-    }
-  }
-
-  tor_free(options);
-  return command;
-}
-
-/** Creates a Tor NT service, set to start on boot. The service will be
- * started if installation succeeds. Returns 0 on success, or -1 on
- * failure. */
-int
-nt_service_install(int argc, char **argv)
-{
-  /* Notes about developing NT services:
-   *
-   * 1. Don't count on your CWD. If an absolute path is not given, the
-   *    fopen() function goes wrong.
-   * 2. The parameters given to the nt_service_body() function differ
-   *    from those given to main() function.
-   */
-
-  SC_HANDLE hSCManager = NULL;
-  SC_HANDLE hService = NULL;
-  SERVICE_DESCRIPTION sdBuff;
-  char *command;
-  char *errmsg;
-  const char *user_acct = GENSRV_USERACCT;
-  const char *password = "";
-  int i;
-  OSVERSIONINFOEX info;
-  SID_NAME_USE sidUse;
-  DWORD sidLen = 0, domainLen = 0;
-  int is_win2k_or_worse = 0;
-  int using_default_torrc = 0;
-
-  nt_service_loadlibrary();
-
-  /* Open the service control manager so we can create a new service */
-  if ((hSCManager = nt_service_open_scm()) == NULL)
-    return -1;
-  /* Build the command line used for the service */
-  if ((command = nt_service_command_line(&using_default_torrc)) == NULL) {
-    printf("Unable to build service command line.\n");
-    service_fns.CloseServiceHandle_fn(hSCManager);
-    return -1;
-  }
-
-  for (i=1; i < argc; ++i) {
-    if (!strcmp(argv[i], "--user") && i+1<argc) {
-      user_acct = argv[i+1];
-      ++i;
-    }
-    if (!strcmp(argv[i], "--password") && i+1<argc) {
-      password = argv[i+1];
-      ++i;
-    }
-  }
-
-  /* Compute our version and see whether we're running win2k or earlier. */
-  memset(&info, 0, sizeof(info));
-  info.dwOSVersionInfoSize = sizeof(info);
-  if (! GetVersionEx((LPOSVERSIONINFO)&info)) {
-    printf("Call to GetVersionEx failed.\n");
-    is_win2k_or_worse = 1;
-  } else {
-    if (info.dwMajorVersion < 5 ||
-        (info.dwMajorVersion == 5 && info.dwMinorVersion == 0))
-      is_win2k_or_worse = 1;
-  }
-
-  if (user_acct == GENSRV_USERACCT) {
-    if (is_win2k_or_worse) {
-      /* On Win2k, there is no LocalService account, so we actually need to
-       * fall back on NULL (the system account). */
-      printf("Running on Win2K or earlier, so the LocalService account "
-             "doesn't exist.  Falling back to SYSTEM account.\n");
-      user_acct = NULL;
-    } else {
-      /* Genericity is apparently _so_ last year in Redmond, where some
-       * accounts are accounts that you can look up, and some accounts
-       * are magic and undetectable via the security subsystem. See
-       * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
-       */
-      printf("Running on a Post-Win2K OS, so we'll assume that the "
-             "LocalService account exists.\n");
-    }
-  } else if (0 && service_fns.LookupAccountNameA_fn(NULL, // On this system
-                            user_acct,
-                            NULL, &sidLen, // Don't care about the SID
-                            NULL, &domainLen, // Don't care about the domain
-                            &sidUse) == 0) {
-    /* XXXX For some reason, the above test segfaults. Fix that. */
-    printf("User \"%s\" doesn't seem to exist.\n", user_acct);
-    return -1;
-  } else {
-    printf("Will try to install service as user \"%s\".\n", user_acct);
-  }
-  /* XXXX This warning could be better about explaining how to resolve the
-   * situation. */
-  if (using_default_torrc)
-    printf("IMPORTANT NOTE:\n"
-        "    The Tor service will run under the account \"%s\".  This means\n"
-        "    that Tor will look for its configuration file under that\n"
-        "    account's Application Data directory, which is probably not\n"
-        "    the same as yours.\n", user_acct?user_acct:"<local system>");
-
-  /* Create the Tor service, set to auto-start on boot */
-  if ((hService = service_fns.CreateServiceA_fn(hSCManager, GENSRV_SERVICENAME,
-                                GENSRV_DISPLAYNAME,
-                                SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
-                                SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
-                                command, NULL, NULL, NULL,
-                                user_acct, password)) == NULL) {
-    errmsg = nt_strerror(GetLastError());
-    printf("CreateService() failed : %s\n", errmsg);
-    service_fns.CloseServiceHandle_fn(hSCManager);
-    LocalFree(errmsg);
-    tor_free(command);
-    return -1;
-  }
-  printf("Done with CreateService.\n");
-
-  /* Set the service's description */
-  sdBuff.lpDescription = (char*)GENSRV_DESCRIPTION;
-  service_fns.ChangeServiceConfig2A_fn(hService, SERVICE_CONFIG_DESCRIPTION,
-                                       &sdBuff);
-  printf("Service installed successfully\n");
-
-  /* Start the service initially */
-  nt_service_start(hService);
-
-  service_fns.CloseServiceHandle_fn(hService);
-  service_fns.CloseServiceHandle_fn(hSCManager);
-  tor_free(command);
-
-  return 0;
-}
-
-/** Removes the Tor NT service. Returns 0 if the service was successfully
- * removed, or -1 on error. */
-int
-nt_service_remove(void)
-{
-  SC_HANDLE hSCManager = NULL;
-  SC_HANDLE hService = NULL;
-  char *errmsg;
-
-  nt_service_loadlibrary();
-  if ((hSCManager = nt_service_open_scm()) == NULL)
-    return -1;
-  if ((hService = nt_service_open(hSCManager)) == NULL) {
-    service_fns.CloseServiceHandle_fn(hSCManager);
-    return -1;
-  }
-
-  nt_service_stop(hService);
-  if (service_fns.DeleteService_fn(hService) == FALSE) {
-    errmsg = nt_strerror(GetLastError());
-    printf("DeleteService() failed : %s\n", errmsg);
-    LocalFree(errmsg);
-    service_fns.CloseServiceHandle_fn(hService);
-    service_fns.CloseServiceHandle_fn(hSCManager);
-    return -1;
-  }
-
-  service_fns.CloseServiceHandle_fn(hService);
-  service_fns.CloseServiceHandle_fn(hSCManager);
-  printf("Service removed successfully\n");
-
-  return 0;
-}
-
-/** Starts the Tor service. Returns 0 on success, or -1 on error. */
-int
-nt_service_cmd_start(void)
-{
-  SC_HANDLE hSCManager;
-  SC_HANDLE hService;
-  int start;
-
-  if ((hSCManager = nt_service_open_scm()) == NULL)
-    return -1;
-  if ((hService = nt_service_open(hSCManager)) == NULL) {
-    service_fns.CloseServiceHandle_fn(hSCManager);
-    return -1;
-  }
-
-  start = nt_service_start(hService);
-  service_fns.CloseServiceHandle_fn(hService);
-  service_fns.CloseServiceHandle_fn(hSCManager);
-
-  return start;
-}
-
-/** Stops the Tor service. Returns 0 on success, or -1 on error. */
-int
-nt_service_cmd_stop(void)
-{
-  SC_HANDLE hSCManager;
-  SC_HANDLE hService;
-  int stop;
-
-  if ((hSCManager = nt_service_open_scm()) == NULL)
-    return -1;
-  if ((hService = nt_service_open(hSCManager)) == NULL) {
-    service_fns.CloseServiceHandle_fn(hSCManager);
-    return -1;
-  }
-
-  stop = nt_service_stop(hService);
-  service_fns.CloseServiceHandle_fn(hService);
-  service_fns.CloseServiceHandle_fn(hSCManager);
-
-  return stop;
-}
-
-/** Given a Win32 error code, this attempts to make Windows
- * return a human-readable error message. The char* returned
- * is allocated by Windows, but should be freed with LocalFree()
- * when finished with it. */
-static char*
-nt_strerror(uint32_t errnum)
-{
-   char *msgbuf;
-   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-                 NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                 (LPSTR)&msgbuf, 0, NULL);
-   return msgbuf;
-}
-#endif
-
 #ifdef USE_DMALLOC
 #include <openssl/crypto.h>
 static void
@@ -2609,53 +1901,16 @@
                                       _tor_dmalloc_free);
   log_notice(LD_CONFIG, "Set up dmalloc; returned %d", r);
 #endif
-#ifdef MS_WINDOWS_SERVICE
-  backup_argv = argv;
-  backup_argc = argc;
-  if ((argc >= 3) &&
-      (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) {
-    nt_service_loadlibrary();
-    if (!strcmp(argv[2], "install"))
-      return nt_service_install(argc, argv);
-    if (!strcmp(argv[2], "remove"))
-      return nt_service_remove();
-    if (!strcmp(argv[2], "start"))
-      return nt_service_cmd_start();
-    if (!strcmp(argv[2], "stop"))
-      return nt_service_cmd_stop();
-    printf("Unrecognized service command '%s'\n", argv[2]);
-    return -1;
-  }
-  if (argc >= 2) {
-    if (!strcmp(argv[1], "-nt-service") || !strcmp(argv[1], "--nt-service")) {
-      nt_service_loadlibrary();
-      nt_service_main();
-      return 0;
-    }
-    // These values have been deprecated since 0.1.1.2-alpha; we've warned
-    // about them since 0.1.2.7-alpha.
-    if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) {
-      nt_service_loadlibrary();
-      fprintf(stderr,
-            "The %s option is deprecated; use \"--service install\" instead.",
-            argv[1]);
-      return nt_service_install(argc, argv);
-    }
-    if (!strcmp(argv[1], "-remove") || !strcmp(argv[1], "--remove")) {
-      nt_service_loadlibrary();
-      fprintf(stderr,
-            "The %s option is deprecated; use \"--service remove\" instead.",
-            argv[1]);
-      return nt_service_remove();
-    }
-  }
+#ifdef NT_SERVICE
+  if ((result = nt_service_parse_options(argv, argc)))
+    return result;
 #endif
   if (tor_init(argc, argv)<0)
     return -1;
   switch (get_options()->command) {
   case CMD_RUN_TOR:
-#ifdef MS_WINDOWS_SERVICE
-    service_status.dwCurrentState = SERVICE_RUNNING;
+#ifdef NT_SERVICE
+    nt_service_set_state(SERVICE_RUNNING);
 #endif
     result = do_main_loop();
     break;

Added: tor/trunk/src/or/ntmain.c
===================================================================
--- tor/trunk/src/or/ntmain.c	                        (rev 0)
+++ tor/trunk/src/or/ntmain.c	2007-09-06 17:42:05 UTC (rev 11388)
@@ -0,0 +1,757 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2007, Roger Dingledine, Nick Mathewson. */
+/* See LICENSE for licensing information */
+/* $Id$ */
+
+#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;
+
+/* XXXX020 This 'backup argv' and 'backup argc' business is an ugly hack. This
+ * is a job for arguments, not globals. */
+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 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;
+}
+
+/** DOCDOC */
+int
+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:
+        do_list_fingerprint();
+        break;
+      case CMD_HASH_PASSWORD:
+        do_hash_password();
+        break;
+      case CMD_VERIFY_CONFIG:
+        printf("Configuration was valid\n");
+        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);
+    }
+  } else {
+    errmsg = nt_strerror(GetLastError());
+    printf("StartService() failed : %s\n", errmsg);
+    LocalFree(errmsg);
+  }
+  return -1;
+}
+
+/** Stop the Tor service. Return 0 if the service is stopped or was not
+ * previously running. Return -1 on error. */
+static int
+nt_service_stop(SC_HANDLE hService)
+{
+/** Wait at most 10 seconds for the service to stop. */
+#define MAX_SERVICE_WAIT_TIME 10
+  int wait_time;
+  char *errmsg = NULL;
+  nt_service_loadlibrary();
+
+  service_fns.QueryServiceStatus_fn(hService, &service_status);
+  if (service_status.dwCurrentState == SERVICE_STOPPED) {
+    printf("Service is already stopped\n");
+    return 0;
+  }
+
+  if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP,
+                                    &service_status)) {
+    wait_time = 0;
+    while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
+           (service_status.dwCurrentState != SERVICE_STOPPED) &&
+           (wait_time < MAX_SERVICE_WAIT_TIME)) {
+      Sleep(1000);
+      wait_time++;
+    }
+    if (service_status.dwCurrentState == SERVICE_STOPPED) {
+      printf("Service stopped successfully\n");
+      return 0;
+    } else if (wait_time == MAX_SERVICE_WAIT_TIME) {
+      printf("Service did not stop within %d seconds.\n", wait_time);
+    } else {
+      errmsg = nt_strerror(GetLastError());
+      printf("QueryServiceStatus() failed : %s\n",errmsg);
+      LocalFree(errmsg);
+    }
+  } else {
+    errmsg = nt_strerror(GetLastError());
+    printf("ControlService() failed : %s\n", errmsg);
+    LocalFree(errmsg);
+  }
+  return -1;
+}
+
+/** Build a formatted command line used for the NT service. Return a
+ * pointer to the formatted string on success, or NULL on failure.  Set
+ * *<b>using_default_torrc</b> to true if we're going to use the default
+ * location to torrc, or 1 if an option was specified on the command line.
+ */
+static char *
+nt_service_command_line(int *using_default_torrc)
+{
+  TCHAR tor_exe[MAX_PATH+1];
+  char *command, *options=NULL;
+  smartlist_t *sl;
+  int i, cmdlen;
+  *using_default_torrc = 1;
+
+  /* Get the location of tor.exe */
+  if (0 == GetModuleFileName(NULL, tor_exe, MAX_PATH))
+    return NULL;
+
+  /* Get the service arguments */
+  sl = smartlist_create();
+  for (i = 1; i < backup_argc; ++i) {
+    if (!strcmp(backup_argv[i], "--options") ||
+        !strcmp(backup_argv[i], "-options")) {
+      while (++i < backup_argc) {
+        if (!strcmp(backup_argv[i], "-f"))
+          *using_default_torrc = 0;
+        smartlist_add(sl, backup_argv[i]);
+      }
+    }
+  }
+  if (smartlist_len(sl))
+    options = smartlist_join_strings(sl,"\" \"",0,NULL);
+  smartlist_free(sl);
+
+  /* Allocate a string for the NT service command line */
+  cmdlen = strlen(tor_exe) + (options?strlen(options):0) + 32;
+  command = tor_malloc(cmdlen);
+
+  /* Format the service command */
+  if (options) {
+    if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service \"%s\"",
+                     tor_exe, options)<0) {
+      tor_free(command); /* sets command to NULL. */
+    }
+  } else { /* ! options */
+    if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service", tor_exe)<0) {
+      tor_free(command); /* sets command to NULL. */
+    }
+  }
+
+  tor_free(options);
+  return command;
+}
+
+/** Creates a Tor NT service, set to start on boot. The service will be
+ * started if installation succeeds. Returns 0 on success, or -1 on
+ * failure. */
+static int
+nt_service_install(int argc, char **argv)
+{
+  /* Notes about developing NT services:
+   *
+   * 1. Don't count on your CWD. If an absolute path is not given, the
+   *    fopen() function goes wrong.
+   * 2. The parameters given to the nt_service_body() function differ
+   *    from those given to main() function.
+   */
+
+  SC_HANDLE hSCManager = NULL;
+  SC_HANDLE hService = NULL;
+  SERVICE_DESCRIPTION sdBuff;
+  char *command;
+  char *errmsg;
+  const char *user_acct = GENSRV_USERACCT;
+  const char *password = "";
+  int i;
+  OSVERSIONINFOEX info;
+  SID_NAME_USE sidUse;
+  DWORD sidLen = 0, domainLen = 0;
+  int is_win2k_or_worse = 0;
+  int using_default_torrc = 0;
+
+  nt_service_loadlibrary();
+
+  /* Open the service control manager so we can create a new service */
+  if ((hSCManager = nt_service_open_scm()) == NULL)
+    return -1;
+  /* Build the command line used for the service */
+  if ((command = nt_service_command_line(&using_default_torrc)) == NULL) {
+    printf("Unable to build service command line.\n");
+    service_fns.CloseServiceHandle_fn(hSCManager);
+    return -1;
+  }
+
+  for (i=1; i < argc; ++i) {
+    if (!strcmp(argv[i], "--user") && i+1<argc) {
+      user_acct = argv[i+1];
+      ++i;
+    }
+    if (!strcmp(argv[i], "--password") && i+1<argc) {
+      password = argv[i+1];
+      ++i;
+    }
+  }
+
+  /* Compute our version and see whether we're running win2k or earlier. */
+  memset(&info, 0, sizeof(info));
+  info.dwOSVersionInfoSize = sizeof(info);
+  if (! GetVersionEx((LPOSVERSIONINFO)&info)) {
+    printf("Call to GetVersionEx failed.\n");
+    is_win2k_or_worse = 1;
+  } else {
+    if (info.dwMajorVersion < 5 ||
+        (info.dwMajorVersion == 5 && info.dwMinorVersion == 0))
+      is_win2k_or_worse = 1;
+  }
+
+  if (user_acct == GENSRV_USERACCT) {
+    if (is_win2k_or_worse) {
+      /* On Win2k, there is no LocalService account, so we actually need to
+       * fall back on NULL (the system account). */
+      printf("Running on Win2K or earlier, so the LocalService account "
+             "doesn't exist.  Falling back to SYSTEM account.\n");
+      user_acct = NULL;
+    } else {
+      /* Genericity is apparently _so_ last year in Redmond, where some
+       * accounts are accounts that you can look up, and some accounts
+       * are magic and undetectable via the security subsystem. See
+       * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
+       */
+      printf("Running on a Post-Win2K OS, so we'll assume that the "
+             "LocalService account exists.\n");
+    }
+  } else if (0 && service_fns.LookupAccountNameA_fn(NULL, // On this system
+                            user_acct,
+                            NULL, &sidLen, // Don't care about the SID
+                            NULL, &domainLen, // Don't care about the domain
+                            &sidUse) == 0) {
+    /* XXXX For some reason, the above test segfaults. Fix that. */
+    printf("User \"%s\" doesn't seem to exist.\n", user_acct);
+    return -1;
+  } else {
+    printf("Will try to install service as user \"%s\".\n", user_acct);
+  }
+  /* XXXX This warning could be better about explaining how to resolve the
+   * situation. */
+  if (using_default_torrc)
+    printf("IMPORTANT NOTE:\n"
+        "    The Tor service will run under the account \"%s\".  This means\n"
+        "    that Tor will look for its configuration file under that\n"
+        "    account's Application Data directory, which is probably not\n"
+        "    the same as yours.\n", user_acct?user_acct:"<local system>");
+
+  /* Create the Tor service, set to auto-start on boot */
+  if ((hService = service_fns.CreateServiceA_fn(hSCManager, GENSRV_SERVICENAME,
+                                GENSRV_DISPLAYNAME,
+                                SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+                                SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
+                                command, NULL, NULL, NULL,
+                                user_acct, password)) == NULL) {
+    errmsg = nt_strerror(GetLastError());
+    printf("CreateService() failed : %s\n", errmsg);
+    service_fns.CloseServiceHandle_fn(hSCManager);
+    LocalFree(errmsg);
+    tor_free(command);
+    return -1;
+  }
+  printf("Done with CreateService.\n");
+
+  /* Set the service's description */
+  sdBuff.lpDescription = (char*)GENSRV_DESCRIPTION;
+  service_fns.ChangeServiceConfig2A_fn(hService, SERVICE_CONFIG_DESCRIPTION,
+                                       &sdBuff);
+  printf("Service installed successfully\n");
+
+  /* Start the service initially */
+  nt_service_start(hService);
+
+  service_fns.CloseServiceHandle_fn(hService);
+  service_fns.CloseServiceHandle_fn(hSCManager);
+  tor_free(command);
+
+  return 0;
+}
+
+/** Removes the Tor NT service. Returns 0 if the service was successfully
+ * removed, or -1 on error. */
+static int
+nt_service_remove(void)
+{
+  SC_HANDLE hSCManager = NULL;
+  SC_HANDLE hService = NULL;
+  char *errmsg;
+
+  nt_service_loadlibrary();
+  if ((hSCManager = nt_service_open_scm()) == NULL)
+    return -1;
+  if ((hService = nt_service_open(hSCManager)) == NULL) {
+    service_fns.CloseServiceHandle_fn(hSCManager);
+    return -1;
+  }
+
+  nt_service_stop(hService);
+  if (service_fns.DeleteService_fn(hService) == FALSE) {
+    errmsg = nt_strerror(GetLastError());
+    printf("DeleteService() failed : %s\n", errmsg);
+    LocalFree(errmsg);
+    service_fns.CloseServiceHandle_fn(hService);
+    service_fns.CloseServiceHandle_fn(hSCManager);
+    return -1;
+  }
+
+  service_fns.CloseServiceHandle_fn(hService);
+  service_fns.CloseServiceHandle_fn(hSCManager);
+  printf("Service removed successfully\n");
+
+  return 0;
+}
+
+/** Starts the Tor service. Returns 0 on success, or -1 on error. */
+static int
+nt_service_cmd_start(void)
+{
+  SC_HANDLE hSCManager;
+  SC_HANDLE hService;
+  int start;
+
+  if ((hSCManager = nt_service_open_scm()) == NULL)
+    return -1;
+  if ((hService = nt_service_open(hSCManager)) == NULL) {
+    service_fns.CloseServiceHandle_fn(hSCManager);
+    return -1;
+  }
+
+  start = nt_service_start(hService);
+  service_fns.CloseServiceHandle_fn(hService);
+  service_fns.CloseServiceHandle_fn(hSCManager);
+
+  return start;
+}
+
+/** Stops the Tor service. Returns 0 on success, or -1 on error. */
+static int
+nt_service_cmd_stop(void)
+{
+  SC_HANDLE hSCManager;
+  SC_HANDLE hService;
+  int stop;
+
+  if ((hSCManager = nt_service_open_scm()) == NULL)
+    return -1;
+  if ((hService = nt_service_open(hSCManager)) == NULL) {
+    service_fns.CloseServiceHandle_fn(hSCManager);
+    return -1;
+  }
+
+  stop = nt_service_stop(hService);
+  service_fns.CloseServiceHandle_fn(hService);
+  service_fns.CloseServiceHandle_fn(hSCManager);
+
+  return stop;
+}
+
+/** Given a Win32 error code, this attempts to make Windows
+ * return a human-readable error message. The char* returned
+ * is allocated by Windows, but should be freed with LocalFree()
+ * when finished with it. */
+static char*
+nt_strerror(uint32_t errnum)
+{
+   char *msgbuf;
+   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                 NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                 (LPSTR)&msgbuf, 0, NULL);
+   return msgbuf;
+}
+#endif
+
+int
+nt_service_parse_options(int argc, char **argv)
+{
+  backup_argv = argv;
+  backup_argc = argc;
+
+  if ((argc >= 3) &&
+      (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) {
+    nt_service_loadlibrary();
+    if (!strcmp(argv[2], "install"))
+      return nt_service_install(argc, argv);
+    if (!strcmp(argv[2], "remove"))
+      return nt_service_remove();
+    if (!strcmp(argv[2], "start"))
+      return nt_service_cmd_start();
+    if (!strcmp(argv[2], "stop"))
+      return nt_service_cmd_stop();
+    printf("Unrecognized service command '%s'\n", argv[2]);
+    return -1;
+  }
+  if (argc >= 2) {
+    if (!strcmp(argv[1], "-nt-service") || !strcmp(argv[1], "--nt-service")) {
+      nt_service_loadlibrary();
+      nt_service_main();
+      return ;
+    }
+    // These values have been deprecated since 0.1.1.2-alpha; we've warned
+    // about them since 0.1.2.7-alpha.
+    if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) {
+      nt_service_loadlibrary();
+      fprintf(stderr,
+            "The %s option is deprecated; use \"--service install\" instead.",
+            argv[1]);
+      return nt_service_install(argc, argv);
+    }
+    if (!strcmp(argv[1], "-remove") || !strcmp(argv[1], "--remove")) {
+      nt_service_loadlibrary();
+      fprintf(stderr,
+            "The %s option is deprecated; use \"--service remove\" instead.",
+            argv[1]);
+      return nt_service_remove();
+    }
+  }
+}
+


Property changes on: tor/trunk/src/or/ntmain.c
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h	2007-09-06 16:27:49 UTC (rev 11387)
+++ tor/trunk/src/or/or.h	2007-09-06 17:42:05 UTC (rev 11388)
@@ -3018,6 +3018,19 @@
 
 int tor_main(int argc, char *argv[]);
 
+/********************************* ntmain.c ***************************/
+#ifdef MS_WINDOWS
+#define NT_SERVICE
+#endif
+
+#ifdef NT_SERVICE
+int nt_service_parse_options(int argc, char **argv);
+int nt_service_is_stopping(void);
+void nt_service_set_state(DWORD state);
+#else
+#define nt_service_is_stopping() (0)
+#endif
+
 /********************************* onion.c ***************************/
 
 int onion_pending_add(or_circuit_t *circ);



More information about the tor-commits mailing list