3 SERVICE_STATUS service_status;
\r
4 SERVICE_STATUS_HANDLE service_handle;
\r
5 HANDLE process_handle;
\r
8 static char service_name[SERVICE_NAME_LENGTH];
\r
9 char exe[EXE_LENGTH];
\r
10 char flags[CMD_LENGTH];
\r
14 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
15 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
17 /* Connect to the service manager */
\r
18 SC_HANDLE open_service_manager() {
\r
19 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
21 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
28 /* About to install the service */
\r
29 int pre_install_service(int argc, char **argv) {
\r
30 /* Show the dialogue box if we didn't give the */
\r
31 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
33 /* Arguments are optional */
\r
35 if (argc == 2) flags = "";
\r
36 else flags = argv[2];
\r
38 return install_service(argv[0], argv[1], flags);
\r
41 /* About to remove the service */
\r
42 int pre_remove_service(int argc, char **argv) {
\r
43 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
44 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
45 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
46 fprintf(stderr, "To remove a service without confirmation: nssm remove <servicename> confirm\n");
\r
50 /* Install the service */
\r
51 int install_service(char *name, char *exe, char *flags) {
\r
52 /* Open service manager */
\r
53 SC_HANDLE services = open_service_manager();
\r
55 fprintf(stderr, "Error opening service manager!\n");
\r
59 /* Get path of this program */
\r
60 char path[MAX_PATH];
\r
61 GetModuleFileName(0, path, MAX_PATH);
\r
63 /* Construct command */
\r
64 char command[CMD_LENGTH];
\r
65 size_t runlen = strlen(NSSM_RUN);
\r
66 size_t pathlen = strlen(path);
\r
67 if (pathlen + runlen + 2 >= VALUE_LENGTH) {
\r
68 fprintf(stderr, "The full path to " NSSM " is too long!\n");
\r
71 if (snprintf(command, sizeof(command), "\"%s\" %s", path, NSSM_RUN) < 0) {
\r
72 fprintf(stderr, "Out of memory for ImagePath!\n");
\r
76 /* Work out directory name */
\r
77 size_t len = strlen(exe);
\r
79 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
81 memmove(dir, exe, i);
\r
84 /* Create the service */
\r
85 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
87 fprintf(stderr, "Error creating service!\n");
\r
88 CloseServiceHandle(services);
\r
92 /* Now we need to put the parameters into the registry */
\r
93 if (create_parameters(name, exe, flags, dir)) {
\r
94 fprintf(stderr, "Error setting startup parameters for the service!\n");
\r
95 DeleteService(service);
\r
96 CloseServiceHandle(services);
\r
101 CloseServiceHandle(service);
\r
102 CloseServiceHandle(services);
\r
104 printf("Service \"%s\" installed successfully!\n", name);
\r
108 /* Remove the service */
\r
109 int remove_service(char *name) {
\r
110 /* Open service manager */
\r
111 SC_HANDLE services = open_service_manager();
\r
113 fprintf(stderr, "Error opening service manager!\n");
\r
117 /* Try to open the service */
\r
118 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
120 fprintf(stderr, "Can't open service!");
\r
121 CloseServiceHandle(services);
\r
125 /* Try to delete the service */
\r
126 if (! DeleteService(service)) {
\r
127 fprintf(stderr, "Error deleting service!\n");
\r
128 CloseServiceHandle(service);
\r
129 CloseServiceHandle(services);
\r
134 CloseServiceHandle(service);
\r
135 CloseServiceHandle(services);
\r
137 printf("Service \"%s\" removed successfully!\n", name);
\r
141 /* Service initialisation */
\r
142 void WINAPI service_main(unsigned long argc, char **argv) {
\r
143 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
144 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
148 /* Initialise status */
\r
149 ZeroMemory(&service_status, sizeof(service_status));
\r
150 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
151 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
152 service_status.dwWin32ExitCode = NO_ERROR;
\r
153 service_status.dwServiceSpecificExitCode = 0;
\r
154 service_status.dwCheckPoint = 0;
\r
155 service_status.dwWaitHint = 1000;
\r
157 /* Signal we AREN'T running the server */
\r
158 process_handle = 0;
\r
161 /* Register control handler */
\r
162 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
163 if (! service_handle) {
\r
164 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, GetLastError(), 0);
\r
168 /* Get startup parameters */
\r
169 int ret = get_parameters(argv[0], exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir));
\r
171 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, argv[0], 0);
\r
172 service_status.dwCurrentState = SERVICE_STOPPED;
\r
173 /* An accurate, if not particularly helpful, status */
\r
174 service_status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
\r
175 SetServiceStatus(service_handle, &service_status);
\r
179 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
180 SetServiceStatus(service_handle, &service_status);
\r
182 /* Try to create the exit action parameters; we don't care if it fails */
\r
183 create_exit_action(argv[0], exit_action_strings[0]);
\r
185 set_service_recovery(service_name);
\r
190 /* Make sure service recovery actions are taken where necessary */
\r
191 void set_service_recovery(char *service_name) {
\r
192 SC_HANDLE services = open_service_manager();
\r
193 if (! services) return;
\r
195 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
196 if (! service) return;
\r
199 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
200 ZeroMemory(&flag, sizeof(flag));
\r
201 flag.fFailureActionsOnNonCrashFailures = true;
\r
203 /* This functionality was added in Vista so the call may fail */
\r
204 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
207 int monitor_service() {
\r
208 /* Set service status to started */
\r
209 int ret = start_service();
\r
212 snprintf(code, sizeof(code), "%d", ret);
\r
213 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
216 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
218 /* Monitor service service */
\r
219 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
220 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, GetLastError(), 0);
\r
226 /* Service control handler */
\r
227 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
229 case SERVICE_CONTROL_SHUTDOWN:
\r
230 case SERVICE_CONTROL_STOP:
\r
231 stop_service(0, true, true);
\r
235 /* Unknown control */
\r
236 return ERROR_CALL_NOT_IMPLEMENTED;
\r
239 /* Start the service */
\r
240 int start_service() {
\r
243 if (process_handle) return 0;
\r
245 /* Allocate a STARTUPINFO structure for a new process */
\r
247 ZeroMemory(&si, sizeof(si));
\r
248 si.cb = sizeof(si);
\r
250 /* Allocate a PROCESSINFO structure for the process */
\r
251 PROCESS_INFORMATION pi;
\r
252 ZeroMemory(&pi, sizeof(pi));
\r
254 /* Launch executable with arguments */
\r
255 char cmd[CMD_LENGTH];
\r
256 if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
\r
257 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
258 return stop_service(2, true, true);
\r
260 if (! CreateProcess(0, cmd, 0, 0, false, 0, 0, dir, &si, &pi)) {
\r
261 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, GetLastError(), 0);
\r
262 return stop_service(3, true, true);
\r
264 process_handle = pi.hProcess;
\r
265 pid = pi.dwProcessId;
\r
267 /* Signal successful start */
\r
268 service_status.dwCurrentState = SERVICE_RUNNING;
\r
269 SetServiceStatus(service_handle, &service_status);
\r
274 /* Stop the service */
\r
275 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
276 if (default_action && ! exitcode && ! graceful) {
\r
277 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
281 /* Signal we are stopping */
\r
283 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
284 SetServiceStatus(service_handle, &service_status);
\r
287 /* Nothing to do if server isn't running */
\r
289 /* Shut down server */
\r
290 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
291 TerminateProcess(process_handle, 0);
\r
292 process_handle = 0;
\r
294 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
296 end_service((void *) pid, true);
\r
298 /* Signal we stopped */
\r
300 service_status.dwCurrentState = SERVICE_STOPPED;
\r
302 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
303 service_status.dwServiceSpecificExitCode = exitcode;
\r
306 service_status.dwWin32ExitCode = NO_ERROR;
\r
307 service_status.dwServiceSpecificExitCode = 0;
\r
309 SetServiceStatus(service_handle, &service_status);
\r
315 /* Callback function triggered when the server exits */
\r
316 void CALLBACK end_service(void *arg, unsigned char why) {
\r
317 if (stopping) return;
\r
321 pid = (unsigned long) arg;
\r
323 /* Check exit code */
\r
324 unsigned long exitcode = 0;
\r
325 GetExitCodeProcess(process_handle, &exitcode);
\r
328 kill_process_tree(service_name, pid, exitcode, pid);
\r
331 The why argument is true if our wait timed out or false otherwise.
\r
332 Our wait is infinite so why will never be true when called by the system.
\r
333 If it is indeed true, assume we were called from stop_service() because
\r
334 this is a controlled shutdown, and don't take any restart action.
\r
339 _snprintf(code, sizeof(code), "%d", exitcode);
\r
340 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
342 /* What action should we take? */
\r
343 int action = NSSM_EXIT_RESTART;
\r
344 unsigned char action_string[ACTION_LEN];
\r
345 bool default_action;
\r
346 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
347 for (int i = 0; exit_action_strings[i]; i++) {
\r
348 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
355 process_handle = 0;
\r
358 /* Try to restart the service or return failure code to service manager */
\r
359 case NSSM_EXIT_RESTART:
\r
360 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
361 while (monitor_service()) {
\r
362 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
367 /* Do nothing, just like srvany would */
\r
368 case NSSM_EXIT_IGNORE:
\r
369 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
373 /* Tell the service manager we are finished */
\r
374 case NSSM_EXIT_REALLY:
\r
375 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
376 stop_service(exitcode, true, default_action);
\r
379 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
380 case NSSM_EXIT_UNCLEAN:
\r
381 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
382 exit(stop_service(exitcode, false, default_action));
\r