From: Iain Patterson Date: Mon, 23 Dec 2013 18:25:28 +0000 (+0000) Subject: Allow editing services. X-Git-Tag: v2.22~114 X-Git-Url: http://git.iain.cx/?p=nssm.git;a=commitdiff_plain;h=2c60e5334f6df07bf42e7a91cf59638453eca473 Allow editing services. "nssm edit " brings up a GUI to edit an existing service. Services not managed by NSSM can also be edited but only system properties such as the display name will appear in the GUI. --- diff --git a/ChangeLog.txt b/ChangeLog.txt index 911aa4d..004f597 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,7 @@ Changes since 2.21 ------------------ + * Existing services can now be edited using the GUI. + * NSSM can now optionally rotate existing files when redirecting I/O. diff --git a/README.txt b/README.txt index d650970..0a0c47c 100644 --- a/README.txt +++ b/README.txt @@ -58,6 +58,8 @@ Since version 2.22, NSSM can rotate existing output files when redirecting I/O. Since version 2.22, NSSM can set service display name, description, startup type and log on details. +Since version 2.22, NSSM can edit existing services with the GUI. + Usage ----- @@ -281,6 +283,21 @@ omit the VALUE but the = symbol is mandatory. srvany only supports AppEnvironment. +Managing services using the GUI +------------------------------- +NSSM can edit the settings of existing services with the same GUI that is +used to install them. Run + + nssm edit + +to bring up the GUI. + +NSSM offers limited editing capabilities for services other than those which +run NSSM itself. When NSSM is asked to edit a service which does not have +the App* registry settings described above, the GUI will allow editing only +system settings such as the service display name and description. + + Removing services using the GUI ------------------------------- NSSM can also remove services. Run diff --git a/gui.cpp b/gui.cpp index 240f62c..6fc5162 100644 --- a/gui.cpp +++ b/gui.cpp @@ -12,6 +12,9 @@ int nssm_gui(int resource, nssm_service_t *service) { return 1; } + /* Remember what the window is for. */ + SetWindowLongPtr(dlg, GWLP_USERDATA, (LONG_PTR) resource); + /* Display the window */ centre_window(dlg); ShowWindow(dlg, SW_SHOW); @@ -29,6 +32,126 @@ int nssm_gui(int resource, nssm_service_t *service) { } } + if (resource == IDD_EDIT) { + /* We'll need the service handle later. */ + SetWindowLongPtr(dlg, DWLP_USER, (LONG_PTR) service); + + /* Service name can't be edited. */ + EnableWindow(GetDlgItem(dlg, IDC_NAME), 0); + SetFocus(GetDlgItem(dlg, IDOK)); + + /* Set existing details. */ + HWND combo; + + /* Application tab. */ + if (service->native) SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->image); + else SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe); + SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir); + SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags); + + /* Details tab. */ + SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname); + SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description); + combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP); + SendMessage(combo, CB_SETCURSEL, service->startup, 0); + + /* Log on tab. */ + if (service->username) { + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_ACCOUNT); + SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), 1); + } + else { + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM); + if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0); + } + + /* Shutdown tab. */ + if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE), 0); + } + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, service->kill_console_delay, 0); + if (! (service->stop_method & NSSM_STOP_METHOD_WINDOW)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW), 0); + } + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, service->kill_window_delay, 0); + if (! (service->stop_method & NSSM_STOP_METHOD_THREADS)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS), 0); + } + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, service->kill_threads_delay, 0); + if (! (service->stop_method & NSSM_STOP_METHOD_TERMINATE)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_UNCHECKED, 0); + } + + /* Restart tab. */ + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0); + combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0); + + /* I/O tab. */ + SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path); + SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDOUT, service->stdout_path); + SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDERR, service->stderr_path); + + /* Rotation tab. */ + if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0); + if (service->rotate_files) { + SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), 1); + } + SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, service->rotate_seconds, 0); + if (! service->rotate_bytes_high) SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, service->rotate_bytes_low, 0); + + /* Check if advanced settings are in use. */ + if (service->stdout_disposition ^ service->stderr_disposition || service->stdout_disposition & ~CREATE_ALWAYS || service->stderr_disposition & ~CREATE_ALWAYS) popup_message(MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO); + if (service->rotate_bytes_high) popup_message(MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES); + + /* Environment tab. */ + TCHAR *env; + unsigned long envlen; + if (service->env_extralen) { + SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0); + env = service->env_extra; + envlen = service->env_extralen; + } + else { + env = service->env; + envlen = service->envlen; + } + + if (envlen) { + /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */ + unsigned long i, j; + unsigned long newlen = envlen; + for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) newlen++; + + TCHAR *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen * sizeof(TCHAR)); + if (formatted) { + for (i = 0, j = 0; i < envlen; i++) { + formatted[j] = env[i]; + if (! env[i]) { + if (env[i + 1]) { + formatted[j] = _T('\r'); + formatted[++j] = _T('\n'); + } + } + j++; + } + SetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, formatted); + HeapFree(GetProcessHeap(), 0, formatted); + } + else popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()")); + } + if (service->envlen && service->env_extralen) popup_message(MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT); + } + /* Go! */ MSG message; while (GetMessage(&message, 0, 0, 0)) { @@ -99,22 +222,26 @@ static inline void check_io(TCHAR *name, TCHAR *buffer, unsigned long len, unsig ZeroMemory(buffer, len * sizeof(TCHAR)); } -/* Install the service. */ -int install(HWND window) { - if (! window) return 1; +/* Set service parameters. */ +int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service) { + if (! service) return 1; - nssm_service_t *service = alloc_nssm_service(); - if (service) { - set_nssm_service_defaults(service); + set_nssm_service_defaults(service); - /* Get service name. */ - if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); - cleanup_nssm_service(service); - return 2; - } + if (orig_service) { + service->native = orig_service->native; + service->handle = orig_service->handle; + } - /* Get executable name */ + /* Get service name. */ + if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); + cleanup_nssm_service(service); + return 2; + } + + /* Get executable name */ + if (! service->native) { if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) { popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH); return 3; @@ -133,54 +260,74 @@ int install(HWND window) { return 4; } } + } - /* Get details. */ - if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) { - if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME); - return 5; - } + /* Get details. */ + if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) { + if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME); + return 5; } + } - if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) { - if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION); - return 5; - } + if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) { + if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION); + return 5; } + } - HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP); - service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); - if (service->startup == CB_ERR) service->startup = 0; + HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP); + service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); + if (service->startup == CB_ERR) service->startup = 0; - /* Get logon stuff. */ - if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) { - if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) { - service->type |= SERVICE_INTERACTIVE_PROCESS; - } + /* Get logon stuff. */ + if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) { + if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) { + service->type |= SERVICE_INTERACTIVE_PROCESS; } - else { - /* Username. */ - service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0); - if (! service->usernamelen) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME); - return 6; - } - service->usernamelen++; + if (service->username) HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + if (service->password) { + SecureZeroMemory(service->password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, service->password); + } + service->password = 0; + service->passwordlen = 0; + } + else { + /* Username. */ + service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0); + if (! service->usernamelen) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME); + return 6; + } + service->usernamelen++; - service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR)); - if (! service->username) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()")); - return 6; - } - if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) { - HeapFree(GetProcessHeap(), 0, service->username); - service->username = 0; - service->usernamelen = 0; - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME); - return 6; - } + service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR)); + if (! service->username) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()")); + return 6; + } + if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) { + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME); + return 6; + } + /* + Special case LOCALSYSTEM. + Ignore the password if we're editing and the username hasn't changed. + */ + if (str_equiv(service->username, NSSM_LOCALSYSTEM_ACCOUNT)) { + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + } + else if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username)) { /* Password. */ service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0); if (! service->passwordlen) { @@ -258,100 +405,116 @@ int install(HWND window) { return 6; } } + } - /* Get stop method stuff. */ - check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE); - check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW); - check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS); - check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE); - check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay); - check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay); - check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay); - - /* Get exit action stuff. */ - check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay); - combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); - service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); - if (service->default_exit_action == CB_ERR) service->default_exit_action = 0; + /* Remaining tabs are only for services we manage. */ + if (service->native) return 0; + + /* Get stop method stuff. */ + check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE); + check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW); + check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS); + check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE); + check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay); + check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay); + check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay); + + /* Get exit action stuff. */ + check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay); + combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); + if (service->default_exit_action == CB_ERR) service->default_exit_action = 0; + + /* Get I/O stuff. */ + check_io(_T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN); + check_io(_T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT); + check_io(_T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR); + + /* Override stdout and/or stderr. */ + if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS; + if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS; + } - /* Get I/O stuff. */ - check_io(_T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN); - check_io(_T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT); - check_io(_T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR); + /* Get rotation stuff. */ + if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + service->rotate_files = true; + check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds); + check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low); + } - /* Override stdout and/or stderr. */ - if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) { - if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS; - if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS; + /* Get environment. */ + unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0); + if (envlen) { + TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR)); + if (! env) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()")); + cleanup_nssm_service(service); + return 5; } - /* Get rotation stuff. */ - if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) { - service->rotate_files = true; - check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds); - check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low); + if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT); + HeapFree(GetProcessHeap(), 0, env); + cleanup_nssm_service(service); + return 5; } - /* Get environment. */ - unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0); - if (envlen) { - TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR)); - if (! env) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()")); - cleanup_nssm_service(service); - return 5; - } + /* Strip CR and replace LF with NULL. */ + unsigned long newlen = 0; + unsigned long i, j; + for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) newlen++; + /* Must end with two NULLs. */ + newlen += 2; - if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT); - HeapFree(GetProcessHeap(), 0, env); - cleanup_nssm_service(service); - return 5; - } + TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen * sizeof(TCHAR)); + if (! newenv) { + HeapFree(GetProcessHeap(), 0, env); + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()")); + cleanup_nssm_service(service); + return 5; + } - /* Strip CR and replace LF with NULL. */ - unsigned long newlen = 0; - unsigned long i, j; - for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) newlen++; - /* Must end with two NULLs. */ - newlen += 2; - - TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen * sizeof(TCHAR)); - if (! newenv) { - HeapFree(GetProcessHeap(), 0, env); - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()")); - cleanup_nssm_service(service); - return 5; - } + for (i = 0, j = 0; i < envlen; i++) { + if (env[i] == _T('\r')) continue; + if (env[i] == _T('\n')) newenv[j] = _T('\0'); + else newenv[j] = env[i]; + j++; + } - for (i = 0, j = 0; i < envlen; i++) { - if (env[i] == _T('\r')) continue; - if (env[i] == _T('\n')) newenv[j] = _T('\0'); - else newenv[j] = env[i]; - j++; - } + HeapFree(GetProcessHeap(), 0, env); + env = newenv; + envlen = newlen; + /* Test the environment is valid. */ + if (test_environment(env)) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT); HeapFree(GetProcessHeap(), 0, env); - env = newenv; - envlen = newlen; - - /* Test the environment is valid. */ - if (test_environment(env)) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT); - HeapFree(GetProcessHeap(), 0, env); - cleanup_nssm_service(service); - return 5; - } + cleanup_nssm_service(service); + return 5; + } - if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) { - service->env = env; - service->envlen = envlen; - } - else { - service->env_extra = env; - service->env_extralen = envlen; - } + if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + service->env = env; + service->envlen = envlen; } + else { + service->env_extra = env; + service->env_extralen = envlen; + } + } + + return 0; +} + +/* Install the service. */ +int install(HWND window) { + if (! window) return 1; + + nssm_service_t *service = alloc_nssm_service(); + if (service) { + int ret = configure(window, service, 0); + if (ret) return ret; } /* See if it works. */ @@ -440,6 +603,42 @@ int remove(HWND window) { return 0; } +int edit(HWND window, nssm_service_t *orig_service) { + if (! window) return 1; + + nssm_service_t *service = alloc_nssm_service(); + if (service) { + int ret = configure(window, service, orig_service); + if (ret) return ret; + } + + switch (edit_service(service, true)) { + case 1: + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()")); + cleanup_nssm_service(service); + return 1; + + case 3: + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM); + cleanup_nssm_service(service); + return 3; + + case 4: + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH); + cleanup_nssm_service(service); + return 4; + + case 6: + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED); + cleanup_nssm_service(service); + return 6; + } + + popup_message(MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name); + cleanup_nssm_service(service); + return 0; +} + static TCHAR *browse_filter(int message) { switch (message) { case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd"); @@ -606,9 +805,13 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) { /* Install/remove dialogue callback */ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { + nssm_service_t *service; + switch (message) { /* Creating the dialogue */ case WM_INITDIALOG: + service = (nssm_service_t *) l; + SetFocus(GetDlgItem(window, IDC_NAME)); HWND tabs; @@ -621,11 +824,19 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { ZeroMemory(&tab, sizeof(tab)); tab.mask = TCIF_TEXT; + selected_tab = 0; + /* Application tab. */ - tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION); + if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE); + else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION); tab.cchTextMax = (int) _tcslen(tab.pszText); SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab); - tablist[NSSM_TAB_APPLICATION] = CreateDialog(0, MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg); + if (service->native) { + tablist[NSSM_TAB_APPLICATION] = CreateDialog(0, MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg); + EnableWindow(tablist[NSSM_TAB_APPLICATION], 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0); + } + else tablist[NSSM_TAB_APPLICATION] = CreateDialog(0, MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg); ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW); /* Details tab. */ @@ -654,6 +865,9 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM); set_logon_enabled(0); + /* Remaining tabs are only for services we manage. */ + if (service->native) return 1; + /* Shutdown tab. */ tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN); tab.cchTextMax = (int) _tcslen(tab.pszText); @@ -712,8 +926,6 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { tablist[NSSM_TAB_ENVIRONMENT] = CreateDialog(0, MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg); ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE); - selected_tab = 0; - return 1; /* Tab change. */ @@ -732,12 +944,8 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0); if (selection != selected_tab) { ShowWindow(tablist[selected_tab], SW_HIDE); - /* - XXX: Sets focus to the service name which isn't ideal but is - better than leaving it in another tab. - */ ShowWindow(tablist[selection], SW_SHOWDEFAULT); - SetFocus(tablist[selection]); + SetFocus(GetDlgItem(window, IDOK)); selected_tab = selection; } return 1; @@ -750,7 +958,10 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { switch (LOWORD(w)) { /* OK button */ case IDOK: - if (! install(window)) PostQuitMessage(0); + if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) { + if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0); + } + else if (! install(window)) PostQuitMessage(0); break; /* Cancel button */ diff --git a/gui.h b/gui.h index 5cc9cea..a0ca9c6 100644 --- a/gui.h +++ b/gui.h @@ -8,8 +8,10 @@ int nssm_gui(int, nssm_service_t *); void centre_window(HWND); +int configure(HWND, nssm_service_t *, nssm_service_t *); int install(HWND); int remove(HWND); +int edit(HWND, nssm_service_t *); void browse(HWND); INT_PTR CALLBACK nssm_dlg(HWND, UINT, WPARAM, LPARAM); diff --git a/messages.mc b/messages.mc index 4304d98..1a36fe2 100644 --- a/messages.mc +++ b/messages.mc @@ -21,6 +21,10 @@ To install a service without confirmation: nssm install [] +To show service editing GUI: + + nssm edit + To show service removal GUI: nssm remove [] @@ -85,6 +89,19 @@ Language = Italian L'installazione di un servizio richiede privilegi di amministratore. . +MessageId = +1 +SymbolicName = NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT +Severity = Informational +Language = English +Administrator access is needed to edit a service. +. +Language = French +Les droits d'administrateur sont requis pour éditer un service. +. +Language = Italian +L'edizione di un servizio richiede privilegi di amministratore. +. + MessageId = +1 SymbolicName = NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE Severity = Informational @@ -124,6 +141,67 @@ Language = Italian Errore apertura Service Manager! . +MessageId = +1 +SymbolicName = NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED +Severity = Informational +Language = English +Error querying service %s! +QueryServiceConfig(): %s%0 +. +Language = French +Error querying service %s! +QueryServiceConfig(): %s%0 +. +Language = Italian +Error querying service %s! +QueryServiceConfig(): %s%0 +. + +MessageId = +1 +SymbolicName = NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED +Severity = Informational +Language = English +Error querying service %s! +QueryServiceConfig2(%s): %s%0 +. +Language = French +Error querying service %s! +QueryServiceConfig2(%s): %s%0 +. +Language = Italian +Error querying service %s! +QueryServiceConfig2(%s): %s%0 +. + +MessageId = +1 +SymbolicName = NSSM_MESSAGE_INVALID_SERVICE +Severity = Informational +Language = English +Service "%s" is not a valid %s service! +Executable is %s%0 +. +Language = French +Service "%s" is not a valid %s service! +Executable is %s%0 +. +Language = Italian +Service "%s" is not a valid %s service! +Executable is %s%0 +. + +MessageId = +1 +SymbolicName = NSSM_MESSAGE_CANNOT_EDIT +Severity = Informational +Language = English +Service "%s" is not a %s service! +. +Language = French +Service "%s" is not a %s service! +. +Language = Italian +Service "%s" is not a %s service! +. + MessageId = +1 SymbolicName = NSSM_MESSAGE_PATH_TOO_LONG Severity = Informational @@ -176,6 +254,22 @@ Language = Italian Errore creazione servizio! . +MessageId = +1 +SymbolicName = NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED +Severity = Informational +Language = English +Error editing service! +ChangeServiceConfig(): %s%0 +. +Language = French +Erreur à l'édition du service! +ChangeServiceConfig(): %s%0 +. +Language = Italian +Errore edizione servizio! +ChangeServiceConfig(): %s%0 +. + MessageId = +1 SymbolicName = NSSM_MESSAGE_CREATE_PARAMETERS_FAILED Severity = Informational @@ -241,6 +335,19 @@ Language = Italian Servizio "%s" rimosso correttamente! . +MessageId = +1 +SymbolicName = NSSM_MESSAGE_SERVICE_EDITED +Severity = Informational +Language = English +Service "%s" edited successfully! +. +Language = French +Le service "%s" a été édité avec succès! +. +Language = Italian +Servizio "%s" edizione correttamente! +. + MessageId = +1 SymbolicName = NSSM_GUI_CREATEDIALOG_FAILED Severity = Informational @@ -436,6 +543,19 @@ Impossibile impostare i parametri di avvio per il servizio! Eliminazione servizio in corso... . +MessageId = +1 +SymbolicName = NSSM_GUI_EDIT_PARAMETERS_FAILED +Severity = Informational +Language = English +Couldn't set startup parameters for the service! +. +Language = French +Impossible de régler les paramètres de démarrage pour le service! +. +Language = Italian +Impossibile impostare i parametri di avvio per il servizio! +. + MessageId = +1 SymbolicName = NSSM_GUI_ASK_REMOVE_SERVICE Severity = Informational @@ -549,6 +669,19 @@ Language = Italian Applicazione%0 . +MessageId = +1 +SymbolicName = NSSM_GUI_TAB_NATIVE +Severity = Informational +Language = English +Service%0 +. +Language = French +Service%0 +. +Language = Italian +Servizio%0 +. + MessageId = +1 SymbolicName = NSSM_GUI_TAB_DETAILS Severity = Informational @@ -744,6 +877,66 @@ Language = Italian Fake crash (pre-Vista)%0 . +MessageId = +1 +SymbolicName = NSSM_GUI_WARN_STDIO +Severity = Informational +Language = English +The service is configured with I/O redirection settings which cannot be +represented by this GUI's simplified set of options. Check the registry +after editing the service to confirm its I/O redirection settings. +. +Language = French +The service is configured with I/O redirection settings which cannot be +represented by this GUI's simplified set of options. Check the registry +after editing the service to confirm its I/O redirection settings. +. +Language = Italian +The service is configured with I/O redirection settings which cannot be +represented by this GUI's simplified set of options. Check the registry +after editing the service to confirm its I/O redirection settings. +. + +MessageId = +1 +SymbolicName = NSSM_GUI_WARN_ROTATE_BYTES +Severity = Informational +Language = English +The service is configured with a 64-bit file size threshold for file +rotation. This GUI can only display 32-bit settings. Check the registry +after editing the service to confirm its file rotation settings. +. +Language = French +The service is configured with a 64-bit file size threshold for file +rotation. This GUI can only display 32-bit settings. Check the registry +after editing the service to confirm its file rotation settings. +. +Language = Italian +The service is configured with a 64-bit file size threshold for file +rotation. This GUI can only display 32-bit settings. Check the registry +after editing the service to confirm its file rotation settings. +. + +MessageId = +1 +SymbolicName = NSSM_GUI_WARN_ENVIRONMENT +Severity = Informational +Language = English +The service is configured with a srvany-compatible environment block +for the application as well as an extra environment block. This GUI +can only display one such block. Editing the service will result in +one of the environment blocks being deleted. +. +Language = French +The service is configured with a srvany-compatible environment block +for the application as well as an extra environment block. This GUI +can only display one such block. Editing the service will result in +one of the environment blocks being deleted. +. +Language = Italian +The service is configured with a srvany-compatible environment block +for the application as well as an extra environment block. This GUI +can only display one such block. Editing the service will result in +one of the environment blocks being deleted. +. + MessageId = 1001 SymbolicName = NSSM_EVENT_DISPATCHER_FAILED Severity = Error diff --git a/nssm.cpp b/nssm.cpp index 86a1827..7c95611 100644 --- a/nssm.cpp +++ b/nssm.cpp @@ -45,7 +45,7 @@ int _tmain(int argc, TCHAR **argv) { /* Elevate */ if (argc > 1) { - /* Valid commands are install or remove */ + /* Valid commands are install, edit or remove */ if (str_equiv(argv[1], _T("install"))) { if (! is_admin) { print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL); @@ -53,6 +53,13 @@ int _tmain(int argc, TCHAR **argv) { } exit(pre_install_service(argc - 2, argv + 2)); } + if (str_equiv(argv[1], _T("edit"))) { + if (! is_admin) { + print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT); + exit(100); + } + exit(pre_edit_service(argc - 2, argv + 2)); + } if (str_equiv(argv[1], _T("remove"))) { if (! is_admin) { print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE); diff --git a/nssm.h b/nssm.h index bdefdbe..46308d5 100644 --- a/nssm.h +++ b/nssm.h @@ -18,6 +18,7 @@ int str_equiv(const TCHAR *, const TCHAR *); void strip_basename(TCHAR *); +int usage(int); #define NSSM _T("nssm") #define NSSM_VERSION _T("2.21") diff --git a/nssm.rc b/nssm.rc index 5c21be3..3cf4d3f 100644 --- a/nssm.rc +++ b/nssm.rc @@ -86,6 +86,19 @@ BEGIN EDITTEXT IDC_NAME,59,7,87,14,ES_AUTOHSCROLL END +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_EDIT DIALOG 0, 0, 286, 126 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "NSSM service editor" +FONT 8, "MS Sans Serif" +{ + CONTROL "", IDC_TAB1, WC_TABCONTROL, 0, 7, 7, 269, 93 + LTEXT "Service name:", IDC_STATIC, 7, 106, 52, 8, SS_LEFT + EDITTEXT IDC_NAME, 64, 104, 98, 12, ES_AUTOHSCROLL + DEFPUSHBUTTON "Edit service", IDOK, 172, 104, 50, 14 + PUSHBUTTON "Cancel", IDCANCEL, 227, 104, 50, 14 +} + LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDD_APPLICATION DIALOG 9, 20, 261, 75 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL @@ -102,6 +115,16 @@ FONT 8, "MS Sans Serif" EDITTEXT IDC_FLAGS, 70, 48, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES } +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_NATIVE DIALOG 9, 20, 261, 75 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Service", IDC_STATIC, 7, 7, 251, 68 + LTEXT "Image path:", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_PATH, 70, 16, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES +} + LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL @@ -308,6 +331,19 @@ BEGIN EDITTEXT IDC_NAME,43,7,90,14,ES_AUTOHSCROLL END +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +IDD_EDIT DIALOG 0, 0, 282, 126 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "Edition d'un service NSSM" +FONT 8, "MS Sans Serif" +{ + CONTROL "", IDC_TAB1, WC_TABCONTROL, 0, 7, 7, 269, 93 + LTEXT "Nom du service:", IDC_STATIC, 7, 106, 52, 8, SS_LEFT + EDITTEXT IDC_NAME, 64, 104, 98, 12, ES_AUTOHSCROLL + DEFPUSHBUTTON "Editer le service", IDOK, 172, 106, 50, 14 + PUSHBUTTON "Anuller", IDCANCEL, 227, 106, 50, 14 +} + LANGUAGE LANG_FRENCH, SUBLANG_FRENCH IDD_APPLICATION DIALOG 9, 20, 261, 75 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL @@ -324,6 +360,16 @@ FONT 8, "MS Sans Serif" EDITTEXT IDC_FLAGS, 80, 45, 174, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES } +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +IDD_NATIVE DIALOG 9, 20, 261, 75 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Service", IDC_STATIC, 7, 7, 251, 68 + LTEXT "Chemin:", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_PATH, 70, 16, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES +} + ///////////////////////////////////////////////////////////////////////////// // @@ -433,6 +479,19 @@ BEGIN EDITTEXT IDC_NAME,59,7,87,14,ES_AUTOHSCROLL END +LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN +IDD_EDIT DIALOG 0, 0, 282, 126 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "NSSM - Edizione Servizio" +FONT 8, "MS Sans Serif" +{ + CONTROL "", IDC_TAB1, WC_TABCONTROL, 0, 7, 7, 269, 93 + LTEXT "Nome servizio:", IDC_STATIC, 7, 106, 52, 8, SS_LEFT + EDITTEXT IDC_NAME, 64, 104, 98, 12, ES_AUTOHSCROLL + DEFPUSHBUTTON "Edita servizio", IDOK, 172, 106, 50, 14 + PUSHBUTTON "Anulla", IDCANCEL, 227, 106, 50, 14 +} + LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN IDD_APPLICATION DIALOG 9, 20, 261, 75 STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL @@ -449,6 +508,16 @@ FONT 8, "MS Sans Serif" EDITTEXT IDC_FLAGS, 70, 46, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES } +LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN +IDD_NATIVE DIALOG 9, 20, 261, 75 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Servizio", IDC_STATIC, 7, 7, 251, 68 + LTEXT "Path:", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_PATH, 70, 16, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES +} + ///////////////////////////////////////////////////////////////////////////// // diff --git a/registry.cpp b/registry.cpp index 74fe047..d226b83 100644 --- a/registry.cpp +++ b/registry.cpp @@ -28,7 +28,7 @@ int create_messages() { return 0; } -int create_parameters(nssm_service_t *service) { +int create_parameters(nssm_service_t *service, bool editing) { /* Get registry */ TCHAR registry[KEY_LENGTH]; if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) { @@ -63,33 +63,54 @@ int create_parameters(nssm_service_t *service) { /* Other non-default parameters. May fail. */ unsigned long stop_method_skip = ~service->stop_method; if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip); - if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action]); + else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP); + if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing); if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay); + else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE); if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay); + else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD); if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay); + else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD); if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay); - if (service->stdin_path[0]) { - set_expand_string(key, NSSM_REG_STDIN, service->stdin_path); + else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD); + if (service->stdin_path[0] || editing) { + if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path); + else if (editing) RegDeleteValue(key, NSSM_REG_STDIN); if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING); if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION); if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS); } - if (service->stdout_path[0]) { - set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path); + if (service->stdout_path[0] || editing) { + if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path); + else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT); if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING); if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION); if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS); } - if (service->stderr_path[0]) { - set_expand_string(key, NSSM_REG_STDERR, service->stderr_path); + if (service->stderr_path[0] || editing) { + if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path); + else if (editing) RegDeleteValue(key, NSSM_REG_STDERR); if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING); if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION); if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags); + else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS); } if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1); + else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE); if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds); + else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS); if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low); + else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW); if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high); + else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH); /* Environment */ if (service->env) { @@ -97,11 +118,13 @@ int create_parameters(nssm_service_t *service) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0); } } + else if (editing) RegDeleteValue(key, NSSM_REG_ENV); if (service->env_extra) { 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) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0); } } + else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA); /* Close registry. */ RegCloseKey(key); @@ -109,7 +132,7 @@ int create_parameters(nssm_service_t *service) { return 0; } -int create_exit_action(TCHAR *service_name, const TCHAR *action_string) { +int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) { /* Get registry */ TCHAR registry[KEY_LENGTH]; if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) { @@ -126,7 +149,7 @@ int create_exit_action(TCHAR *service_name, const TCHAR *action_string) { } /* Do nothing if the key already existed */ - if (disposition == REG_OPENED_EXISTING_KEY) { + if (disposition == REG_OPENED_EXISTING_KEY && ! editing) { RegCloseKey(key); return 0; } @@ -452,6 +475,19 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) { 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); 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); + /* Try to get default exit action. */ + bool default_action; + service->default_exit_action = NSSM_EXIT_RESTART; + TCHAR action_string[ACTION_LEN]; + if (! get_exit_action(service->name, 0, action_string, &default_action)) { + for (int i = 0; exit_action_strings[i]; i++) { + if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) { + service->default_exit_action = i; + break; + } + } + } + /* Close registry */ RegCloseKey(key); diff --git a/registry.h b/registry.h index 600c188..564e674 100644 --- a/registry.h +++ b/registry.h @@ -26,8 +26,8 @@ #define NSSM_STDIO_LENGTH 29 int create_messages(); -int create_parameters(nssm_service_t *); -int create_exit_action(TCHAR *, const TCHAR *); +int create_parameters(nssm_service_t *, bool); +int create_exit_action(TCHAR *, const TCHAR *, bool); int set_environment(TCHAR *, HKEY, TCHAR *, TCHAR **, unsigned long *); int expand_parameter(HKEY, TCHAR *, TCHAR *, unsigned long, bool, bool); int expand_parameter(HKEY, TCHAR *, TCHAR *, unsigned long, bool); diff --git a/resource.h b/resource.h index 6964003..bebd411 100644 --- a/resource.h +++ b/resource.h @@ -6,14 +6,16 @@ #define IDI_NSSM 101 #define IDD_INSTALL 102 #define IDD_REMOVE 103 -#define IDD_APPLICATION 104 -#define IDD_DETAILS 105 -#define IDD_LOGON 106 -#define IDD_IO 107 -#define IDD_ROTATION 108 -#define IDD_APPEXIT 109 -#define IDD_SHUTDOWN 110 -#define IDD_ENVIRONMENT 111 +#define IDD_EDIT 104 +#define IDD_APPLICATION 105 +#define IDD_DETAILS 106 +#define IDD_LOGON 107 +#define IDD_IO 108 +#define IDD_ROTATION 109 +#define IDD_APPEXIT 110 +#define IDD_SHUTDOWN 111 +#define IDD_ENVIRONMENT 112 +#define IDD_NATIVE 113 #define IDC_PATH 1000 #define IDC_TAB1 1001 #define IDC_CANCEL 1002 @@ -58,7 +60,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 112 +#define _APS_NEXT_RESOURCE_VALUE 114 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1040 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/service.cpp b/service.cpp index 09fbe1f..b246443 100644 --- a/service.cpp +++ b/service.cpp @@ -1,5 +1,7 @@ #include "nssm.h" +extern const TCHAR *exit_action_strings[]; + bool is_admin; bool use_critical_section; @@ -120,6 +122,172 @@ int pre_install_service(int argc, TCHAR **argv) { return ret; } +/* About to edit the service. */ +int pre_edit_service(int argc, TCHAR **argv) { + /* Require service name. */ + if (argc < 1) return usage(1); + + nssm_service_t *service = alloc_nssm_service(); + _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]); + + /* Open service manager */ + SC_HANDLE services = open_service_manager(); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 2; + } + + /* Try to open the service */ + service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS); + if (! service->handle) { + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED); + return 3; + } + + /* Get system details. */ + unsigned long bufsize; + unsigned long error; + QUERY_SERVICE_CONFIG *qsc; + + QueryServiceConfig(service->handle, 0, 0, &bufsize); + error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { + qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize); + if (! qsc) { + print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("pre_edit_service()"), 0); + return 4; + } + } + else { + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service->name, error_string(error), 0); + return 4; + } + + if (! QueryServiceConfig(service->handle, qsc, bufsize, &bufsize)) { + HeapFree(GetProcessHeap(), 0, qsc); + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service->name, error_string(GetLastError()), 0); + return 4; + } + + service->type = qsc->dwServiceType; + if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) { + HeapFree(GetProcessHeap(), 0, qsc); + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, _T("SERVICE_WIN32_OWN_PROCESS"), 0); + return 3; + } + + switch (qsc->dwStartType) { + case SERVICE_DEMAND_START: service->startup = NSSM_STARTUP_MANUAL; break; + case SERVICE_DISABLED: service->startup = NSSM_STARTUP_DISABLED; break; + default: service->startup = NSSM_STARTUP_AUTOMATIC; + } + if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) { + size_t len = _tcslen(qsc->lpServiceStartName); + service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR)); + if (service->username) { + memmove(service->username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR)); + service->usernamelen = (unsigned long) len; + } + else { + HeapFree(GetProcessHeap(), 0, qsc); + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, _T("username"), _T("pre_edit_service()")); + return 4; + } + } + _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName); + + /* Remember the executable in case it isn't NSSM. */ + _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName); + HeapFree(GetProcessHeap(), 0, qsc); + + /* Get extended system details. */ + if (service->startup == NSSM_STARTUP_AUTOMATIC) { + QueryServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize); + error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { + SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize); + if (! info) { + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("pre_edit_service()")); + return 5; + } + + if (QueryServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) { + if (info->fDelayedAutostart) service->startup = NSSM_STARTUP_DELAYED; + } + else { + error = GetLastError(); + if (error != ERROR_INVALID_LEVEL) { + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service->name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error)); + return 5; + } + } + } + else if (error != ERROR_INVALID_LEVEL) { + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service->name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error)); + return 5; + } + } + + QueryServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize); + error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { + SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize); + if (! description) { + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("pre_edit_service()")); + return 6; + } + + if (QueryServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) { + if (description->lpDescription) _sntprintf_s(service->description, _countof(service->description), _TRUNCATE, _T("%s"), description->lpDescription); + HeapFree(GetProcessHeap(), 0, description); + } + else { + HeapFree(GetProcessHeap(), 0, description); + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service->name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error)); + return 6; + } + } + else { + CloseHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service->name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error)); + return 6; + } + + /* Get NSSM details. */ + get_parameters(service, 0); + + CloseServiceHandle(services); + + if (! service->exe[0]) { + print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image); + service->native = true; + } + + nssm_gui(IDD_EDIT, service); + + return 0; +} + /* About to remove the service */ int pre_remove_service(int argc, TCHAR **argv) { nssm_service_t *service = alloc_nssm_service(); @@ -150,8 +318,33 @@ int install_service(nssm_service_t *service) { } /* Get path of this program */ - TCHAR command[MAX_PATH]; - GetModuleFileName(0, command, _countof(command)); + GetModuleFileName(0, service->image, _countof(service->image)); + + /* Create the service - settings will be changed in edit_service() */ + service->handle = CreateService(services, service->name, service->name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0); + if (! service->handle) { + print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED); + CloseServiceHandle(services); + return 5; + } + + if (edit_service(service, false)) { + DeleteService(service->handle); + CloseServiceHandle(services); + return 6; + } + + print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name); + + /* Cleanup */ + CloseServiceHandle(services); + + return 0; +} + +/* Edit the service. */ +int edit_service(nssm_service_t *service, bool editing) { + if (! service) return 1; /* The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS @@ -171,51 +364,59 @@ int install_service(nssm_service_t *service) { /* Display name. */ if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name); - /* Create the service */ - service->handle = CreateService(services, service->name, service->displayname, SC_MANAGER_ALL_ACCESS, service->type, startup, SERVICE_ERROR_NORMAL, command, 0, 0, 0, service->username, service->password); - if (! service->handle) { - print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED); - CloseServiceHandle(services); + /* + Username must be NULL if we aren't changing or an account name. + We must explicitly user LOCALSYSTEM to change it when we are editing. + Password must be NULL if we aren't changing, a password or "". + Empty passwords are valid but we won't allow them in the GUI. + */ + TCHAR *username = 0; + TCHAR *password = 0; + if (service->usernamelen) { + username = service->username; + if (service->passwordlen) password = service->password; + else password = _T(""); + } + else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT; + + if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); return 5; } - if (service->description[0]) { + if (service->description[0] || editing) { SERVICE_DESCRIPTION description; ZeroMemory(&description, sizeof(description)); - description.lpDescription = service->description; + if (service->description[0]) description.lpDescription = service->description; + else description.lpDescription = 0; if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, &description)) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service->name, error_string(GetLastError()), 0); } } - if (service->startup == NSSM_STARTUP_DELAYED) { - SERVICE_DELAYED_AUTO_START_INFO delayed; - ZeroMemory(&delayed, sizeof(delayed)); - delayed.fDelayedAutostart = 1; - /* Delayed startup isn't supported until Vista. */ - if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) { - unsigned long error = GetLastError(); - /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ - if (error != ERROR_INVALID_LEVEL) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0); - } + SERVICE_DELAYED_AUTO_START_INFO delayed; + ZeroMemory(&delayed, sizeof(delayed)); + if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1; + else delayed.fDelayedAutostart = 0; + /* Delayed startup isn't supported until Vista. */ + if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) { + unsigned long error = GetLastError(); + /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ + if (error != ERROR_INVALID_LEVEL) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0); } } - /* Now we need to put the parameters into the registry */ - if (create_parameters(service)) { - print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED); - DeleteService(service->handle); - CloseServiceHandle(services); - return 6; - } - - set_service_recovery(service); - - print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name); + /* Don't mess with parameters which aren't ours. */ + if (! service->native) { + /* Now we need to put the parameters into the registry */ + if (create_parameters(service, editing)) { + print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED); + return 6; + } - /* Cleanup */ - CloseServiceHandle(services); + set_service_recovery(service); + } return 0; } @@ -295,7 +496,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) { if (is_admin) { /* Try to create the exit action parameters; we don't care if it fails */ - create_exit_action(service->name, exit_action_strings[0]); + create_exit_action(service->name, exit_action_strings[0], false); SC_HANDLE services = open_service_manager(); if (services) { diff --git a/service.h b/service.h index dc1715b..3460625 100644 --- a/service.h +++ b/service.h @@ -17,7 +17,10 @@ #define ACTION_LEN 16 +#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem") + typedef struct { + bool native; TCHAR name[SERVICE_NAME_LENGTH]; TCHAR displayname[SERVICE_DISPLAYNAME_LENGTH]; TCHAR description[VALUE_LENGTH]; @@ -27,6 +30,7 @@ typedef struct { TCHAR *password; size_t passwordlen; unsigned long type; + TCHAR image[MAX_PATH]; TCHAR exe[EXE_LENGTH]; TCHAR flags[VALUE_LENGTH]; TCHAR dir[MAX_PATH]; @@ -85,8 +89,10 @@ void cleanup_nssm_service(nssm_service_t *); SC_HANDLE open_service_manager(); int pre_install_service(int, TCHAR **); int pre_remove_service(int, TCHAR **); +int pre_edit_service(int, TCHAR **); int install_service(nssm_service_t *); int remove_service(nssm_service_t *); +int edit_service(nssm_service_t *, bool); void set_service_recovery(nssm_service_t *); int monitor_service(nssm_service_t *); int start_service(nssm_service_t *);