3 /* Send some window messages and hope the window respects one or more. */
4 int CALLBACK kill_window(HWND window, LPARAM arg) {
5 kill_t *k = (kill_t *) arg;
8 if (! GetWindowThreadProcessId(window, &pid)) return 1;
9 if (pid != k->pid) return 1;
11 /* First try sending WM_CLOSE to request that the window close. */
12 k->signalled |= PostMessage(window, WM_CLOSE, k->exitcode, 0);
15 Then tell the window that the user is logging off and it should exit
16 without worrying about saving any data.
18 k->signalled |= PostMessage(window, WM_ENDSESSION, 1, ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF);
24 Try to post a message to the message queues of threads associated with the
25 given process ID. Not all threads have message queues so there's no
26 guarantee of success, and we don't want to be left waiting for unsignalled
27 processes so this function returns only true if at least one thread was
30 int kill_threads(char *service_name, kill_t *k) {
33 /* Get a snapshot of all threads in the system. */
34 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
36 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_THREAD_FAILED, service_name, error_string(GetLastError()), 0);
41 ZeroMemory(&te, sizeof(te));
42 te.dwSize = sizeof(te);
44 if (! Thread32First(snapshot, &te)) {
45 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
49 /* This thread belongs to the doomed process so signal it. */
50 if (te.th32OwnerProcessID == k->pid) {
51 ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0);
55 /* Try to get the next thread. */
56 if (! Thread32Next(snapshot, &te)) {
57 unsigned long error = GetLastError();
58 if (error == ERROR_NO_MORE_FILES) break;
59 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
63 if (te.th32OwnerProcessID == k->pid) {
64 ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0);
71 /* Give the process a chance to die gracefully. */
72 int kill_process(char *service_name, HANDLE process_handle, unsigned long pid, unsigned long exitcode) {
73 /* Shouldn't happen. */
76 kill_t k = { pid, exitcode, 0 };
79 Try to post messages to the windows belonging to the given process ID.
80 If the process is a console application it won't have any windows so there's
81 no guarantee of success.
83 EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
85 if (! WaitForSingleObject(process_handle, NSSM_KILL_WINDOW_GRACE_PERIOD)) return 1;
89 Try to post messages to any thread message queues associated with the
90 process. Console applications might have them (but probably won't) so
91 there's still no guarantee of success.
93 if (kill_threads(service_name, &k)) {
94 if (! WaitForSingleObject(process_handle, NSSM_KILL_THREADS_GRACE_PERIOD)) return 1;
97 /* We tried being nice. Time for extreme prejudice. */
98 return TerminateProcess(process_handle, exitcode);
101 void kill_process_tree(char *service_name, unsigned long pid, unsigned long exitcode, unsigned long ppid) {
102 /* Shouldn't happen unless the service failed to start. */
105 char pid_string[16], code[16];
106 _snprintf(pid_string, sizeof(pid_string), "%d", pid);
107 _snprintf(code, sizeof(code), "%d", exitcode);
108 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, service_name, pid_string, code, 0);
110 /* Get a snapshot of all processes in the system. */
111 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
113 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, service_name, error_string(GetLastError()), 0);
118 ZeroMemory(&pe, sizeof(pe));
119 pe.dwSize = sizeof(pe);
121 if (! Process32First(snapshot, &pe)) {
122 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
126 /* This is a child of the doomed process so kill it. */
127 if (pe.th32ParentProcessID == pid) kill_process_tree(service_name, pe.th32ProcessID, exitcode, ppid);
130 /* Try to get the next process. */
131 if (! Process32Next(snapshot, &pe)) {
132 unsigned long ret = GetLastError();
133 if (ret == ERROR_NO_MORE_FILES) break;
134 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
138 if (pe.th32ParentProcessID == pid) kill_process_tree(service_name, pe.th32ProcessID, exitcode, ppid);
141 /* We will need a process handle in order to call TerminateProcess() later. */
142 HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid);
143 if (! process_handle) {
144 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0);
148 char ppid_string[16];
149 _snprintf(ppid_string, sizeof(ppid_string), "%d", ppid);
150 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, service_name, 0);
151 if (! kill_process(service_name, process_handle, pid, exitcode)) {
152 /* Maybe it already died. */
154 if (! GetExitCodeProcess(process_handle, &ret)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0);