taskkill.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. * Task termination utility
  3. *
  4. * Copyright 2008 Andrew Riedi
  5. * Copyright 2010 Andrew Nguyen
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  20. */
  21. #include <stdlib.h>
  22. #include <windows.h>
  23. #include <psapi.h>
  24. #include <wine/debug.h>
  25. #include "taskkill.h"
  26. WINE_DEFAULT_DEBUG_CHANNEL(taskkill);
  27. static BOOL force_termination = FALSE;
  28. static WCHAR **task_list;
  29. static unsigned int task_count;
  30. struct pid_close_info
  31. {
  32. DWORD pid;
  33. BOOL found;
  34. };
  35. static int taskkill_vprintfW(const WCHAR *msg, va_list va_args)
  36. {
  37. int wlen;
  38. DWORD count;
  39. WCHAR msg_buffer[8192];
  40. wlen = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, msg_buffer,
  41. ARRAY_SIZE(msg_buffer), &va_args);
  42. if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg_buffer, wlen, &count, NULL))
  43. {
  44. DWORD len;
  45. char *msgA;
  46. /* On Windows WriteConsoleW() fails if the output is redirected. So fall
  47. * back to WriteFile() using OEM code page.
  48. */
  49. len = WideCharToMultiByte(GetOEMCP(), 0, msg_buffer, wlen,
  50. NULL, 0, NULL, NULL);
  51. msgA = HeapAlloc(GetProcessHeap(), 0, len);
  52. if (!msgA)
  53. return 0;
  54. WideCharToMultiByte(GetOEMCP(), 0, msg_buffer, wlen, msgA, len, NULL, NULL);
  55. WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
  56. HeapFree(GetProcessHeap(), 0, msgA);
  57. }
  58. return count;
  59. }
  60. static int WINAPIV taskkill_printfW(const WCHAR *msg, ...)
  61. {
  62. va_list va_args;
  63. int len;
  64. va_start(va_args, msg);
  65. len = taskkill_vprintfW(msg, va_args);
  66. va_end(va_args);
  67. return len;
  68. }
  69. static int WINAPIV taskkill_message_printfW(int msg, ...)
  70. {
  71. va_list va_args;
  72. WCHAR msg_buffer[8192];
  73. int len;
  74. LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
  75. va_start(va_args, msg);
  76. len = taskkill_vprintfW(msg_buffer, va_args);
  77. va_end(va_args);
  78. return len;
  79. }
  80. static int taskkill_message(int msg)
  81. {
  82. WCHAR msg_buffer[8192];
  83. LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
  84. return taskkill_printfW(L"%1", msg_buffer);
  85. }
  86. /* Post WM_CLOSE to all top-level windows belonging to the process with specified PID. */
  87. static BOOL CALLBACK pid_enum_proc(HWND hwnd, LPARAM lParam)
  88. {
  89. struct pid_close_info *info = (struct pid_close_info *)lParam;
  90. DWORD hwnd_pid;
  91. GetWindowThreadProcessId(hwnd, &hwnd_pid);
  92. if (hwnd_pid == info->pid)
  93. {
  94. PostMessageW(hwnd, WM_CLOSE, 0, 0);
  95. info->found = TRUE;
  96. }
  97. return TRUE;
  98. }
  99. static DWORD *enumerate_processes(DWORD *list_count)
  100. {
  101. DWORD *pid_list, alloc_bytes = 1024 * sizeof(*pid_list), needed_bytes;
  102. pid_list = HeapAlloc(GetProcessHeap(), 0, alloc_bytes);
  103. if (!pid_list)
  104. return NULL;
  105. for (;;)
  106. {
  107. DWORD *realloc_list;
  108. if (!EnumProcesses(pid_list, alloc_bytes, &needed_bytes))
  109. {
  110. HeapFree(GetProcessHeap(), 0, pid_list);
  111. return NULL;
  112. }
  113. /* EnumProcesses can't signal an insufficient buffer condition, so the
  114. * only way to possibly determine whether a larger buffer is required
  115. * is to see whether the written number of bytes is the same as the
  116. * buffer size. If so, the buffer will be reallocated to twice the
  117. * size. */
  118. if (alloc_bytes != needed_bytes)
  119. break;
  120. alloc_bytes *= 2;
  121. realloc_list = HeapReAlloc(GetProcessHeap(), 0, pid_list, alloc_bytes);
  122. if (!realloc_list)
  123. {
  124. HeapFree(GetProcessHeap(), 0, pid_list);
  125. return NULL;
  126. }
  127. pid_list = realloc_list;
  128. }
  129. *list_count = needed_bytes / sizeof(*pid_list);
  130. return pid_list;
  131. }
  132. static BOOL get_process_name_from_pid(DWORD pid, WCHAR *buf, DWORD chars)
  133. {
  134. HANDLE process;
  135. HMODULE module;
  136. DWORD required_size;
  137. process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
  138. if (!process)
  139. return FALSE;
  140. if (!EnumProcessModules(process, &module, sizeof(module), &required_size))
  141. {
  142. CloseHandle(process);
  143. return FALSE;
  144. }
  145. if (!GetModuleBaseNameW(process, module, buf, chars))
  146. {
  147. CloseHandle(process);
  148. return FALSE;
  149. }
  150. CloseHandle(process);
  151. return TRUE;
  152. }
  153. /* The implemented task enumeration and termination behavior does not
  154. * exactly match native behavior. On Windows:
  155. *
  156. * In the case of terminating by process name, specifying a particular
  157. * process name more times than the number of running instances causes
  158. * all instances to be terminated, but termination failure messages to
  159. * be printed as many times as the difference between the specification
  160. * quantity and the number of running instances.
  161. *
  162. * Successful terminations are all listed first in order, with failing
  163. * terminations being listed at the end.
  164. *
  165. * A PID of zero causes taskkill to warn about the inability to terminate
  166. * system processes. */
  167. static int send_close_messages(void)
  168. {
  169. DWORD *pid_list, pid_list_size;
  170. DWORD self_pid = GetCurrentProcessId();
  171. unsigned int i;
  172. int status_code = 0;
  173. pid_list = enumerate_processes(&pid_list_size);
  174. if (!pid_list)
  175. {
  176. taskkill_message(STRING_ENUM_FAILED);
  177. return 1;
  178. }
  179. for (i = 0; i < task_count; i++)
  180. {
  181. WCHAR *p = task_list[i];
  182. BOOL is_numeric = TRUE;
  183. /* Determine whether the string is not numeric. */
  184. while (*p)
  185. {
  186. if (!iswdigit(*p++))
  187. {
  188. is_numeric = FALSE;
  189. break;
  190. }
  191. }
  192. if (is_numeric)
  193. {
  194. DWORD pid = wcstol(task_list[i], NULL, 10);
  195. struct pid_close_info info = { pid };
  196. if (pid == self_pid)
  197. {
  198. taskkill_message(STRING_SELF_TERMINATION);
  199. status_code = 1;
  200. continue;
  201. }
  202. EnumWindows(pid_enum_proc, (LPARAM)&info);
  203. if (info.found)
  204. taskkill_message_printfW(STRING_CLOSE_PID_SEARCH, pid);
  205. else
  206. {
  207. taskkill_message_printfW(STRING_SEARCH_FAILED, task_list[i]);
  208. status_code = 128;
  209. }
  210. }
  211. else
  212. {
  213. DWORD index;
  214. BOOL found_process = FALSE;
  215. for (index = 0; index < pid_list_size; index++)
  216. {
  217. WCHAR process_name[MAX_PATH];
  218. if (get_process_name_from_pid(pid_list[index], process_name, MAX_PATH) &&
  219. !wcsicmp(process_name, task_list[i]))
  220. {
  221. struct pid_close_info info = { pid_list[index] };
  222. found_process = TRUE;
  223. if (pid_list[index] == self_pid)
  224. {
  225. taskkill_message(STRING_SELF_TERMINATION);
  226. status_code = 1;
  227. continue;
  228. }
  229. EnumWindows(pid_enum_proc, (LPARAM)&info);
  230. taskkill_message_printfW(STRING_CLOSE_PROC_SRCH, process_name, pid_list[index]);
  231. }
  232. }
  233. if (!found_process)
  234. {
  235. taskkill_message_printfW(STRING_SEARCH_FAILED, task_list[i]);
  236. status_code = 128;
  237. }
  238. }
  239. }
  240. HeapFree(GetProcessHeap(), 0, pid_list);
  241. return status_code;
  242. }
  243. static int terminate_processes(void)
  244. {
  245. DWORD *pid_list, pid_list_size;
  246. DWORD self_pid = GetCurrentProcessId();
  247. unsigned int i;
  248. int status_code = 0;
  249. pid_list = enumerate_processes(&pid_list_size);
  250. if (!pid_list)
  251. {
  252. taskkill_message(STRING_ENUM_FAILED);
  253. return 1;
  254. }
  255. for (i = 0; i < task_count; i++)
  256. {
  257. WCHAR *p = task_list[i];
  258. BOOL is_numeric = TRUE;
  259. /* Determine whether the string is not numeric. */
  260. while (*p)
  261. {
  262. if (!iswdigit(*p++))
  263. {
  264. is_numeric = FALSE;
  265. break;
  266. }
  267. }
  268. if (is_numeric)
  269. {
  270. DWORD pid = wcstol(task_list[i], NULL, 10);
  271. HANDLE process;
  272. if (pid == self_pid)
  273. {
  274. taskkill_message(STRING_SELF_TERMINATION);
  275. status_code = 1;
  276. continue;
  277. }
  278. process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
  279. if (!process)
  280. {
  281. taskkill_message_printfW(STRING_SEARCH_FAILED, task_list[i]);
  282. status_code = 128;
  283. continue;
  284. }
  285. if (!TerminateProcess(process, 0))
  286. {
  287. taskkill_message_printfW(STRING_TERMINATE_FAILED, task_list[i]);
  288. status_code = 1;
  289. CloseHandle(process);
  290. continue;
  291. }
  292. taskkill_message_printfW(STRING_TERM_PID_SEARCH, pid);
  293. CloseHandle(process);
  294. }
  295. else
  296. {
  297. DWORD index;
  298. BOOL found_process = FALSE;
  299. for (index = 0; index < pid_list_size; index++)
  300. {
  301. WCHAR process_name[MAX_PATH];
  302. if (get_process_name_from_pid(pid_list[index], process_name, MAX_PATH) &&
  303. !wcsicmp(process_name, task_list[i]))
  304. {
  305. HANDLE process;
  306. if (pid_list[index] == self_pid)
  307. {
  308. taskkill_message(STRING_SELF_TERMINATION);
  309. status_code = 1;
  310. continue;
  311. }
  312. process = OpenProcess(PROCESS_TERMINATE, FALSE, pid_list[index]);
  313. if (!process)
  314. {
  315. taskkill_message_printfW(STRING_SEARCH_FAILED, task_list[i]);
  316. status_code = 128;
  317. continue;
  318. }
  319. if (!TerminateProcess(process, 0))
  320. {
  321. taskkill_message_printfW(STRING_TERMINATE_FAILED, task_list[i]);
  322. status_code = 1;
  323. CloseHandle(process);
  324. continue;
  325. }
  326. found_process = TRUE;
  327. taskkill_message_printfW(STRING_TERM_PROC_SEARCH, task_list[i], pid_list[index]);
  328. CloseHandle(process);
  329. }
  330. }
  331. if (!found_process)
  332. {
  333. taskkill_message_printfW(STRING_SEARCH_FAILED, task_list[i]);
  334. status_code = 128;
  335. }
  336. }
  337. }
  338. HeapFree(GetProcessHeap(), 0, pid_list);
  339. return status_code;
  340. }
  341. static BOOL add_to_task_list(WCHAR *name)
  342. {
  343. static unsigned int list_size = 16;
  344. if (!task_list)
  345. {
  346. task_list = HeapAlloc(GetProcessHeap(), 0,
  347. list_size * sizeof(*task_list));
  348. if (!task_list)
  349. return FALSE;
  350. }
  351. else if (task_count == list_size)
  352. {
  353. void *realloc_list;
  354. list_size *= 2;
  355. realloc_list = HeapReAlloc(GetProcessHeap(), 0, task_list,
  356. list_size * sizeof(*task_list));
  357. if (!realloc_list)
  358. return FALSE;
  359. task_list = realloc_list;
  360. }
  361. task_list[task_count++] = name;
  362. return TRUE;
  363. }
  364. /* FIXME Argument processing does not match behavior observed on Windows.
  365. * Stringent argument counting and processing is performed, and unrecognized
  366. * options are detected as parameters when placed after options that accept one. */
  367. static BOOL process_arguments(int argc, WCHAR *argv[])
  368. {
  369. if (argc > 1)
  370. {
  371. int i;
  372. WCHAR *argdata;
  373. BOOL has_im = FALSE, has_pid = FALSE;
  374. /* Only the lone help option is recognized. */
  375. if (argc == 2)
  376. {
  377. argdata = argv[1];
  378. if ((*argdata == '/' || *argdata == '-') && !lstrcmpW(L"?", argdata + 1))
  379. {
  380. taskkill_message(STRING_USAGE);
  381. exit(0);
  382. }
  383. }
  384. for (i = 1; i < argc; i++)
  385. {
  386. BOOL got_im = FALSE, got_pid = FALSE;
  387. argdata = argv[i];
  388. if (*argdata != '/' && *argdata != '-')
  389. goto invalid;
  390. argdata++;
  391. if (!wcsicmp(L"t", argdata))
  392. WINE_FIXME("argument T not supported\n");
  393. if (!wcsicmp(L"f", argdata))
  394. force_termination = TRUE;
  395. /* Options /IM and /PID appear to behave identically, except for
  396. * the fact that they cannot be specified at the same time. */
  397. else if ((got_im = !wcsicmp(L"im", argdata)) ||
  398. (got_pid = !wcsicmp(L"pid", argdata)))
  399. {
  400. if (!argv[i + 1])
  401. {
  402. taskkill_message_printfW(STRING_MISSING_PARAM, argv[i]);
  403. taskkill_message(STRING_USAGE);
  404. return FALSE;
  405. }
  406. if (got_im) has_im = TRUE;
  407. if (got_pid) has_pid = TRUE;
  408. if (has_im && has_pid)
  409. {
  410. taskkill_message(STRING_MUTUAL_EXCLUSIVE);
  411. taskkill_message(STRING_USAGE);
  412. return FALSE;
  413. }
  414. if (!add_to_task_list(argv[i + 1]))
  415. return FALSE;
  416. i++;
  417. }
  418. else
  419. {
  420. invalid:
  421. taskkill_message(STRING_INVALID_OPTION);
  422. taskkill_message(STRING_USAGE);
  423. return FALSE;
  424. }
  425. }
  426. }
  427. else
  428. {
  429. taskkill_message(STRING_MISSING_OPTION);
  430. taskkill_message(STRING_USAGE);
  431. return FALSE;
  432. }
  433. return TRUE;
  434. }
  435. int __cdecl wmain(int argc, WCHAR *argv[])
  436. {
  437. int status_code = 0;
  438. if (!process_arguments(argc, argv))
  439. {
  440. HeapFree(GetProcessHeap(), 0, task_list);
  441. return 1;
  442. }
  443. if (force_termination)
  444. status_code = terminate_processes();
  445. else
  446. status_code = send_close_messages();
  447. HeapFree(GetProcessHeap(), 0, task_list);
  448. return status_code;
  449. }