Fix valgrind error when marking a descriptor as never-downloadable.
[tor/rransom.git] / src / or / ntmain.c
blob2304be6526d1df1fbf8a766a785ffa8b9da613b0
1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2009, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
6 #define MAIN_PRIVATE
7 #include "or.h"
9 #include <tchar.h>
10 #define GENSRV_SERVICENAME TEXT("tor")
11 #define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service")
12 #define GENSRV_DESCRIPTION \
13 TEXT("Provides an anonymous Internet communication system")
14 #define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService")
16 // Cheating: using the pre-defined error codes, tricks Windows into displaying
17 // a semi-related human-readable error message if startup fails as
18 // opposed to simply scaring people with Error: 0xffffffff
19 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
21 static SERVICE_STATUS service_status;
22 static SERVICE_STATUS_HANDLE hStatus;
24 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
25 * is a job for arguments, not globals. Alas, some of the functions that
26 * use them use them need to have fixed signatures, so they can be passed
27 * to the NT service functions. */
28 static char **backup_argv;
29 static int backup_argc;
30 static char* nt_strerror(uint32_t errnum);
32 static void nt_service_control(DWORD request);
33 static void nt_service_body(int argc, char **argv);
34 static void nt_service_main(void);
35 static SC_HANDLE nt_service_open_scm(void);
36 static SC_HANDLE nt_service_open(SC_HANDLE hSCManager);
37 static int nt_service_start(SC_HANDLE hService);
38 static int nt_service_stop(SC_HANDLE hService);
39 static int nt_service_install(int argc, char **argv);
40 static int nt_service_remove(void);
41 static int nt_service_cmd_start(void);
42 static int nt_service_cmd_stop(void);
44 /** Struct to hold dynamically loaded NT-service related function pointers.
46 struct service_fns {
47 int loaded;
49 BOOL (WINAPI *ChangeServiceConfig2A_fn)(
50 SC_HANDLE hService,
51 DWORD dwInfoLevel,
52 LPVOID lpInfo);
54 BOOL (WINAPI *CloseServiceHandle_fn)(
55 SC_HANDLE hSCObject);
57 BOOL (WINAPI *ControlService_fn)(
58 SC_HANDLE hService,
59 DWORD dwControl,
60 LPSERVICE_STATUS lpServiceStatus);
62 SC_HANDLE (WINAPI *CreateServiceA_fn)(
63 SC_HANDLE hSCManager,
64 LPCTSTR lpServiceName,
65 LPCTSTR lpDisplayName,
66 DWORD dwDesiredAccess,
67 DWORD dwServiceType,
68 DWORD dwStartType,
69 DWORD dwErrorControl,
70 LPCTSTR lpBinaryPathName,
71 LPCTSTR lpLoadOrderGroup,
72 LPDWORD lpdwTagId,
73 LPCTSTR lpDependencies,
74 LPCTSTR lpServiceStartName,
75 LPCTSTR lpPassword);
77 BOOL (WINAPI *DeleteService_fn)(
78 SC_HANDLE hService);
80 SC_HANDLE (WINAPI *OpenSCManagerA_fn)(
81 LPCTSTR lpMachineName,
82 LPCTSTR lpDatabaseName,
83 DWORD dwDesiredAccess);
85 SC_HANDLE (WINAPI *OpenServiceA_fn)(
86 SC_HANDLE hSCManager,
87 LPCTSTR lpServiceName,
88 DWORD dwDesiredAccess);
90 BOOL (WINAPI *QueryServiceStatus_fn)(
91 SC_HANDLE hService,
92 LPSERVICE_STATUS lpServiceStatus);
94 SERVICE_STATUS_HANDLE (WINAPI *RegisterServiceCtrlHandlerA_fn)(
95 LPCTSTR lpServiceName,
96 LPHANDLER_FUNCTION lpHandlerProc);
98 BOOL (WINAPI *SetServiceStatus_fn)(SERVICE_STATUS_HANDLE,
99 LPSERVICE_STATUS);
101 BOOL (WINAPI *StartServiceCtrlDispatcherA_fn)(
102 const SERVICE_TABLE_ENTRY* lpServiceTable);
104 BOOL (WINAPI *StartServiceA_fn)(
105 SC_HANDLE hService,
106 DWORD dwNumServiceArgs,
107 LPCTSTR* lpServiceArgVectors);
109 BOOL (WINAPI *LookupAccountNameA_fn)(
110 LPCTSTR lpSystemName,
111 LPCTSTR lpAccountName,
112 PSID Sid,
113 LPDWORD cbSid,
114 LPTSTR ReferencedDomainName,
115 LPDWORD cchReferencedDomainName,
116 PSID_NAME_USE peUse);
117 } service_fns = { 0,
118 NULL, NULL, NULL, NULL, NULL, NULL,
119 NULL, NULL, NULL, NULL, NULL, NULL,
120 NULL};
122 /** Loads functions used by NT services. Returns on success, or prints a
123 * complaint to stdout and exits on error. */
124 static void
125 nt_service_loadlibrary(void)
127 HMODULE library = 0;
128 void *fn;
130 if (service_fns.loaded)
131 return;
133 /* XXXX Possibly, we should hardcode the location of this DLL. */
134 if (!(library = LoadLibrary("advapi32.dll"))) {
135 log_err(LD_GENERAL, "Couldn't open advapi32.dll. Are you trying to use "
136 "NT services on Windows 98? That doesn't work.");
137 goto err;
140 #define LOAD(f) STMT_BEGIN \
141 if (!(fn = GetProcAddress(library, #f))) { \
142 log_err(LD_BUG, \
143 "Couldn't find %s in advapi32.dll! We probably got the " \
144 "name wrong.", #f); \
145 goto err; \
146 } else { \
147 service_fns.f ## _fn = fn; \
149 STMT_END
151 LOAD(ChangeServiceConfig2A);
152 LOAD(CloseServiceHandle);
153 LOAD(ControlService);
154 LOAD(CreateServiceA);
155 LOAD(DeleteService);
156 LOAD(OpenSCManagerA);
157 LOAD(OpenServiceA);
158 LOAD(QueryServiceStatus);
159 LOAD(RegisterServiceCtrlHandlerA);
160 LOAD(SetServiceStatus);
161 LOAD(StartServiceCtrlDispatcherA);
162 LOAD(StartServiceA);
163 LOAD(LookupAccountNameA);
165 service_fns.loaded = 1;
167 return;
168 err:
169 printf("Unable to load library support for NT services: exiting.\n");
170 exit(1);
173 /** If we're compiled to run as an NT service, and the service wants to
174 * shut down, then change our current status and return 1. Else
175 * return 0.
178 nt_service_is_stopping(void)
179 /* XXXX this function would probably _love_ to be inline, in 0.2.0. */
181 /* If we haven't loaded the function pointers, we can't possibly be an NT
182 * service trying to shut down. */
183 if (!service_fns.loaded)
184 return 0;
186 if (service_status.dwCurrentState == SERVICE_STOP_PENDING) {
187 service_status.dwWin32ExitCode = 0;
188 service_status.dwCurrentState = SERVICE_STOPPED;
189 service_fns.SetServiceStatus_fn(hStatus, &service_status);
190 return 1;
191 } else if (service_status.dwCurrentState == SERVICE_STOPPED) {
192 return 1;
194 return 0;
197 /** Set the dwCurrentState field for our service to <b>state</b>. */
198 void
199 nt_service_set_state(DWORD state)
201 service_status.dwCurrentState = state;
204 /** Handles service control requests, such as stopping or starting the
205 * Tor service. */
206 static void
207 nt_service_control(DWORD request)
209 static struct timeval exit_now;
210 exit_now.tv_sec = 0;
211 exit_now.tv_usec = 0;
213 nt_service_loadlibrary();
215 switch (request) {
216 case SERVICE_CONTROL_STOP:
217 case SERVICE_CONTROL_SHUTDOWN:
218 log_notice(LD_GENERAL,
219 "Got stop/shutdown request; shutting down cleanly.");
220 service_status.dwCurrentState = SERVICE_STOP_PENDING;
221 event_loopexit(&exit_now);
222 return;
224 service_fns.SetServiceStatus_fn(hStatus, &service_status);
227 /** Called when the service is started via the system's service control
228 * manager. This calls tor_init() and starts the main event loop. If
229 * tor_init() fails, the service will be stopped and exit code set to
230 * NT_SERVICE_ERROR_TORINIT_FAILED. */
231 static void
232 nt_service_body(int argc, char **argv)
234 int r;
235 (void) argc; /* unused */
236 (void) argv; /* unused */
237 nt_service_loadlibrary();
238 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
239 service_status.dwCurrentState = SERVICE_START_PENDING;
240 service_status.dwControlsAccepted =
241 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
242 service_status.dwWin32ExitCode = 0;
243 service_status.dwServiceSpecificExitCode = 0;
244 service_status.dwCheckPoint = 0;
245 service_status.dwWaitHint = 1000;
246 hStatus = service_fns.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME,
247 (LPHANDLER_FUNCTION) nt_service_control);
249 if (hStatus == 0) {
250 /* Failed to register the service control handler function */
251 return;
254 r = tor_init(backup_argc, backup_argv);
255 if (r) {
256 /* Failed to start the Tor service */
257 r = NT_SERVICE_ERROR_TORINIT_FAILED;
258 service_status.dwCurrentState = SERVICE_STOPPED;
259 service_status.dwWin32ExitCode = r;
260 service_status.dwServiceSpecificExitCode = r;
261 service_fns.SetServiceStatus_fn(hStatus, &service_status);
262 return;
265 /* Set the service's status to SERVICE_RUNNING and start the main
266 * event loop */
267 service_status.dwCurrentState = SERVICE_RUNNING;
268 service_fns.SetServiceStatus_fn(hStatus, &service_status);
269 do_main_loop();
270 tor_cleanup();
273 /** Main service entry point. Starts the service control dispatcher and waits
274 * until the service status is set to SERVICE_STOPPED. */
275 static void
276 nt_service_main(void)
278 SERVICE_TABLE_ENTRY table[2];
279 DWORD result = 0;
280 char *errmsg;
281 nt_service_loadlibrary();
282 table[0].lpServiceName = (char*)GENSRV_SERVICENAME;
283 table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)nt_service_body;
284 table[1].lpServiceName = NULL;
285 table[1].lpServiceProc = NULL;
287 if (!service_fns.StartServiceCtrlDispatcherA_fn(table)) {
288 result = GetLastError();
289 errmsg = nt_strerror(result);
290 printf("Service error %d : %s\n", (int) result, errmsg);
291 LocalFree(errmsg);
292 if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
293 if (tor_init(backup_argc, backup_argv) < 0)
294 return;
295 switch (get_options()->command) {
296 case CMD_RUN_TOR:
297 do_main_loop();
298 break;
299 case CMD_LIST_FINGERPRINT:
300 case CMD_HASH_PASSWORD:
301 case CMD_VERIFY_CONFIG:
302 log_err(LD_CONFIG, "Unsupported command (--list-fingerint, "
303 "--hash-password, or --verify-config) in NT service.");
304 break;
305 case CMD_RUN_UNITTESTS:
306 default:
307 log_err(LD_CONFIG, "Illegal command number %d: internal error.",
308 get_options()->command);
310 tor_cleanup();
315 /** Return a handle to the service control manager on success, or NULL on
316 * failure. */
317 static SC_HANDLE
318 nt_service_open_scm(void)
320 SC_HANDLE hSCManager;
321 char *errmsg = NULL;
323 nt_service_loadlibrary();
324 if ((hSCManager = service_fns.OpenSCManagerA_fn(
325 NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) {
326 errmsg = nt_strerror(GetLastError());
327 printf("OpenSCManager() failed : %s\n", errmsg);
328 LocalFree(errmsg);
330 return hSCManager;
333 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
334 * on failure. */
335 static SC_HANDLE
336 nt_service_open(SC_HANDLE hSCManager)
338 SC_HANDLE hService;
339 char *errmsg = NULL;
340 nt_service_loadlibrary();
341 if ((hService = service_fns.OpenServiceA_fn(hSCManager, GENSRV_SERVICENAME,
342 SERVICE_ALL_ACCESS)) == NULL) {
343 errmsg = nt_strerror(GetLastError());
344 printf("OpenService() failed : %s\n", errmsg);
345 LocalFree(errmsg);
347 return hService;
350 /** Start the Tor service. Return 0 if the service is started or was
351 * previously running. Return -1 on error. */
352 static int
353 nt_service_start(SC_HANDLE hService)
355 char *errmsg = NULL;
357 nt_service_loadlibrary();
359 service_fns.QueryServiceStatus_fn(hService, &service_status);
360 if (service_status.dwCurrentState == SERVICE_RUNNING) {
361 printf("Service is already running\n");
362 return 0;
365 if (service_fns.StartServiceA_fn(hService, 0, NULL)) {
366 /* Loop until the service has finished attempting to start */
367 while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
368 (service_status.dwCurrentState == SERVICE_START_PENDING)) {
369 Sleep(500);
372 /* Check if it started successfully or not */
373 if (service_status.dwCurrentState == SERVICE_RUNNING) {
374 printf("Service started successfully\n");
375 return 0;
376 } else {
377 errmsg = nt_strerror(service_status.dwWin32ExitCode);
378 printf("Service failed to start : %s\n", errmsg);
379 LocalFree(errmsg);
381 } else {
382 errmsg = nt_strerror(GetLastError());
383 printf("StartService() failed : %s\n", errmsg);
384 LocalFree(errmsg);
386 return -1;
389 /** Stop the Tor service. Return 0 if the service is stopped or was not
390 * previously running. Return -1 on error. */
391 static int
392 nt_service_stop(SC_HANDLE hService)
394 /** Wait at most 10 seconds for the service to stop. */
395 #define MAX_SERVICE_WAIT_TIME 10
396 int wait_time;
397 char *errmsg = NULL;
398 nt_service_loadlibrary();
400 service_fns.QueryServiceStatus_fn(hService, &service_status);
401 if (service_status.dwCurrentState == SERVICE_STOPPED) {
402 printf("Service is already stopped\n");
403 return 0;
406 if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP,
407 &service_status)) {
408 wait_time = 0;
409 while (service_fns.QueryServiceStatus_fn(hService, &service_status) &&
410 (service_status.dwCurrentState != SERVICE_STOPPED) &&
411 (wait_time < MAX_SERVICE_WAIT_TIME)) {
412 Sleep(1000);
413 wait_time++;
415 if (service_status.dwCurrentState == SERVICE_STOPPED) {
416 printf("Service stopped successfully\n");
417 return 0;
418 } else if (wait_time == MAX_SERVICE_WAIT_TIME) {
419 printf("Service did not stop within %d seconds.\n", wait_time);
420 } else {
421 errmsg = nt_strerror(GetLastError());
422 printf("QueryServiceStatus() failed : %s\n",errmsg);
423 LocalFree(errmsg);
425 } else {
426 errmsg = nt_strerror(GetLastError());
427 printf("ControlService() failed : %s\n", errmsg);
428 LocalFree(errmsg);
430 return -1;
433 /** Build a formatted command line used for the NT service. Return a
434 * pointer to the formatted string on success, or NULL on failure. Set
435 * *<b>using_default_torrc</b> to true if we're going to use the default
436 * location to torrc, or 1 if an option was specified on the command line.
438 static char *
439 nt_service_command_line(int *using_default_torrc)
441 TCHAR tor_exe[MAX_PATH+1];
442 char *command, *options=NULL;
443 smartlist_t *sl;
444 int i, cmdlen;
445 *using_default_torrc = 1;
447 /* Get the location of tor.exe */
448 if (0 == GetModuleFileName(NULL, tor_exe, MAX_PATH))
449 return NULL;
451 /* Get the service arguments */
452 sl = smartlist_create();
453 for (i = 1; i < backup_argc; ++i) {
454 if (!strcmp(backup_argv[i], "--options") ||
455 !strcmp(backup_argv[i], "-options")) {
456 while (++i < backup_argc) {
457 if (!strcmp(backup_argv[i], "-f"))
458 *using_default_torrc = 0;
459 smartlist_add(sl, backup_argv[i]);
463 if (smartlist_len(sl))
464 options = smartlist_join_strings(sl,"\" \"",0,NULL);
465 smartlist_free(sl);
467 /* Allocate a string for the NT service command line */
468 cmdlen = strlen(tor_exe) + (options?strlen(options):0) + 32;
469 command = tor_malloc(cmdlen);
471 /* Format the service command */
472 if (options) {
473 if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service \"%s\"",
474 tor_exe, options)<0) {
475 tor_free(command); /* sets command to NULL. */
477 } else { /* ! options */
478 if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service", tor_exe)<0) {
479 tor_free(command); /* sets command to NULL. */
483 tor_free(options);
484 return command;
487 /** Creates a Tor NT service, set to start on boot. The service will be
488 * started if installation succeeds. Returns 0 on success, or -1 on
489 * failure. */
490 static int
491 nt_service_install(int argc, char **argv)
493 /* Notes about developing NT services:
495 * 1. Don't count on your CWD. If an absolute path is not given, the
496 * fopen() function goes wrong.
497 * 2. The parameters given to the nt_service_body() function differ
498 * from those given to main() function.
501 SC_HANDLE hSCManager = NULL;
502 SC_HANDLE hService = NULL;
503 SERVICE_DESCRIPTION sdBuff;
504 char *command;
505 char *errmsg;
506 const char *user_acct = GENSRV_USERACCT;
507 const char *password = "";
508 int i;
509 OSVERSIONINFOEX info;
510 SID_NAME_USE sidUse;
511 DWORD sidLen = 0, domainLen = 0;
512 int is_win2k_or_worse = 0;
513 int using_default_torrc = 0;
515 nt_service_loadlibrary();
517 /* Open the service control manager so we can create a new service */
518 if ((hSCManager = nt_service_open_scm()) == NULL)
519 return -1;
520 /* Build the command line used for the service */
521 if ((command = nt_service_command_line(&using_default_torrc)) == NULL) {
522 printf("Unable to build service command line.\n");
523 service_fns.CloseServiceHandle_fn(hSCManager);
524 return -1;
527 for (i=1; i < argc; ++i) {
528 if (!strcmp(argv[i], "--user") && i+1<argc) {
529 user_acct = argv[i+1];
530 ++i;
532 if (!strcmp(argv[i], "--password") && i+1<argc) {
533 password = argv[i+1];
534 ++i;
538 /* Compute our version and see whether we're running win2k or earlier. */
539 memset(&info, 0, sizeof(info));
540 info.dwOSVersionInfoSize = sizeof(info);
541 if (! GetVersionEx((LPOSVERSIONINFO)&info)) {
542 printf("Call to GetVersionEx failed.\n");
543 is_win2k_or_worse = 1;
544 } else {
545 if (info.dwMajorVersion < 5 ||
546 (info.dwMajorVersion == 5 && info.dwMinorVersion == 0))
547 is_win2k_or_worse = 1;
550 if (user_acct == GENSRV_USERACCT) {
551 if (is_win2k_or_worse) {
552 /* On Win2k, there is no LocalService account, so we actually need to
553 * fall back on NULL (the system account). */
554 printf("Running on Win2K or earlier, so the LocalService account "
555 "doesn't exist. Falling back to SYSTEM account.\n");
556 user_acct = NULL;
557 } else {
558 /* Genericity is apparently _so_ last year in Redmond, where some
559 * accounts are accounts that you can look up, and some accounts
560 * are magic and undetectable via the security subsystem. See
561 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
563 printf("Running on a Post-Win2K OS, so we'll assume that the "
564 "LocalService account exists.\n");
566 } else if (0 && service_fns.LookupAccountNameA_fn(NULL, // On this system
567 user_acct,
568 NULL, &sidLen, // Don't care about the SID
569 NULL, &domainLen, // Don't care about the domain
570 &sidUse) == 0) {
571 /* XXXX For some reason, the above test segfaults. Fix that. */
572 printf("User \"%s\" doesn't seem to exist.\n", user_acct);
573 return -1;
574 } else {
575 printf("Will try to install service as user \"%s\".\n", user_acct);
577 /* XXXX This warning could be better about explaining how to resolve the
578 * situation. */
579 if (using_default_torrc)
580 printf("IMPORTANT NOTE:\n"
581 " The Tor service will run under the account \"%s\". This means\n"
582 " that Tor will look for its configuration file under that\n"
583 " account's Application Data directory, which is probably not\n"
584 " the same as yours.\n", user_acct?user_acct:"<local system>");
586 /* Create the Tor service, set to auto-start on boot */
587 if ((hService = service_fns.CreateServiceA_fn(hSCManager, GENSRV_SERVICENAME,
588 GENSRV_DISPLAYNAME,
589 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
590 SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
591 command, NULL, NULL, NULL,
592 user_acct, password)) == NULL) {
593 errmsg = nt_strerror(GetLastError());
594 printf("CreateService() failed : %s\n", errmsg);
595 service_fns.CloseServiceHandle_fn(hSCManager);
596 LocalFree(errmsg);
597 tor_free(command);
598 return -1;
600 printf("Done with CreateService.\n");
602 /* Set the service's description */
603 sdBuff.lpDescription = (char*)GENSRV_DESCRIPTION;
604 service_fns.ChangeServiceConfig2A_fn(hService, SERVICE_CONFIG_DESCRIPTION,
605 &sdBuff);
606 printf("Service installed successfully\n");
608 /* Start the service initially */
609 nt_service_start(hService);
611 service_fns.CloseServiceHandle_fn(hService);
612 service_fns.CloseServiceHandle_fn(hSCManager);
613 tor_free(command);
615 return 0;
618 /** Removes the Tor NT service. Returns 0 if the service was successfully
619 * removed, or -1 on error. */
620 static int
621 nt_service_remove(void)
623 SC_HANDLE hSCManager = NULL;
624 SC_HANDLE hService = NULL;
625 char *errmsg;
627 nt_service_loadlibrary();
628 if ((hSCManager = nt_service_open_scm()) == NULL)
629 return -1;
630 if ((hService = nt_service_open(hSCManager)) == NULL) {
631 service_fns.CloseServiceHandle_fn(hSCManager);
632 return -1;
635 nt_service_stop(hService);
636 if (service_fns.DeleteService_fn(hService) == FALSE) {
637 errmsg = nt_strerror(GetLastError());
638 printf("DeleteService() failed : %s\n", errmsg);
639 LocalFree(errmsg);
640 service_fns.CloseServiceHandle_fn(hService);
641 service_fns.CloseServiceHandle_fn(hSCManager);
642 return -1;
645 service_fns.CloseServiceHandle_fn(hService);
646 service_fns.CloseServiceHandle_fn(hSCManager);
647 printf("Service removed successfully\n");
649 return 0;
652 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
653 static int
654 nt_service_cmd_start(void)
656 SC_HANDLE hSCManager;
657 SC_HANDLE hService;
658 int start;
660 if ((hSCManager = nt_service_open_scm()) == NULL)
661 return -1;
662 if ((hService = nt_service_open(hSCManager)) == NULL) {
663 service_fns.CloseServiceHandle_fn(hSCManager);
664 return -1;
667 start = nt_service_start(hService);
668 service_fns.CloseServiceHandle_fn(hService);
669 service_fns.CloseServiceHandle_fn(hSCManager);
671 return start;
674 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
675 static int
676 nt_service_cmd_stop(void)
678 SC_HANDLE hSCManager;
679 SC_HANDLE hService;
680 int stop;
682 if ((hSCManager = nt_service_open_scm()) == NULL)
683 return -1;
684 if ((hService = nt_service_open(hSCManager)) == NULL) {
685 service_fns.CloseServiceHandle_fn(hSCManager);
686 return -1;
689 stop = nt_service_stop(hService);
690 service_fns.CloseServiceHandle_fn(hService);
691 service_fns.CloseServiceHandle_fn(hSCManager);
693 return stop;
696 /** Given a Win32 error code, this attempts to make Windows
697 * return a human-readable error message. The char* returned
698 * is allocated by Windows, but should be freed with LocalFree()
699 * when finished with it. */
700 static char*
701 nt_strerror(uint32_t errnum)
703 char *msgbuf;
704 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
705 NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
706 (LPSTR)&msgbuf, 0, NULL);
707 return msgbuf;
711 nt_service_parse_options(int argc, char **argv, int *should_exit)
713 backup_argv = argv;
714 backup_argc = argc;
715 *should_exit = 0;
717 if ((argc >= 3) &&
718 (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) {
719 nt_service_loadlibrary();
720 if (!strcmp(argv[2], "install"))
721 return nt_service_install(argc, argv);
722 if (!strcmp(argv[2], "remove"))
723 return nt_service_remove();
724 if (!strcmp(argv[2], "start"))
725 return nt_service_cmd_start();
726 if (!strcmp(argv[2], "stop"))
727 return nt_service_cmd_stop();
728 printf("Unrecognized service command '%s'\n", argv[2]);
729 *should_exit = 1;
730 return 1;
732 if (argc >= 2) {
733 if (!strcmp(argv[1], "-nt-service") || !strcmp(argv[1], "--nt-service")) {
734 nt_service_loadlibrary();
735 nt_service_main();
736 *should_exit = 1;
737 return 0;
739 // These values have been deprecated since 0.1.1.2-alpha; we've warned
740 // about them since 0.1.2.7-alpha.
741 if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) {
742 nt_service_loadlibrary();
743 fprintf(stderr,
744 "The %s option is deprecated; use \"--service install\" instead.",
745 argv[1]);
746 *should_exit = 1;
747 return nt_service_install(argc, argv);
749 if (!strcmp(argv[1], "-remove") || !strcmp(argv[1], "--remove")) {
750 nt_service_loadlibrary();
751 fprintf(stderr,
752 "The %s option is deprecated; use \"--service remove\" instead.",
753 argv[1]);
754 *should_exit = 1;
755 return nt_service_remove();
758 *should_exit = 0;
759 return 0;