main.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "win32.hpp"
  15. #include "winprocess.hpp"
  16. #include "Tlhelp32.h"
  17. #include <list>
  18. using namespace win32;
  19. struct Param1
  20. {
  21. Param1(unsigned _ctrlC)
  22. {
  23. HMODULE kernel=::GetModuleHandle("kernel32.dll");
  24. SetErrorMode=::GetProcAddress(kernel,"SetErrorMode");
  25. GenerateConsoleCtrlEvent=::GetProcAddress(kernel,"GenerateConsoleCtrlEvent");
  26. Sleep=::GetProcAddress(kernel,"Sleep");
  27. Exit=::GetProcAddress(kernel,"ExitProcess");
  28. ctrlC=_ctrlC;
  29. }
  30. FARPROC SetErrorMode;
  31. FARPROC GenerateConsoleCtrlEvent;
  32. FARPROC Sleep;
  33. FARPROC Exit;
  34. DWORD ctrlC;
  35. };
  36. #pragma code_seg(".eclfunc")
  37. #ifndef _WIN64
  38. static DWORD _declspec(naked) WINAPI ExitProc(LPVOID param)
  39. {
  40. _asm
  41. {
  42. push ebp
  43. mov ebp, esp
  44. sub esp, __LOCAL_SIZE
  45. mov esi,[param]
  46. push SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX
  47. call [esi]Param1.SetErrorMode
  48. push 0
  49. push CTRL_BREAK_EVENT
  50. call [esi]Param1.GenerateConsoleCtrlEvent
  51. push [esi]Param1.ctrlC
  52. call [esi]Param1.Sleep
  53. push 0
  54. call [esi]Param1.Exit
  55. mov esp, ebp
  56. pop ebp
  57. ret 4
  58. }
  59. }
  60. #else
  61. static DWORD WINAPI ExitProc(LPVOID param)
  62. {
  63. return 0;
  64. }
  65. #endif
  66. #pragma code_seg()
  67. class ProcessKillList: public ProcessList
  68. {
  69. public:
  70. ProcessKillList(): ProcessList(SYNCHRONIZE|PROCESS_QUERY_INFORMATION|PROCESS_TERMINATE)
  71. {
  72. _restart=false;
  73. _other=true;
  74. _killchildren=false;
  75. ctrlC=0;
  76. timeout=1000;
  77. }
  78. bool isEmpty() const
  79. {
  80. return empty();
  81. }
  82. void kill(ProcessPid &pid,DWORD mysession)
  83. {
  84. if (_killchildren) {
  85. HANDLE hProcessSnap = hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  86. if (hProcessSnap != INVALID_HANDLE_VALUE) {
  87. PROCESSENTRY32 pe32;
  88. memset(&pe32,0,sizeof(pe32));
  89. pe32.dwSize = sizeof(PROCESSENTRY32);
  90. if (Process32First(hProcessSnap, &pe32)) {
  91. do {
  92. if (pe32.th32ParentProcessID==pid.GetPid()) {
  93. ProcessPid childpid(pe32.th32ProcessID,access);
  94. printf("child: ");
  95. kill(childpid,mysession);
  96. }
  97. } while (Process32Next(hProcessSnap, &pe32));
  98. }
  99. CloseHandle (hProcessSnap);
  100. }
  101. else
  102. printf("Could not take process snapshot, no children killed\n");
  103. }
  104. printf("%d - ",pid.GetPid());
  105. DWORD exit=0;
  106. if(::GetExitCodeProcess(pid,&exit) && exit!=STILL_ACTIVE)
  107. {
  108. printf("exited with code %d\n",exit);
  109. }
  110. else
  111. {
  112. if(pid.GetSession()!=mysession)
  113. {
  114. printf("different session (%d) - ",pid.GetSession());
  115. if(!_other)
  116. {
  117. printf(" skipped\n");
  118. return;
  119. }
  120. }
  121. if(Process(pid.GetPid(),PROCESS_TERMINATE).Terminate())
  122. {
  123. printf("terminated\n");
  124. }
  125. else
  126. {
  127. Error::perror();
  128. }
  129. }
  130. }
  131. void kill()
  132. {
  133. Param1 param(ctrlC);
  134. DWORD session=-1;
  135. ::ProcessIdToSessionId(::GetCurrentProcessId(),&session);
  136. bool empty=true;
  137. for(iterator ik=begin();ik!=end();ik++)
  138. {
  139. if(session==ik->GetSession())
  140. {
  141. RunRemote(*ik,ExitProc,4096,&param,sizeof(param));
  142. empty=false;
  143. }
  144. }
  145. if(!empty)
  146. {
  147. wait(timeout+ctrlC);
  148. }
  149. for(iterator it=begin();it!=end();it++)
  150. {
  151. kill(*it,session);
  152. }
  153. }
  154. void superuser()
  155. {
  156. ProcessToken token(TOKEN_ADJUST_PRIVILEGES);
  157. if(!token.AdjustPrivilege(SE_DEBUG_NAME,true))
  158. Error::perror();
  159. }
  160. void restart(const char* _path)
  161. {
  162. _restart=true;
  163. if(_path && *_path)
  164. {
  165. char path[MAX_PATH];
  166. _fullpath(path,_path,sizeof(path));
  167. startupDir.resize(mbstowcs(0,path,0)+1);
  168. mbstowcs(&startupDir.begin()[0],path,strlen(path));
  169. }
  170. }
  171. void setTimeout(unsigned msecs)
  172. {
  173. timeout=msecs;
  174. }
  175. void setCtrlC(unsigned msecs)
  176. {
  177. ctrlC=msecs;
  178. }
  179. void setOther(bool o)
  180. {
  181. _other=o;
  182. }
  183. void setKillChildren(bool o)
  184. {
  185. _killchildren=o;
  186. }
  187. protected:
  188. void wait(unsigned msecs)
  189. {
  190. HANDLE handles[MAXIMUM_WAIT_OBJECTS];
  191. unsigned count=0;
  192. for(iterator ih=begin();ih!=end();ih++)
  193. {
  194. handles[count++]=*ih;
  195. if(count>=MAXIMUM_WAIT_OBJECTS)
  196. {
  197. if(::WaitForMultipleObjects(count,handles,TRUE,msecs)==WAIT_FAILED)
  198. Error::perror();
  199. count=0;
  200. }
  201. }
  202. if(count)
  203. if(::WaitForMultipleObjects(count,handles,TRUE,msecs)==WAIT_FAILED)
  204. {
  205. Error::perror();
  206. }
  207. }
  208. void RunRemote(HANDLE h, void* func,unsigned fsize,void* param, unsigned psize)
  209. {
  210. Process proc(h,PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_VM_WRITE);
  211. RemoteMemoryAsync code(proc,func,fsize,PAGE_EXECUTE_READWRITE);
  212. RemoteMemoryAsync data(proc,param,psize,PAGE_READWRITE);
  213. if(!code || !data || !RemoteThread(proc, reinterpret_cast<PTHREAD_START_ROUTINE>((void*)code), data, 0, 4096))
  214. {
  215. Error::perror();
  216. }
  217. }
  218. bool _restart;
  219. bool _other;
  220. bool _killchildren;
  221. wstring startupDir;
  222. unsigned timeout,ctrlC;
  223. };
  224. void usage()
  225. {
  226. printf("pskill pid - kills a pid\n"
  227. "pskill [-sr] [-ttimeout] [-ctimeout] filename1 filename2 ... - kills instances of named processes\n"
  228. " -t Time in seconds to wait for the process to exit, default is 2 seconds.\n"
  229. " -c Send Ctr-C signal first, the process has <timeout> seconds to exit.\n"
  230. " -s Enable SE_DEBUG privilege so the system processes can be killed.\n"
  231. " -o Terminate processes from the same terminal session only.\n"
  232. " -a Terminate child processes also.\n"
  233. " -r[dir] Restart the process with the new working directory specified.\n");
  234. exit(2);
  235. }
  236. int main(int argc, char** argv)
  237. {
  238. try
  239. {
  240. ProcessKillList procs;
  241. for(int i=1;i<argc;i++)
  242. {
  243. if(argv[i][0]=='-' || argv[i][0]=='/')
  244. {
  245. const char* arg=argv[i]+1;
  246. switch(tolower(*arg))
  247. {
  248. case 's':
  249. procs.superuser();
  250. break;
  251. case 'r':
  252. procs.restart(arg+1);
  253. break;
  254. case 't':
  255. procs.setTimeout(1000*atoi(arg+1));
  256. break;
  257. case 'c':
  258. procs.setCtrlC(1000*atoi(arg+1));
  259. break;
  260. case 'o':
  261. procs.setOther(false);
  262. break;
  263. case 'a':
  264. procs.setKillChildren(true);
  265. break;
  266. default:
  267. usage();
  268. }
  269. }
  270. else
  271. {
  272. const char* arg=argv[i];
  273. if(atoi(arg))
  274. {
  275. printf("Killing process %s\n", arg);
  276. procs.add(atoi(arg));
  277. }
  278. else
  279. {
  280. char imagepath[MAX_PATH];
  281. if(::SearchPath(NULL,arg,".exe",sizeof(imagepath),imagepath,NULL))
  282. {
  283. printf("Killing processes matching %s\n", imagepath);
  284. procs.add(imagepath);
  285. }
  286. else
  287. {
  288. printf("Can not find %s\n", arg);
  289. }
  290. }
  291. }
  292. }
  293. if(!procs.isEmpty())
  294. procs.kill();
  295. }
  296. catch(Error& e)
  297. {
  298. printf(e.GetMessage());
  299. }
  300. return 0;
  301. }