Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

610 lines
17 KiB

  1. //Copyright (c) 1998 - 1999 Microsoft Corporation
  2. /*****************************************************************************
  3. *
  4. * TSKILL.C for Windows NT Terminal Server
  5. *
  6. * Description:
  7. *
  8. * tskill [processID] [/v] [/?]
  9. *
  10. ****************************************************************************/
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <ntddkbd.h>
  16. #include <ntddmou.h>
  17. #include <winstaw.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <locale.h>
  21. #include <utilsub.h>
  22. #include <allproc.h>
  23. #include "tskill.h"
  24. #include "printfoa.h"
  25. WCHAR user_string[MAX_IDS_LEN+1];
  26. #define MAXCBMSGBUFFER 2048
  27. WCHAR MsgBuf[MAXCBMSGBUFFER];
  28. USHORT help_flag = FALSE;
  29. USHORT v_flag = FALSE;
  30. USHORT a_flag = FALSE;
  31. HANDLE hServerName = SERVERNAME_CURRENT;
  32. WCHAR ServerName[MAX_IDS_LEN+1];
  33. WCHAR ipLogonId[MAX_IDS_LEN+1];
  34. TOKMAP ptm[] =
  35. {
  36. {L" ", TMFLAG_REQUIRED, TMFORM_STRING, MAX_IDS_LEN, user_string},
  37. {L"/server", TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, ServerName},
  38. {L"/id", TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN, &ipLogonId},
  39. {L"/?", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &help_flag},
  40. {L"/v", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &v_flag},
  41. {L"/a", TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT), &a_flag},
  42. {0, 0, 0, 0, 0}
  43. };
  44. /*
  45. * Local function prototypes.
  46. */
  47. void Usage( BOOLEAN bError );
  48. BOOLEAN KillProcessUseName();
  49. BOOLEAN KillProcessButConfirmTheID( ULONG TargetPid );
  50. BOOLEAN KillProcess(ULONG TargetPid);
  51. BOOLEAN MatchPattern(PWCHAR String, PWCHAR Pattern);
  52. BOOLEAN CheckImageNameAndKill(PTS_SYS_PROCESS_INFORMATION pProcessInfo);
  53. /*******************************************************************************
  54. *
  55. * main
  56. *
  57. ******************************************************************************/
  58. int __cdecl
  59. main(INT argc, CHAR **argv)
  60. {
  61. ULONG TargetPid;
  62. int i;
  63. DWORD rc;
  64. WCHAR *CmdLine, **argvW, *StopChar;
  65. setlocale(LC_ALL, ".OCP");
  66. /*
  67. * Massage the command line.
  68. */
  69. argvW = MassageCommandLine((DWORD)argc);
  70. if (argvW == NULL) {
  71. ErrorPrintf(IDS_ERROR_MALLOC);
  72. return(FAILURE);
  73. }
  74. /*
  75. * parse the cmd line without parsing the program name (argc-1, argv+1)
  76. */
  77. rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
  78. /*
  79. * Check for error from ParseCommandLine
  80. */
  81. if ( help_flag || rc ) {
  82. if ( !help_flag ) {
  83. Usage(TRUE);
  84. return(FAILURE);
  85. } else {
  86. Usage(FALSE);
  87. return(SUCCESS);
  88. }
  89. }
  90. // If no remote server was specified, then check if we are running under Terminal Server
  91. if ((!IsTokenPresent(ptm, L"/server") ) && (!AreWeRunningTerminalServices()))
  92. {
  93. ErrorPrintf(IDS_ERROR_NOT_TS);
  94. return(FAILURE);
  95. }
  96. /*
  97. * Open the specified server
  98. */
  99. if( ServerName[0] ) {
  100. hServerName = WinStationOpenServer( ServerName );
  101. if( hServerName == NULL ) {
  102. StringErrorPrintf(IDS_ERROR_SERVER,ServerName);
  103. PutStdErr( GetLastError(), 0 );
  104. return(FAILURE);
  105. }
  106. }
  107. /*
  108. * Check for the command line pid and convert to a ULONG
  109. */
  110. TargetPid = wcstoul(user_string, &StopChar, 10);
  111. if (!TargetPid) {
  112. //Get the process IDs and Kill.
  113. return (KillProcessUseName());
  114. } else if (*StopChar) {
  115. StringErrorPrintf(IDS_ERROR_BAD_PID_NUMBER, user_string);
  116. return(FAILURE);
  117. // end of getting the process ids from names
  118. } else {
  119. return( KillProcessButConfirmTheID( TargetPid ) );
  120. }
  121. } /* main() */
  122. /*******************************************************************************
  123. *
  124. * Usage
  125. *
  126. * Output the usage message for this utility.
  127. *
  128. * ENTRY:
  129. * bError (input)
  130. * TRUE if the 'invalid parameter(s)' message should preceed the usage
  131. * message and the output go to stderr; FALSE for no such error
  132. * string and output goes to stdout.
  133. *
  134. * EXIT:
  135. *
  136. *
  137. ******************************************************************************/
  138. void
  139. Usage( BOOLEAN bError )
  140. {
  141. if ( bError ) {
  142. ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
  143. }
  144. ErrorPrintf(IDS_USAGE1);
  145. ErrorPrintf(IDS_USAGE2);
  146. ErrorPrintf(IDS_USAGE3);
  147. ErrorPrintf(IDS_USAGE4);
  148. ErrorPrintf(IDS_USAGE5);
  149. ErrorPrintf(IDS_USAGE6);
  150. ErrorPrintf(IDS_USAGE7);
  151. ErrorPrintf(IDS_USAGE8);
  152. ErrorPrintf(IDS_USAGE9);
  153. ErrorPrintf(IDS_USAGEA);
  154. } /* Usage() */
  155. // ***********************************************************************
  156. // KillProcessUseName
  157. // Gets all the ProcessIDs of all the processes with the name passed
  158. // to the command line and kill them. Returns FALSE if there are no
  159. // processes running with the process names.
  160. //
  161. // ***********************************************************************
  162. BOOLEAN KillProcessUseName()
  163. {
  164. ULONG LogonId, ReturnLength, BufferOffset=0, ulLogonId;
  165. PBYTE pProcessBuffer;
  166. PTS_SYS_PROCESS_INFORMATION pProcessInfo;
  167. PCITRIX_PROCESS_INFORMATION pCitrixInfo;
  168. PTS_ALL_PROCESSES_INFO ProcessArray = NULL;
  169. ULONG NumberOfProcesses;
  170. ULONG j;
  171. BOOLEAN bRet;
  172. short nTasks=0;
  173. ULONG CurrentLogonId = (ULONG) -1;
  174. ULONG ProcessSessionId;
  175. DWORD dwError;
  176. // if a servername is specified and session id is not specified,
  177. // prompt an error.
  178. if (ServerName[0] && !ipLogonId[0] && !a_flag) {
  179. StringErrorPrintf(IDS_ERROR_ID_ABSENT, ServerName);
  180. return FAILURE;
  181. }
  182. // convert the input task name to lower
  183. _wcslwr(user_string);
  184. /*
  185. * Get current LogonId
  186. */
  187. CurrentLogonId = GetCurrentLogonId();
  188. // get the login id of the current user.
  189. //if (!WinStationQueryInformation(hServerName, LOGONID_CURRENT,
  190. // WinStationInformation, &WSInfo, sizeof(WSInfo), &ReturnLength)) {
  191. // fprintf(stdout, "Error QueryInfo failed");
  192. //}
  193. //convert the input logon id to ulong
  194. ulLogonId = wcstoul(ipLogonId, NULL, 10);
  195. //Use the input logon id if passed. If not use the current logon ID.
  196. //LogonId = (!wcscmp(ipLogonId,""))? WSInfo.LogonId:ulLogonId;
  197. LogonId = (!ipLogonId[0])? CurrentLogonId:ulLogonId;
  198. bRet = WinStationGetAllProcesses( hServerName,
  199. GAP_LEVEL_BASIC,
  200. &NumberOfProcesses,
  201. &ProcessArray);
  202. if (bRet == TRUE)
  203. {
  204. for (j=0; j<NumberOfProcesses; j++)
  205. {
  206. pProcessInfo = (PTS_SYS_PROCESS_INFORMATION)(ProcessArray[j].pTsProcessInfo);
  207. ProcessSessionId = pProcessInfo->SessionId;
  208. //if a_flag is set, check for the processes in all sessions;
  209. //if not, check for the processes in one LogonSession only.
  210. if(( ProcessSessionId == LogonId)|| a_flag)
  211. {
  212. if (CheckImageNameAndKill(pProcessInfo))
  213. {
  214. nTasks++;
  215. }
  216. }
  217. }
  218. //
  219. // Free ppProcessArray and all child pointers allocated by the client stub.
  220. //
  221. WinStationFreeGAPMemory(GAP_LEVEL_BASIC, ProcessArray, NumberOfProcesses);
  222. }
  223. else // Maybe a Hydra 4 server ?
  224. {
  225. //
  226. // Check the return code indicating that the interface is not available.
  227. //
  228. dwError = GetLastError();
  229. if (dwError != RPC_S_PROCNUM_OUT_OF_RANGE)
  230. {
  231. return (FALSE);
  232. }
  233. //Enumerate All the processes in order to get the ProcessId
  234. if (!WinStationEnumerateProcesses(hServerName, (PVOID *)&pProcessBuffer)) {
  235. if( pProcessBuffer)
  236. WinStationFreeMemory(pProcessBuffer);
  237. ErrorPrintf(IDS_ERROR_ENUM_PROCESS);
  238. return FAILURE;
  239. }
  240. //Make use of the ProcessBuffer to get the Process ID after
  241. //checking for a match in Logon User Name
  242. do {
  243. pProcessInfo = (PTS_SYS_PROCESS_INFORMATION)
  244. &(((PUCHAR)pProcessBuffer)[BufferOffset]);
  245. /*
  246. * Point to the CITRIX_INFORMATION which follows the Threads
  247. */
  248. pCitrixInfo = (PCITRIX_PROCESS_INFORMATION)
  249. (((PUCHAR)pProcessInfo) +
  250. SIZEOF_TS4_SYSTEM_PROCESS_INFORMATION +
  251. (SIZEOF_TS4_SYSTEM_THREAD_INFORMATION * (int)pProcessInfo->NumberOfThreads));
  252. if( pCitrixInfo->MagicNumber == CITRIX_PROCESS_INFO_MAGIC ) {
  253. ProcessSessionId = pCitrixInfo->LogonId;
  254. } else {
  255. ProcessSessionId = (ULONG)(-1);
  256. }
  257. //if a_flag is set, check for the processes in all sessions;
  258. //if not, check for the processes in one LogonSession only.
  259. if(( ProcessSessionId == LogonId)|| a_flag)
  260. {
  261. if (CheckImageNameAndKill(pProcessInfo))
  262. {
  263. nTasks++;
  264. }
  265. }
  266. BufferOffset += pProcessInfo->NextEntryOffset;
  267. } while (pProcessInfo->NextEntryOffset != 0);
  268. if( pProcessBuffer)
  269. {
  270. WinStationFreeMemory(pProcessBuffer);
  271. }
  272. }
  273. if(!nTasks)
  274. {
  275. StringErrorPrintf(IDS_ERROR_BAD_PROCESS, user_string);
  276. return FAILURE;
  277. }
  278. return SUCCESS;
  279. }
  280. // ***********************************************************************
  281. BOOLEAN
  282. CheckImageNameAndKill(PTS_SYS_PROCESS_INFORMATION pProcessInfo)
  283. {
  284. WCHAR ImageName[MAXNAME + 2] = { 0 };
  285. PWCHAR p;
  286. ULONG TargetPid;
  287. BOOLEAN bRet = FALSE;
  288. ImageName[MAXNAME+1] = 0; //force the end of string
  289. if( pProcessInfo->ImageName.Length == 0 )
  290. {
  291. ImageName[0] = 0;
  292. }
  293. else if( pProcessInfo->ImageName.Length > MAXNAME * 2)
  294. {
  295. wcsncpy(ImageName, pProcessInfo->ImageName.Buffer, MAXNAME);
  296. }
  297. else
  298. {
  299. wcsncpy(ImageName, pProcessInfo->ImageName.Buffer, pProcessInfo->ImageName.Length/2);
  300. ImageName[pProcessInfo->ImageName.Length/2] = 0;
  301. }
  302. //convert the imagename to lower
  303. _wcslwr(ImageName);
  304. if(ImageName != NULL) {
  305. p = wcschr(ImageName,'.');
  306. if (p)
  307. p[0] = L'\0';
  308. //get the ProcessID if the imagename matches
  309. if(MatchPattern(ImageName, user_string) ) {
  310. TargetPid = (ULONG)(ULONG_PTR)(pProcessInfo->UniqueProcessId);
  311. bRet = TRUE;
  312. KillProcess(TargetPid);
  313. }
  314. }
  315. return bRet;
  316. }
  317. // ***********************************************************************
  318. // KillProcess:
  319. // Kills the process with the specific ProcessID.
  320. // ***********************************************************************
  321. BOOLEAN KillProcess(ULONG TargetPid)
  322. {
  323. DWORD rc;
  324. /*
  325. * Kill the specified process.
  326. */
  327. if (v_flag)
  328. Message(IDS_KILL_PROCESS, TargetPid);
  329. if ( !WinStationTerminateProcess( hServerName, TargetPid, 0 ) ) {
  330. rc = GetLastError();
  331. StringErrorPrintf(IDS_ERROR_KILL_PROCESS_FAILED, user_string);
  332. if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
  333. NULL, rc, 0, MsgBuf, MAXCBMSGBUFFER, NULL) != 0)
  334. {
  335. fwprintf( stderr, MsgBuf );
  336. }
  337. fwprintf( stderr, L"\n");
  338. return FAILURE;
  339. }
  340. return SUCCESS;
  341. }
  342. // ***********************************************************************
  343. // MatchPattern
  344. // Checks if the passed string matches with the pattern used
  345. // Return TRUE if it matches and FALSE if not.
  346. //
  347. // String(input)
  348. // String being checked for the match
  349. // processname (input)
  350. // Pattern used for the check
  351. // ***********************************************************************
  352. BOOLEAN
  353. MatchPattern(
  354. PWCHAR String,
  355. PWCHAR Pattern
  356. )
  357. {
  358. WCHAR c, p, l;
  359. for (; ;) {
  360. switch (p = *Pattern++) {
  361. case 0: // end of pattern
  362. return *String ? FALSE : TRUE; // if end of string TRUE
  363. case '*':
  364. while (*String) { // match zero or more char
  365. if (MatchPattern (String++, Pattern))
  366. return TRUE;
  367. }
  368. return MatchPattern (String, Pattern);
  369. case '?':
  370. if (*String++ == 0) // match any one char
  371. return FALSE; // not end of string
  372. break;
  373. case '[':
  374. if ( (c = *String++) == 0) // match char set
  375. return FALSE; // syntax
  376. c = towupper(c);
  377. l = 0;
  378. while (p = *Pattern++) {
  379. if (p == ']') // if end of char set, then
  380. return FALSE; // no match found
  381. if (p == '-') { // check a range of chars?
  382. p = *Pattern; // get high limit of range
  383. if (p == 0 || p == ']')
  384. return FALSE; // syntax
  385. if (c >= l && c <= p)
  386. break; // if in range, move on
  387. }
  388. l = p;
  389. if (c == p) // if char matches this element
  390. break; // move on
  391. }
  392. while (p && p != ']') // got a match in char set
  393. p = *Pattern++; // skip to end of set
  394. break;
  395. default:
  396. c = *String++;
  397. if (c != p) // check for exact char
  398. return FALSE; // not a match
  399. break;
  400. }
  401. }
  402. }
  403. // ***********************************************************************
  404. // KillProcessButConfirmTheID
  405. // Gets all the ProcessIDs of all the processes with the name passed
  406. // to the command line and kill them. Returns FALSE if there are no
  407. // processes running with the process names.
  408. //
  409. // ***********************************************************************
  410. BOOLEAN KillProcessButConfirmTheID( ULONG TargetPid )
  411. {
  412. ULONG BufferOffset=0;
  413. PBYTE pProcessBuffer;
  414. PTS_SYS_PROCESS_INFORMATION pProcessInfo;
  415. PTS_ALL_PROCESSES_INFO ProcessArray = NULL;
  416. ULONG NumberOfProcesses;
  417. ULONG j;
  418. BOOLEAN bRet;
  419. BOOLEAN bFound = FALSE;
  420. DWORD dwError;
  421. bRet = WinStationGetAllProcesses( hServerName,
  422. GAP_LEVEL_BASIC,
  423. &NumberOfProcesses,
  424. &ProcessArray);
  425. if (bRet == TRUE)
  426. {
  427. for (j=0; j<NumberOfProcesses; j++)
  428. {
  429. pProcessInfo = (PTS_SYS_PROCESS_INFORMATION)(ProcessArray[j].pTsProcessInfo);
  430. pProcessInfo->SessionId;
  431. //if a_flag is set, check for the processes in all sessions;
  432. //if not, check for the processes in one LogonSession only.
  433. if( pProcessInfo->UniqueProcessId == TargetPid )
  434. {
  435. KillProcess( TargetPid );
  436. bFound = TRUE;
  437. break;
  438. }
  439. }
  440. //
  441. // Free ppProcessArray and all child pointers allocated by the client stub.
  442. //
  443. WinStationFreeGAPMemory(GAP_LEVEL_BASIC, ProcessArray, NumberOfProcesses);
  444. }
  445. else // Maybe a Hydra 4 server ?
  446. {
  447. //
  448. // Check the return code indicating that the interface is not available.
  449. //
  450. dwError = GetLastError();
  451. if (dwError != RPC_S_PROCNUM_OUT_OF_RANGE)
  452. {
  453. return (FALSE);
  454. }
  455. //Enumerate All the processes in order to get the ProcessId
  456. if (!WinStationEnumerateProcesses(hServerName, (PVOID *)&pProcessBuffer)) {
  457. if( pProcessBuffer)
  458. WinStationFreeMemory(pProcessBuffer);
  459. ErrorPrintf(IDS_ERROR_ENUM_PROCESS);
  460. return FAILURE;
  461. }
  462. //Make use of the ProcessBuffer to get the Process ID after
  463. //checking for a match in Logon User Name
  464. do {
  465. pProcessInfo = (PTS_SYS_PROCESS_INFORMATION) &(((PUCHAR)pProcessBuffer)[BufferOffset]);
  466. if( pProcessInfo->UniqueProcessId == TargetPid )
  467. {
  468. KillProcess( TargetPid );
  469. bFound = TRUE;
  470. break;
  471. }
  472. BufferOffset += pProcessInfo->NextEntryOffset;
  473. } while (pProcessInfo->NextEntryOffset != 0);
  474. if( pProcessBuffer)
  475. {
  476. WinStationFreeMemory(pProcessBuffer);
  477. }
  478. }
  479. if(!bFound)
  480. {
  481. StringErrorPrintf(IDS_ERROR_BAD_PROCESS, user_string);
  482. return FAILURE;
  483. }
  484. return SUCCESS;
  485. }