Leaked source code of windows server 2003
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.

643 lines
18 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. start.c
  5. Abstract:
  6. Start command support
  7. --*/
  8. #include "cmd.h"
  9. extern UINT CurrentCP;
  10. extern unsigned DosErr;
  11. extern TCHAR CurDrvDir[] ;
  12. extern TCHAR SwitChar, PathChar;
  13. extern TCHAR ComExt[], ComSpecStr[];
  14. extern struct envdata * OriginalEnvironment;
  15. extern int LastRetCode;
  16. WORD
  17. GetProcessSubsystemType(
  18. HANDLE hProcess
  19. );
  20. STATUS
  21. getparam(
  22. IN BOOL LeadingSwitChar,
  23. IN OUT TCHAR **chptr,
  24. OUT TCHAR *param,
  25. IN int maxlen )
  26. /*++
  27. Routine Description:
  28. Copy the token starting at the current position to an output buffer.
  29. Terminate the copy on end-of-quotes, unquoted whitespace, unquoted
  30. switch character, or end-of-line
  31. Arguments:
  32. LeadingSwitChar - TRUE => we should terminate on unquoted switch character
  33. chptr - address of pointer to token. This is advanced over the parsed
  34. token
  35. param - destination of copy. String is NUL terminated
  36. maxlen - size of destination buffer
  37. Return Value:
  38. Return: STATUS of copy. Only failure is buffer exceeded.
  39. --*/
  40. {
  41. TCHAR *ch2;
  42. int count = 0;
  43. BOOL QuoteFound = FALSE;
  44. ch2 = param;
  45. //
  46. // get characters until a space, tab, slash, or end of line
  47. //
  48. while (TRUE) {
  49. //
  50. // If we're at the end of string, then there's no more token
  51. //
  52. if (**chptr == NULLC) {
  53. break;
  54. }
  55. //
  56. // If we're not quoting and we're at a whitespace or switch char
  57. // then there's no more token
  58. //
  59. if (!QuoteFound &&
  60. (_istspace( **chptr ) || (LeadingSwitChar && **chptr == SwitChar))) {
  61. break;
  62. }
  63. //
  64. // If there's still room in the buffer, copy in the character and note
  65. // if it's a quote or not
  66. //
  67. if (count < maxlen) {
  68. *ch2++ = (**chptr);
  69. if (**chptr == QUOTE) {
  70. QuoteFound = !QuoteFound;
  71. }
  72. }
  73. //
  74. // Advance over this character
  75. //
  76. (*chptr)++;
  77. count++;
  78. }
  79. //
  80. // If we've exceeded the buffer, display the error and return failure
  81. //
  82. if (count > maxlen) {
  83. **chptr = NULLC;
  84. *chptr = *chptr - count - 1;
  85. PutStdErr(MSG_START_INVALID_PARAMETER, ONEARG, *chptr);
  86. return(FAILURE);
  87. } else {
  88. *ch2 = NULLC;
  89. return(SUCCESS);
  90. }
  91. }
  92. /*
  93. Start /MIN /MAX "title" /P:x,y /S:dx,dy /D:directory /I cmd args
  94. */
  95. int
  96. Start(
  97. IN PTCHAR pszCmdLine
  98. )
  99. {
  100. STARTUPINFO StartupInfo;
  101. PROCESS_INFORMATION ChildProcessInfo;
  102. TCHAR szTitle[MAX_PATH];
  103. TCHAR szDirCur[MAX_PATH];
  104. TCHAR szT[MAXTOKLEN];
  105. TCHAR szPgmArgs[MAXTOKLEN];
  106. TCHAR szParam[MAXTOKLEN];
  107. TCHAR szPgm[MAXTOKLEN];
  108. TCHAR szPgmSave[MAXTOKLEN];
  109. TCHAR szTemp[MAXTOKLEN];
  110. TCHAR szPgmQuoted[MAXTOKLEN];
  111. HDESK hdesk;
  112. HWINSTA hwinsta;
  113. LPTSTR p;
  114. LPTSTR lpDesktop;
  115. DWORD cbDesktop = 0;
  116. DWORD cbWinsta = 0;
  117. TCHAR flags;
  118. BOOLEAN fNeedCmd;
  119. BOOLEAN fNeedExpl;
  120. BOOLEAN fKSwitch = FALSE;
  121. BOOLEAN fCSwitch = FALSE;
  122. PTCHAR pszCmdCur = NULL;
  123. PTCHAR pszDirCur = NULL;
  124. PTCHAR pszPgmArgs = NULL;
  125. PTCHAR pszEnv = NULL;
  126. TCHAR pszFakePgm[] = TEXT("cmd.exe");
  127. ULONG status;
  128. struct cmdnode cmdnd;
  129. DWORD CreationFlags;
  130. BOOL SafeFromControlC = FALSE;
  131. BOOL WaitForProcess = FALSE;
  132. BOOL b;
  133. DWORD uPgmLength;
  134. int retc;
  135. szPgm[0] = NULLC;
  136. szPgmArgs[0] = NULLC;
  137. pszDirCur = NULL;
  138. CreationFlags = CREATE_NEW_CONSOLE;
  139. StartupInfo.cb = sizeof( StartupInfo );
  140. StartupInfo.lpReserved = NULL;
  141. StartupInfo.lpDesktop = NULL;
  142. StartupInfo.lpTitle = NULL;
  143. StartupInfo.dwX = 0;
  144. StartupInfo.dwY = 0;
  145. StartupInfo.dwXSize = 0;
  146. StartupInfo.dwYSize = 0;
  147. StartupInfo.dwFlags = 0;
  148. StartupInfo.wShowWindow = SW_SHOWNORMAL;
  149. StartupInfo.cbReserved2 = 0;
  150. StartupInfo.lpReserved2 = NULL;
  151. StartupInfo.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
  152. StartupInfo.hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
  153. StartupInfo.hStdError = GetStdHandle( STD_ERROR_HANDLE );
  154. pszCmdCur = pszCmdLine;
  155. //
  156. // If there isn't a command line then make
  157. // up the default
  158. //
  159. if (pszCmdCur == NULL) {
  160. pszCmdCur = pszFakePgm;
  161. }
  162. while( *pszCmdCur != NULLC) {
  163. pszCmdCur = EatWS( pszCmdCur, NULL );
  164. if ((*pszCmdCur == QUOTE) && (StartupInfo.lpTitle == NULL)) {
  165. //
  166. // "Title" Parse off the quoted text, strip off quotes and set the
  167. // title for the child window.
  168. //
  169. if (getparam( TRUE, &pszCmdCur, szTitle, sizeof( szTitle ) / sizeof( TCHAR )) == FAILURE) {
  170. return FAILURE;
  171. }
  172. mystrcpy( szTitle, StripQuotes( szTitle ));
  173. StartupInfo.lpTitle = szTitle;
  174. } else if (*pszCmdCur == SwitChar) {
  175. pszCmdCur++;
  176. if (getparam( TRUE, &pszCmdCur, szParam, MAXTOKLEN) == FAILURE) {
  177. return(FAILURE);
  178. }
  179. if (!_tcsicmp( szParam, TEXT("ABOVENORMAL"))) {
  180. CreationFlags |= ABOVE_NORMAL_PRIORITY_CLASS;
  181. } else
  182. if (!_tcsicmp( szParam, TEXT("BELOWNORMAL"))) {
  183. CreationFlags |= BELOW_NORMAL_PRIORITY_CLASS;
  184. } else
  185. if (!_tcsicmp( szParam, TEXT("B"))) {
  186. WaitForProcess = FALSE;
  187. SafeFromControlC = TRUE;
  188. CreationFlags &= ~CREATE_NEW_CONSOLE;
  189. CreationFlags |= CREATE_NEW_PROCESS_GROUP;
  190. } else
  191. if (_totupper(szParam[0]) == TEXT('D')) {
  192. //
  193. // /Dpath or /D"path" or /D path or /D "path"
  194. //
  195. if (mystrlen( szParam + 1 ) > 0) {
  196. //
  197. // /Dpath or /D"path"
  198. //
  199. pszDirCur = szParam + 1;
  200. } else {
  201. //
  202. // /D path or /D "path"
  203. //
  204. pszCmdCur = EatWS( pszCmdCur, NULL );
  205. if (getparam( TRUE, &pszCmdCur, szParam, MAXTOKLEN) == FAILURE) {
  206. return FAILURE;
  207. }
  208. pszDirCur = szParam;
  209. }
  210. //
  211. // remove quotes if necessary
  212. //
  213. mystrcpy( szDirCur, StripQuotes( pszDirCur ));
  214. pszDirCur = szDirCur;
  215. if (mystrlen( pszDirCur ) > MAX_PATH) {
  216. PutStdErr( MSG_START_INVALID_PARAMETER, ONEARG, pszDirCur);
  217. return FAILURE;
  218. }
  219. } else
  220. if (_tcsicmp(szParam, TEXT("HIGH")) == 0) {
  221. CreationFlags |= HIGH_PRIORITY_CLASS;
  222. } else
  223. if (_totupper(szParam[0]) == TEXT('I')) {
  224. //
  225. // OriginalEnvironment was save at init time after path
  226. // and compsec were setup.
  227. // If OriginalEnvironment did not get allocated then
  228. // use the default.
  229. //
  230. if (OriginalEnvironment) {
  231. pszEnv = GetCapturedEnvironmentStrings( OriginalEnvironment );
  232. }
  233. } else
  234. if (_totupper(szParam[0]) == QMARK) {
  235. BeginHelpPause();
  236. PutStdOut(MSG_HELP_START, NOARGS);
  237. if (fEnableExtensions)
  238. PutStdOut(MSG_HELP_START_X, NOARGS);
  239. EndHelpPause();
  240. return( FAILURE );
  241. } else
  242. if (_tcsicmp(szParam, TEXT("LOW")) == 0) {
  243. CreationFlags |= IDLE_PRIORITY_CLASS;
  244. } else
  245. if (_tcsicmp(szParam, TEXT("MIN")) == 0) {
  246. StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
  247. StartupInfo.wShowWindow &= ~SW_SHOWNORMAL;
  248. StartupInfo.wShowWindow |= SW_SHOWMINNOACTIVE;
  249. } else
  250. if (_tcsicmp(szParam, TEXT("MAX")) == 0) {
  251. StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
  252. StartupInfo.wShowWindow &= ~SW_SHOWNORMAL;
  253. StartupInfo.wShowWindow |= SW_SHOWMAXIMIZED;
  254. } else
  255. if (_tcsicmp(szParam, TEXT("NORMAL")) == 0) {
  256. CreationFlags |= NORMAL_PRIORITY_CLASS;
  257. } else
  258. if (_tcsicmp(szParam, TEXT("REALTIME")) == 0) {
  259. CreationFlags |= REALTIME_PRIORITY_CLASS;
  260. } else
  261. if (_tcsicmp(szParam, TEXT("SEPARATE")) == 0) {
  262. #ifndef WIN95_CMD
  263. CreationFlags |= CREATE_SEPARATE_WOW_VDM;
  264. #endif // WIN95_CMD
  265. } else
  266. if (_tcsicmp(szParam, TEXT("SHARED")) == 0) {
  267. #ifndef WIN95_CMD
  268. CreationFlags |= CREATE_SHARED_WOW_VDM;
  269. #endif // WIN95_CMD
  270. } else
  271. if ( _tcsicmp(szParam, TEXT("WAIT")) == 0 ||
  272. _tcsicmp(szParam, TEXT("W")) == 0 ) {
  273. WaitForProcess = TRUE;
  274. } else {
  275. #ifdef FE_SB // KKBUGFIX
  276. mystrcpy(szT, TEXT("/"));
  277. #else
  278. mystrcpy(szT, TEXT("\\"));
  279. #endif
  280. mystrcat(szT, szParam );
  281. PutStdErr(MSG_INVALID_SWITCH, ONEARG, szT);
  282. return( FAILURE );
  283. }
  284. } else {
  285. if ((getparam(FALSE,&pszCmdCur,szPgm,sizeof( szPgm ) / sizeof( TCHAR ))) == FAILURE) {
  286. return( FAILURE );
  287. }
  288. //
  289. // if there are argument get them.
  290. //
  291. if (*pszCmdCur) {
  292. mystrcpy(szPgmArgs, pszCmdCur);
  293. pszPgmArgs = szPgmArgs;
  294. }
  295. //
  296. // there rest was args to pgm so move to eol
  297. //
  298. pszCmdCur = mystrchr(pszCmdCur, NULLC);
  299. }
  300. } // while
  301. //
  302. // If a program was not picked up do so now.
  303. //
  304. if (*szPgm == NULLC) {
  305. mystrcpy(szPgm, pszFakePgm);
  306. }
  307. //
  308. // Need both quoted and unquoted versions of program name
  309. //
  310. if (szPgm[0] != QUOTE && _tcschr(szPgm, SPACE)) {
  311. szPgmQuoted[0] = QUOTE;
  312. mystrcpy(&szPgmQuoted[1], StripQuotes(szPgm));
  313. mystrcat(szPgmQuoted, TEXT("\""));
  314. }
  315. else {
  316. mystrcpy(szPgmQuoted, szPgm);
  317. mystrcpy(szPgm, StripQuotes(szPgm));
  318. }
  319. //
  320. // see of a cmd.exe is needed to run a batch or internal command
  321. //
  322. fNeedCmd = FALSE;
  323. fNeedExpl = FALSE;
  324. //
  325. // is it an internal command?
  326. //
  327. if (FindCmd(CMDMAX, szPgm, &flags) != -1) {
  328. fNeedCmd = TRUE;
  329. } else {
  330. // Save szPgm since SearchForExecutable may override it.
  331. mystrcpy(szPgmSave, szPgm);
  332. //
  333. // Try to find it as a batch or exe file
  334. //
  335. cmdnd.cmdline = szPgm;
  336. status = SearchForExecutable(&cmdnd, szPgm);
  337. if (status == SFE_FAIL) {
  338. ExecError( szPgm ) ;
  339. return FAILURE;
  340. }
  341. if (status == SFE_NOTFND) {
  342. //
  343. // If we can find it, let Explorer have a shot.
  344. //
  345. fNeedExpl = TRUE;
  346. mystrcpy(szPgm, szPgmSave);
  347. } else if (status == SFE_ISBAT || status == SFE_ISDIR) {
  348. if (status == SFE_ISBAT)
  349. fNeedCmd = TRUE;
  350. else
  351. fNeedExpl = TRUE;
  352. }
  353. }
  354. if (!fNeedExpl) {
  355. if (fNeedCmd) {
  356. TCHAR *Cmd = GetEnvVar( ComSpecStr );
  357. if (Cmd == NULL) {
  358. PutStdErr( MSG_INVALID_COMSPEC, NOARGS );
  359. return FAILURE;
  360. }
  361. //
  362. // if a cmd.exe is need then szPgm need to be inserted before
  363. // the start of szPgms along with a /K parameter.
  364. // szPgm has to recieve the full path name of cmd.exe from
  365. // the compsec environment variable.
  366. //
  367. mystrcpy(szT, TEXT(" /K "));
  368. mystrcat(szT, szPgmQuoted);
  369. //
  370. // Get the location of the cmd processor from the environment
  371. //
  372. mystrcpy( szPgm, Cmd );
  373. mystrcpy( szPgmQuoted, szPgm );
  374. //
  375. // is there a command parameter at all
  376. //
  377. if (_tcsicmp(szT, TEXT(" /K ")) != 0) {
  378. //
  379. // If we have any arguments to add do so
  380. //
  381. if (*szPgmArgs) {
  382. if ((mystrlen(szPgmArgs) + mystrlen(szT)) < MAXTOKLEN) {
  383. mystrcat(szT, TEXT(" "));
  384. mystrcat(szT, szPgmArgs);
  385. } else {
  386. PutStdErr( MSG_CMD_FILE_NOT_FOUND, ONEARG, szPgmArgs);
  387. }
  388. }
  389. }
  390. pszPgmArgs = szT;
  391. }
  392. // Prepare for CreateProcess :
  393. // ImageName = <full path and command name ONLY>
  394. // CmdLine = <command name with NO FULL PATH> + <args as entered>
  395. mystrcpy(szTemp, szPgmQuoted);
  396. mystrcat(szTemp, TEXT(" "));
  397. mystrcat(szTemp, pszPgmArgs);
  398. pszPgmArgs = szTemp;
  399. }
  400. if (SafeFromControlC) {
  401. SetConsoleCtrlHandler(NULL,TRUE);
  402. }
  403. // Pass current Desktop to a new process.
  404. hwinsta = GetProcessWindowStation();
  405. GetUserObjectInformation( hwinsta, UOI_NAME, NULL, 0, &cbWinsta );
  406. hdesk = GetThreadDesktop ( GetCurrentThreadId() );
  407. GetUserObjectInformation (hdesk, UOI_NAME, NULL, 0, &cbDesktop);
  408. if ((lpDesktop = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cbDesktop + cbWinsta + 32) ) != NULL ) {
  409. p = lpDesktop;
  410. if ( GetUserObjectInformation (hwinsta, UOI_NAME, p, cbWinsta, &cbWinsta) ) {
  411. if (cbWinsta > 0) {
  412. p += ((cbWinsta/sizeof(TCHAR))-1);
  413. *p++ = L'\\';
  414. }
  415. if ( GetUserObjectInformation (hdesk, UOI_NAME, p, cbDesktop, &cbDesktop) ) {
  416. StartupInfo.lpDesktop = lpDesktop;
  417. }
  418. }
  419. }
  420. if (fNeedExpl) {
  421. b = FALSE;
  422. } else {
  423. b = CreateProcess( szPgm, // was NULL, wrong.
  424. pszPgmArgs,
  425. NULL,
  426. (LPSECURITY_ATTRIBUTES) NULL,
  427. TRUE, // bInherit
  428. #ifdef UNICODE
  429. CREATE_UNICODE_ENVIRONMENT |
  430. #endif // UNICODE
  431. CreationFlags,
  432. // CreationFlags
  433. pszEnv, // Environment
  434. pszDirCur, // Current directory
  435. &StartupInfo, // Startup Info Struct
  436. &ChildProcessInfo // ProcessInfo Struct
  437. );
  438. }
  439. if (SafeFromControlC) {
  440. SetConsoleCtrlHandler(NULL,FALSE);
  441. }
  442. HeapFree (GetProcessHeap(), 0, lpDesktop);
  443. if (b) {
  444. CloseHandle(ChildProcessInfo.hThread);
  445. } else {
  446. DosErr = GetLastError();
  447. if ( fNeedExpl ||
  448. (fEnableExtensions && DosErr == ERROR_BAD_EXE_FORMAT)) {
  449. SHELLEXECUTEINFO sei;
  450. memset(&sei, 0, sizeof(sei));
  451. //
  452. // Use the DDEWAIT flag so apps can finish their DDE conversation
  453. // before ShellExecuteEx returns. Otherwise, apps like Word will
  454. // complain when they try to exit, confusing the user.
  455. //
  456. sei.cbSize = sizeof(sei);
  457. sei.fMask = SEE_MASK_HASTITLE |
  458. SEE_MASK_NO_CONSOLE |
  459. SEE_MASK_FLAG_DDEWAIT |
  460. SEE_MASK_NOCLOSEPROCESS;
  461. if (CreationFlags & CREATE_NEW_CONSOLE) {
  462. sei.fMask &= ~SEE_MASK_NO_CONSOLE;
  463. }
  464. sei.lpFile = szPgm;
  465. sei.lpClass = StartupInfo.lpTitle;
  466. sei.lpParameters = szPgmArgs;
  467. sei.lpDirectory = pszDirCur;
  468. sei.nShow = StartupInfo.wShowWindow;
  469. try {
  470. b = ShellExecuteEx( &sei );
  471. if (b) {
  472. ChildProcessInfo.hProcess = sei.hProcess;
  473. } else if (!sei.hInstApp) {
  474. DosErr = ERROR_NOT_ENOUGH_MEMORY;
  475. } else if ((DWORD_PTR)sei.hInstApp == HINSTANCE_ERROR) {
  476. DosErr = ERROR_FILE_NOT_FOUND;
  477. } else {
  478. DosErr = HandleToUlong(sei.hInstApp);
  479. }
  480. } except (DosErr = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER) {
  481. b = FALSE;
  482. }
  483. }
  484. if (!b) {
  485. ExecError( szPgm ) ;
  486. return(FAILURE) ;
  487. }
  488. }
  489. if (ChildProcessInfo.hProcess != NULL) {
  490. if (WaitForProcess) {
  491. //
  492. // Wait for process to terminate, otherwise things become very
  493. // messy and confusing to the user (with 2 processes sharing
  494. // the console).
  495. //
  496. LastRetCode = WaitProc((ChildProcessInfo.hProcess) );
  497. } else {
  498. CloseHandle( ChildProcessInfo.hProcess );
  499. }
  500. }
  501. return(SUCCESS);
  502. }