spawn.c 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
  1. /*++
  2. Copyright (c) 2017 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. spawn.c
  9. Abstract:
  10. This module implements the spawn module, which can be used to launch
  11. child processes from Chalk.
  12. Author:
  13. Evan Green 21-Jun-2017
  14. Environment:
  15. Chalk
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <assert.h>
  21. #include <errno.h>
  22. #include <fcntl.h>
  23. #include <signal.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28. #include "spawnp.h"
  29. //
  30. // --------------------------------------------------------------------- Macros
  31. //
  32. //
  33. // ---------------------------------------------------------------- Definitions
  34. //
  35. //
  36. // ------------------------------------------------------ Data Type Definitions
  37. //
  38. //
  39. // ----------------------------------------------- Internal Function Prototypes
  40. //
  41. VOID
  42. CkpChildProcessInit (
  43. PCK_VM Vm
  44. );
  45. VOID
  46. CkpChildProcessGet (
  47. PCK_VM Vm
  48. );
  49. VOID
  50. CkpChildProcessSet (
  51. PCK_VM Vm
  52. );
  53. VOID
  54. CkpChildProcessLaunch (
  55. PCK_VM Vm
  56. );
  57. VOID
  58. CkpChildProcessPoll (
  59. PCK_VM Vm
  60. );
  61. VOID
  62. CkpChildProcessWait (
  63. PCK_VM Vm
  64. );
  65. VOID
  66. CkpChildProcessCommunicate (
  67. PCK_VM Vm
  68. );
  69. VOID
  70. CkpChildProcessTerminate (
  71. PCK_VM Vm
  72. );
  73. VOID
  74. CkpChildProcessKill (
  75. PCK_VM Vm
  76. );
  77. INT
  78. CkpSpawnGetDescriptor (
  79. PCK_VM Vm,
  80. PCSTR Name,
  81. PSPAWN_DESCRIPTOR Descriptor
  82. );
  83. PSTR *
  84. CkpSpawnCreateEnvironment (
  85. PCK_VM Vm
  86. );
  87. INT
  88. CkpSpawnGetStringList (
  89. PCK_VM Vm,
  90. PCSTR Name,
  91. BOOL Optional,
  92. char ***NewList
  93. );
  94. INT
  95. CkpSpawnWait (
  96. PCK_VM Vm,
  97. PSPAWN_ATTRIBUTES Attributes,
  98. INT Milliseconds
  99. );
  100. INT
  101. CkpSpawnSetReturnCode (
  102. PCK_VM Vm,
  103. PSPAWN_ATTRIBUTES Attributes
  104. );
  105. VOID
  106. CkpDestroySpawnAttributes (
  107. PVOID Attributes
  108. );
  109. VOID
  110. CkpTearDownSpawnAttributes (
  111. PSPAWN_ATTRIBUTES Attributes
  112. );
  113. VOID
  114. CkpSpawnRaiseSpawnError (
  115. PCK_VM Vm,
  116. PSPAWN_ATTRIBUTES Attributes
  117. );
  118. VOID
  119. CkpSpawnRaiseError (
  120. PCK_VM Vm,
  121. PCSTR ExceptionType,
  122. PCSTR Message
  123. );
  124. //
  125. // -------------------------------------------------------------------- Globals
  126. //
  127. CK_VARIABLE_DESCRIPTION CkSpawnModuleValues[] = {
  128. {CkTypeInteger, "NONE", NULL, SPAWN_NONE},
  129. {CkTypeInteger, "DEVNULL", NULL, SPAWN_DEVNULL},
  130. {CkTypeInteger, "PIPE", NULL, SPAWN_PIPE},
  131. {CkTypeInteger, "OPTION_SHELL", NULL, SPAWN_OPTION_SHELL},
  132. {CkTypeInteger, "OPTION_CHECK", NULL, SPAWN_OPTION_CHECK},
  133. {CkTypeInteger, "OPTION_CLOSE_FDS", NULL, SPAWN_OPTION_CLOSE_FDS},
  134. {CkTypeInteger, "OPTION_NEW_SESSION", NULL, SPAWN_OPTION_NEW_SESSION},
  135. {CkTypeInteger, "DEBUG_BASIC_LAUNCH", NULL, SPAWN_DEBUG_BASIC_LAUNCH},
  136. {CkTypeInteger, "DEBUG_DETAILED_LAUNCH", NULL, SPAWN_DEBUG_DETAILED_LAUNCH},
  137. {CkTypeInteger, "DEBUG_IO", NULL, SPAWN_DEBUG_IO},
  138. {CkTypeInvalid, NULL, NULL, 0}
  139. };
  140. //
  141. // ------------------------------------------------------------------ Functions
  142. //
  143. BOOL
  144. CkPreloadSpawnModule (
  145. PCK_VM Vm
  146. )
  147. /*++
  148. Routine Description:
  149. This routine preloads the spawn module. It is called to make the presence
  150. of the os module known in cases where the module is statically linked.
  151. Arguments:
  152. Vm - Supplies a pointer to the virtual machine.
  153. Return Value:
  154. TRUE on success.
  155. FALSE on failure.
  156. --*/
  157. {
  158. return CkPreloadForeignModule(Vm, "spawn", NULL, NULL, CkpSpawnModuleInit);
  159. }
  160. VOID
  161. CkpSpawnModuleInit (
  162. PCK_VM Vm
  163. )
  164. /*++
  165. Routine Description:
  166. This routine populates the spawn module namespace.
  167. Arguments:
  168. Vm - Supplies a pointer to the virtual machine.
  169. Return Value:
  170. None.
  171. --*/
  172. {
  173. CkPushString(Vm, "SpawnError", 10);
  174. CkGetVariable(Vm, 0, "Exception");
  175. CkPushClass(Vm, 0, 0);
  176. CkSetVariable(Vm, 0, "SpawnError");
  177. CkPushString(Vm, "TimeoutExpired", 14);
  178. CkGetVariable(Vm, 0, "Exception");
  179. CkPushClass(Vm, 0, 0);
  180. CkSetVariable(Vm, 0, "TimeoutExpired");
  181. CkPushString(Vm, "ProcessExited", 13);
  182. CkGetVariable(Vm, 0, "Exception");
  183. CkPushClass(Vm, 0, 0);
  184. CkSetVariable(Vm, 0, "ProcessExited");
  185. CkPushString(Vm, "ChildProcessError", 17);
  186. CkGetVariable(Vm, 0, "Exception");
  187. CkPushClass(Vm, 0, 0);
  188. CkSetVariable(Vm, 0, "ChildProcessError");
  189. //
  190. // Register the functions and definitions.
  191. //
  192. CkDeclareVariables(Vm, 0, CkSpawnModuleValues);
  193. //
  194. // Create the LzmaEncoder class.
  195. //
  196. CkPushString(Vm, "ChildProcess", 12);
  197. CkGetVariable(Vm, 0, "Object");
  198. CkPushClass(Vm, 0, 2);
  199. CkPushValue(Vm, -1);
  200. CkSetVariable(Vm, 0, "ChildProcess");
  201. CkPushFunction(Vm, CkpChildProcessInit, "__init", 0, 0);
  202. CkPushString(Vm, "__init", 6);
  203. CkBindMethod(Vm, 1);
  204. CkPushFunction(Vm, CkpChildProcessInit, "__init", 1, 0);
  205. CkPushString(Vm, "__init", 6);
  206. CkBindMethod(Vm, 1);
  207. CkPushFunction(Vm, CkpChildProcessGet, "__get", 1, 0);
  208. CkPushString(Vm, "__get", 5);
  209. CkBindMethod(Vm, 1);
  210. CkPushFunction(Vm, CkpChildProcessSet, "__set", 2, 0);
  211. CkPushString(Vm, "__set", 5);
  212. CkBindMethod(Vm, 1);
  213. CkPushFunction(Vm, CkpChildProcessLaunch, "launch", 0, 0);
  214. CkPushString(Vm, "launch", 6);
  215. CkBindMethod(Vm, 1);
  216. CkPushFunction(Vm, CkpChildProcessPoll, "poll", 0, 0);
  217. CkPushString(Vm, "poll", 4);
  218. CkBindMethod(Vm, 1);
  219. CkPushFunction(Vm, CkpChildProcessWait, "wait", 1, 0);
  220. CkPushString(Vm, "wait", 4);
  221. CkBindMethod(Vm, 1);
  222. CkPushFunction(Vm, CkpChildProcessCommunicate, "communicate", 2, 0);
  223. CkPushString(Vm, "communicate", 11);
  224. CkBindMethod(Vm, 1);
  225. CkPushFunction(Vm, CkpChildProcessTerminate, "terminate", 0, 0);
  226. CkPushString(Vm, "terminate", 9);
  227. CkBindMethod(Vm, 1);
  228. CkPushFunction(Vm, CkpChildProcessGet, "kill", 0, 0);
  229. CkPushString(Vm, "kill", 4);
  230. CkBindMethod(Vm, 1);
  231. CkStackPop(Vm);
  232. return;
  233. }
  234. //
  235. // --------------------------------------------------------- Internal Functions
  236. //
  237. VOID
  238. CkpChildProcessInit (
  239. PCK_VM Vm
  240. )
  241. /*++
  242. Routine Description:
  243. This routine initializes a new ChildProcess instance.
  244. Arguments:
  245. Vm - Supplies a pointer to the virtual machine.
  246. Return Value:
  247. None.
  248. --*/
  249. {
  250. UINTN ArgumentCount;
  251. PSPAWN_ATTRIBUTES Attributes;
  252. ArgumentCount = CkGetStackSize(Vm) - 1;
  253. //
  254. // Create the dict information, and push an extra copy of the dict.
  255. //
  256. CkPushDict(Vm);
  257. CkPushValue(Vm, -1);
  258. CkSetField(Vm, 0);
  259. //
  260. // Create the attributes structure.
  261. //
  262. Attributes = malloc(sizeof(SPAWN_ATTRIBUTES));
  263. if (Attributes == NULL) {
  264. CkRaiseBasicException(Vm, "MemoryError", "");
  265. return;
  266. }
  267. memset(Attributes, 0, sizeof(SPAWN_ATTRIBUTES));
  268. Attributes->Stdin.Fd = -1;
  269. Attributes->Stdin.CloseFd = -1;
  270. Attributes->Stdin.ParentPipe = -1;
  271. Attributes->Stdout.Fd = -1;
  272. Attributes->Stdout.CloseFd = -1;
  273. Attributes->Stdout.ParentPipe = -1;
  274. Attributes->Stderr.Fd = -1;
  275. Attributes->Stderr.CloseFd = -1;
  276. Attributes->Stderr.ParentPipe = -1;
  277. CkPushData(Vm, Attributes, CkpDestroySpawnAttributes);
  278. CkSetField(Vm, 1);
  279. //
  280. // Set the optional args.
  281. //
  282. CkPushString(Vm, "args", 4);
  283. if (ArgumentCount == 1) {
  284. if (CkIsList(Vm, 1)) {
  285. CkPushValue(Vm, 1);
  286. //
  287. // If the argument isn't a list, create a list and wrap the argument
  288. // around it, so things like ChildProcess("ls -la") work.
  289. //
  290. } else {
  291. CkPushList(Vm);
  292. CkPushValue(Vm, 1);
  293. CkListSet(Vm, -2, 0);
  294. }
  295. } else {
  296. CkPushNull(Vm);
  297. }
  298. CkDictSet(Vm, -3);
  299. CkPushString(Vm, "stdin", 5);
  300. CkPushInteger(Vm, SPAWN_NONE);
  301. CkDictSet(Vm, -3);
  302. CkPushString(Vm, "stdout", 6);
  303. CkPushInteger(Vm, SPAWN_NONE);
  304. CkDictSet(Vm, -3);
  305. CkPushString(Vm, "stderr", 6);
  306. CkPushInteger(Vm, SPAWN_NONE);
  307. CkDictSet(Vm, -3);
  308. CkPushString(Vm, "options", 7);
  309. CkPushInteger(Vm, 0);
  310. CkDictSet(Vm, -3);
  311. CkPushString(Vm, "cwd", 3);
  312. CkPushNull(Vm);
  313. CkDictSet(Vm, -3);
  314. CkPushString(Vm, "env", 3);
  315. CkPushNull(Vm);
  316. CkDictSet(Vm, -3);
  317. CkPushString(Vm, "passFds", 7);
  318. CkPushNull(Vm);
  319. CkDictSet(Vm, -3);
  320. CkPushString(Vm, "executable", 10);
  321. CkPushNull(Vm);
  322. CkDictSet(Vm, -3);
  323. CkPushString(Vm, "return", 6);
  324. CkPushNull(Vm);
  325. CkDictSet(Vm, -3);
  326. CkPushString(Vm, "pid", 3);
  327. CkPushNull(Vm);
  328. CkDictSet(Vm, -3);
  329. return;
  330. }
  331. VOID
  332. CkpChildProcessGet (
  333. PCK_VM Vm
  334. )
  335. /*++
  336. Routine Description:
  337. This routine implements the __get function for the ChildProcess. It takes a
  338. key and returns a value.
  339. Arguments:
  340. Vm - Supplies a pointer to the virtual machine.
  341. Return Value:
  342. None.
  343. --*/
  344. {
  345. CkGetField(Vm, 0);
  346. CkPushValue(Vm, 1);
  347. if (CkDictGet(Vm, 2) == FALSE) {
  348. CkRaiseBasicException(Vm, "KeyError", "Key not found");
  349. return;
  350. }
  351. CkStackReplace(Vm, 0);
  352. return;
  353. }
  354. VOID
  355. CkpChildProcessSet (
  356. PCK_VM Vm
  357. )
  358. /*++
  359. Routine Description:
  360. This routine implements the __set function for the ChildProcess. It takes
  361. two arguments: a key and a value, and sets that value for the key in the
  362. ChildProcess. Returns null.
  363. Arguments:
  364. Vm - Supplies a pointer to the virtual machine.
  365. Return Value:
  366. None.
  367. --*/
  368. {
  369. CkGetField(Vm, 0);
  370. CkPushValue(Vm, 1);
  371. CkPushValue(Vm, 2);
  372. CkDictSet(Vm, 3);
  373. CkReturnNull(Vm);
  374. return;
  375. }
  376. VOID
  377. CkpChildProcessLaunch (
  378. PCK_VM Vm
  379. )
  380. /*++
  381. Routine Description:
  382. This routine starts the child process, if it has not yet been started.
  383. Arguments:
  384. Vm - Supplies a pointer to the virtual machine.
  385. Return Value:
  386. None.
  387. --*/
  388. {
  389. PSPAWN_ATTRIBUTES Attributes;
  390. UINTN Index;
  391. INT Status;
  392. CkGetField(Vm, 1);
  393. Attributes = CkGetData(Vm, -1);
  394. CkStackPop(Vm);
  395. CkGetField(Vm, 0);
  396. //
  397. // If the process is already launched, don't launch it again, just return
  398. // the pid.
  399. //
  400. if (Attributes->Pid != 0) {
  401. if (Attributes->Pid < 0) {
  402. CkpSpawnRaiseError(Vm, "ProcessExited", "Process exited");
  403. } else {
  404. CkReturnInteger(Vm, Attributes->Pid);
  405. }
  406. return;
  407. }
  408. Status = CkpSpawnGetDescriptor(Vm, "stdin", &(Attributes->Stdin));
  409. if (Status != 0) {
  410. goto ChildProcessLaunchEnd;
  411. }
  412. Status = CkpSpawnGetDescriptor(Vm, "stdout", &(Attributes->Stdout));
  413. if (Status != 0) {
  414. goto ChildProcessLaunchEnd;
  415. }
  416. Status = CkpSpawnGetDescriptor(Vm, "stderr", &(Attributes->Stderr));
  417. if (Status != 0) {
  418. goto ChildProcessLaunchEnd;
  419. }
  420. CkPushString(Vm, "cwd", 3);
  421. if (CkDictGet(Vm, -2)) {
  422. Attributes->Cwd = CkGetString(Vm, -1, NULL);
  423. CkStackPop(Vm);
  424. }
  425. Attributes->Environment = CkpSpawnCreateEnvironment(Vm);
  426. Status = CkpSpawnGetStringList(Vm, "args", FALSE, &(Attributes->Arguments));
  427. if (Status != 0) {
  428. goto ChildProcessLaunchEnd;
  429. }
  430. CkPushString(Vm, "executable", 10);
  431. if (CkDictGet(Vm, -2)) {
  432. Attributes->Executable = CkGetString(Vm, -1, NULL);
  433. CkStackPop(Vm);
  434. }
  435. CkPushString(Vm, "options", 7);
  436. if (CkDictGet(Vm, -2)) {
  437. Attributes->Options = CkGetInteger(Vm, -1);
  438. CkStackPop(Vm);
  439. //
  440. // Consider implementing closing all other FDs if it's needed.
  441. //
  442. if ((Attributes->Options & SPAWN_OPTION_CLOSE_FDS) != 0) {
  443. CkpSpawnRaiseError(Vm,
  444. "ValueError",
  445. "CLOSE_FDS not currently implemented");
  446. Status = -1;
  447. goto ChildProcessLaunchEnd;
  448. }
  449. }
  450. CkPushString(Vm, "debug", 5);
  451. if (CkDictGet(Vm, -2)) {
  452. Attributes->Debug = CkGetInteger(Vm, -1);
  453. CkStackPop(Vm);
  454. }
  455. //
  456. // Get the list of descriptors not to close, if present.
  457. //
  458. CkPushString(Vm, "passFds", 7);
  459. if (CkDictGet(Vm, -2)) {
  460. Attributes->PassFdCount = CkListSize(Vm, -1);
  461. Attributes->PassFds = malloc(sizeof(int) * Attributes->PassFdCount);
  462. if (Attributes->PassFds == NULL) {
  463. CkRaiseBasicException(Vm, "MemoryError", "");
  464. Status = -1;
  465. goto ChildProcessLaunchEnd;
  466. }
  467. for (Index = 0; Index < Attributes->PassFdCount; Index += 1) {
  468. CkListGet(Vm, -1, Index);
  469. if (!CkIsInteger(Vm, -1)) {
  470. CkRaiseBasicException(Vm,
  471. "TypeError",
  472. "Expected an integer in passFds");
  473. Status = -1;
  474. goto ChildProcessLaunchEnd;
  475. }
  476. Attributes->PassFds[Index] = CkGetInteger(Vm, -1);
  477. CkStackPop(Vm);
  478. }
  479. CkStackPop(Vm);
  480. }
  481. //
  482. // Call out to the OS-specific part to actually spawn the process.
  483. //
  484. Status = CkpOsSpawn(Attributes);
  485. if (Status != 0) {
  486. CkpSpawnRaiseSpawnError(Vm, Attributes);
  487. goto ChildProcessLaunchEnd;
  488. }
  489. //
  490. // Set the in/out/error pipe file descriptors.
  491. //
  492. if (Attributes->Stdin.ParentPipe >= 0) {
  493. CkPushString(Vm, "stdin", 5);
  494. CkPushInteger(Vm, Attributes->Stdin.ParentPipe);
  495. CkDictSet(Vm, -3);
  496. }
  497. if (Attributes->Stdout.ParentPipe >= 0) {
  498. CkPushString(Vm, "stdout", 6);
  499. CkPushInteger(Vm, Attributes->Stdout.ParentPipe);
  500. CkDictSet(Vm, -3);
  501. }
  502. if (Attributes->Stderr.ParentPipe >= 0) {
  503. CkPushString(Vm, "stderr", 6);
  504. CkPushInteger(Vm, Attributes->Stderr.ParentPipe);
  505. CkDictSet(Vm, -3);
  506. }
  507. CkStackPop(Vm);
  508. ChildProcessLaunchEnd:
  509. if (Attributes->Environment != NULL) {
  510. free(Attributes->Environment);
  511. Attributes->Environment = NULL;
  512. }
  513. if (Attributes->Arguments != NULL) {
  514. free(Attributes->Arguments);
  515. Attributes->Arguments = NULL;
  516. }
  517. if (Attributes->PassFds != NULL) {
  518. free(Attributes->PassFds);
  519. Attributes->PassFds = NULL;
  520. }
  521. if (Attributes->ErrorMessage != NULL) {
  522. free(Attributes->ErrorMessage);
  523. Attributes->ErrorMessage = NULL;
  524. }
  525. if (Status != 0) {
  526. CkpTearDownSpawnAttributes(Attributes);
  527. } else {
  528. CkReturnInteger(Vm, Attributes->Pid);
  529. }
  530. return;
  531. }
  532. VOID
  533. CkpChildProcessPoll (
  534. PCK_VM Vm
  535. )
  536. /*++
  537. Routine Description:
  538. This routine determines if the child process has exited yet, and sets the
  539. returncode if it has.
  540. Arguments:
  541. Vm - Supplies a pointer to the virtual machine.
  542. Return Value:
  543. None.
  544. --*/
  545. {
  546. PSPAWN_ATTRIBUTES Attributes;
  547. INT Status;
  548. CkGetField(Vm, 1);
  549. Attributes = CkGetData(Vm, -1);
  550. CkStackPop(Vm);
  551. Status = CkpSpawnWait(Vm, Attributes, 0);
  552. if (Status == 1) {
  553. CkReturnNull(Vm);
  554. }
  555. return;
  556. }
  557. VOID
  558. CkpChildProcessWait (
  559. PCK_VM Vm
  560. )
  561. /*++
  562. Routine Description:
  563. This routine waits for the child process to exit, and returns its return
  564. code. On timeout, a TimeoutExpired error is raised. On failure, a
  565. SpawnError is raised.
  566. Arguments:
  567. Vm - Supplies a pointer to the virtual machine.
  568. Return Value:
  569. None.
  570. --*/
  571. {
  572. PSPAWN_ATTRIBUTES Attributes;
  573. INT Status;
  574. if (!CkCheckArguments(Vm, 1, CkTypeInteger)) {
  575. return;
  576. }
  577. CkGetField(Vm, 1);
  578. Attributes = CkGetData(Vm, -1);
  579. CkStackPop(Vm);
  580. Status = CkpSpawnWait(Vm, Attributes, CkGetInteger(Vm, 1));
  581. if (Status == 1) {
  582. CkpSpawnRaiseError(Vm, "TimeoutExpired", "Timeout expired");
  583. return;
  584. }
  585. return;
  586. }
  587. VOID
  588. CkpChildProcessCommunicate (
  589. PCK_VM Vm
  590. )
  591. /*++
  592. Routine Description:
  593. This routine communicates with the child process. It takes two arguments:
  594. an optional input to send to the process and a timeout in milliseconds.
  595. Upon return, a list containing stdout and stderr data will be returned.
  596. The caller must have launched the child process with pipe options to get
  597. any data across. On timeout, a TimeoutExpired error is raised.
  598. Arguments:
  599. Vm - Supplies a pointer to the virtual machine.
  600. Return Value:
  601. None.
  602. --*/
  603. {
  604. PSPAWN_ATTRIBUTES Attributes;
  605. PSTR ErrorData;
  606. size_t ErrorDataSize;
  607. BOOL ExceptionRaised;
  608. PCSTR Input;
  609. UINTN InputSize;
  610. PSTR OutData;
  611. size_t OutDataSize;
  612. INT Status;
  613. CK_INTEGER Timeout;
  614. ErrorData = NULL;
  615. ErrorDataSize = 0;
  616. ExceptionRaised = FALSE;
  617. Input = NULL;
  618. InputSize = 0;
  619. OutData = NULL;
  620. OutDataSize = 0;
  621. Input = CkGetString(Vm, 1, &InputSize);
  622. Timeout = CkGetInteger(Vm, 2);
  623. CkGetField(Vm, 1);
  624. Attributes = CkGetData(Vm, -1);
  625. CkStackPop(Vm);
  626. Status = CkpOsCommunicate(Attributes,
  627. Input,
  628. InputSize,
  629. Timeout,
  630. &OutData,
  631. &OutDataSize,
  632. &ErrorData,
  633. &ErrorDataSize);
  634. if (Attributes->Pid < 0) {
  635. if (CkpSpawnSetReturnCode(Vm, Attributes) < 0) {
  636. ExceptionRaised = TRUE;
  637. if (Attributes->ErrorMessage != NULL) {
  638. free(Attributes->ErrorMessage);
  639. Attributes->ErrorMessage = NULL;
  640. }
  641. }
  642. }
  643. if (ExceptionRaised == FALSE) {
  644. if (Status == 1) {
  645. CkpSpawnRaiseError(Vm, "TimeoutExpired", "Timeout expired");
  646. ExceptionRaised = TRUE;
  647. } else if (Status != 0) {
  648. CkpSpawnRaiseSpawnError(Vm, Attributes);
  649. ExceptionRaised = TRUE;
  650. } else if ((InputSize == 0) && (OutDataSize == 0) &&
  651. (ErrorDataSize == 0)) {
  652. assert(Attributes->Pid < 0);
  653. CkpSpawnRaiseError(Vm, "ProcessExited", "Process Exited");
  654. ExceptionRaised = TRUE;
  655. }
  656. }
  657. if (ExceptionRaised == FALSE) {
  658. CkPushList(Vm);
  659. CkPushString(Vm, OutData, OutDataSize);
  660. CkListSet(Vm, -2, 0);
  661. CkPushString(Vm, ErrorData, ErrorDataSize);
  662. CkListSet(Vm, -2, 1);
  663. CkStackReplace(Vm, 0);
  664. }
  665. if (OutData != NULL) {
  666. free(OutData);
  667. }
  668. if (ErrorData != NULL) {
  669. free(ErrorData);
  670. }
  671. return;
  672. }
  673. VOID
  674. CkpChildProcessTerminate (
  675. PCK_VM Vm
  676. )
  677. /*++
  678. Routine Description:
  679. This routine sends a SIGTERM to the child process. On Windows, it calls
  680. TerminateProcess.
  681. Arguments:
  682. Vm - Supplies a pointer to the virtual machine.
  683. Return Value:
  684. None.
  685. --*/
  686. {
  687. PSPAWN_ATTRIBUTES Attributes;
  688. INT Status;
  689. CkGetField(Vm, 1);
  690. Attributes = CkGetData(Vm, -1);
  691. CkStackPop(Vm);
  692. Status = CkpOsSendSignal(Attributes, SIGTERM);
  693. if (Status != 0) {
  694. CkpSpawnRaiseSpawnError(Vm, Attributes);
  695. return;
  696. }
  697. return;
  698. }
  699. VOID
  700. CkpChildProcessKill (
  701. PCK_VM Vm
  702. )
  703. /*++
  704. Routine Description:
  705. This routine sends a SIGKILL to the child process. On Windows, it calls
  706. TerminateProcess.
  707. Arguments:
  708. Vm - Supplies a pointer to the virtual machine.
  709. Return Value:
  710. None.
  711. --*/
  712. {
  713. PSPAWN_ATTRIBUTES Attributes;
  714. INT Status;
  715. CkGetField(Vm, 1);
  716. Attributes = CkGetData(Vm, -1);
  717. CkStackPop(Vm);
  718. Status = CkpOsSendSignal(Attributes, SIGKILL);
  719. if (Status != 0) {
  720. CkpSpawnRaiseSpawnError(Vm, Attributes);
  721. return;
  722. }
  723. return;
  724. }
  725. INT
  726. CkpSpawnGetDescriptor (
  727. PCK_VM Vm,
  728. PCSTR Name,
  729. PSPAWN_DESCRIPTOR Descriptor
  730. )
  731. /*++
  732. Routine Description:
  733. This routine gets or sets up a standard descriptor.
  734. Arguments:
  735. Vm - Supplies a pointer to the virtual machine.
  736. Name - Supplies the name of the descriptor to get within the field
  737. dictionary.
  738. Descriptor - Supplies a pointer where the descriptor information will be
  739. returned.
  740. Return Value:
  741. 0 on success.
  742. Non-zero on failure.
  743. --*/
  744. {
  745. CK_INTEGER Integer;
  746. int Pipe[2];
  747. CkGetField(Vm, 0);
  748. CkPushString(Vm, Name, strlen(Name));
  749. CkDictGet(Vm, -2);
  750. if (!CkIsInteger(Vm, -1)) {
  751. CkRaiseBasicException(Vm,
  752. "TypeError",
  753. "Expected an integer for %s",
  754. Name);
  755. return -1;
  756. }
  757. Integer = CkGetInteger(Vm, -1);
  758. CkStackPop(Vm);
  759. CkStackPop(Vm);
  760. if (Integer == SPAWN_DEVNULL) {
  761. Descriptor->Fd = open(SPAWN_DEVNULL_PATH, O_RDWR);
  762. if (Descriptor->Fd < 0) {
  763. CkpSpawnRaiseError(Vm, "SpawnError", "Failed to open null device");
  764. return -1;
  765. }
  766. Descriptor->CloseFd = Descriptor->Fd;
  767. } else if (Integer == SPAWN_PIPE) {
  768. if (pipe(Pipe) != 0) {
  769. CkpSpawnRaiseError(Vm, "SpawnError", strerror(errno));
  770. return -1;
  771. }
  772. if (strcmp(Name, "stdin") == 0) {
  773. Descriptor->Fd = Pipe[0];
  774. Descriptor->ParentPipe = Pipe[1];
  775. } else {
  776. Descriptor->Fd = Pipe[1];
  777. Descriptor->ParentPipe = Pipe[0];
  778. }
  779. Descriptor->CloseFd = Descriptor->Fd;
  780. } else if (Integer > 0) {
  781. Descriptor->Fd = Integer;
  782. } else {
  783. Descriptor->Fd = -1;
  784. }
  785. return 0;
  786. }
  787. PSTR *
  788. CkpSpawnCreateEnvironment (
  789. PCK_VM Vm
  790. )
  791. /*++
  792. Routine Description:
  793. This routine creates an environment from a dictionary.
  794. Arguments:
  795. Vm - Supplies a pointer to the virtual machine.
  796. Return Value:
  797. 0 on success.
  798. -1 if an exception was raised.
  799. --*/
  800. {
  801. PSTR *Array;
  802. PSTR Buffer;
  803. UINTN Count;
  804. PSTR Current;
  805. PSTR End;
  806. UINTN Index;
  807. PCSTR Key;
  808. UINTN KeyLength;
  809. UINTN NeededSize;
  810. PSTR NewBuffer;
  811. UINTN NewCapacity;
  812. PCSTR Value;
  813. UINTN ValueLength;
  814. Array = NULL;
  815. Buffer = NULL;
  816. Current = NULL;
  817. End = NULL;
  818. CkGetField(Vm, 0);
  819. CkPushString(Vm, "env", 3);
  820. if (!CkDictGet(Vm, -2)) {
  821. CkStackPop(Vm);
  822. return NULL;
  823. }
  824. //
  825. // Iterate over all the keys in the dictionary.
  826. //
  827. Count = 0;
  828. CkPushNull(Vm);
  829. while (CkDictIterate(Vm, -2)) {
  830. Key = CkGetString(Vm, -2, &KeyLength);
  831. Value = CkGetString(Vm, -1, &ValueLength);
  832. CkStackPop(Vm);
  833. CkStackPop(Vm);
  834. if ((Key == NULL) || (Value == NULL) || (KeyLength == 0)) {
  835. continue;
  836. }
  837. //
  838. // Space is needed for key=value\0.
  839. //
  840. NeededSize = KeyLength + ValueLength + 2;
  841. if (Current + NeededSize > End) {
  842. if (Buffer == NULL) {
  843. NewCapacity = 1024;
  844. } else {
  845. NewCapacity = (End - Buffer) * 2;
  846. }
  847. while (NewCapacity < (Current - Buffer) + NeededSize) {
  848. NewCapacity *= 2;
  849. }
  850. NewBuffer = realloc(Buffer, NewCapacity);
  851. if ((NewCapacity >= CK_SPAWN_MAX_OUTPUT) || (NewBuffer == NULL)) {
  852. free(NewBuffer);
  853. free(Buffer);
  854. Buffer = NULL;
  855. goto SpawnCreateEnvironmentEnd;
  856. }
  857. Current = NewBuffer + (Current - Buffer);
  858. Buffer = NewBuffer;
  859. End = Buffer + NewCapacity;
  860. }
  861. Current += snprintf(Current, End - Current, "%s=%s", Key, Value) + 1;
  862. Count += 1;
  863. }
  864. if (Count == 0) {
  865. assert(Buffer == NULL);
  866. goto SpawnCreateEnvironmentEnd;
  867. }
  868. //
  869. // Create a single allocation containing enough space for the array and
  870. // all the entries.
  871. //
  872. Array = malloc(((Count + 1) * sizeof(PSTR)) + (Current - Buffer) + 1);
  873. if (Array == NULL) {
  874. goto SpawnCreateEnvironmentEnd;
  875. }
  876. //
  877. // Copy the contents in.
  878. //
  879. memcpy(&(Array[Count + 1]), Buffer, Current - Buffer);
  880. //
  881. // Assign the array elements within the string.
  882. //
  883. Current = (PSTR)&(Array[Count + 1]);
  884. for (Index = 0; Index < Count; Index += 1) {
  885. Array[Index] = Current;
  886. Current += strlen(Current) + 1;
  887. }
  888. Array[Index] = NULL;
  889. *Current = '\0';
  890. SpawnCreateEnvironmentEnd:
  891. if (Buffer != NULL) {
  892. free(Buffer);
  893. }
  894. //
  895. // Pop the iterator, dict, and field.
  896. //
  897. CkStackPop(Vm);
  898. CkStackPop(Vm);
  899. CkStackPop(Vm);
  900. return Array;
  901. }
  902. INT
  903. CkpSpawnGetStringList (
  904. PCK_VM Vm,
  905. PCSTR Name,
  906. BOOL Optional,
  907. char ***NewList
  908. )
  909. /*++
  910. Routine Description:
  911. This routine creates a string list.
  912. Arguments:
  913. Vm - Supplies a pointer to the virtual machine.
  914. Name - Supplies the name of the list to get within the field
  915. dictionary.
  916. Optional - Supplies a boolean indicating if the list is optional or not.
  917. NewList - Supplies a pointer where the array will be returned.
  918. Return Value:
  919. 0 on success.
  920. -1 if an exception was raised.
  921. --*/
  922. {
  923. char **Array;
  924. UINTN Index;
  925. UINTN Size;
  926. *NewList = NULL;
  927. CkGetField(Vm, 0);
  928. CkPushString(Vm, Name, strlen(Name));
  929. CkDictGet(Vm, -2);
  930. if ((Optional != FALSE) && (CkIsNull(Vm, -1))) {
  931. CkStackPop(Vm);
  932. CkStackPop(Vm);
  933. return 0;
  934. }
  935. if (!CkIsList(Vm, -1)) {
  936. CkRaiseBasicException(Vm,
  937. "TypeError",
  938. "Expected a list for %s",
  939. Name);
  940. return -1;
  941. }
  942. Size = CkListSize(Vm, -1);
  943. if ((Optional == FALSE) && (Size == 0)) {
  944. CkRaiseBasicException(Vm,
  945. "ValueError",
  946. "Expected non-empty list for %s",
  947. Name);
  948. return -1;
  949. }
  950. Array = malloc(sizeof(char *) * (Size + 1));
  951. if (Array == NULL) {
  952. CkRaiseBasicException(Vm, "MemoryError", "");
  953. return -1;
  954. }
  955. Array[Size] = NULL;
  956. for (Index = 0; Index < Size; Index += 1) {
  957. CkListGet(Vm, -1, Index);
  958. Array[Index] = (PSTR)CkGetString(Vm, -1, NULL);
  959. CkStackPop(Vm);
  960. if (Array[Index] == NULL) {
  961. CkRaiseBasicException(Vm,
  962. "TypeError",
  963. "Expected a string at index %d of %s",
  964. (int)Index,
  965. Name);
  966. break;
  967. }
  968. }
  969. CkStackPop(Vm);
  970. CkStackPop(Vm);
  971. if (Index != Size) {
  972. free(Array);
  973. return -1;
  974. }
  975. *NewList = Array;
  976. return 0;
  977. }
  978. INT
  979. CkpSpawnWait (
  980. PCK_VM Vm,
  981. PSPAWN_ATTRIBUTES Attributes,
  982. INT Milliseconds
  983. )
  984. /*++
  985. Routine Description:
  986. This routine waits for the process to exit. It sets the return code if
  987. the process exited, and sets the return value.
  988. Arguments:
  989. Vm - Supplies a pointer to the virtual machine.
  990. Attributes - Supplies a pointer to the attributes.
  991. Milliseconds - Supplies the number of milliseconds to wait.
  992. Return Value:
  993. 0 on success.
  994. 1 on timeout.
  995. Non-zero if an exception was raised.
  996. --*/
  997. {
  998. INT Status;
  999. if (Attributes->Pid > 0) {
  1000. Status = CkpOsWait(Attributes, Milliseconds);
  1001. if (Status != 0) {
  1002. //
  1003. // If the request timed out, just return back to the caller without
  1004. // necessarily raising an exception.
  1005. //
  1006. if (Status == 1) {
  1007. return Status;
  1008. }
  1009. CkpSpawnRaiseSpawnError(Vm, Attributes);
  1010. //
  1011. // The wait succeeded, so set the return code.
  1012. //
  1013. } else {
  1014. Status = CkpSpawnSetReturnCode(Vm, Attributes);
  1015. if (Status == 0) {
  1016. CkReturnInteger(Vm, Attributes->ReturnCode);
  1017. }
  1018. return 0;
  1019. }
  1020. }
  1021. //
  1022. // If the process is finished, return the return code.
  1023. //
  1024. if (Attributes->Pid == -1) {
  1025. CkReturnInteger(Vm, Attributes->ReturnCode);
  1026. //
  1027. // The process is not yet finished or not yet started, return null.
  1028. //
  1029. } else {
  1030. CkReturnNull(Vm);
  1031. }
  1032. return 0;
  1033. }
  1034. INT
  1035. CkpSpawnSetReturnCode (
  1036. PCK_VM Vm,
  1037. PSPAWN_ATTRIBUTES Attributes
  1038. )
  1039. /*++
  1040. Routine Description:
  1041. This routine sets the publicly visible return code. If the check option is
  1042. set, it may raise an exception as well.
  1043. Arguments:
  1044. Vm - Supplies a pointer to the virtual machine.
  1045. Attributes - Supplies a pointer to the attributes.
  1046. Return Value:
  1047. 0 on success.
  1048. -1 if an exception was raised.
  1049. --*/
  1050. {
  1051. CHAR Message[128];
  1052. CkGetField(Vm, 0);
  1053. CkPushString(Vm, "returncode", 10);
  1054. CkPushInteger(Vm, Attributes->ReturnCode);
  1055. CkDictSet(Vm, -3);
  1056. CkStackPop(Vm);
  1057. if (((Attributes->Options & SPAWN_OPTION_CHECK) != 0) &&
  1058. (Attributes->ReturnCode != 0)) {
  1059. snprintf(Message,
  1060. sizeof(Message),
  1061. "Child exited with status %d",
  1062. Attributes->ReturnCode);
  1063. CkpSpawnRaiseError(Vm, "ChildProcessError", Message);
  1064. return -1;
  1065. }
  1066. return 0;
  1067. }
  1068. VOID
  1069. CkpDestroySpawnAttributes (
  1070. PVOID Attributes
  1071. )
  1072. /*++
  1073. Routine Description:
  1074. This routine closes all resources associated with a spawn attributes
  1075. structure and frees the structure.
  1076. Arguments:
  1077. Attributes - Supplies a pointer to the attributes to tear down.
  1078. Return Value:
  1079. None.
  1080. --*/
  1081. {
  1082. CkpTearDownSpawnAttributes(Attributes);
  1083. free(Attributes);
  1084. return;
  1085. }
  1086. VOID
  1087. CkpTearDownSpawnAttributes (
  1088. PSPAWN_ATTRIBUTES Attributes
  1089. )
  1090. /*++
  1091. Routine Description:
  1092. This routine closes all resources associated with a spawn attributes
  1093. structure.
  1094. Arguments:
  1095. Attributes - Supplies a pointer to the attributes to tear down.
  1096. Return Value:
  1097. None.
  1098. --*/
  1099. {
  1100. assert(Attributes->Environment == NULL);
  1101. assert(Attributes->Arguments == NULL);
  1102. assert(Attributes->PassFds == NULL);
  1103. assert(Attributes->ErrorMessage == NULL);
  1104. if (Attributes->Stdin.ParentPipe >= 0) {
  1105. close(Attributes->Stdin.ParentPipe);
  1106. Attributes->Stdin.ParentPipe = -1;
  1107. }
  1108. if (Attributes->Stdin.CloseFd >= 0) {
  1109. close(Attributes->Stdin.CloseFd);
  1110. Attributes->Stdin.CloseFd = -1;
  1111. }
  1112. if (Attributes->Stdout.ParentPipe >= 0) {
  1113. close(Attributes->Stdout.ParentPipe);
  1114. Attributes->Stdout.ParentPipe = -1;
  1115. }
  1116. if (Attributes->Stdout.CloseFd >= 0) {
  1117. close(Attributes->Stdout.CloseFd);
  1118. Attributes->Stdout.CloseFd = -1;
  1119. }
  1120. if (Attributes->Stderr.ParentPipe >= 0) {
  1121. close(Attributes->Stderr.ParentPipe);
  1122. Attributes->Stderr.ParentPipe = -1;
  1123. }
  1124. if (Attributes->Stderr.CloseFd >= 0) {
  1125. close(Attributes->Stderr.CloseFd);
  1126. Attributes->Stderr.CloseFd = -1;
  1127. }
  1128. CkpOsTearDownSpawnAttributes(Attributes);
  1129. return;
  1130. }
  1131. VOID
  1132. CkpSpawnRaiseSpawnError (
  1133. PCK_VM Vm,
  1134. PSPAWN_ATTRIBUTES Attributes
  1135. )
  1136. /*++
  1137. Routine Description:
  1138. This routine raises a spawn error. If there is an error message it is used
  1139. and freed. Otherwise the errno description is used.
  1140. Arguments:
  1141. Vm - Supplies a pointer to the virtual machine.
  1142. Attributes - Supplies a pointer to the attributes to tear down.
  1143. Return Value:
  1144. None.
  1145. --*/
  1146. {
  1147. if (Attributes->ErrorMessage != NULL) {
  1148. CkpSpawnRaiseError(Vm, "SpawnError", Attributes->ErrorMessage);
  1149. free(Attributes->ErrorMessage);
  1150. Attributes->ErrorMessage = NULL;
  1151. } else {
  1152. CkpSpawnRaiseError(Vm, "SpawnError", strerror(errno));
  1153. }
  1154. return;
  1155. }
  1156. VOID
  1157. CkpSpawnRaiseError (
  1158. PCK_VM Vm,
  1159. PCSTR ExceptionType,
  1160. PCSTR Message
  1161. )
  1162. /*++
  1163. Routine Description:
  1164. This routine raises an error with a message.
  1165. Arguments:
  1166. Vm - Supplies a pointer to the virtual machine.
  1167. ExceptionType - Supplies a pointer to the type of exception to raise.
  1168. Message - Supplies the message to send along with the exception.
  1169. Return Value:
  1170. None. The foreign function should return as soon as possible and not
  1171. manipulate the Chalk stack any longer.
  1172. --*/
  1173. {
  1174. //
  1175. // Create an exception.
  1176. //
  1177. CkPushModule(Vm, "spawn");
  1178. CkGetVariable(Vm, -1, ExceptionType);
  1179. CkPushString(Vm, Message, strlen(Message));
  1180. CkCall(Vm, 1);
  1181. //
  1182. // Raise the exception.
  1183. //
  1184. CkRaiseException(Vm, -1);
  1185. return;
  1186. }