exec.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. exec.c
  9. Abstract:
  10. This module implements the exec() family of functions.
  11. Author:
  12. Evan Green 3-Apr-2013
  13. Environment:
  14. User Mode C Library
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include "libcp.h"
  20. #include <assert.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <limits.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <unistd.h>
  29. //
  30. // ---------------------------------------------------------------- Definitions
  31. //
  32. //
  33. // ------------------------------------------------------ Data Type Definitions
  34. //
  35. //
  36. // ----------------------------------------------- Internal Function Prototypes
  37. //
  38. //
  39. // -------------------------------------------------------------------- Globals
  40. //
  41. //
  42. // ------------------------------------------------------------------ Functions
  43. //
  44. LIBC_API
  45. int
  46. execl (
  47. const char *Path,
  48. const char *Argument0,
  49. ...
  50. )
  51. /*++
  52. Routine Description:
  53. This routine replaces the current process image with a new image.
  54. Arguments:
  55. Path - Supplies a pointer to a string containing the fully specified path
  56. of the file to execute.
  57. Argument0 - Supplies the first argument to execute, usually the same as
  58. the command name.
  59. ... - Supplies the arguments to the program. The argument list must be
  60. terminated with a NULL.
  61. Return Value:
  62. Does not return on success, the current process is gutted and replaced with
  63. the specified image.
  64. -1 on error, and the errno variable will be set to contain more information.
  65. --*/
  66. {
  67. char *Argument;
  68. char **ArgumentArray;
  69. ULONG ArgumentCount;
  70. ULONG ArgumentIndex;
  71. va_list ArgumentList;
  72. int Result;
  73. //
  74. // Determine the number of arguments.
  75. //
  76. ArgumentCount = 1;
  77. va_start(ArgumentList, Argument0);
  78. Argument = va_arg(ArgumentList, char *);
  79. while (Argument != NULL) {
  80. ArgumentCount += 1;
  81. Argument = va_arg(ArgumentList, char *);
  82. }
  83. va_end(ArgumentList);
  84. //
  85. // Create an array for them.
  86. //
  87. ArgumentArray = malloc((ArgumentCount + 1) * sizeof(char *));
  88. if (ArgumentArray == NULL) {
  89. errno = ENOMEM;
  90. return -1;
  91. }
  92. //
  93. // Copy the arguments into the array.
  94. //
  95. ArgumentIndex = 1;
  96. ArgumentArray[0] = (char *)Argument0;
  97. va_start(ArgumentList, Argument0);
  98. Argument = va_arg(ArgumentList, char *);
  99. while (Argument != NULL) {
  100. ArgumentArray[ArgumentIndex] = Argument;
  101. ArgumentIndex += 1;
  102. Argument = va_arg(ArgumentList, char *);
  103. }
  104. ArgumentArray[ArgumentIndex] = NULL;
  105. //
  106. // The environment is one more along.
  107. //
  108. Result = execve(Path,
  109. (char *const *)ArgumentArray,
  110. (char *const *)environ);
  111. free(ArgumentArray);
  112. return Result;
  113. }
  114. LIBC_API
  115. int
  116. execv (
  117. const char *Path,
  118. char *const Arguments[]
  119. )
  120. /*++
  121. Routine Description:
  122. This routine replaces the current process image with a new image.
  123. Arguments:
  124. Path - Supplies a pointer to a string containing the fully specified path
  125. of the file to execute.
  126. Arguments - Supplies an array of pointers to strings containing the
  127. arguments to pass to the program.
  128. Return Value:
  129. Does not return on success, the current process is gutted and replaced with
  130. the specified image.
  131. -1 on error, and the errno variable will be set to contain more information.
  132. --*/
  133. {
  134. return execve(Path, Arguments, (char *const *)environ);
  135. }
  136. LIBC_API
  137. int
  138. execle (
  139. const char *Path,
  140. const char *Argument0,
  141. ...
  142. )
  143. /*++
  144. Routine Description:
  145. This routine replaces the current process image with a new image. The
  146. parameters to this function also include the environment variables to use.
  147. Arguments:
  148. Path - Supplies a pointer to a string containing the fully specified path
  149. of the file to execute.
  150. Argument0 - Supplies the first argument to the program, usually the same
  151. as the program name.
  152. ... - Supplies the arguments to the program. The argument list must be
  153. terminated with a NULL. After the NULL an array of strings representing
  154. the environment is expected (think of it like a final argument after
  155. the NULL in the form const char *envp[]).
  156. Return Value:
  157. Does not return on success, the current process is gutted and replaced with
  158. the specified image.
  159. -1 on error, and the errno variable will be set to contain more information.
  160. --*/
  161. {
  162. char *Argument;
  163. char **ArgumentArray;
  164. ULONG ArgumentCount;
  165. ULONG ArgumentIndex;
  166. va_list ArgumentList;
  167. char *const *Environment;
  168. int Result;
  169. //
  170. // Determine the number of arguments.
  171. //
  172. ArgumentCount = 1;
  173. va_start(ArgumentList, Argument0);
  174. Argument = va_arg(ArgumentList, char *);
  175. while (Argument != NULL) {
  176. ArgumentCount += 1;
  177. Argument = va_arg(ArgumentList, char *);
  178. }
  179. va_end(ArgumentList);
  180. //
  181. // Create an array for them.
  182. //
  183. ArgumentArray = malloc((ArgumentCount + 1) * sizeof(char *));
  184. if (ArgumentArray == NULL) {
  185. errno = ENOMEM;
  186. return -1;
  187. }
  188. //
  189. // Copy the arguments into the array.
  190. //
  191. ArgumentIndex = 1;
  192. ArgumentArray[0] = (char *)Argument0;
  193. va_start(ArgumentList, Argument0);
  194. Argument = va_arg(ArgumentList, char *);
  195. while (Argument != NULL) {
  196. ArgumentArray[ArgumentIndex] = Argument;
  197. ArgumentIndex += 1;
  198. Argument = va_arg(ArgumentList, char *);
  199. }
  200. ArgumentArray[ArgumentIndex] = NULL;
  201. //
  202. // The environment is one more along.
  203. //
  204. Environment = va_arg(ArgumentList, char *const *);
  205. Result = execve(Path, (char *const *)ArgumentArray, Environment);
  206. free(ArgumentArray);
  207. return Result;
  208. }
  209. LIBC_API
  210. int
  211. execve (
  212. const char *Path,
  213. char *const Arguments[],
  214. char *const Environment[]
  215. )
  216. /*++
  217. Routine Description:
  218. This routine replaces the current process image with a new image. The
  219. parameters to this function also include the environment variables to use.
  220. Arguments:
  221. Path - Supplies a pointer to a string containing the fully specified path
  222. of the file to execute.
  223. Arguments - Supplies an array of pointers to strings containing the
  224. arguments to pass to the program.
  225. Environment - Supplies an array of pointers to strings containing the
  226. environment variables to pass to the program.
  227. Return Value:
  228. Does not return on success, the current process is gutted and replaced with
  229. the specified image.
  230. -1 on error, and the errno variable will be set to contain more information.
  231. --*/
  232. {
  233. UINTN ArgumentCount;
  234. UINTN ArgumentIndex;
  235. UINTN ArgumentValuesTotalLength;
  236. PSTR End;
  237. UINTN EnvironmentCount;
  238. UINTN EnvironmentValuesTotalLength;
  239. UINTN ExtraArguments;
  240. PSTR Interpreter;
  241. PSTR InterpreterArgument;
  242. UINTN InterpreterArgumentLength;
  243. UINTN InterpreterLength;
  244. PSTR Line;
  245. UINTN PathLength;
  246. PPROCESS_ENVIRONMENT ProcessEnvironment;
  247. FILE *Script;
  248. char **ShellArguments;
  249. KSTATUS Status;
  250. fflush(NULL);
  251. ArgumentCount = 0;
  252. ArgumentValuesTotalLength = 0;
  253. EnvironmentCount = 0;
  254. EnvironmentValuesTotalLength = 0;
  255. Script = NULL;
  256. ShellArguments = NULL;
  257. Line = NULL;
  258. PathLength = strlen(Path) + 1;
  259. while (Arguments[ArgumentCount] != NULL) {
  260. ArgumentValuesTotalLength += strlen(Arguments[ArgumentCount]) + 1;
  261. ArgumentCount += 1;
  262. }
  263. if (Environment != NULL) {
  264. while (Environment[EnvironmentCount] != NULL) {
  265. EnvironmentValuesTotalLength +=
  266. strlen(Environment[EnvironmentCount]) + 1;
  267. EnvironmentCount += 1;
  268. }
  269. }
  270. ProcessEnvironment = OsCreateEnvironment((PSTR)Path,
  271. PathLength,
  272. (PSTR *)Arguments,
  273. ArgumentValuesTotalLength,
  274. ArgumentCount,
  275. (PSTR *)Environment,
  276. EnvironmentValuesTotalLength,
  277. EnvironmentCount);
  278. if (ProcessEnvironment == NULL) {
  279. errno = ENOMEM;
  280. goto execveEnd;
  281. }
  282. Status = OsExecuteImage(ProcessEnvironment);
  283. if ((!KSUCCESS(Status)) && (Status != STATUS_UNKNOWN_IMAGE_FORMAT)) {
  284. errno = ClConvertKstatusToErrorNumber(Status);
  285. goto execveEnd;
  286. }
  287. //
  288. // Take a peek at the file. If it begins with #!, then interpret it as a
  289. // shell script.
  290. //
  291. Script = fopen(Path, "r");
  292. if (Script == NULL) {
  293. goto execveEnd;
  294. }
  295. Line = malloc(PATH_MAX + 6);
  296. if (Line == NULL) {
  297. errno = ENOMEM;
  298. goto execveEnd;
  299. }
  300. if (fgets(Line, PATH_MAX + 6, Script) == NULL) {
  301. goto execveEnd;
  302. }
  303. if ((Line[0] != '#') || (Line[1] != '!')) {
  304. errno = ENOEXEC;
  305. goto execveEnd;
  306. }
  307. Interpreter = Line + 2;
  308. while (isblank(*Interpreter) != 0) {
  309. Interpreter += 1;
  310. }
  311. if (*Interpreter == '\0') {
  312. errno = ENOEXEC;
  313. goto execveEnd;
  314. }
  315. //
  316. // Find the first space, newline, or ending and terminate the interpreter
  317. // path.
  318. //
  319. End = Interpreter;
  320. while ((*End != '\0') && (!isspace(*End))) {
  321. End += 1;
  322. }
  323. ExtraArguments = 1;
  324. InterpreterArgument = NULL;
  325. InterpreterArgumentLength = 0;
  326. //
  327. // If there's more to the line, then there's an argument. Treat the
  328. // remainder of the line as one big argument.
  329. //
  330. if ((*End != '\0') && (*End != '\n') && (*End != '\r')) {
  331. ExtraArguments += 1;
  332. *End = '\0';
  333. End += 1;
  334. while (isblank(*End)) {
  335. End += 1;
  336. }
  337. if ((*End != '\0') && (*End != '\n') && (*End != '\r')) {
  338. InterpreterArgument = End;
  339. while ((*End != '\r') && (*End != '\n') && (*End != '\0')) {
  340. End += 1;
  341. }
  342. InterpreterArgumentLength =
  343. (UINTN)End - (UINTN)InterpreterArgument + 1;
  344. *End = '\0';
  345. }
  346. //
  347. // No argument, just terminate the interpreter.
  348. //
  349. } else {
  350. *End = '\0';
  351. }
  352. //
  353. // Perform some basic (but not nearly foolproof) infinite loop detection
  354. // by looking to see if the interpreter is the same as the file itself.
  355. //
  356. if (strcmp(Interpreter, Path) == 0) {
  357. fprintf(stderr, "Error: Infinite exec loop for '%s'.\n", Path);
  358. errno = ENOEXEC;
  359. goto execveEnd;
  360. }
  361. if (access(Interpreter, X_OK) != 0) {
  362. goto execveEnd;
  363. }
  364. //
  365. // Create the arguments for a shell interpreter.
  366. //
  367. OsDestroyEnvironment(ProcessEnvironment);
  368. ProcessEnvironment = NULL;
  369. ShellArguments = malloc(
  370. sizeof(char *) * (ArgumentCount + ExtraArguments + 1));
  371. if (ShellArguments == NULL) {
  372. errno = ENOMEM;
  373. return -1;
  374. }
  375. InterpreterLength = strlen(Interpreter) + 1;
  376. ShellArguments[0] = Interpreter;
  377. ArgumentValuesTotalLength += InterpreterLength;
  378. if (ArgumentCount == 0) {
  379. ArgumentCount = 1;
  380. } else {
  381. ArgumentValuesTotalLength -= strlen(Arguments[0]) + 1;
  382. }
  383. if (InterpreterArgument != NULL) {
  384. ShellArguments[1] = InterpreterArgument;
  385. ArgumentValuesTotalLength += InterpreterArgumentLength;
  386. }
  387. ShellArguments[ExtraArguments] = (char *)Path;
  388. ArgumentValuesTotalLength += PathLength;
  389. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  390. ShellArguments[ArgumentIndex + ExtraArguments] =
  391. Arguments[ArgumentIndex];
  392. }
  393. ShellArguments[ArgumentIndex + ExtraArguments] = NULL;
  394. //
  395. // Add the extras for the interpreter and possibly its parameter.
  396. //
  397. ArgumentCount += ExtraArguments;
  398. //
  399. // Create the environment and try to execute this puppy. The interpreter
  400. // line cannot itself point to a script, that would be downright silly.
  401. //
  402. ProcessEnvironment = OsCreateEnvironment(Interpreter,
  403. InterpreterLength,
  404. (PSTR *)ShellArguments,
  405. ArgumentValuesTotalLength,
  406. ArgumentCount,
  407. (PSTR *)Environment,
  408. EnvironmentValuesTotalLength,
  409. EnvironmentCount);
  410. if (ProcessEnvironment == NULL) {
  411. errno = ENOMEM;
  412. goto execveEnd;
  413. }
  414. Status = OsExecuteImage(ProcessEnvironment);
  415. errno = ClConvertKstatusToErrorNumber(Status);
  416. execveEnd:
  417. if (ProcessEnvironment != NULL) {
  418. OsDestroyEnvironment(ProcessEnvironment);
  419. }
  420. if (Script != NULL) {
  421. fclose(Script);
  422. }
  423. if (Line != NULL) {
  424. free(Line);
  425. }
  426. if (ShellArguments != NULL) {
  427. free(ShellArguments);
  428. }
  429. return -1;
  430. }
  431. LIBC_API
  432. int
  433. execlp (
  434. const char *File,
  435. const char *Argument0,
  436. ...
  437. )
  438. /*++
  439. Routine Description:
  440. This routine replaces the current process image with a new image. If the
  441. given file is found but of an unrecognized binary format, then a shell
  442. interpreter will be launched and passed the file.
  443. Arguments:
  444. File - Supplies a pointer to a string containing the name of the executable,
  445. which will be searched for on the PATH if the string does not contain a
  446. slash.
  447. Argument0 - Supplies the first argument to the program. Additional arguments
  448. follow in the ellipses. The argument list must be terminated with a
  449. NULL.
  450. ... - Supplies any remaining arguments.
  451. Return Value:
  452. Does not return on success, the current process is gutted and replaced with
  453. the specified image.
  454. -1 on error, and the errno variable will be set to contain more information.
  455. --*/
  456. {
  457. char *Argument;
  458. char **ArgumentArray;
  459. ULONG ArgumentCount;
  460. ULONG ArgumentIndex;
  461. va_list ArgumentList;
  462. int Result;
  463. //
  464. // Determine the number of arguments.
  465. //
  466. ArgumentCount = 1;
  467. va_start(ArgumentList, Argument0);
  468. Argument = va_arg(ArgumentList, char *);
  469. while (Argument != NULL) {
  470. ArgumentCount += 1;
  471. Argument = va_arg(ArgumentList, char *);
  472. }
  473. va_end(ArgumentList);
  474. //
  475. // Create an array for them.
  476. //
  477. ArgumentArray = malloc((ArgumentCount + 1) * sizeof(char *));
  478. if (ArgumentArray == NULL) {
  479. errno = ENOMEM;
  480. return -1;
  481. }
  482. //
  483. // Copy the arguments into the array.
  484. //
  485. ArgumentIndex = 1;
  486. ArgumentArray[0] = (char *)Argument0;
  487. va_start(ArgumentList, Argument0);
  488. Argument = va_arg(ArgumentList, char *);
  489. while (Argument != NULL) {
  490. ArgumentArray[ArgumentIndex] = Argument;
  491. ArgumentIndex += 1;
  492. Argument = va_arg(ArgumentList, char *);
  493. }
  494. ArgumentArray[ArgumentIndex] = NULL;
  495. Result = execvpe(File, (char *const *)ArgumentArray, environ);
  496. free(ArgumentArray);
  497. return Result;
  498. }
  499. LIBC_API
  500. int
  501. execvp (
  502. const char *File,
  503. char *const Arguments[]
  504. )
  505. /*++
  506. Routine Description:
  507. This routine replaces the current process image with a new image. If the
  508. given file is found but of an unrecognized binary format, then a shell
  509. interpreter will be launched and passed the file.
  510. Arguments:
  511. File - Supplies a pointer to a string containing the name of the executable,
  512. which will be searched for on the PATH if the string does not contain a
  513. slash.
  514. Arguments - Supplies an array of pointers to strings containing the
  515. arguments to pass to the program.
  516. Return Value:
  517. Does not return on success, the current process is gutted and replaced with
  518. the specified image.
  519. -1 on error, and the errno variable will be set to contain more information.
  520. --*/
  521. {
  522. return execvpe(File, Arguments, (char *const *)environ);
  523. }
  524. LIBC_API
  525. int
  526. execvpe (
  527. const char *File,
  528. char *const Arguments[],
  529. char *const Environment[]
  530. )
  531. /*++
  532. Routine Description:
  533. This routine replaces the current process image with a new image. The
  534. parameters to this function also include the environment variables to use.
  535. If the given file is found but of an unrecognized binary format, then a
  536. shell interpreter will be launched and passed the file.
  537. Arguments:
  538. File - Supplies a pointer to a string containing the name of the executable,
  539. which will be searched for on the PATH if the string does not contain a
  540. slash.
  541. Arguments - Supplies an array of pointers to strings containing the
  542. arguments to pass to the program.
  543. Environment - Supplies an array of pointers to strings containing the
  544. environment variables to pass to the program.
  545. Return Value:
  546. Does not return on success, the current process is gutted and replaced with
  547. the specified image.
  548. -1 on error, and the errno variable will be set to contain more information.
  549. --*/
  550. {
  551. PSTR CombinedPath;
  552. size_t FileLength;
  553. int OriginalError;
  554. PSTR PathCopy;
  555. PSTR PathEntry;
  556. size_t PathEntryLength;
  557. PSTR PathVariable;
  558. char *Token;
  559. PathVariable = getenv("PATH");
  560. //
  561. // If the path contains a slash, use it directly without searching the
  562. // PATH variable.
  563. //
  564. if ((strchr(File, '/') != NULL) || (PathVariable == NULL) ||
  565. (*PathVariable == '\0')) {
  566. return execve(File, Arguments, Environment);
  567. }
  568. //
  569. // The path doesn't have a slash and there's a path variable, so try
  570. // searching the path.
  571. //
  572. PathCopy = strdup(PathVariable);
  573. if (PathCopy == NULL) {
  574. errno = ENOMEM;
  575. return -1;
  576. }
  577. FileLength = strlen(File);
  578. PathEntry = strtok_r(PathCopy, ":", &Token);
  579. while (PathEntry != NULL) {
  580. PathEntryLength = strlen(PathEntry);
  581. if (PathEntryLength == 0) {
  582. PathEntry = ".";
  583. PathEntryLength = 1;
  584. }
  585. if (PathEntry[PathEntryLength - 1] == '/') {
  586. PathEntryLength -= 1;
  587. }
  588. CombinedPath = malloc(PathEntryLength + FileLength + 2);
  589. if (CombinedPath == NULL) {
  590. errno = ENOMEM;
  591. break;
  592. }
  593. memcpy(CombinedPath, PathEntry, PathEntryLength);
  594. CombinedPath[PathEntryLength] = '/';
  595. strcpy(CombinedPath + PathEntryLength + 1, File);
  596. //
  597. // Recurse, though this won't recurse further because now there's a
  598. // slash in the path.
  599. //
  600. OriginalError = errno;
  601. if (access(CombinedPath, X_OK) == 0) {
  602. execvpe(CombinedPath, Arguments, Environment);
  603. } else {
  604. errno = OriginalError;
  605. }
  606. free(CombinedPath);
  607. //
  608. // Move to the next path entry.
  609. //
  610. PathEntry = strtok_r(NULL, ":", &Token);
  611. }
  612. if (errno == 0) {
  613. errno = ENOENT;
  614. }
  615. free(PathCopy);
  616. return -1;
  617. }
  618. //
  619. // --------------------------------------------------------- Internal Functions
  620. //