3 extern const TCHAR *exit_action_strings[];
\r
5 static int service_registry_path(const TCHAR *service_name, bool parameters, const TCHAR *sub, TCHAR *buffer, unsigned long buflen) {
\r
9 if (sub) ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS _T("\\%s"), service_name, sub);
\r
10 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS, service_name);
\r
12 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY, service_name);
\r
17 static long open_registry_key(const TCHAR *registry, REGSAM sam, HKEY *key, bool must_exist) {
\r
20 if (sam & KEY_SET_VALUE) {
\r
21 error = RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, key, 0);
\r
22 if (error != ERROR_SUCCESS) {
\r
24 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
29 error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, key);
\r
30 if (error != ERROR_SUCCESS) {
\r
32 if (error != ERROR_FILE_NOT_FOUND || must_exist) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
39 static HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {
\r
41 long error = open_registry_key(registry, sam, &key, must_exist);
\r
45 int create_messages() {
\r
48 TCHAR registry[KEY_LENGTH];
\r
49 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {
\r
50 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);
\r
54 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {
\r
55 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
59 /* Get path of this program */
\r
60 const TCHAR *path = nssm_unquoted_imagepath();
\r
62 /* Try to register the module but don't worry so much on failure */
\r
63 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
64 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
65 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
70 long enumerate_registry_values(HKEY key, unsigned long *index, TCHAR *name, unsigned long namelen) {
\r
72 unsigned long datalen = namelen;
\r
73 long error = RegEnumValue(key, *index, name, &datalen, 0, &type, 0, 0);
\r
74 if (error == ERROR_SUCCESS) ++*index;
\r
78 int create_parameters(nssm_service_t *service, bool editing) {
\r
79 /* Try to open the registry */
\r
80 HKEY key = open_registry(service->name, KEY_WRITE);
\r
81 if (! key) return 1;
\r
83 /* Remember parameters in case we need to delete them. */
\r
84 TCHAR registry[KEY_LENGTH];
\r
85 int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));
\r
87 /* Try to create the parameters */
\r
88 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
89 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
93 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
94 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
98 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
99 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
104 /* Other non-default parameters. May fail. */
\r
105 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
106 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
107 if (service->affinity) {
\r
109 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
110 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
111 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
112 HeapFree(GetProcessHeap(), 0, string);
\r
116 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
118 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
119 unsigned long stop_method_skip = ~service->stop_method;
\r
120 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
121 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
122 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
123 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
124 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
125 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
126 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
127 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
128 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
129 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
130 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
131 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
132 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
133 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
134 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
135 if (service->stdin_path[0] || editing) {
\r
136 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
137 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
138 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
139 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
140 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
141 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
142 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
143 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
145 if (service->stdout_path[0] || editing) {
\r
146 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
147 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
148 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
149 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
150 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
151 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
152 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
153 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
154 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
155 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
157 if (service->stderr_path[0] || editing) {
\r
158 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
159 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
160 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
161 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
162 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
163 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
164 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
165 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
166 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
167 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
169 if (service->timestamp_log) set_number(key, NSSM_REG_TIMESTAMP_LOG, 1);
\r
170 else if (editing) RegDeleteValue(key, NSSM_REG_TIMESTAMP_LOG);
\r
171 if (service->hook_share_output_handles) set_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, 1);
\r
172 else if (editing) RegDeleteValue(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES);
\r
173 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
174 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
175 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
176 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
177 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
178 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
179 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
180 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
181 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
182 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
183 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
184 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
185 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
186 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
189 if (service->env) {
\r
190 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
191 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
194 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
195 if (service->env_extra) {
\r
196 if (RegSetValueEx(key, NSSM_REG_ENV_EXTRA, 0, REG_MULTI_SZ, (const unsigned char *) service->env_extra, (unsigned long) service->env_extralen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
197 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
200 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
202 /* Close registry. */
\r
208 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
210 TCHAR registry[KEY_LENGTH];
\r
211 if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {
\r
212 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
216 /* Try to open the registry */
\r
218 unsigned long disposition;
\r
219 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
220 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
224 /* Do nothing if the key already existed */
\r
225 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
230 /* Create the default value */
\r
231 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
232 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
237 /* Close registry */
\r
243 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
244 unsigned long type = REG_MULTI_SZ;
\r
245 unsigned long envsize;
\r
249 /* Dummy test to find buffer size */
\r
250 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, &envsize);
\r
251 if (ret != ERROR_SUCCESS) {
\r
253 /* The service probably doesn't have any environment configured */
\r
254 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
255 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
259 if (type != REG_MULTI_SZ) {
\r
260 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
265 /* Minimum usable environment would be A= NULL NULL. */
\r
266 if (envsize < 4 * sizeof(TCHAR)) {
\r
271 /* Previously initialised? */
\r
272 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
274 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envsize);
\r
276 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
280 /* Actually get the strings. */
\r
281 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, &envsize);
\r
282 if (ret != ERROR_SUCCESS) {
\r
283 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
284 HeapFree(GetProcessHeap(), 0, *env);
\r
289 /* Value retrieved by RegQueryValueEx() is SIZE not COUNT. */
\r
290 *envlen = (unsigned long) environment_length(*env);
\r
296 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
297 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
299 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
303 ZeroMemory(data, datalen);
\r
305 unsigned long type = REG_EXPAND_SZ;
\r
306 unsigned long buflen = datalen;
\r
308 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
309 if (ret != ERROR_SUCCESS) {
\r
310 HeapFree(GetProcessHeap(), 0, buffer);
\r
312 if (ret == ERROR_FILE_NOT_FOUND) {
\r
313 if (! must_exist) return 0;
\r
316 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
320 /* Paths aren't allowed to contain quotes. */
\r
321 if (sanitise) PathUnquoteSpaces(buffer);
\r
323 /* Do we want to expand the string? */
\r
325 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
328 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
329 if (type != REG_EXPAND_SZ) {
\r
330 memmove(data, buffer, buflen);
\r
331 HeapFree(GetProcessHeap(), 0, buffer);
\r
335 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
336 if (! ret || ret > datalen) {
\r
337 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
338 HeapFree(GetProcessHeap(), 0, buffer);
\r
342 HeapFree(GetProcessHeap(), 0, buffer);
\r
346 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
347 return get_string(key, value, data, datalen, false, sanitise, true);
\r
350 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
351 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
354 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
355 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
359 Sets a string in the registry.
\r
360 Returns: 0 if it was set.
\r
363 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
364 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
365 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
366 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
370 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
371 return set_string(key, value, string, false);
\r
374 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
375 return set_string(key, value, string, true);
\r
379 Set an unsigned long in the registry.
\r
380 Returns: 0 if it was set.
\r
383 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
384 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
385 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
390 Query an unsigned long from the registry.
\r
391 Returns: 1 if a number was retrieved.
\r
392 0 if none was found and must_exist is false.
\r
393 -1 if none was found and must_exist is true.
\r
396 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
397 unsigned long type = REG_DWORD;
\r
398 unsigned long number_len = sizeof(unsigned long);
\r
400 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
401 if (ret == ERROR_SUCCESS) return 1;
\r
403 if (ret == ERROR_FILE_NOT_FOUND) {
\r
404 if (! must_exist) return 0;
\r
407 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
408 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
413 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
414 return get_number(key, value, number, true);
\r
417 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
418 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
419 unsigned long i, j;
\r
427 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
429 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
430 if (! *formatted) {
\r
435 for (i = 0, j = 0; i < dnlen; i++) {
\r
436 (*formatted)[j] = dn[i];
\r
439 (*formatted)[j] = _T('\r');
\r
440 (*formatted)[++j] = _T('\n');
\r
449 /* Strip CR and replace LF with NULL. */
\r
450 int unformat_double_null(TCHAR *formatted, unsigned long formattedlen, TCHAR **dn, unsigned long *newlen) {
\r
451 unsigned long i, j;
\r
454 /* Don't count trailing NULLs. */
\r
455 for (i = 0; i < formattedlen; i++) {
\r
456 if (! formatted[i]) {
\r
462 if (! formattedlen) {
\r
467 for (i = 0; i < formattedlen; i++) if (formatted[i] != _T('\r')) ++*newlen;
\r
469 /* Skip blank lines. */
\r
470 for (i = 0; i < formattedlen; i++) {
\r
471 if (formatted[i] == _T('\r') && formatted[i + 1] == _T('\n')) {
\r
472 /* This is the last CRLF. */
\r
473 if (i >= formattedlen - 2) break;
\r
476 Strip at the start of the block or if the next characters are
\r
479 if (! i || (formatted[i + 2] == _T('\r') && formatted[i + 3] == _T('\n'))) {
\r
480 for (j = i + 2; j < formattedlen; j++) formatted[j - 2] = formatted[j];
\r
481 formatted[formattedlen--] = _T('\0');
\r
482 formatted[formattedlen--] = _T('\0');
\r
489 /* Must end with two NULLs. */
\r
492 *dn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
493 if (! *dn) return 1;
\r
495 for (i = 0, j = 0; i < formattedlen; i++) {
\r
496 if (formatted[i] == _T('\r')) continue;
\r
497 if (formatted[i] == _T('\n')) (*dn)[j] = _T('\0');
\r
498 else (*dn)[j] = formatted[i];
\r
505 /* Copy a block. */
\r
506 int copy_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn) {
\r
507 if (! newdn) return 1;
\r
510 if (! dn) return 0;
\r
512 *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, dnlen * sizeof(TCHAR));
\r
514 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("dn"), _T("copy_double_null()"), 0);
\r
518 memmove(*newdn, dn, dnlen * sizeof(TCHAR));
\r
523 Create a new block with all the strings of the first block plus a new string.
\r
524 The new string may be specified as <key> <delimiter> <value> and the keylen
\r
525 gives the offset into the string to compare against existing entries.
\r
526 If the key is already present its value will be overwritten in place.
\r
527 If the key is blank or empty the new block will still be allocated and have
\r
530 int append_to_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *append, size_t keylen, bool case_sensitive) {
\r
531 if (! append || ! append[0]) return copy_double_null(dn, dnlen, newdn);
\r
532 size_t appendlen = _tcslen(append);
\r
533 int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;
\r
535 /* Identify the key, if any, or treat the whole string as the key. */
\r
537 if (! keylen || keylen > appendlen) keylen = appendlen;
\r
538 key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));
\r
540 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("append_to_double_null()"), 0);
\r
543 memmove(key, append, keylen * sizeof(TCHAR));
\r
544 key[keylen] = _T('\0');
\r
546 /* Find the length of the block not including any existing key. */
\r
549 for (s = dn; *s; s++) {
\r
550 if (fn(s, key, keylen)) len += _tcslen(s) + 1;
\r
554 /* Account for new entry. */
\r
555 len += _tcslen(append) + 1;
\r
557 /* Account for trailing NULL. */
\r
560 /* Allocate a new block. */
\r
561 *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
563 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("append_to_double_null()"), 0);
\r
564 HeapFree(GetProcessHeap(), 0, key);
\r
568 /* Copy existing entries.*/
\r
569 *newlen = (unsigned long) len;
\r
572 bool replaced = false;
\r
573 for (s = dn; *s; s++) {
\r
574 if (fn(s, key, keylen)) u = s;
\r
579 len = _tcslen(u) + 1;
\r
580 memmove(t, u, len * sizeof(TCHAR));
\r
585 /* Add the entry if it wasn't already replaced. The buffer was zeroed. */
\r
586 if (! replaced) memmove(t, append, _tcslen(append) * sizeof(TCHAR));
\r
588 HeapFree(GetProcessHeap(), 0, key);
\r
593 Create a new block with all the string of the first block minus the given
\r
595 The keylen parameter gives the offset into the string to compare against
\r
596 existing entries. If a substring of existing value matches the string to
\r
597 the given length it will be removed.
\r
598 If the last entry is removed the new block will still be allocated and
\r
599 have non-zero length.
\r
601 int remove_from_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *remove, size_t keylen, bool case_sensitive) {
\r
602 if (! remove || !remove[0]) return copy_double_null(dn, dnlen, newdn);
\r
603 size_t removelen = _tcslen(remove);
\r
604 int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;
\r
606 /* Identify the key, if any, or treat the whole string as the key. */
\r
608 if (! keylen || keylen > removelen) keylen = removelen;
\r
609 key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));
\r
611 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("remove_from_double_null()"), 0);
\r
614 memmove(key, remove, keylen * sizeof(TCHAR));
\r
615 key[keylen] = _T('\0');
\r
617 /* Find the length of the block not including any existing key. */
\r
620 for (s = dn; *s; s++) {
\r
621 if (fn(s, key, keylen)) len += _tcslen(s) + 1;
\r
625 /* Account for trailing NULL. */
\r
626 if (++len < 2) len = 2;
\r
628 /* Allocate a new block. */
\r
629 *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
631 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("remove_from_double_null()"), 0);
\r
632 HeapFree(GetProcessHeap(), 0, key);
\r
636 /* Copy existing entries.*/
\r
637 *newlen = (unsigned long) len;
\r
639 for (s = dn; *s; s++) {
\r
640 if (fn(s, key, keylen)) {
\r
641 len = _tcslen(s) + 1;
\r
642 memmove(t, s, len * sizeof(TCHAR));
\r
648 HeapFree(GetProcessHeap(), 0, key);
\r
652 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
653 unsigned long type = REG_DWORD;
\r
654 unsigned long buflen = sizeof(unsigned long);
\r
656 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
657 if (ret != ERROR_SUCCESS) {
\r
658 if (ret != ERROR_FILE_NOT_FOUND) {
\r
659 if (type != REG_DWORD) {
\r
660 TCHAR milliseconds[16];
\r
661 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
662 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
664 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
669 if (! ok) *buffer = default_value;
\r
672 /* Open the key of the service itself Services\<service_name>. */
\r
673 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
675 TCHAR registry[KEY_LENGTH];
\r
676 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
677 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
681 return open_registry_key(registry, sam, must_exist);
\r
684 /* Open a subkey of the service Services\<service_name>\<sub>. */
\r
685 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
687 TCHAR registry[KEY_LENGTH];
\r
688 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
689 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
693 return open_registry_key(registry, sam, key, must_exist);
\r
696 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
698 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
702 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
703 return open_registry(service_name, sub, sam, true);
\r
706 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
707 return open_registry(service_name, 0, sam, true);
\r
710 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
712 if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS, 0)) {
\r
713 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
714 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
719 if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS, &service->stdout_copy_and_truncate)) {
\r
720 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
721 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
726 if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS, &service->stderr_copy_and_truncate)) {
\r
727 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
728 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
735 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
738 /* Try to open the registry */
\r
739 HKEY key = open_registry(service->name, KEY_READ);
\r
740 if (! key) return 1;
\r
742 /* Don't expand parameters when retrieving for the GUI. */
\r
743 bool expand = si ? true : false;
\r
745 /* Try to get environment variables - may fail */
\r
746 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
747 /* Environment variables to add to existing rather than replace - may fail. */
\r
748 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
750 /* Set environment if we are starting the service. */
\r
751 if (si) set_service_environment(service);
\r
753 /* Try to get executable file - MUST succeed */
\r
754 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
759 /* Try to get flags - may fail and we don't care */
\r
760 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
761 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
762 ZeroMemory(service->flags, sizeof(service->flags));
\r
765 /* Try to get startup directory - may fail and we fall back to a default */
\r
766 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
767 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
768 strip_basename(service->dir);
\r
769 if (service->dir[0] == _T('\0')) {
\r
771 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
772 if (! ret || ret > sizeof(service->dir)) {
\r
773 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
778 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
781 /* Try to get processor affinity - may fail. */
\r
783 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
784 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
785 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
786 service->affinity = 0LL;
\r
789 DWORD_PTR affinity, system_affinity;
\r
791 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
792 _int64 effective_affinity = service->affinity & system_affinity;
\r
793 if (effective_affinity != service->affinity) {
\r
795 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
796 TCHAR *effective = 0;
\r
797 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
798 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
800 HeapFree(GetProcessHeap(), 0, effective);
\r
802 HeapFree(GetProcessHeap(), 0, system);
\r
807 /* Try to get priority - may fail. */
\r
808 unsigned long priority;
\r
809 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
810 if (priority == (priority & priority_mask())) service->priority = priority;
\r
811 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
814 /* Try to get hook I/O sharing - may fail. */
\r
815 unsigned long hook_share_output_handles;
\r
816 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
817 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
818 else service->hook_share_output_handles = false;
\r
820 else hook_share_output_handles = false;
\r
821 /* Try to get file rotation settings - may fail. */
\r
822 unsigned long rotate_files;
\r
823 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
824 if (rotate_files) service->rotate_files = true;
\r
825 else service->rotate_files = false;
\r
827 else service->rotate_files = false;
\r
828 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
829 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
830 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
832 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
833 /* Log timestamping requires a logging thread.*/
\r
834 unsigned long timestamp_log;
\r
835 if (get_number(key, NSSM_REG_TIMESTAMP_LOG, ×tamp_log, false) == 1) {
\r
836 if (timestamp_log) service->timestamp_log = true;
\r
837 else service->timestamp_log = false;
\r
839 else service->timestamp_log = false;
\r
841 /* Hook I/O sharing and online rotation need a pipe. */
\r
842 service->use_stdout_pipe = service->rotate_stdout_online || service->timestamp_log || hook_share_output_handles;
\r
843 service->use_stderr_pipe = service->rotate_stderr_online || service->timestamp_log || hook_share_output_handles;
\r
844 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
845 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
846 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
847 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
849 /* Try to get force new console setting - may fail. */
\r
850 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
852 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
853 TCHAR cwd[PATH_LENGTH];
\r
854 GetCurrentDirectory(_countof(cwd), cwd);
\r
855 SetCurrentDirectory(service->dir);
\r
857 /* Try to get stdout and stderr */
\r
858 if (get_io_parameters(service, key)) {
\r
859 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
861 SetCurrentDirectory(cwd);
\r
865 /* Change back in case the startup directory needs to be deleted. */
\r
866 SetCurrentDirectory(cwd);
\r
868 /* Try to get mandatory restart delay */
\r
869 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
871 /* Try to get throttle restart delay */
\r
872 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
874 /* Try to get service stop flags. */
\r
875 unsigned long type = REG_DWORD;
\r
876 unsigned long stop_method_skip;
\r
877 unsigned long buflen = sizeof(stop_method_skip);
\r
878 bool stop_ok = false;
\r
879 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
880 if (ret != ERROR_SUCCESS) {
\r
881 if (ret != ERROR_FILE_NOT_FOUND) {
\r
882 if (type != REG_DWORD) {
\r
883 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
885 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);
\r
888 else stop_ok = true;
\r
890 /* Try all methods except those requested to be skipped. */
\r
891 service->stop_method = ~0;
\r
892 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
894 /* Try to get kill delays - may fail. */
\r
895 override_milliseconds(service->name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, &service->kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD);
\r
896 override_milliseconds(service->name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, &service->kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD);
\r
897 override_milliseconds(service->name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, &service->kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD);
\r
899 /* Try to get process tree settings - may fail. */
\r
900 unsigned long kill_process_tree;
\r
901 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
902 if (kill_process_tree) service->kill_process_tree = true;
\r
903 else service->kill_process_tree = false;
\r
905 else service->kill_process_tree = true;
\r
907 /* Try to get default exit action. */
\r
908 bool default_action;
\r
909 service->default_exit_action = NSSM_EXIT_RESTART;
\r
910 TCHAR action_string[ACTION_LEN];
\r
911 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
912 for (int i = 0; exit_action_strings[i]; i++) {
\r
913 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
914 service->default_exit_action = i;
\r
920 /* Close registry */
\r
927 Sets the string for the exit action corresponding to the exit code.
\r
929 ret is a pointer to an unsigned long containing the exit code.
\r
930 If ret is NULL, we retrieve the default exit action unconditionally.
\r
932 action is a buffer which receives the string.
\r
934 default_action is a pointer to a bool which is set to false if there
\r
935 was an explicit string for the given exit code, or true if we are
\r
936 returning the default action.
\r
938 Returns: 0 on success.
\r
941 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
942 /* Are we returning the default action or a status-specific one? */
\r
943 *default_action = ! ret;
\r
945 /* Try to open the registry */
\r
946 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
947 if (! key) return 1;
\r
949 unsigned long type = REG_SZ;
\r
950 unsigned long action_len = ACTION_LEN;
\r
953 if (! ret) code[0] = _T('\0');
\r
954 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
956 return get_exit_action(service_name, 0, action, default_action);
\r
958 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
960 /* Try again with * as the key if an exit code was defined */
\r
961 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
965 /* Close registry */
\r
971 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
972 /* Try to open the registry */
\r
973 TCHAR registry[KEY_LENGTH];
\r
974 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
975 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
982 /* Don't create keys needlessly. */
\r
983 if (! _tcslen(cmd)) {
\r
984 key = open_registry(service_name, registry, KEY_READ, false);
\r
985 if (! key) return 0;
\r
986 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
988 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
991 key = open_registry(service_name, registry, KEY_WRITE);
\r
992 if (! key) return 1;
\r
995 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
997 error = RegDeleteValue(key, hook_action);
\r
998 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
1001 /* Close registry */
\r
1007 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
1008 /* Try to open the registry */
\r
1009 TCHAR registry[KEY_LENGTH];
\r
1010 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
1011 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
1015 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
1017 if (error == ERROR_FILE_NOT_FOUND) {
\r
1018 ZeroMemory(buffer, buflen);
\r
1024 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
1026 /* Close registry */
\r