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.

592 lines
20 KiB

  1. /***
  2. *popen.c - initiate a pipe and a child command
  3. *
  4. * Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * Defines _popen() and _pclose().
  8. *
  9. *Revision History:
  10. * 01-06-89 GJF Initial version (I plead temporary insanity).
  11. * 01-09-89 GJF Fixed several bugs.
  12. * 01-10-89 GJF Implemented several improvements from Trapper.
  13. * 01-12-89 GJF Added underscores to function names. Also, in _pclose,
  14. * pstream must be close before the cwait call if it is
  15. * attached to the write handle of the pipe (otherwise,
  16. * may get a deadlock).
  17. * 01-13-89 GJF Added multi-thread/dll support.
  18. * 02-09-89 GJF Prevent child process from inheriting unwanted handles.
  19. * Also, always close pstream before doing the cwait.
  20. * 05-10-89 GJF Ported to 386 (OS/2 2.0)
  21. * 08-14-89 GJF Use DOSCALLS.H for API prototypes, fixed _rotl call
  22. * in _pclose (rotate 24 bits for 386!), re-tested.
  23. * 11-16-89 GJF Changed DOS32SETFILEHSTATE to DOS32SETFHSTATE
  24. * 11-20-89 GJF Added const attribute to types of _popen()'s args.
  25. * Also, fixed copyright.
  26. * 03-19-90 GJF Replaced _LOAD_DS with _CALLTYPE1 and added #include
  27. * <cruntime.h>.
  28. * 03-26-90 GJF Made ibtab() and setinherit() _CALLTYPE4.
  29. * 07-25-90 SBM Compiles cleanly with -W3 (removed unreferenced
  30. * variables), removed '32' from API names
  31. * 08-13-90 SBM Compiles cleanly with -W3 with new build of compiler
  32. * 10-03-90 GJF New-style function declarators.
  33. * 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h>
  34. * 12-06-90 SRW Added _CRUISER_ and _WIN32 conditionals.
  35. * 01-18-91 GJF ANSI naming.
  36. * 02-25-91 SRW Renamed _get_free_osfhnd to be _alloc_osfhnd [_WIN32_]
  37. * 09-29-91 GJF Picked up NT implementation (_WIN32_).
  38. * 04-06-92 SRW Fixed to not rely on setinherit function (_WIN32_).
  39. * 04-28-92 DJM ifndef for POSIX
  40. * 05-06-92 GJF Set _osfile[stddup] so that _get_osfhandle knows it's
  41. * open (bug found by Markl).
  42. * 05-15-92 GJF Fixed regression Markl found - _close(stddup) to ensure
  43. * that _osfile[] entry is cleared.
  44. * 01-07-93 GJF Substantially revised: purged Cruiser support, removed
  45. * needlessly repeated API calls, closed down a pipe
  46. * handle accidently left open at end of _popen, removed
  47. * reduntant CloseHandle call from _pclose, tried to clean
  48. * up the format and reduce the number of silly casts,
  49. * and added or revised many comments.
  50. * 04-06-93 SKS Replace _CRTAPI* with __cdecl
  51. * 04-10-93 GJF Removed redundant close of child process handle in
  52. * _pclose().
  53. * 12-07-93 CFW Wide char enable.
  54. * 07-26-94 CFW Bug fix #14666, make data global so _wpopen sees it.
  55. * 01-10-95 CFW Debug CRT allocs.
  56. * 01-16-95 SKS Assume command.com for Win95, but cmd.exe for Win. NT.
  57. * 02-22-95 GJF Replaced WPRFLAG with _UNICODE.
  58. * 06-12-95 GJF Replaced _osfile[] and _osfhnd[] with _osfile() and
  59. * _osfhnd() (macros referencing fields in the ioinfo
  60. * struct).
  61. * 02-17-98 GJF Changes for Win64:removed so long casts
  62. * 02-25-98 GJF Exception-safe locking.
  63. * 01-19-00 GB Made popen 100% multithreaded.
  64. * 02-20-01 PML vs7#172586 Avoid _RT_LOCK by preallocating all locks
  65. * that will be required, and returning failure back on
  66. * inability to allocate a lock.
  67. * 02-19-01 GB Added check for return value of malloc.
  68. * 05-30-01 BWT Fix handle leak in popen - close thread handle we don't need.
  69. *
  70. *******************************************************************************/
  71. #ifndef _POSIX_
  72. #include <cruntime.h>
  73. #include <stdio.h>
  74. #include <stdlib.h>
  75. #include <malloc.h>
  76. #include <process.h>
  77. #include <io.h>
  78. #include <fcntl.h>
  79. #include <internal.h>
  80. #include <errno.h>
  81. #include <msdos.h>
  82. #include <mtdll.h>
  83. #include <oscalls.h>
  84. #include <tchar.h>
  85. #include <dbgint.h>
  86. /* size for pipe buffer
  87. */
  88. #define PSIZE 1024
  89. #define STDIN 0
  90. #define STDOUT 1
  91. #define SLASH _T("\\")
  92. #define SLASHCHAR _T('\\')
  93. #define XSLASHCHAR _T('/')
  94. #define DELIMITER _T(";")
  95. /* definitions for table of stream pointer - process handle pairs. the table
  96. * is created, maintained and accessed by the idtab function. _popen and
  97. * _pclose gain access to table entries only by calling idtab. Note that the
  98. * table is expanded as necessary (by idtab) and free table entries are reused
  99. * (an entry is free if its stream field is NULL), but the table is never
  100. * contracted.
  101. */
  102. typedef struct {
  103. FILE *stream;
  104. intptr_t prochnd;
  105. } IDpair;
  106. /* number of entries in idpairs table
  107. */
  108. #ifndef _UNICODE
  109. unsigned __idtabsiz = 0;
  110. #else
  111. extern unsigned __idtabsiz;
  112. #endif
  113. /* pointer to first table entry
  114. */
  115. #ifndef _UNICODE
  116. IDpair *__idpairs = NULL;
  117. #else
  118. extern IDpair *__idpairs;
  119. #endif
  120. /* function to find specified table entries. also, creates and maintains
  121. * the table.
  122. */
  123. static IDpair * __cdecl idtab(FILE *);
  124. /***
  125. *FILE *_popen(cmdstring,type) - initiate a pipe and a child command
  126. *
  127. *Purpose:
  128. * Creates a pipe and asynchronously executes a child copy of the command
  129. * processor with cmdstring (see system()). If the type string contains
  130. * an 'r', the calling process can read child command's standard output
  131. * via the returned stream. If the type string contains a 'w', the calling
  132. * process can write to the child command's standard input via the
  133. * returned stream.
  134. *
  135. *Entry:
  136. * _TSCHAR *cmdstring - command to be executed
  137. * _TSCHAR *type - string of the form "r|w[b|t]", determines the mode
  138. * of the returned stream (i.e., read-only vs write-only,
  139. * binary vs text mode)
  140. *
  141. *Exit:
  142. * If successful, returns a stream associated with one end of the created
  143. * pipe (the other end of the pipe is associated with either the child
  144. * command's standard input or standard output).
  145. *
  146. * If an error occurs, NULL is returned.
  147. *
  148. *Exceptions:
  149. *
  150. *******************************************************************************/
  151. FILE * __cdecl _tpopen (
  152. const _TSCHAR *cmdstring,
  153. const _TSCHAR *type
  154. )
  155. {
  156. int phdls[2]; /* I/O handles for pipe */
  157. int ph_open[2]; /* flags, set if correspond phdls is open */
  158. int i1; /* index into phdls[] */
  159. int i2; /* index into phdls[] */
  160. int tm = 0; /* flag indicating text or binary mode */
  161. int stdhdl; /* either STDIN or STDOUT */
  162. HANDLE newhnd; /* ...in calls to DuplicateHandle API */
  163. FILE *pstream = NULL; /* stream to be associated with pipe */
  164. HANDLE prochnd; /* handle for current process */
  165. _TSCHAR *cmdexe; /* pathname for the command processor */
  166. intptr_t childhnd; /* handle for child process (cmd.exe) */
  167. IDpair *locidpair; /* pointer to IDpair table entry */
  168. _TSCHAR *buf = NULL, *pfin, *env;
  169. _TSCHAR *CommandLine;
  170. /* Info for spawning the child. */
  171. STARTUPINFO StartupInfo; /* Info for spawning a child */
  172. BOOL childstatus = 0;
  173. PROCESS_INFORMATION ProcessInfo; /* child process information */
  174. #ifdef _MT
  175. int fh_lock_held = 0;
  176. int popen_lock_held = 0;
  177. #endif
  178. /* first check for errors in the arguments
  179. */
  180. if ( (cmdstring == NULL) || (type == NULL) || ((*type != 'w') &&
  181. (*type != _T('r'))) )
  182. goto error1;
  183. /* do the _pipe(). note that neither of the resulting handles will
  184. * be inheritable.
  185. */
  186. if ( *(type + 1) == _T('t') )
  187. tm = _O_TEXT;
  188. else if ( *(type + 1) == _T('b') )
  189. tm = _O_BINARY;
  190. tm |= _O_NOINHERIT;
  191. if ( _pipe( phdls, PSIZE, tm ) == -1 )
  192. goto error1;
  193. /* test *type and set stdhdl, i1 and i2 accordingly.
  194. */
  195. if ( *type == _T('w') ) {
  196. stdhdl = STDIN;
  197. i1 = 0;
  198. i2 = 1;
  199. }
  200. else {
  201. stdhdl = STDOUT;
  202. i1 = 1;
  203. i2 = 0;
  204. }
  205. #ifdef _MT
  206. /* ASSERT LOCK FOR IDPAIRS HERE!!!!
  207. */
  208. if ( !_mtinitlocknum( _POPEN_LOCK )) {
  209. _close( phdls[0] );
  210. _close( phdls[1] );
  211. return NULL;
  212. }
  213. _mlock( _POPEN_LOCK );
  214. __try
  215. {
  216. #endif
  217. /* set flags to indicate pipe handles are open. note, these are only
  218. * used for error recovery.
  219. */
  220. ph_open[ 0 ] = ph_open[ 1 ] = 1;
  221. /* get the process handle, it will be needed in some API calls
  222. */
  223. prochnd = GetCurrentProcess();
  224. if ( !DuplicateHandle( prochnd,
  225. (HANDLE)_osfhnd( phdls[i1] ),
  226. prochnd,
  227. &newhnd,
  228. 0L,
  229. TRUE, /* inheritable */
  230. DUPLICATE_SAME_ACCESS )
  231. ) {
  232. goto error2;
  233. }
  234. (void)_close( phdls[i1] );
  235. ph_open[ i1 ] = 0;
  236. /* associate a stream with phdls[i2]. note that if there are no
  237. * errors, pstream is the return value to the caller.
  238. */
  239. if ( (pstream = _tfdopen( phdls[i2], type )) == NULL )
  240. goto error2;
  241. /* next, set locidpair to a free entry in the idpairs table.
  242. */
  243. if ( (locidpair = idtab( NULL )) == NULL )
  244. goto error3;
  245. /* Find what to use. command.com or cmd.exe */
  246. if ( ((cmdexe = _tgetenv(_T("COMSPEC"))) == NULL &&
  247. ((errno == ENOENT) || (errno == EACCES))) )
  248. cmdexe = ( _osver & 0x8000 ) ? _T("command.com") : _T("cmd.exe");
  249. /*
  250. * Initialise the variable for passing to CreateProcess
  251. */
  252. memset(&StartupInfo, 0, sizeof(StartupInfo));
  253. StartupInfo.cb = sizeof(StartupInfo);
  254. /* Used by os for duplicating the Handles. */
  255. StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  256. StartupInfo.hStdInput = stdhdl == STDIN ? (HANDLE) newhnd
  257. : (HANDLE) _osfhnd(0);
  258. StartupInfo.hStdOutput = stdhdl == STDOUT ? (HANDLE) newhnd
  259. : (HANDLE) _osfhnd(1);
  260. StartupInfo.hStdError = (HANDLE) _osfhnd(2);
  261. if ((CommandLine = _malloc_crt( (_tcslen(cmdexe) + _tcslen(_T(" /c ")) + (_tcslen(cmdstring)) +1) * sizeof(_TSCHAR))) == NULL)
  262. goto error3;
  263. _tcscpy(CommandLine, cmdexe);
  264. _tcscat(CommandLine, _T(" /c "));
  265. _tcscat(CommandLine, cmdstring);
  266. /* Check if cmdexe can be accessed. If yes CreateProcess else try
  267. * Searching Path.
  268. */
  269. if (_taccess(cmdexe, 0) != -1) {
  270. childstatus = CreateProcess( (LPTSTR) cmdexe,
  271. (LPTSTR) CommandLine,
  272. NULL,
  273. NULL,
  274. TRUE,
  275. 0,
  276. NULL,
  277. NULL,
  278. &StartupInfo,
  279. &ProcessInfo
  280. );
  281. }
  282. else {
  283. env = _tgetenv(_T("PATH"));
  284. if ((buf = _malloc_crt(_MAX_PATH * sizeof(_TSCHAR))) == NULL)
  285. {
  286. _free_crt(CommandLine);
  287. goto error3;
  288. }
  289. #ifdef WPRFLAG
  290. while ( (env = _wgetpath(env, buf, _MAX_PATH -1)) && (*buf) ) {
  291. #else
  292. while ( (env = _getpath(env, buf, _MAX_PATH -1)) && (*buf) ) {
  293. #endif
  294. pfin = buf + _tcslen(buf) -1;
  295. #ifdef _MBCS
  296. if (*pfin == SLASHCHAR) {
  297. if (pfin != _mbsrchr(buf, SLASHCHAR))
  298. strcat(buf, SLASH);
  299. }
  300. else if (*pfin != XSLASHCHAR)
  301. strcat(buf, SLASH);
  302. #else /* _MBCS */
  303. if (*pfin != SLASHCHAR && *pfin != XSLASHCHAR)
  304. _tcscat(buf, SLASH);
  305. #endif
  306. /* check that the final path will be of legal size. if so,
  307. * build it. otherwise, return to the caller (return value
  308. * and errno rename set from initial call to _spawnve()).
  309. */
  310. if ( (_tcslen(buf) + _tcslen(cmdexe)) < _MAX_PATH )
  311. _tcscat(buf, cmdexe);
  312. else
  313. break;
  314. /* Check if buf can be accessed. If yes CreateProcess else try
  315. * again.
  316. */
  317. if (_taccess(buf, 0) != -1) {
  318. childstatus = CreateProcess( (LPTSTR) buf,
  319. CommandLine,
  320. NULL,
  321. NULL,
  322. TRUE,
  323. 0,
  324. NULL,
  325. NULL,
  326. &StartupInfo,
  327. &ProcessInfo
  328. );
  329. break;
  330. }
  331. }
  332. _free_crt(buf);
  333. }
  334. _free_crt(CommandLine);
  335. CloseHandle((HANDLE)newhnd);
  336. CloseHandle((HANDLE)ProcessInfo.hThread);
  337. /* check if the CreateProcess was sucessful.
  338. */
  339. if ( childstatus)
  340. childhnd = (intptr_t)ProcessInfo.hProcess;
  341. else
  342. goto error4;
  343. locidpair->prochnd = childhnd;
  344. locidpair->stream = pstream;
  345. /* success, return the stream to the caller
  346. */
  347. goto done;
  348. /**
  349. * error handling code. all detected errors end up here, entering
  350. * via a goto one of the labels. note that the logic is currently
  351. * a straight fall-thru scheme (e.g., if entered at error4, the
  352. * code for error4, error3,...,error1 is all executed).
  353. **********************************************************************/
  354. error4: /* make sure locidpair is reusable
  355. */
  356. locidpair->stream = NULL;
  357. error3: /* close pstream (also, clear ph_open[i2] since the stream
  358. * close will also close the pipe handle)
  359. */
  360. (void)fclose( pstream );
  361. ph_open[ i2 ] = 0;
  362. pstream = NULL;
  363. error2: /* close handles on pipe (if they are still open)
  364. */
  365. if ( ph_open[i1] )
  366. _close( phdls[i1] );
  367. if ( ph_open[i2] )
  368. _close( phdls[i2] );
  369. done:
  370. #ifdef _MT
  371. ;}
  372. __finally {
  373. _munlock(_POPEN_LOCK);
  374. }
  375. #endif
  376. error1:
  377. return pstream;
  378. }
  379. #ifndef _UNICODE
  380. /***
  381. *int _pclose(pstream) - wait on a child command and close the stream on the
  382. * associated pipe
  383. *
  384. *Purpose:
  385. * Closes pstream then waits on the associated child command. The
  386. * argument, pstream, must be the return value from a previous call to
  387. * _popen. _pclose first looks up the process handle of child command
  388. * started by that _popen and does a cwait on it. Then, it closes pstream
  389. * and returns the exit status of the child command to the caller.
  390. *
  391. *Entry:
  392. * FILE *pstream - file stream returned by a previous call to _popen
  393. *
  394. *Exit:
  395. * If successful, _pclose returns the exit status of the child command.
  396. * The format of the return value is that same as for cwait, except that
  397. * the low order and high order bytes are swapped.
  398. *
  399. * If an error occurs, -1 is returned.
  400. *
  401. *Exceptions:
  402. *
  403. *******************************************************************************/
  404. int __cdecl _pclose (
  405. FILE *pstream
  406. )
  407. {
  408. IDpair *locidpair; /* pointer to entry in idpairs table */
  409. int termstat; /* termination status word */
  410. int retval = -1; /* return value (to caller) */
  411. #ifdef _MT
  412. if (!_mtinitlocknum(_POPEN_LOCK))
  413. return -1;
  414. _mlock(_POPEN_LOCK);
  415. __try {
  416. #endif
  417. if ( (pstream == NULL) || ((locidpair = idtab(pstream)) == NULL) )
  418. /* invalid pstream, exit with retval == -1
  419. */
  420. goto done;
  421. /* close pstream
  422. */
  423. (void)fclose(pstream);
  424. /* wait on the child (copy of the command processor) and all of its
  425. * children.
  426. */
  427. if ( (_cwait(&termstat, locidpair->prochnd, _WAIT_GRANDCHILD) != -1) ||
  428. (errno == EINTR) )
  429. retval = termstat;
  430. /* Mark the IDpairtable entry as free (note: prochnd was closed by the
  431. * preceding call to _cwait).
  432. */
  433. locidpair->stream = NULL;
  434. locidpair->prochnd = 0;
  435. /* only return path!
  436. */
  437. done:
  438. #ifdef _MT
  439. ; }
  440. __finally {
  441. _munlock(_POPEN_LOCK);
  442. }
  443. #endif
  444. return(retval);
  445. }
  446. #endif /* _UNICODE */
  447. /***
  448. * static IDpair * idtab(FILE *pstream) - find an idpairs table entry
  449. *
  450. *Purpose:
  451. * Find an entry in the idpairs table. This function finds the entry the
  452. * idpairs table entry corresponding to pstream. In the case where pstream
  453. * is NULL, the entry being searched for is any free entry. In this case,
  454. * idtab will create the idpairs table if it doesn't exist, or expand it (by
  455. * exactly one entry) if there are no free entries.
  456. *
  457. * [MTHREAD NOTE: This routine assumes that the caller has acquired the
  458. * idpairs table lock.]
  459. *
  460. *Entry:
  461. * FILE *pstream - stream corresponding to table entry to be found (if NULL
  462. * then find any free table entry)
  463. *
  464. *Exit:
  465. * if successful, returns a pointer to the idpairs table entry. otherwise,
  466. * returns NULL.
  467. *
  468. *Exceptions:
  469. *
  470. *******************************************************************************/
  471. static IDpair * __cdecl idtab (
  472. FILE *pstream
  473. )
  474. {
  475. IDpair * pairptr; /* ptr to entry */
  476. IDpair * newptr; /* ptr to newly malloc'd memory */
  477. /* search the table. if table is empty, appropriate action should
  478. * fall out automatically.
  479. */
  480. for ( pairptr = __idpairs ; pairptr < (__idpairs+__idtabsiz) ; pairptr++ )
  481. if ( pairptr->stream == pstream )
  482. break;
  483. /* if we found an entry, return it.
  484. */
  485. if ( pairptr < (__idpairs + __idtabsiz) )
  486. return(pairptr);
  487. /* did not find an entry in the table. if pstream was NULL, then try
  488. * creating/expanding the table. otherwise, return NULL. note that
  489. * when the table is created or expanded, exactly one new entry is
  490. * produced. this must not be changed unless code is added to mark
  491. * the extra entries as being free (i.e., set their stream fields to
  492. * to NULL).
  493. */
  494. if ( (pstream != NULL) || ((newptr = (IDpair *)_realloc_crt((void *)__idpairs,
  495. (__idtabsiz + 1)*sizeof(IDpair))) == NULL) )
  496. /* either pstream was non-NULL or the attempt to create/expand
  497. * the table failed. in either case, return a NULL to indicate
  498. * failure.
  499. */
  500. return( NULL );
  501. __idpairs = newptr; /* new table ptr */
  502. pairptr = newptr + __idtabsiz; /* first new entry */
  503. __idtabsiz++; /* new table size */
  504. return( pairptr );
  505. }
  506. #endif /* _POSIX_ */