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 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
672 TCHAR registry[KEY_LENGTH];
\r
673 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
674 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
678 return open_registry_key(registry, sam, must_exist);
\r
681 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
683 TCHAR registry[KEY_LENGTH];
\r
684 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
685 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
689 return open_registry_key(registry, sam, key, must_exist);
\r
692 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
694 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
698 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
699 return open_registry(service_name, sub, sam, true);
\r
702 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
703 return open_registry(service_name, 0, sam, true);
\r
706 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
708 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
709 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
710 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
715 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
716 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
717 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
722 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
723 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
724 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
731 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
734 /* Try to open the registry */
\r
735 HKEY key = open_registry(service->name, KEY_READ);
\r
736 if (! key) return 1;
\r
738 /* Don't expand parameters when retrieving for the GUI. */
\r
739 bool expand = si ? true : false;
\r
741 /* Try to get environment variables - may fail */
\r
742 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
743 /* Environment variables to add to existing rather than replace - may fail. */
\r
744 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
746 /* Set environment if we are starting the service. */
\r
747 if (si) set_service_environment(service);
\r
749 /* Try to get executable file - MUST succeed */
\r
750 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
755 /* Try to get flags - may fail and we don't care */
\r
756 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
757 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
758 ZeroMemory(service->flags, sizeof(service->flags));
\r
761 /* Try to get startup directory - may fail and we fall back to a default */
\r
762 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
763 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
764 strip_basename(service->dir);
\r
765 if (service->dir[0] == _T('\0')) {
\r
767 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
768 if (! ret || ret > sizeof(service->dir)) {
\r
769 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
774 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
777 /* Try to get processor affinity - may fail. */
\r
779 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
780 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
781 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
782 service->affinity = 0LL;
\r
785 DWORD_PTR affinity, system_affinity;
\r
787 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
788 _int64 effective_affinity = service->affinity & system_affinity;
\r
789 if (effective_affinity != service->affinity) {
\r
791 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
792 TCHAR *effective = 0;
\r
793 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
794 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
796 HeapFree(GetProcessHeap(), 0, effective);
\r
798 HeapFree(GetProcessHeap(), 0, system);
\r
803 /* Try to get priority - may fail. */
\r
804 unsigned long priority;
\r
805 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
806 if (priority == (priority & priority_mask())) service->priority = priority;
\r
807 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
810 /* Try to get hook I/O sharing - may fail. */
\r
811 unsigned long hook_share_output_handles;
\r
812 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
813 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
814 else service->hook_share_output_handles = false;
\r
816 else hook_share_output_handles = false;
\r
817 /* Try to get file rotation settings - may fail. */
\r
818 unsigned long rotate_files;
\r
819 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
820 if (rotate_files) service->rotate_files = true;
\r
821 else service->rotate_files = false;
\r
823 else service->rotate_files = false;
\r
824 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
825 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
826 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
828 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
829 /* Hook I/O sharing and online rotation need a pipe. */
\r
830 service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;
\r
831 service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;
\r
832 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
833 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
834 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
835 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
837 /* Try to get force new console setting - may fail. */
\r
838 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
840 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
841 TCHAR cwd[PATH_LENGTH];
\r
842 GetCurrentDirectory(_countof(cwd), cwd);
\r
843 SetCurrentDirectory(service->dir);
\r
845 /* Try to get stdout and stderr */
\r
846 if (get_io_parameters(service, key)) {
\r
847 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
849 SetCurrentDirectory(cwd);
\r
853 /* Change back in case the startup directory needs to be deleted. */
\r
854 SetCurrentDirectory(cwd);
\r
856 /* Try to get mandatory restart delay */
\r
857 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
859 /* Try to get throttle restart delay */
\r
860 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
862 /* Try to get service stop flags. */
\r
863 unsigned long type = REG_DWORD;
\r
864 unsigned long stop_method_skip;
\r
865 unsigned long buflen = sizeof(stop_method_skip);
\r
866 bool stop_ok = false;
\r
867 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
868 if (ret != ERROR_SUCCESS) {
\r
869 if (ret != ERROR_FILE_NOT_FOUND) {
\r
870 if (type != REG_DWORD) {
\r
871 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
873 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);
\r
876 else stop_ok = true;
\r
878 /* Try all methods except those requested to be skipped. */
\r
879 service->stop_method = ~0;
\r
880 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
882 /* Try to get kill delays - may fail. */
\r
883 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
884 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
885 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
887 /* Try to get process tree settings - may fail. */
\r
888 unsigned long kill_process_tree;
\r
889 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
890 if (kill_process_tree) service->kill_process_tree = true;
\r
891 else service->kill_process_tree = false;
\r
893 else service->kill_process_tree = true;
\r
895 /* Try to get default exit action. */
\r
896 bool default_action;
\r
897 service->default_exit_action = NSSM_EXIT_RESTART;
\r
898 TCHAR action_string[ACTION_LEN];
\r
899 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
900 for (int i = 0; exit_action_strings[i]; i++) {
\r
901 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
902 service->default_exit_action = i;
\r
908 /* Close registry */
\r
915 Sets the string for the exit action corresponding to the exit code.
\r
917 ret is a pointer to an unsigned long containing the exit code.
\r
918 If ret is NULL, we retrieve the default exit action unconditionally.
\r
920 action is a buffer which receives the string.
\r
922 default_action is a pointer to a bool which is set to false if there
\r
923 was an explicit string for the given exit code, or true if we are
\r
924 returning the default action.
\r
926 Returns: 0 on success.
\r
929 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
930 /* Are we returning the default action or a status-specific one? */
\r
931 *default_action = ! ret;
\r
933 /* Try to open the registry */
\r
934 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
935 if (! key) return 1;
\r
937 unsigned long type = REG_SZ;
\r
938 unsigned long action_len = ACTION_LEN;
\r
941 if (! ret) code[0] = _T('\0');
\r
942 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
944 return get_exit_action(service_name, 0, action, default_action);
\r
946 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
948 /* Try again with * as the key if an exit code was defined */
\r
949 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
953 /* Close registry */
\r
959 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
960 /* Try to open the registry */
\r
961 TCHAR registry[KEY_LENGTH];
\r
962 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
963 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
970 /* Don't create keys needlessly. */
\r
971 if (! _tcslen(cmd)) {
\r
972 key = open_registry(service_name, registry, KEY_READ, false);
\r
973 if (! key) return 0;
\r
974 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
976 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
979 key = open_registry(service_name, registry, KEY_WRITE);
\r
980 if (! key) return 1;
\r
983 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
985 error = RegDeleteValue(key, hook_action);
\r
986 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
989 /* Close registry */
\r
995 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
996 /* Try to open the registry */
\r
997 TCHAR registry[KEY_LENGTH];
\r
998 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
999 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
1003 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
1005 if (error == ERROR_FILE_NOT_FOUND) {
\r
1006 ZeroMemory(buffer, buflen);
\r
1012 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
1014 /* Close registry */
\r