int main(int argc, char **argv) { /* Remember if we are admin */ check_admin(); /* Elevate */ if (argc > 1) { /* Valid commands are install or remove */ if (str_equiv(argv[1], "install")) { if (! is_admin) { print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL); exit(100); } exit(pre_install_service(argc - 2, argv + 2)); } if (str_equiv(argv[1], "remove")) { if (! is_admin) { print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE); exit(100); } exit(pre_remove_service(argc - 2, argv + 2)); } } /* Thread local storage for error message buffer */ tls_index = TlsAlloc(); /* Register messages */ if (is_admin) create_messages(); /* Optimisation for Windows 2000: When we're run from the command line the StartServiceCtrlDispatcher() call will time out after a few seconds on Windows 2000. On newer versions the call returns instantly. Check for stdin first and only try to call the function if there's no input stream found. Although it's possible that we're running with input redirected it's much more likely that we're actually running as a service. This will save time when running with no arguments from a command prompt. */ if (_fileno(stdin) < 0) { /* Set up function pointers. */ if (get_imports()) exit(111); /* Start service magic */ SERVICE_TABLE_ENTRY table[] = { { NSSM, service_main }, { 0, 0 } }; if (! StartServiceCtrlDispatcher(table)) { unsigned long error = GetLastError(); /* User probably ran nssm with no argument */ if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) exit(usage(1)); log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DISPATCHER_FAILED, error_string(error), 0); free_imports(); exit(100); } } else exit(usage(1)); /* And nothing more to do */ exit(0); }
/* About to remove the service */ int pre_remove_service(int argc, char **argv) { /* Show dialogue box if we didn't pass service name and "confirm" */ if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]); if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]); print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE); return 100; }
static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { HKEY key = (HKEY) param; if (! key) return -1; long error; /* Resetting to default? */ if (! value || ! value->string) { if (default_value) value->string = (TCHAR *) default_value; else { error = RegDeleteValue(key, name); if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); return -1; } } if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) { error = RegDeleteValue(key, name); if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); return -1; } if (set_expand_string(key, (TCHAR *) name, value->string)) return -1; return 1; }
int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { SC_HANDLE service_handle = (SC_HANDLE) param; if (! service_handle) return -1; /* It makes no sense to try to reset the service type. */ if (! value || ! value->string) { print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name); return -1; } /* We can only manage services of type SERVICE_WIN32_OWN_PROCESS and SERVICE_INTERACTIVE_PROCESS. */ unsigned long type = SERVICE_WIN32_OWN_PROCESS; if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS; else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) { print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string); _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS); _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS); return -1; } /* ChangeServiceConfig() will fail if the service runs under an account other than LOCALSYSTEM and we try to make it interactive. */ if (type & SERVICE_INTERACTIVE_PROCESS) { QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); if (! qsc) return -1; if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) { HeapFree(GetProcessHeap(), 0, qsc); print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT); return -1; } HeapFree(GetProcessHeap(), 0, qsc); } if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) { print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); return -1; } return 1; }
static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { unsigned long exitcode; TCHAR *code; TCHAR action_string[ACTION_LEN]; if (additional) { /* Default action? */ if (is_default(additional)) code = 0; else { if (str_number(additional, &exitcode)) return -1; code = (TCHAR *) additional; } } HKEY key = open_registry(service_name, name, KEY_WRITE); if (! key) return -1; long error; int ret = 1; /* Resetting to default? */ if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string); else { if (code) { /* Delete explicit action. */ error = RegDeleteValue(key, code); RegCloseKey(key); if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error)); return -1; } else { /* Explicitly keep the default action. */ if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value); ret = 0; } } /* Validate the string. */ for (int i = 0; exit_action_strings[i]; i++) { if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) { if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0; if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) { print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError())); RegCloseKey(key); return -1; } RegCloseKey(key); return ret; } } print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string); for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]); return -1; }
static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { HKEY key = (HKEY) param; if (! key) return -1; long error; __int64 mask; __int64 system_affinity = 0LL; if (value && value->string) { DWORD_PTR affinity; if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0; if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL; else if (affinity_string_to_mask(value->string, &mask)) { print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1); return -1; } } else mask = 0LL; if (! mask) { error = RegDeleteValue(key, name); if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); return -1; } /* Canonicalise. */ TCHAR *canon = 0; if (affinity_mask_to_string(mask, &canon)) canon = value->string; __int64 effective_affinity = mask & system_affinity; if (effective_affinity != mask) { /* Requested CPUs did not intersect with available CPUs? */ if (! effective_affinity) mask = effective_affinity = system_affinity; TCHAR *system = 0; if (! affinity_mask_to_string(system_affinity, &system)) { TCHAR *effective = 0; if (! affinity_mask_to_string(effective_affinity, &effective)) { print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective); HeapFree(GetProcessHeap(), 0, effective); } HeapFree(GetProcessHeap(), 0, system); } } if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) { if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon); log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0); return -1; } if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon); return 1; }
int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { SC_HANDLE service_handle = (SC_HANDLE) param; if (! service_handle) return -1; /* It makes no sense to try to reset the startup type. */ if (! value || ! value->string) { print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name); return -1; } /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */ int service_startup = -1; int i; for (i = 0; startup_strings[i]; i++) { if (str_equiv(value->string, startup_strings[i])) { service_startup = i; break; } } if (service_startup < 0) { print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string); for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]); return -1; } unsigned long startup; switch (service_startup) { case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break; case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break; default: startup = SERVICE_AUTO_START; } if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) { print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); return -1; } SERVICE_DELAYED_AUTO_START_INFO delayed; ZeroMemory(&delayed, sizeof(delayed)); if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1; else delayed.fDelayedAutostart = 0; if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) { unsigned long error = GetLastError(); /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ if (error != ERROR_INVALID_LEVEL) { log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0); } } return 1; }
static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { HKEY key = (HKEY) param; if (! param) return -1; TCHAR *priority_string; int i; long error; if (value && value->string) priority_string = value->string; else if (default_value) priority_string = (TCHAR *) default_value; else { error = RegDeleteValue(key, name); if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); return -1; } for (i = 0; priority_strings[i]; i++) { if (! str_equiv(priority_strings[i], priority_string)) continue; if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) { error = RegDeleteValue(key, name); if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); return -1; } if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1; return 1; } print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string); for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]); return -1; }
int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { SC_HANDLE service_handle = (SC_HANDLE) param; if (! service_handle) return -1; TCHAR *displayname = 0; if (value && value->string) displayname = value->string; else displayname = (TCHAR *) service_name; if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) { print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); return -1; } /* If the display name and service name differ only in case, ChangeServiceConfig() will return success but the display name will be set to the service name, NOT the value passed to the function. This appears to be a quirk of Windows rather than a bug here. */ if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1; return 0; }
int _tmain(int argc, TCHAR **argv) { check_console(); #ifdef UNICODE /* Ensure we write in UTF-16 mode, so that non-ASCII characters don't get mangled. If we were compiled in ANSI mode it won't work. */ _setmode(_fileno(stdout), _O_U16TEXT); _setmode(_fileno(stderr), _O_U16TEXT); #endif /* Remember if we are admin */ check_admin(); /* Set up function pointers. */ if (get_imports()) exit(111); /* Remember our path for later. */ GetModuleFileName(0, unquoted_imagepath, _countof(unquoted_imagepath)); GetModuleFileName(0, imagepath, _countof(imagepath)); PathQuoteSpaces(imagepath); /* Elevate */ if (argc > 1) { /* Valid commands are: start, stop, pause, continue, install, edit, get, set, reset, unset, remove */ if (str_equiv(argv[1], _T("start"))) exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("stop"))) exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("restart"))) { int ret = control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2); if (ret) exit(ret); exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2)); } if (str_equiv(argv[1], _T("pause"))) exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("continue"))) exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("status"))) exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("rotate"))) exit(control_service(NSSM_SERVICE_CONTROL_ROTATE, argc - 2, argv + 2)); if (str_equiv(argv[1], _T("install"))) { if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL)); create_messages(); exit(pre_install_service(argc - 2, argv + 2)); } if (str_equiv(argv[1], _T("edit")) || str_equiv(argv[1], _T("get")) || str_equiv(argv[1], _T("set")) || str_equiv(argv[1], _T("reset")) || str_equiv(argv[1], _T("unset"))) { int ret = pre_edit_service(argc - 1, argv + 1); if (ret == 3 && ! is_admin && argc == 3) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT)); /* There might be a password here. */ for (int i = 0; i < argc; i++) SecureZeroMemory(argv[i], _tcslen(argv[i]) * sizeof(TCHAR)); exit(ret); } if (str_equiv(argv[1], _T("list"))) exit(list_nssm_services()); if (str_equiv(argv[1], _T("remove"))) { if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE)); exit(pre_remove_service(argc - 2, argv + 2)); } } /* Thread local storage for error message buffer */ tls_index = TlsAlloc(); /* Register messages */ if (is_admin) create_messages(); /* Optimisation for Windows 2000: When we're run from the command line the StartServiceCtrlDispatcher() call will time out after a few seconds on Windows 2000. On newer versions the call returns instantly. Check for stdin first and only try to call the function if there's no input stream found. Although it's possible that we're running with input redirected it's much more likely that we're actually running as a service. This will save time when running with no arguments from a command prompt. */ if (! GetStdHandle(STD_INPUT_HANDLE)) { /* Start service magic */ SERVICE_TABLE_ENTRY table[] = { { NSSM, service_main }, { 0, 0 } }; if (! StartServiceCtrlDispatcher(table)) { unsigned long error = GetLastError(); /* User probably ran nssm with no argument */ if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) exit(usage(1)); log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DISPATCHER_FAILED, error_string(error), 0); free_imports(); exit(100); } } else exit(usage(1)); /* And nothing more to do */ exit(0); }
bool valid_hook_name(const TCHAR *hook_event, const TCHAR *hook_action, bool quiet) { bool valid_event = false; bool valid_action = false; /* Exit/Post */ if (str_equiv(hook_event, NSSM_HOOK_EVENT_EXIT)) { if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; if (quiet) return false; print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); return false; } /* Power/{Change,Resume} */ if (str_equiv(hook_event, NSSM_HOOK_EVENT_POWER)) { if (str_equiv(hook_action, NSSM_HOOK_ACTION_CHANGE)) return true; if (str_equiv(hook_action, NSSM_HOOK_ACTION_RESUME)) return true; if (quiet) return false; print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_CHANGE); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_RESUME); return false; } /* Rotate/{Pre,Post} */ if (str_equiv(hook_event, NSSM_HOOK_EVENT_ROTATE)) { if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; if (quiet) return false; print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); return false; } /* Start/{Pre,Post} */ if (str_equiv(hook_event, NSSM_HOOK_EVENT_START)) { if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; if (quiet) return false; print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); return false; } /* Stop/Pre */ if (str_equiv(hook_event, NSSM_HOOK_EVENT_STOP)) { if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; if (quiet) return false; print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); return false; } if (quiet) return false; print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_EVENT); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_EXIT); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_POWER); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_ROTATE); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_START); _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_STOP); return false; }
/* Returns: NSSM_HOOK_STATUS_SUCCESS if the hook ran successfully. NSSM_HOOK_STATUS_NOTFOUND if no hook was found. NSSM_HOOK_STATUS_ABORT if the hook failed and we should cancel service start. NSSM_HOOK_STATUS_ERROR on error. NSSM_HOOK_STATUS_NOTRUN if the hook didn't run. NSSM_HOOK_STATUS_TIMEOUT if the hook timed out. NSSM_HOOK_STATUS_FAILED if the hook failed. */ int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline, bool async) { int ret = 0; hook_t *hook = (hook_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(hook_t)); if (! hook) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook"), _T("nssm_hook()"), 0); return NSSM_HOOK_STATUS_ERROR; } FILETIME now; GetSystemTimeAsFileTime(&now); EnterCriticalSection(&service->hook_section); /* Set the environment. */ if (service->env) duplicate_environment(service->env); if (service->env_extra) set_environment_block(service->env_extra); /* ABI version. */ TCHAR number[16]; _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), NSSM_HOOK_VERSION); SetEnvironmentVariable(NSSM_HOOK_ENV_VERSION, number); /* Event triggering this action. */ SetEnvironmentVariable(NSSM_HOOK_ENV_EVENT, hook_event); /* Hook action. */ SetEnvironmentVariable(NSSM_HOOK_ENV_ACTION, hook_action); /* Control triggering this action. May be empty. */ if (hook_control) SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, service_control_text(*hook_control)); else SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, _T("")); /* Last control handled. */ SetEnvironmentVariable(NSSM_HOOK_ENV_LAST_CONTROL, service_control_text(service->last_control)); /* Path to NSSM. */ TCHAR path[PATH_LENGTH]; GetModuleFileName(0, path, _countof(path)); SetEnvironmentVariable(NSSM_HOOK_ENV_IMAGE_PATH, path); /* NSSM version. */ SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_CONFIGURATION, NSSM_CONFIGURATION); SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_VERSION, NSSM_VERSION); SetEnvironmentVariable(NSSM_HOOK_ENV_BUILD_DATE, NSSM_DATE); /* NSSM PID. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), GetCurrentProcessId()); SetEnvironmentVariable(NSSM_HOOK_ENV_PID, number); /* NSSM runtime. */ set_hook_runtime(NSSM_HOOK_ENV_RUNTIME, &service->nssm_creation_time, &now); /* Application PID. */ if (service->pid) { _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->pid); SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, number); /* Application runtime. */ set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &now); /* Exit code. */ SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T("")); } else { SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, _T("")); if (str_equiv(hook_event, NSSM_HOOK_EVENT_START) && str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) { SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_RUNTIME, _T("")); SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T("")); } else { set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &service->exit_time); /* Exit code. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exitcode); SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, number); } } /* Deadline for this script. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), deadline); SetEnvironmentVariable(NSSM_HOOK_ENV_DEADLINE, number); /* Service name. */ SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_NAME, service->name); SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_DISPLAYNAME, service->displayname); /* Times the service was asked to start. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_requested_count); SetEnvironmentVariable(NSSM_HOOK_ENV_START_REQUESTED_COUNT, number); /* Times the service actually did start. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_count); SetEnvironmentVariable(NSSM_HOOK_ENV_START_COUNT, number); /* Times the service exited. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exit_count); SetEnvironmentVariable(NSSM_HOOK_ENV_EXIT_COUNT, number); /* Throttled count. */ _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->throttle); SetEnvironmentVariable(NSSM_HOOK_ENV_THROTTLE_COUNT, number); /* Command line. */ TCHAR app[CMD_LENGTH]; _sntprintf_s(app, _countof(app), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags); SetEnvironmentVariable(NSSM_HOOK_ENV_COMMAND_LINE, app); TCHAR cmd[CMD_LENGTH]; if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_HOOK_FAILED, hook_event, hook_action, service->name, 0); duplicate_environment_strings(service->initial_env); LeaveCriticalSection(&service->hook_section); HeapFree(GetProcessHeap(), 0, hook); return NSSM_HOOK_STATUS_ERROR; } /* No hook. */ if (! _tcslen(cmd)) { duplicate_environment_strings(service->initial_env); LeaveCriticalSection(&service->hook_section); HeapFree(GetProcessHeap(), 0, hook); return NSSM_HOOK_STATUS_NOTFOUND; } /* Run the command. */ STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); unsigned long flags = 0; #ifdef UNICODE flags |= CREATE_UNICODE_ENVIRONMENT; #endif ret = NSSM_HOOK_STATUS_NOTRUN; if (CreateProcess(0, cmd, 0, 0, false, flags, 0, service->dir, &si, &pi)) { hook->name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, HOOK_NAME_LENGTH * sizeof(TCHAR)); if (hook->name) _sntprintf_s(hook->name, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s (%s/%s)"), service->name, hook_event, hook_action); hook->process_handle = pi.hProcess; hook->pid = pi.dwProcessId; hook->deadline = deadline; if (get_process_creation_time(hook->process_handle, &hook->creation_time)) GetSystemTimeAsFileTime(&hook->creation_time); unsigned long tid; HANDLE thread_handle = CreateThread(NULL, 0, await_hook, (void *) hook, 0, &tid); if (thread_handle) { if (async) { ret = 0; await_hook_threads(hook_threads, service->status_handle, &service->status, 0); add_thread_handle(hook_threads, thread_handle, hook->name); } else { await_single_handle(service->status_handle, &service->status, thread_handle, hook->name, _T(__FUNCTION__), deadline + NSSM_SERVICE_STATUS_DEADLINE); unsigned long exitcode; GetExitCodeThread(thread_handle, &exitcode); ret = (int) exitcode; CloseHandle(thread_handle); } } else { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0); await_hook(hook); if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); HeapFree(GetProcessHeap(), 0, hook); } } else { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_HOOK_CREATEPROCESS_FAILED, hook_event, hook_action, service->name, cmd, error_string(GetLastError()), 0); HeapFree(GetProcessHeap(), 0, hook); } /* Restore our environment. */ duplicate_environment_strings(service->initial_env); LeaveCriticalSection(&service->hook_section); return ret; }
int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { SC_HANDLE service_handle = (SC_HANDLE) param; if (! service_handle) return -1; /* Logical syntax is: nssm set <service> ObjectName <username> <password> That means the username is actually passed in the additional parameter. */ bool localsystem = false; TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT; TCHAR *password = 0; if (additional) { username = (TCHAR *) additional; if (value && value->string) password = value->string; } else if (value && value->string) username = value->string; const TCHAR *well_known = well_known_username(username); size_t passwordsize = 0; if (well_known) { if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true; username = (TCHAR *) well_known; password = _T(""); } else if (! password) { /* We need a password if the account requires it. */ print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name); return -1; } else passwordsize = _tcslen(password) * sizeof(TCHAR); /* ChangeServiceConfig() will fail to set the username if the service is set to interact with the desktop. */ unsigned long type = SERVICE_NO_CHANGE; if (! localsystem) { QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); if (! qsc) { if (passwordsize) SecureZeroMemory(password, passwordsize); return -1; } type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS; HeapFree(GetProcessHeap(), 0, qsc); } if (! well_known) { if (grant_logon_as_service(username)) { if (passwordsize) SecureZeroMemory(password, passwordsize); print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); return -1; } } if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) { if (passwordsize) SecureZeroMemory(password, passwordsize); print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); return -1; } if (passwordsize) SecureZeroMemory(password, passwordsize); if (localsystem) return 0; return 1; }
/* Does the parameter refer to the default value of the setting? */ static inline int is_default(const TCHAR *value) { return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]); }