3 SERVICE_STATUS service_status;
\r
4 SERVICE_STATUS_HANDLE service_handle;
\r
7 static char service_name[SERVICE_NAME_LENGTH];
\r
8 char exe[EXE_LENGTH];
\r
9 char flags[CMD_LENGTH];
\r
12 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
13 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
15 /* Connect to the service manager */
\r
16 SC_HANDLE open_service_manager() {
\r
17 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
19 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
26 /* About to install the service */
\r
27 int pre_install_service(int argc, char **argv) {
\r
28 /* Show the dialogue box if we didn't give the */
\r
29 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
31 /* Arguments are optional */
\r
33 if (argc == 2) flags = "";
\r
34 else flags = argv[2];
\r
36 return install_service(argv[0], argv[1], flags);
\r
39 /* About to remove the service */
\r
40 int pre_remove_service(int argc, char **argv) {
\r
41 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
42 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
43 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
44 fprintf(stderr, "To remove a service without confirmation: nssm remove <servicename> confirm\n");
\r
48 /* Install the service */
\r
49 int install_service(char *name, char *exe, char *flags) {
\r
50 /* Open service manager */
\r
51 SC_HANDLE services = open_service_manager();
\r
53 fprintf(stderr, "Error opening service manager!\n");
\r
57 /* Get path of this program */
\r
58 char path[MAX_PATH];
\r
59 GetModuleFileName(0, path, MAX_PATH);
\r
61 /* Construct command */
\r
62 char command[CMD_LENGTH];
\r
63 size_t runlen = strlen(NSSM_RUN);
\r
64 size_t pathlen = strlen(path);
\r
65 if (pathlen + runlen + 2 >= VALUE_LENGTH) {
\r
66 fprintf(stderr, "The full path to " NSSM " is too long!\n");
\r
69 if (snprintf(command, sizeof(command), "\"%s\" %s", path, NSSM_RUN) < 0) {
\r
70 fprintf(stderr, "Out of memory for ImagePath!\n");
\r
74 /* Work out directory name */
\r
75 size_t len = strlen(exe);
\r
77 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
79 memmove(dir, exe, i);
\r
82 /* Create the service */
\r
83 SC_HANDLE service = CreateService(services, name, name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0);
\r
85 fprintf(stderr, "Error creating service!\n");
\r
86 CloseServiceHandle(services);
\r
90 /* Now we need to put the parameters into the registry */
\r
91 if (create_parameters(name, exe, flags, dir)) {
\r
92 fprintf(stderr, "Error setting startup parameters for the service!\n");
\r
93 DeleteService(service);
\r
94 CloseServiceHandle(services);
\r
99 CloseServiceHandle(service);
\r
100 CloseServiceHandle(services);
\r
102 printf("Service \"%s\" installed successfully!\n", name);
\r
106 /* Remove the service */
\r
107 int remove_service(char *name) {
\r
108 /* Open service manager */
\r
109 SC_HANDLE services = open_service_manager();
\r
111 fprintf(stderr, "Error opening service manager!\n");
\r
115 /* Try to open the service */
\r
116 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
118 fprintf(stderr, "Can't open service!");
\r
119 CloseServiceHandle(services);
\r
123 /* Try to delete the service */
\r
124 if (! DeleteService(service)) {
\r
125 fprintf(stderr, "Error deleting service!\n");
\r
126 CloseServiceHandle(service);
\r
127 CloseServiceHandle(services);
\r
132 CloseServiceHandle(service);
\r
133 CloseServiceHandle(services);
\r
135 printf("Service \"%s\" removed successfully!\n", name);
\r
139 /* Service initialisation */
\r
140 void WINAPI service_main(unsigned long argc, char **argv) {
\r
141 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
142 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
146 /* Initialise status */
\r
147 ZeroMemory(&service_status, sizeof(service_status));
\r
148 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
149 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
150 service_status.dwWin32ExitCode = NO_ERROR;
\r
151 service_status.dwServiceSpecificExitCode = 0;
\r
152 service_status.dwCheckPoint = 0;
\r
153 service_status.dwWaitHint = 1000;
\r
155 /* Signal we AREN'T running the server */
\r
158 /* Register control handler */
\r
159 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
160 if (! service_handle) {
\r
161 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, GetLastError(), 0);
\r
165 /* Get startup parameters */
\r
166 int ret = get_parameters(argv[0], exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir));
\r
168 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, argv[0], 0);
\r
169 service_status.dwCurrentState = SERVICE_STOPPED;
\r
170 /* An accurate, if not particularly helpful, status */
\r
171 service_status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
\r
172 SetServiceStatus(service_handle, &service_status);
\r
176 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
177 SetServiceStatus(service_handle, &service_status);
\r
179 /* Try to create the exit action parameters; we don't care if it fails */
\r
180 create_exit_action(argv[0], exit_action_strings[0]);
\r
182 set_service_recovery(service_name);
\r
187 /* Make sure service recovery actions are taken where necessary */
\r
188 void set_service_recovery(char *service_name) {
\r
189 SC_HANDLE services = open_service_manager();
\r
190 if (! services) return;
\r
192 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
193 if (! service) return;
\r
196 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
197 ZeroMemory(&flag, sizeof(flag));
\r
198 flag.fFailureActionsOnNonCrashFailures = true;
\r
200 /* This functionality was added in Vista so the call may fail */
\r
201 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
204 int monitor_service() {
\r
205 /* Set service status to started */
\r
206 int ret = start_service();
\r
209 snprintf(code, sizeof(code), "%d", ret);
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
213 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
215 /* Monitor service service */
\r
216 if (! RegisterWaitForSingleObject(&wait_handle, pid, end_service, 0, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
217 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, GetLastError(), 0);
\r
223 /* Service control handler */
\r
224 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
226 case SERVICE_CONTROL_SHUTDOWN:
\r
227 case SERVICE_CONTROL_STOP:
\r
228 stop_service(0, true, true);
\r
232 /* Unknown control */
\r
233 return ERROR_CALL_NOT_IMPLEMENTED;
\r
236 /* Start the service */
\r
237 int start_service() {
\r
240 /* Allocate a STARTUPINFO structure for a new process */
\r
242 ZeroMemory(&si, sizeof(si));
\r
243 si.cb = sizeof(si);
\r
245 /* Allocate a PROCESSINFO structure for the process */
\r
246 PROCESS_INFORMATION pi;
\r
247 ZeroMemory(&pi, sizeof(pi));
\r
249 /* Launch executable with arguments */
\r
250 char cmd[CMD_LENGTH];
\r
251 if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
\r
252 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
253 return stop_service(2, true, true);
\r
255 if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {
\r
256 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, GetLastError(), 0);
\r
257 return stop_service(3, true, true);
\r
261 /* Signal successful start */
\r
262 service_status.dwCurrentState = SERVICE_RUNNING;
\r
263 SetServiceStatus(service_handle, &service_status);
\r
268 /* Stop the service */
\r
269 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
270 if (default_action && ! exitcode && ! graceful) {
\r
271 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0);
\r
275 /* Signal we are stopping */
\r
277 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
278 SetServiceStatus(service_handle, &service_status);
\r
281 /* Nothing to do if server isn't running */
\r
283 /* Shut down server */
\r
284 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
285 TerminateProcess(pid, 0);
\r
288 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
290 /* Signal we stopped */
\r
292 service_status.dwCurrentState = SERVICE_STOPPED;
\r
294 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
295 service_status.dwServiceSpecificExitCode = exitcode;
\r
298 service_status.dwWin32ExitCode = NO_ERROR;
\r
299 service_status.dwServiceSpecificExitCode = 0;
\r
301 SetServiceStatus(service_handle, &service_status);
\r
307 /* Callback function triggered when the server exits */
\r
308 void CALLBACK end_service(void *arg, unsigned char why) {
\r
309 /* Check exit code */
\r
310 unsigned long ret = 0;
\r
311 GetExitCodeProcess(pid, &ret);
\r
314 _snprintf(code, sizeof(code), "%d", ret);
\r
315 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
317 /* What action should we take? */
\r
318 int action = NSSM_EXIT_RESTART;
\r
319 unsigned char action_string[ACTION_LEN];
\r
320 bool default_action;
\r
321 if (! get_exit_action(service_name, &ret, action_string, &default_action)) {
\r
322 for (int i = 0; exit_action_strings[i]; i++) {
\r
323 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
332 /* Try to restart the service or return failure code to service manager */
\r
333 case NSSM_EXIT_RESTART:
\r
334 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
335 while (monitor_service()) {
\r
336 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
341 /* Do nothing, just like srvany would */
\r
342 case NSSM_EXIT_IGNORE:
\r
343 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
347 /* Tell the service manager we are finished */
\r
348 case NSSM_EXIT_REALLY:
\r
349 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
350 stop_service(ret, true, default_action);
\r
353 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
354 case NSSM_EXIT_UNCLEAN:
\r
355 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
356 exit(stop_service(ret, false, default_action));
\r