tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

ntmain.c (26290B)


      1 /* Copyright (c) 2001-2004, Roger Dingledine.
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 /* See LICENSE for licensing information */
      5 
      6 /**
      7 * \file ntmain.c
      8 *
      9 * \brief Entry points for running/configuring Tor as a Windows Service.
     10 *
     11 * Windows Services expect to be registered with the operating system, and to
     12 * have entry points for starting, stopping, and monitoring them.  This module
     13 * implements those entry points so that a tor relay or client or hidden
     14 * service can run as a Windows service.  Therefore, this module
     15 * is only compiled when building for Windows.
     16 *
     17 * Warning: this module is not very well tested or very well maintained.
     18 */
     19 
     20 #ifdef _WIN32
     21 
     22 #include "core/or/or.h"
     23 
     24 #include "app/config/config.h"
     25 #include "app/main/main.h"
     26 #include "app/main/ntmain.h"
     27 #include "app/main/shutdown.h"
     28 #include "core/mainloop/mainloop.h"
     29 #include "lib/evloop/compat_libevent.h"
     30 #include "lib/fs/winlib.h"
     31 #include "lib/log/win32err.h"
     32 
     33 #include <windows.h>
     34 #define GENSRV_SERVICENAME  "tor"
     35 #define GENSRV_DISPLAYNAME  "Tor Win32 Service"
     36 #define GENSRV_DESCRIPTION  \
     37  "Provides an anonymous Internet communication system"
     38 #define GENSRV_USERACCT "NT AUTHORITY\\LocalService"
     39 
     40 // Cheating: using the pre-defined error codes, tricks Windows into displaying
     41 //           a semi-related human-readable error message if startup fails as
     42 //           opposed to simply scaring people with Error: 0xffffffff
     43 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
     44 
     45 static SERVICE_STATUS service_status;
     46 static SERVICE_STATUS_HANDLE hStatus;
     47 
     48 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
     49 * is a job for arguments, not globals.  Alas, some of the functions that
     50 * use them use them need to have fixed signatures, so they can be passed
     51 * to the NT service functions. */
     52 static char **backup_argv;
     53 static int backup_argc;
     54 
     55 static void nt_service_control(DWORD request);
     56 static void nt_service_body(int argc, char **argv);
     57 static void nt_service_main(void);
     58 static SC_HANDLE nt_service_open_scm(void);
     59 static SC_HANDLE nt_service_open(SC_HANDLE hSCManager);
     60 static int nt_service_start(SC_HANDLE hService);
     61 static int nt_service_stop(SC_HANDLE hService);
     62 static int nt_service_install(int argc, char **argv);
     63 static int nt_service_remove(void);
     64 static int nt_service_cmd_start(void);
     65 static int nt_service_cmd_stop(void);
     66 
     67 /** Struct to hold dynamically loaded NT-service related function pointers.
     68 */
     69 typedef struct nt_services {
     70  int loaded;
     71 
     72  /** @{ */
     73  /** Function pointers for Windows API functions related to service
     74   * management.  These are NULL, or they point to the .  They're set by
     75   * calling the LOAD macro below.  */
     76 
     77  BOOL (WINAPI *ChangeServiceConfig2A_fn)(
     78                             SC_HANDLE hService,
     79                             DWORD dwInfoLevel,
     80                             LPVOID lpInfo);
     81 
     82  BOOL (WINAPI *CloseServiceHandle_fn)(
     83                             SC_HANDLE hSCObject);
     84 
     85  BOOL (WINAPI *ControlService_fn)(
     86                             SC_HANDLE hService,
     87                             DWORD dwControl,
     88                             LPSERVICE_STATUS lpServiceStatus);
     89 
     90  SC_HANDLE (WINAPI *CreateServiceA_fn)(
     91                             SC_HANDLE hSCManager,
     92                             LPCSTR lpServiceName,
     93                             LPCSTR lpDisplayName,
     94                             DWORD dwDesiredAccess,
     95                             DWORD dwServiceType,
     96                             DWORD dwStartType,
     97                             DWORD dwErrorControl,
     98                             LPCSTR lpBinaryPathName,
     99                             LPCSTR lpLoadOrderGroup,
    100                             LPDWORD lpdwTagId,
    101                             LPCSTR lpDependencies,
    102                             LPCSTR lpServiceStartName,
    103                             LPCSTR lpPassword);
    104 
    105  BOOL (WINAPI *DeleteService_fn)(
    106                             SC_HANDLE hService);
    107 
    108  SC_HANDLE (WINAPI *OpenSCManagerA_fn)(
    109                             LPCSTR lpMachineName,
    110                             LPCSTR lpDatabaseName,
    111                             DWORD dwDesiredAccess);
    112 
    113  SC_HANDLE (WINAPI *OpenServiceA_fn)(
    114                             SC_HANDLE hSCManager,
    115                             LPCSTR lpServiceName,
    116                             DWORD dwDesiredAccess);
    117 
    118  BOOL (WINAPI *QueryServiceStatus_fn)(
    119                             SC_HANDLE hService,
    120                             LPSERVICE_STATUS lpServiceStatus);
    121 
    122  SERVICE_STATUS_HANDLE (WINAPI *RegisterServiceCtrlHandlerA_fn)(
    123                             LPCSTR lpServiceName,
    124                             LPHANDLER_FUNCTION lpHandlerProc);
    125 
    126  BOOL (WINAPI *SetServiceStatus_fn)(SERVICE_STATUS_HANDLE,
    127                             LPSERVICE_STATUS);
    128 
    129  BOOL (WINAPI *StartServiceCtrlDispatcherA_fn)(
    130                             const SERVICE_TABLE_ENTRYA* lpServiceTable);
    131 
    132  BOOL (WINAPI *StartServiceA_fn)(
    133                             SC_HANDLE hService,
    134                             DWORD dwNumServiceArgs,
    135                             LPCSTR* lpServiceArgVectors);
    136 
    137  BOOL (WINAPI *LookupAccountNameA_fn)(
    138                             LPCSTR lpSystemName,
    139                             LPCSTR lpAccountName,
    140                             PSID Sid,
    141                             LPDWORD cbSid,
    142                             LPTSTR ReferencedDomainName,
    143                             LPDWORD cchReferencedDomainName,
    144                             PSID_NAME_USE peUse);
    145  /** @} */
    146 } nt_services_t;
    147 
    148 static nt_services_t service_fns = { 0,
    149                  NULL, NULL, NULL, NULL, NULL, NULL,
    150                  NULL, NULL, NULL, NULL, NULL, NULL,
    151                  NULL};
    152 
    153 /** Loads functions used by NT services. Returns on success, or prints a
    154 * complaint to stdout and exits on error. */
    155 static void
    156 nt_service_loadlibrary(void)
    157 {
    158  HMODULE library = 0;
    159  void *fn;
    160 
    161  if (service_fns.loaded)
    162    return;
    163 
    164  if (!(library = load_windows_system_library(TEXT("advapi32.dll")))) {
    165    log_err(LD_GENERAL, "Couldn't open advapi32.dll.  Are you trying to use "
    166            "NT services on Windows 98? That doesn't work.");
    167    goto err;
    168  }
    169 
    170 /* Helper macro: try to load a function named <b>f</b> from "library" into
    171 * service_functions.<b>f</b>_fn.  On failure, log an error message, and goto
    172 * err.
    173 */
    174 #define LOAD(f) STMT_BEGIN                                              \
    175    if (!(fn = GetProcAddress(library, #f))) {                          \
    176      log_err(LD_BUG,                                                   \
    177              "Couldn't find %s in advapi32.dll! We probably got the "  \
    178              "name wrong.", #f);                                       \
    179      goto err;                                                         \
    180    } else {                                                            \
    181      service_fns.f ## _fn = fn;                                        \
    182    }                                                                   \
    183  STMT_END
    184 
    185  LOAD(ChangeServiceConfig2A);
    186  LOAD(CloseServiceHandle);
    187  LOAD(ControlService);
    188  LOAD(CreateServiceA);
    189  LOAD(DeleteService);
    190  LOAD(OpenSCManagerA);
    191  LOAD(OpenServiceA);
    192  LOAD(QueryServiceStatus);
    193  LOAD(RegisterServiceCtrlHandlerA);
    194  LOAD(SetServiceStatus);
    195  LOAD(StartServiceCtrlDispatcherA);
    196  LOAD(StartServiceA);
    197  LOAD(LookupAccountNameA);
    198 
    199  service_fns.loaded = 1;
    200 
    201  return;
    202 err:
    203  printf("Unable to load library support for NT services: exiting.\n");
    204  exit(1); // exit ok: ntmain can't read libraries
    205 }
    206 
    207 /** If we're compiled to run as an NT service, and the service wants to
    208 * shut down, then change our current status and return 1.  Else
    209 * return 0.
    210 */
    211 int
    212 nt_service_is_stopping(void)
    213 {
    214  /* If we haven't loaded the function pointers, we can't possibly be an NT
    215   * service trying to shut down. */
    216  if (!service_fns.loaded)
    217    return 0;
    218 
    219  if (service_status.dwCurrentState == SERVICE_STOP_PENDING) {
    220    service_status.dwWin32ExitCode = 0;
    221    service_status.dwCurrentState = SERVICE_STOPPED;
    222    service_fns.SetServiceStatus_fn(hStatus, &service_status);
    223    return 1;
    224  } else if (service_status.dwCurrentState == SERVICE_STOPPED) {
    225    return 1;
    226  }
    227  return 0;
    228 }
    229 
    230 /** Set the dwCurrentState field for our service to <b>state</b>. */
    231 void
    232 nt_service_set_state(DWORD state)
    233 {
    234  service_status.dwCurrentState = state;
    235 }
    236 
    237 /** Handles service control requests, such as stopping or starting the
    238 * Tor service. */
    239 static void
    240 nt_service_control(DWORD request)
    241 {
    242  static struct timeval exit_now;
    243  exit_now.tv_sec  = 0;
    244  exit_now.tv_usec = 0;
    245 
    246  nt_service_loadlibrary();
    247 
    248  switch (request) {
    249    case SERVICE_CONTROL_STOP:
    250        case SERVICE_CONTROL_SHUTDOWN:
    251          log_notice(LD_GENERAL,
    252                     "Got stop/shutdown request; shutting down cleanly.");
    253          service_status.dwCurrentState = SERVICE_STOP_PENDING;
    254          tor_libevent_exit_loop_after_delay(tor_libevent_get_base(),
    255                                             &exit_now);
    256          return;
    257  }
    258  service_fns.SetServiceStatus_fn(hStatus, &service_status);
    259 }
    260 
    261 /** Called when the service is started via the system's service control
    262 * manager. This calls tor_init() and starts the main event loop. If
    263 * tor_init() fails, the service will be stopped and exit code set to
    264 * NT_SERVICE_ERROR_TORINIT_FAILED. */
    265 static void
    266 nt_service_body(int argc, char **argv)
    267 {
    268  int r;
    269  (void) argc; /* unused */
    270  (void) argv; /* unused */
    271  nt_service_loadlibrary();
    272  service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    273  service_status.dwCurrentState = SERVICE_START_PENDING;
    274  service_status.dwControlsAccepted =
    275        SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    276  service_status.dwWin32ExitCode = 0;
    277  service_status.dwServiceSpecificExitCode = 0;
    278  service_status.dwCheckPoint = 0;
    279  service_status.dwWaitHint = 1000;
    280  hStatus = service_fns.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME,
    281                                   (LPHANDLER_FUNCTION) nt_service_control);
    282 
    283  if (hStatus == 0) {
    284    /* Failed to register the service control handler function */
    285    return;
    286  }
    287 
    288  pubsub_install();
    289  r = tor_init(backup_argc, backup_argv);
    290 
    291  if (r) {
    292    /* Failed to start the Tor service */
    293    r = NT_SERVICE_ERROR_TORINIT_FAILED;
    294    service_status.dwCurrentState = SERVICE_STOPPED;
    295    service_status.dwWin32ExitCode = r;
    296    service_status.dwServiceSpecificExitCode = r;
    297    service_fns.SetServiceStatus_fn(hStatus, &service_status);
    298    return;
    299  }
    300 
    301  pubsub_connect();
    302 
    303  /* Set the service's status to SERVICE_RUNNING and start the main
    304   * event loop */
    305  service_status.dwCurrentState = SERVICE_RUNNING;
    306  service_fns.SetServiceStatus_fn(hStatus, &service_status);
    307  set_main_thread();
    308  run_tor_main_loop();
    309  tor_cleanup();
    310 }
    311 
    312 /** Main service entry point. Starts the service control dispatcher and waits
    313 * until the service status is set to SERVICE_STOPPED. */
    314 static void
    315 nt_service_main(void)
    316 {
    317  SERVICE_TABLE_ENTRYA table[2];
    318  DWORD result = 0;
    319  char *errmsg;
    320  nt_service_loadlibrary();
    321  table[0].lpServiceName = (char*)GENSRV_SERVICENAME;
    322  table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)nt_service_body;
    323  table[1].lpServiceName = NULL;
    324  table[1].lpServiceProc = NULL;
    325 
    326  if (!service_fns.StartServiceCtrlDispatcherA_fn(table)) {
    327    result = GetLastError();
    328    errmsg = format_win32_error(result);
    329    printf("Service error %d : %s\n", (int) result, errmsg);
    330    tor_free(errmsg);
    331 
    332    pubsub_install();
    333    if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
    334      if (tor_init(backup_argc, backup_argv))
    335        return;
    336      pubsub_connect();
    337      switch (get_options()->command) {
    338      case CMD_RUN_TOR:
    339        run_tor_main_loop();
    340        break;
    341      case CMD_LIST_FINGERPRINT:
    342      case CMD_HASH_PASSWORD:
    343      case CMD_VERIFY_CONFIG:
    344      case CMD_DUMP_CONFIG:
    345      case CMD_KEYGEN:
    346      case CMD_KEYGEN_FAMILY:
    347      case CMD_KEY_EXPIRATION:
    348        log_err(LD_CONFIG, "Unsupported command (--list-fingerprint, "
    349               "--hash-password, --keygen, --dump-config, --verify-config, "
    350               "or --key-expiration) in NT service.");
    351        break;
    352      case CMD_RUN_UNITTESTS:
    353      case CMD_IMMEDIATE:
    354      default:
    355        log_err(LD_CONFIG, "Illegal command number %d: internal error.",
    356                get_options()->command);
    357      }
    358      tor_cleanup();
    359    }
    360  }
    361 }
    362 
    363 /** Return a handle to the service control manager on success, or NULL on
    364 * failure. */
    365 static SC_HANDLE
    366 nt_service_open_scm(void)
    367 {
    368  SC_HANDLE hSCManager;
    369  char *errmsg = NULL;
    370 
    371  nt_service_loadlibrary();
    372  if ((hSCManager = service_fns.OpenSCManagerA_fn(
    373                            NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) {
    374    errmsg = format_win32_error(GetLastError());
    375    printf("OpenSCManager() failed : %s\n", errmsg);
    376    tor_free(errmsg);
    377  }
    378  return hSCManager;
    379 }
    380 
    381 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
    382 * on failure. */
    383 static SC_HANDLE
    384 nt_service_open(SC_HANDLE hSCManager)
    385 {
    386  SC_HANDLE hService;
    387  char *errmsg = NULL;
    388  nt_service_loadlibrary();
    389  if ((hService = service_fns.OpenServiceA_fn(hSCManager, GENSRV_SERVICENAME,
    390                              SERVICE_ALL_ACCESS)) == NULL) {
    391    errmsg = format_win32_error(GetLastError());
    392    printf("OpenService() failed : %s\n", errmsg);
    393    tor_free(errmsg);
    394  }
    395  return hService;
    396 }
    397 
    398 /** Start the Tor service. Return 0 if the service is started or was
    399 * previously running. Return -1 on error. */
    400 static int
    401 nt_service_start(SC_HANDLE hService)
    402 {
    403  char *errmsg = NULL;
    404 
    405  nt_service_loadlibrary();
    406 
    407  service_fns.QueryServiceStatus_fn(hService, &service_status);
    408  if (service_status.dwCurrentState == SERVICE_RUNNING) {
    409    printf("Service is already running\n");
    410    return 0;
    411  }
    412 
    413  if (service_fns.StartServiceA_fn(hService, 0, NULL)) {
    414    /* Loop until the service has finished attempting to start */
    415    while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
    416           (service_status.dwCurrentState == SERVICE_START_PENDING)) {
    417      Sleep(500);
    418    }
    419 
    420    /* Check if it started successfully or not */
    421    if (service_status.dwCurrentState == SERVICE_RUNNING) {
    422      printf("Service started successfully\n");
    423      return 0;
    424    } else {
    425      errmsg = format_win32_error(service_status.dwWin32ExitCode);
    426      printf("Service failed to start : %s\n", errmsg);
    427      tor_free(errmsg);
    428    }
    429  } else {
    430    errmsg = format_win32_error(GetLastError());
    431    printf("StartService() failed : %s\n", errmsg);
    432    tor_free(errmsg);
    433  }
    434  return -1;
    435 }
    436 
    437 /** Stop the Tor service. Return 0 if the service is stopped or was not
    438 * previously running. Return -1 on error. */
    439 static int
    440 nt_service_stop(SC_HANDLE hService)
    441 {
    442 /** Wait at most 10 seconds for the service to stop. */
    443 #define MAX_SERVICE_WAIT_TIME 10
    444  int wait_time;
    445  char *errmsg = NULL;
    446  nt_service_loadlibrary();
    447 
    448  service_fns.QueryServiceStatus_fn(hService, &service_status);
    449  if (service_status.dwCurrentState == SERVICE_STOPPED) {
    450    printf("Service is already stopped\n");
    451    return 0;
    452  }
    453 
    454  if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP,
    455                                    &service_status)) {
    456    wait_time = 0;
    457    while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
    458           (service_status.dwCurrentState != SERVICE_STOPPED) &&
    459           (wait_time < MAX_SERVICE_WAIT_TIME)) {
    460      Sleep(1000);
    461      wait_time++;
    462    }
    463    if (service_status.dwCurrentState == SERVICE_STOPPED) {
    464      printf("Service stopped successfully\n");
    465      return 0;
    466    } else if (wait_time == MAX_SERVICE_WAIT_TIME) {
    467      printf("Service did not stop within %d seconds.\n", wait_time);
    468    } else {
    469      errmsg = format_win32_error(GetLastError());
    470      printf("QueryServiceStatus() failed : %s\n",errmsg);
    471      tor_free(errmsg);
    472    }
    473  } else {
    474    errmsg = format_win32_error(GetLastError());
    475    printf("ControlService() failed : %s\n", errmsg);
    476    tor_free(errmsg);
    477  }
    478  return -1;
    479 }
    480 
    481 /** Build a formatted command line used for the NT service. Return a
    482 * pointer to the formatted string on success, or NULL on failure.  Set
    483 * *<b>using_default_torrc</b> to true if we're going to use the default
    484 * location to torrc, or 1 if an option was specified on the command line.
    485 */
    486 static char *
    487 nt_service_command_line(int *using_default_torrc)
    488 {
    489  TCHAR tor_exe[MAX_PATH+1];
    490  char tor_exe_ascii[MAX_PATH*2+1];
    491  char *command=NULL, *options=NULL;
    492  smartlist_t *sl;
    493  int i;
    494  *using_default_torrc = 1;
    495 
    496  /* Get the location of tor.exe */
    497  if (0 == GetModuleFileName(NULL, tor_exe, MAX_PATH))
    498    return NULL;
    499 
    500  /* Get the service arguments */
    501  sl = smartlist_new();
    502  for (i = 1; i < backup_argc; ++i) {
    503    if (!strcmp(backup_argv[i], "--options") ||
    504        !strcmp(backup_argv[i], "-options")) {
    505      while (++i < backup_argc) {
    506        if (!strcmp(backup_argv[i], "-f") ||
    507            !strcmp(backup_argv[i], "--torrc-file"))
    508          *using_default_torrc = 0;
    509        smartlist_add(sl, backup_argv[i]);
    510      }
    511    }
    512  }
    513  if (smartlist_len(sl))
    514    options = smartlist_join_strings(sl,"\" \"",0,NULL);
    515  smartlist_free(sl);
    516 
    517 #ifdef UNICODE
    518  wcstombs(tor_exe_ascii, tor_exe, sizeof(tor_exe_ascii));
    519  tor_exe_ascii[sizeof(tor_exe_ascii)-1] = '\0';
    520 #else
    521  strlcpy(tor_exe_ascii, tor_exe, sizeof(tor_exe_ascii));
    522 #endif /* defined(UNICODE) */
    523 
    524  /* Allocate a string for the NT service command line and */
    525  /* Format the service command */
    526  if (options) {
    527    tor_asprintf(&command, "\"%s\" --nt-service \"%s\"",
    528                 tor_exe_ascii, options);
    529  } else { /* ! options */
    530    tor_asprintf(&command, "\"%s\" --nt-service", tor_exe_ascii);
    531  }
    532 
    533  tor_free(options);
    534  return command;
    535 }
    536 
    537 /** Creates a Tor NT service, set to start on boot. The service will be
    538 * started if installation succeeds. Returns 0 on success, or -1 on
    539 * failure. */
    540 static int
    541 nt_service_install(int argc, char **argv)
    542 {
    543  /* Notes about developing NT services:
    544   *
    545   * 1. Don't count on your CWD. If an absolute path is not given, the
    546   *    fopen() function goes wrong.
    547   * 2. The parameters given to the nt_service_body() function differ
    548   *    from those given to main() function.
    549   */
    550 
    551  SC_HANDLE hSCManager = NULL;
    552  SC_HANDLE hService = NULL;
    553  SERVICE_DESCRIPTIONA sdBuff;
    554  char *command;
    555  char *errmsg;
    556  const char *user_acct = NULL;
    557  const char *password = "";
    558  int i;
    559  OSVERSIONINFOEX info;
    560  SID_NAME_USE sidUse;
    561  DWORD sidLen = 0, domainLen = 0;
    562  int is_win2k_or_worse = 0;
    563  int using_default_torrc = 0;
    564 
    565  nt_service_loadlibrary();
    566 
    567  /* Open the service control manager so we can create a new service */
    568  if ((hSCManager = nt_service_open_scm()) == NULL)
    569    return -1;
    570  /* Build the command line used for the service */
    571  if ((command = nt_service_command_line(&using_default_torrc)) == NULL) {
    572    printf("Unable to build service command line.\n");
    573    service_fns.CloseServiceHandle_fn(hSCManager);
    574    return -1;
    575  }
    576 
    577  for (i=1; i < argc; ++i) {
    578    if (!strcmp(argv[i], "--user") && i+1<argc) {
    579      user_acct = argv[i+1];
    580      ++i;
    581    }
    582    if (!strcmp(argv[i], "--password") && i+1<argc) {
    583      password = argv[i+1];
    584      ++i;
    585    }
    586  }
    587 
    588  /* Compute our version and see whether we're running win2k or earlier. */
    589  memset(&info, 0, sizeof(info));
    590  info.dwOSVersionInfoSize = sizeof(info);
    591  if (! GetVersionEx((LPOSVERSIONINFO)&info)) {
    592    printf("Call to GetVersionEx failed.\n");
    593    is_win2k_or_worse = 1;
    594  } else {
    595    if (info.dwMajorVersion < 5 ||
    596        (info.dwMajorVersion == 5 && info.dwMinorVersion == 0))
    597      is_win2k_or_worse = 1;
    598  }
    599 
    600  if (!user_acct) {
    601    if (is_win2k_or_worse) {
    602      /* On Win2k, there is no LocalService account, so we actually need to
    603       * fall back on NULL (the system account). */
    604      printf("Running on Win2K or earlier, so the LocalService account "
    605             "doesn't exist.  Falling back to SYSTEM account.\n");
    606    } else {
    607      /* Genericity is apparently _so_ last year in Redmond, where some
    608       * accounts are accounts that you can look up, and some accounts
    609       * are magic and undetectable via the security subsystem. See
    610       * https://msdn2.microsoft.com/en-us/library/ms684188.aspx
    611       */
    612      printf("Running on a Post-Win2K OS, so we'll assume that the "
    613             "LocalService account exists.\n");
    614      user_acct = GENSRV_USERACCT;
    615    }
    616  } else if (0 && service_fns.LookupAccountNameA_fn(NULL, // On this system
    617                            user_acct,
    618                            NULL, &sidLen, // Don't care about the SID
    619                            NULL, &domainLen, // Don't care about the domain
    620                            &sidUse) == 0) {
    621    /* XXXX For some reason, the above test segfaults. Fix that. */
    622    printf("User \"%s\" doesn't seem to exist.\n", user_acct);
    623    tor_free(command);
    624    return -1;
    625  } else {
    626    printf("Will try to install service as user \"%s\".\n", user_acct);
    627  }
    628  /* XXXX This warning could be better about explaining how to resolve the
    629   * situation. */
    630  if (using_default_torrc)
    631    printf("IMPORTANT NOTE:\n"
    632        "    The Tor service will run under the account \"%s\".  This means\n"
    633        "    that Tor will look for its configuration file under that\n"
    634        "    account's Application Data directory, which is probably not\n"
    635        "    the same as yours.\n", user_acct?user_acct:"<local system>");
    636 
    637  /* Create the Tor service, set to auto-start on boot */
    638  if ((hService = service_fns.CreateServiceA_fn(hSCManager, GENSRV_SERVICENAME,
    639                                GENSRV_DISPLAYNAME,
    640                                SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
    641                                SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
    642                                command, NULL, NULL, NULL,
    643                                user_acct, password)) == NULL) {
    644    errmsg = format_win32_error(GetLastError());
    645    printf("CreateService() failed : %s\n", errmsg);
    646    service_fns.CloseServiceHandle_fn(hSCManager);
    647    tor_free(errmsg);
    648    tor_free(command);
    649    return -1;
    650  }
    651  printf("Done with CreateService.\n");
    652 
    653  /* Set the service's description */
    654  sdBuff.lpDescription = (char*)GENSRV_DESCRIPTION;
    655  service_fns.ChangeServiceConfig2A_fn(hService, SERVICE_CONFIG_DESCRIPTION,
    656                                       &sdBuff);
    657  printf("Service installed successfully\n");
    658 
    659  /* Start the service initially */
    660  nt_service_start(hService);
    661 
    662  service_fns.CloseServiceHandle_fn(hService);
    663  service_fns.CloseServiceHandle_fn(hSCManager);
    664  tor_free(command);
    665 
    666  return 0;
    667 }
    668 
    669 /** Removes the Tor NT service. Returns 0 if the service was successfully
    670 * removed, or -1 on error. */
    671 static int
    672 nt_service_remove(void)
    673 {
    674  SC_HANDLE hSCManager = NULL;
    675  SC_HANDLE hService = NULL;
    676  char *errmsg;
    677 
    678  nt_service_loadlibrary();
    679  if ((hSCManager = nt_service_open_scm()) == NULL)
    680    return -1;
    681  if ((hService = nt_service_open(hSCManager)) == NULL) {
    682    service_fns.CloseServiceHandle_fn(hSCManager);
    683    return -1;
    684  }
    685 
    686  nt_service_stop(hService);
    687  if (service_fns.DeleteService_fn(hService) == FALSE) {
    688    errmsg = format_win32_error(GetLastError());
    689    printf("DeleteService() failed : %s\n", errmsg);
    690    tor_free(errmsg);
    691    service_fns.CloseServiceHandle_fn(hService);
    692    service_fns.CloseServiceHandle_fn(hSCManager);
    693    return -1;
    694  }
    695 
    696  service_fns.CloseServiceHandle_fn(hService);
    697  service_fns.CloseServiceHandle_fn(hSCManager);
    698  printf("Service removed successfully\n");
    699 
    700  return 0;
    701 }
    702 
    703 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
    704 static int
    705 nt_service_cmd_start(void)
    706 {
    707  SC_HANDLE hSCManager;
    708  SC_HANDLE hService;
    709  int start;
    710 
    711  if ((hSCManager = nt_service_open_scm()) == NULL)
    712    return -1;
    713  if ((hService = nt_service_open(hSCManager)) == NULL) {
    714    service_fns.CloseServiceHandle_fn(hSCManager);
    715    return -1;
    716  }
    717 
    718  start = nt_service_start(hService);
    719  service_fns.CloseServiceHandle_fn(hService);
    720  service_fns.CloseServiceHandle_fn(hSCManager);
    721 
    722  return start;
    723 }
    724 
    725 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
    726 static int
    727 nt_service_cmd_stop(void)
    728 {
    729  SC_HANDLE hSCManager;
    730  SC_HANDLE hService;
    731  int stop;
    732 
    733  if ((hSCManager = nt_service_open_scm()) == NULL)
    734    return -1;
    735  if ((hService = nt_service_open(hSCManager)) == NULL) {
    736    service_fns.CloseServiceHandle_fn(hSCManager);
    737    return -1;
    738  }
    739 
    740  stop = nt_service_stop(hService);
    741  service_fns.CloseServiceHandle_fn(hService);
    742  service_fns.CloseServiceHandle_fn(hSCManager);
    743 
    744  return stop;
    745 }
    746 
    747 int
    748 nt_service_parse_options(int argc, char **argv, int *should_exit)
    749 {
    750  backup_argv = argv;
    751  backup_argc = argc;
    752  *should_exit = 0;
    753 
    754  if ((argc >= 3) &&
    755      (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) {
    756    nt_service_loadlibrary();
    757    *should_exit = 1;
    758    if (!strcmp(argv[2], "install"))
    759      return nt_service_install(argc, argv);
    760    if (!strcmp(argv[2], "remove"))
    761      return nt_service_remove();
    762    if (!strcmp(argv[2], "start"))
    763      return nt_service_cmd_start();
    764    if (!strcmp(argv[2], "stop"))
    765      return nt_service_cmd_stop();
    766    printf("Unrecognized service command '%s'\n", argv[2]);
    767    return 1;
    768  }
    769  if (argc >= 2) {
    770    if (!strcmp(argv[1], "-nt-service") || !strcmp(argv[1], "--nt-service")) {
    771      nt_service_loadlibrary();
    772      nt_service_main();
    773      *should_exit = 1;
    774      return 0;
    775    }
    776    // These values have been deprecated since 0.1.1.2-alpha; we've warned
    777    // about them since 0.1.2.7-alpha.
    778    if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) {
    779      nt_service_loadlibrary();
    780      fprintf(stderr,
    781            "The %s option is deprecated; use \"--service install\" instead.",
    782            argv[1]);
    783      *should_exit = 1;
    784      return nt_service_install(argc, argv);
    785    }
    786    if (!strcmp(argv[1], "-remove") || !strcmp(argv[1], "--remove")) {
    787      nt_service_loadlibrary();
    788      fprintf(stderr,
    789            "The %s option is deprecated; use \"--service remove\" instead.",
    790            argv[1]);
    791      *should_exit = 1;
    792      return nt_service_remove();
    793    }
    794  }
    795  *should_exit = 0;
    796  return 0;
    797 }
    798 
    799 #endif /* defined(_WIN32) */