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->hook_share_output_handles) set_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, 1);
\r
170 else if (editing) RegDeleteValue(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES);
\r
171 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
172 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
173 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
174 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
175 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
176 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
177 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
178 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
179 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
180 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
181 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
182 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
183 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
184 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
187 if (service->env) {
\r
188 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
189 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
192 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
193 if (service->env_extra) {
\r
194 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
195 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
198 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
200 /* Close registry. */
\r
206 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
208 TCHAR registry[KEY_LENGTH];
\r
209 if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
214 /* Try to open the registry */
\r
216 unsigned long disposition;
\r
217 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
218 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
222 /* Do nothing if the key already existed */
\r
223 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
228 /* Create the default value */
\r
229 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
230 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
235 /* Close registry */
\r
241 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
242 unsigned long type = REG_MULTI_SZ;
\r
243 unsigned long envsize;
\r
247 /* Dummy test to find buffer size */
\r
248 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, &envsize);
\r
249 if (ret != ERROR_SUCCESS) {
\r
251 /* The service probably doesn't have any environment configured */
\r
252 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
253 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
257 if (type != REG_MULTI_SZ) {
\r
258 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
263 /* Minimum usable environment would be A= NULL NULL. */
\r
264 if (envsize < 4 * sizeof(TCHAR)) {
\r
269 /* Previously initialised? */
\r
270 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
272 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envsize);
\r
274 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
278 /* Actually get the strings. */
\r
279 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, &envsize);
\r
280 if (ret != ERROR_SUCCESS) {
\r
281 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
282 HeapFree(GetProcessHeap(), 0, *env);
\r
287 /* Value retrieved by RegQueryValueEx() is SIZE not COUNT. */
\r
288 *envlen = (unsigned long) environment_length(*env);
\r
294 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
295 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
297 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
301 ZeroMemory(data, datalen);
\r
303 unsigned long type = REG_EXPAND_SZ;
\r
304 unsigned long buflen = datalen;
\r
306 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
307 if (ret != ERROR_SUCCESS) {
\r
308 HeapFree(GetProcessHeap(), 0, buffer);
\r
310 if (ret == ERROR_FILE_NOT_FOUND) {
\r
311 if (! must_exist) return 0;
\r
314 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
318 /* Paths aren't allowed to contain quotes. */
\r
319 if (sanitise) PathUnquoteSpaces(buffer);
\r
321 /* Do we want to expand the string? */
\r
323 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
326 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
327 if (type != REG_EXPAND_SZ) {
\r
328 memmove(data, buffer, buflen);
\r
329 HeapFree(GetProcessHeap(), 0, buffer);
\r
333 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
334 if (! ret || ret > datalen) {
\r
335 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
336 HeapFree(GetProcessHeap(), 0, buffer);
\r
340 HeapFree(GetProcessHeap(), 0, buffer);
\r
344 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
345 return get_string(key, value, data, datalen, false, sanitise, true);
\r
348 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
349 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
352 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
353 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
357 Sets a string in the registry.
\r
358 Returns: 0 if it was set.
\r
361 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
362 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
363 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
364 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
368 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
369 return set_string(key, value, string, false);
\r
372 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
373 return set_string(key, value, string, true);
\r
377 Set an unsigned long in the registry.
\r
378 Returns: 0 if it was set.
\r
381 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
382 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
383 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
388 Query an unsigned long from the registry.
\r
389 Returns: 1 if a number was retrieved.
\r
390 0 if none was found and must_exist is false.
\r
391 -1 if none was found and must_exist is true.
\r
394 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
395 unsigned long type = REG_DWORD;
\r
396 unsigned long number_len = sizeof(unsigned long);
\r
398 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
399 if (ret == ERROR_SUCCESS) return 1;
\r
401 if (ret == ERROR_FILE_NOT_FOUND) {
\r
402 if (! must_exist) return 0;
\r
405 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
406 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
411 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
412 return get_number(key, value, number, true);
\r
415 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
416 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
417 unsigned long i, j;
\r
425 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
427 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
428 if (! *formatted) {
\r
433 for (i = 0, j = 0; i < dnlen; i++) {
\r
434 (*formatted)[j] = dn[i];
\r
437 (*formatted)[j] = _T('\r');
\r
438 (*formatted)[++j] = _T('\n');
\r
447 /* Strip CR and replace LF with NULL. */
\r
448 int unformat_double_null(TCHAR *formatted, unsigned long formattedlen, TCHAR **dn, unsigned long *newlen) {
\r
449 unsigned long i, j;
\r
452 /* Don't count trailing NULLs. */
\r
453 for (i = 0; i < formattedlen; i++) {
\r
454 if (! formatted[i]) {
\r
460 if (! formattedlen) {
\r
465 for (i = 0; i < formattedlen; i++) if (formatted[i] != _T('\r')) ++*newlen;
\r
467 /* Skip blank lines. */
\r
468 for (i = 0; i < formattedlen; i++) {
\r
469 if (formatted[i] == _T('\r') && formatted[i + 1] == _T('\n')) {
\r
470 /* This is the last CRLF. */
\r
471 if (i >= formattedlen - 2) break;
\r
474 Strip at the start of the block or if the next characters are
\r
477 if (! i || (formatted[i + 2] == _T('\r') && formatted[i + 3] == _T('\n'))) {
\r
478 for (j = i + 2; j < formattedlen; j++) formatted[j - 2] = formatted[j];
\r
479 formatted[formattedlen--] = _T('\0');
\r
480 formatted[formattedlen--] = _T('\0');
\r
487 /* Must end with two NULLs. */
\r
490 *dn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
491 if (! *dn) return 1;
\r
493 for (i = 0, j = 0; i < formattedlen; i++) {
\r
494 if (formatted[i] == _T('\r')) continue;
\r
495 if (formatted[i] == _T('\n')) (*dn)[j] = _T('\0');
\r
496 else (*dn)[j] = formatted[i];
\r
503 /* Copy a block. */
\r
504 int copy_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn) {
\r
505 if (! newdn) return 1;
\r
508 if (! dn) return 0;
\r
510 *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, dnlen * sizeof(TCHAR));
\r
512 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("dn"), _T("copy_double_null()"), 0);
\r
516 memmove(*newdn, dn, dnlen * sizeof(TCHAR));
\r
521 Create a new block with all the strings of the first block plus a new string.
\r
522 The new string may be specified as <key> <delimiter> <value> and the keylen
\r
523 gives the offset into the string to compare against existing entries.
\r
524 If the key is already present its value will be overwritten in place.
\r
525 If the key is blank or empty the new block will still be allocated and have
\r
528 int append_to_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *append, size_t keylen, bool case_sensitive) {
\r
529 if (! append || ! append[0]) return copy_double_null(dn, dnlen, newdn);
\r
530 size_t appendlen = _tcslen(append);
\r
531 int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;
\r
533 /* Identify the key, if any, or treat the whole string as the key. */
\r
535 if (! keylen || keylen > appendlen) keylen = appendlen;
\r
536 key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));
\r
538 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("append_to_double_null()"), 0);
\r
541 memmove(key, append, keylen * sizeof(TCHAR));
\r
542 key[keylen] = _T('\0');
\r
544 /* Find the length of the block not including any existing key. */
\r
547 for (s = dn; *s; s++) {
\r
548 if (fn(s, key, keylen)) len += _tcslen(s) + 1;
\r
552 /* Account for new entry. */
\r
553 len += _tcslen(append) + 1;
\r
555 /* Account for trailing NULL. */
\r
558 /* Allocate a new block. */
\r
559 *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
561 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("append_to_double_null()"), 0);
\r
562 HeapFree(GetProcessHeap(), 0, key);
\r
566 /* Copy existing entries.*/
\r
567 *newlen = (unsigned long) len;
\r
570 bool replaced = false;
\r
571 for (s = dn; *s; s++) {
\r
572 if (fn(s, key, keylen)) u = s;
\r
577 len = _tcslen(u) + 1;
\r
578 memmove(t, u, len * sizeof(TCHAR));
\r
583 /* Add the entry if it wasn't already replaced. The buffer was zeroed. */
\r
584 if (! replaced) memmove(t, append, _tcslen(append) * sizeof(TCHAR));
\r
586 HeapFree(GetProcessHeap(), 0, key);
\r
591 Create a new block with all the string of the first block minus the given
\r
593 The keylen parameter gives the offset into the string to compare against
\r
594 existing entries. If a substring of existing value matches the string to
\r
595 the given length it will be removed.
\r
596 If the last entry is removed the new block will still be allocated and
\r
597 have non-zero length.
\r
599 int remove_from_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *remove, size_t keylen, bool case_sensitive) {
\r
600 if (! remove || !remove[0]) return copy_double_null(dn, dnlen, newdn);
\r
601 size_t removelen = _tcslen(remove);
\r
602 int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;
\r
604 /* Identify the key, if any, or treat the whole string as the key. */
\r
606 if (! keylen || keylen > removelen) keylen = removelen;
\r
607 key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));
\r
609 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("remove_from_double_null()"), 0);
\r
612 memmove(key, remove, keylen * sizeof(TCHAR));
\r
613 key[keylen] = _T('\0');
\r
615 /* Find the length of the block not including any existing key. */
\r
618 for (s = dn; *s; s++) {
\r
619 if (fn(s, key, keylen)) len += _tcslen(s) + 1;
\r
623 /* Account for trailing NULL. */
\r
624 if (++len < 2) len = 2;
\r
626 /* Allocate a new block. */
\r
627 *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
629 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("remove_from_double_null()"), 0);
\r
630 HeapFree(GetProcessHeap(), 0, key);
\r
634 /* Copy existing entries.*/
\r
635 *newlen = (unsigned long) len;
\r
637 for (s = dn; *s; s++) {
\r
638 if (fn(s, key, keylen)) {
\r
639 len = _tcslen(s) + 1;
\r
640 memmove(t, s, len * sizeof(TCHAR));
\r
646 HeapFree(GetProcessHeap(), 0, key);
\r
650 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
651 unsigned long type = REG_DWORD;
\r
652 unsigned long buflen = sizeof(unsigned long);
\r
654 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
655 if (ret != ERROR_SUCCESS) {
\r
656 if (ret != ERROR_FILE_NOT_FOUND) {
\r
657 if (type != REG_DWORD) {
\r
658 TCHAR milliseconds[16];
\r
659 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
660 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
662 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
667 if (! ok) *buffer = default_value;
\r
670 /* Open the key of the service itself Services\<service_name>. */
\r
671 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
673 TCHAR registry[KEY_LENGTH];
\r
674 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
675 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
679 return open_registry_key(registry, sam, must_exist);
\r
682 /* Open a subkey of the service Services\<service_name>\<sub>. */
\r
683 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
685 TCHAR registry[KEY_LENGTH];
\r
686 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
687 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
691 return open_registry_key(registry, sam, key, must_exist);
\r
694 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
696 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
700 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
701 return open_registry(service_name, sub, sam, true);
\r
704 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
705 return open_registry(service_name, 0, sam, true);
\r
708 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
710 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
711 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
712 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
717 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
718 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
719 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
724 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
725 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
726 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
733 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
736 /* Try to open the registry */
\r
737 HKEY key = open_registry(service->name, KEY_READ);
\r
738 if (! key) return 1;
\r
740 /* Don't expand parameters when retrieving for the GUI. */
\r
741 bool expand = si ? true : false;
\r
743 /* Try to get environment variables - may fail */
\r
744 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
745 /* Environment variables to add to existing rather than replace - may fail. */
\r
746 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
748 /* Set environment if we are starting the service. */
\r
749 if (si) set_service_environment(service);
\r
751 /* Try to get executable file - MUST succeed */
\r
752 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
757 /* Try to get flags - may fail and we don't care */
\r
758 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
759 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
760 ZeroMemory(service->flags, sizeof(service->flags));
\r
763 /* Try to get startup directory - may fail and we fall back to a default */
\r
764 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
765 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
766 strip_basename(service->dir);
\r
767 if (service->dir[0] == _T('\0')) {
\r
769 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
770 if (! ret || ret > sizeof(service->dir)) {
\r
771 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
776 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
779 /* Try to get processor affinity - may fail. */
\r
781 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
782 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
783 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
784 service->affinity = 0LL;
\r
787 DWORD_PTR affinity, system_affinity;
\r
789 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
790 _int64 effective_affinity = service->affinity & system_affinity;
\r
791 if (effective_affinity != service->affinity) {
\r
793 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
794 TCHAR *effective = 0;
\r
795 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
796 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
798 HeapFree(GetProcessHeap(), 0, effective);
\r
800 HeapFree(GetProcessHeap(), 0, system);
\r
805 /* Try to get priority - may fail. */
\r
806 unsigned long priority;
\r
807 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
808 if (priority == (priority & priority_mask())) service->priority = priority;
\r
809 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
812 /* Try to get hook I/O sharing - may fail. */
\r
813 unsigned long hook_share_output_handles;
\r
814 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
815 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
816 else service->hook_share_output_handles = false;
\r
818 else hook_share_output_handles = false;
\r
819 /* Try to get file rotation settings - may fail. */
\r
820 unsigned long rotate_files;
\r
821 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
822 if (rotate_files) service->rotate_files = true;
\r
823 else service->rotate_files = false;
\r
825 else service->rotate_files = false;
\r
826 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
827 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
828 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
830 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
831 /* Hook I/O sharing and online rotation need a pipe. */
\r
832 service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;
\r
833 service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;
\r
834 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
835 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
836 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
837 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
839 /* Try to get force new console setting - may fail. */
\r
840 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
842 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
843 TCHAR cwd[PATH_LENGTH];
\r
844 GetCurrentDirectory(_countof(cwd), cwd);
\r
845 SetCurrentDirectory(service->dir);
\r
847 /* Try to get stdout and stderr */
\r
848 if (get_io_parameters(service, key)) {
\r
849 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
851 SetCurrentDirectory(cwd);
\r
855 /* Change back in case the startup directory needs to be deleted. */
\r
856 SetCurrentDirectory(cwd);
\r
858 /* Try to get mandatory restart delay */
\r
859 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
861 /* Try to get throttle restart delay */
\r
862 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
864 /* Try to get service stop flags. */
\r
865 unsigned long type = REG_DWORD;
\r
866 unsigned long stop_method_skip;
\r
867 unsigned long buflen = sizeof(stop_method_skip);
\r
868 bool stop_ok = false;
\r
869 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
870 if (ret != ERROR_SUCCESS) {
\r
871 if (ret != ERROR_FILE_NOT_FOUND) {
\r
872 if (type != REG_DWORD) {
\r
873 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
875 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);
\r
878 else stop_ok = true;
\r
880 /* Try all methods except those requested to be skipped. */
\r
881 service->stop_method = ~0;
\r
882 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
884 /* Try to get kill delays - may fail. */
\r
885 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
886 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
887 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
889 /* Try to get process tree settings - may fail. */
\r
890 unsigned long kill_process_tree;
\r
891 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
892 if (kill_process_tree) service->kill_process_tree = true;
\r
893 else service->kill_process_tree = false;
\r
895 else service->kill_process_tree = true;
\r
897 /* Try to get default exit action. */
\r
898 bool default_action;
\r
899 service->default_exit_action = NSSM_EXIT_RESTART;
\r
900 TCHAR action_string[ACTION_LEN];
\r
901 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
902 for (int i = 0; exit_action_strings[i]; i++) {
\r
903 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
904 service->default_exit_action = i;
\r
910 /* Close registry */
\r
917 Sets the string for the exit action corresponding to the exit code.
\r
919 ret is a pointer to an unsigned long containing the exit code.
\r
920 If ret is NULL, we retrieve the default exit action unconditionally.
\r
922 action is a buffer which receives the string.
\r
924 default_action is a pointer to a bool which is set to false if there
\r
925 was an explicit string for the given exit code, or true if we are
\r
926 returning the default action.
\r
928 Returns: 0 on success.
\r
931 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
932 /* Are we returning the default action or a status-specific one? */
\r
933 *default_action = ! ret;
\r
935 /* Try to open the registry */
\r
936 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
937 if (! key) return 1;
\r
939 unsigned long type = REG_SZ;
\r
940 unsigned long action_len = ACTION_LEN;
\r
943 if (! ret) code[0] = _T('\0');
\r
944 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
946 return get_exit_action(service_name, 0, action, default_action);
\r
948 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
950 /* Try again with * as the key if an exit code was defined */
\r
951 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
955 /* Close registry */
\r
961 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
962 /* Try to open the registry */
\r
963 TCHAR registry[KEY_LENGTH];
\r
964 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
965 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
972 /* Don't create keys needlessly. */
\r
973 if (! _tcslen(cmd)) {
\r
974 key = open_registry(service_name, registry, KEY_READ, false);
\r
975 if (! key) return 0;
\r
976 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
978 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
981 key = open_registry(service_name, registry, KEY_WRITE);
\r
982 if (! key) return 1;
\r
985 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
987 error = RegDeleteValue(key, hook_action);
\r
988 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
991 /* Close registry */
\r
997 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
998 /* Try to open the registry */
\r
999 TCHAR registry[KEY_LENGTH];
\r
1000 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
1001 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
1005 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
1007 if (error == ERROR_FILE_NOT_FOUND) {
\r
1008 ZeroMemory(buffer, buflen);
\r
1014 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
1016 /* Close registry */
\r