sys_start_process.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249
  1. /**
  2. * @file sys_start_process.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * @section DESCRIPTION
  30. *
  31. * Synopsis:
  32. * sys.start_process(list command, string mode [, map options])
  33. *
  34. * Options:
  35. * "keep_stdout":"true" - Start the program with the same stdout as the NCD process.
  36. * Must not be present if the process is being opened for reading.
  37. * "keep_stderr":"true" - Start the program with the same stderr as the NCD process.
  38. * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to
  39. * start the 'agetty' program.
  40. * "username":username_string - Start the process under the permissions of the
  41. * specified user.
  42. * "term_on_deinit":"false" - do not send SIGTERM to the process when this statement
  43. * is requested to terminate
  44. * "deinit_kill_time":milliseconds - how long to wait for the process to terminate
  45. * after this statement is requested to terminate until we send SIGKILL. If this option
  46. * is not present or is "never", SIGKILL will not be sent. If this option is empty, the
  47. * process will be sent SIGKILL immediately when the statement is requested to terminate.
  48. *
  49. * Variables:
  50. * is_error - "true" if there was an error starting the process, "false" if the process
  51. * has been started successfully
  52. *
  53. * Synopsis:
  54. * sys.start_process::wait()
  55. *
  56. * Variables:
  57. * exit_status - the exit code if the process terminated normally, -1 if it terminated
  58. * with a signal
  59. *
  60. * Synopsis:
  61. * sys.start_process::terminate()
  62. * sys.start_process::kill()
  63. *
  64. * Synopsis:
  65. * sys.start_process::read_pipe()
  66. *
  67. * Description:
  68. * Creates a read interface to the process's standard output. Data is read using the
  69. * read() method on this object. Read errors are reported implicitly by this statement
  70. * going down and the 'is_error' variable changing to "true".
  71. * When read_pipe() is initialized for a process, it takes ownership of the read pipe
  72. * to the process. When read_pipe() is requested to terminate, it will close the pipe.
  73. * Attempting to initialize read_pipe() on a process which was not started with 'r'
  74. * in the mode argument, or where another read_pipe() object has already taken ownership
  75. * of the read pipe, will result in throwing an error to the interpreter.
  76. *
  77. * Variables:
  78. * string is_error - "true" if there was a read error, "false" if not
  79. *
  80. * Synopsis:
  81. * sys.start_process::read_pipe::read()
  82. *
  83. * Description:
  84. * Reads some data. If a read error occurs, it is reported implicitly via the
  85. * read_pipe() object going down. If end of file is reached, this and any future read()
  86. * operations will indicate that via the 'not_eof' variable. It is guaranteed that after
  87. * EOF is reached, the read_pipe() object will not go down to report any errors.
  88. * WARNING: if a read() is requested to terminate before it has completed, the
  89. * read_pipe() will become unusable and any read() invocation after that will
  90. * throw an error to the interpreter.
  91. *
  92. * Variables:
  93. * string (empty) - data that was read, or an empty string on EOF
  94. * string not_eof - "true" is EOF was not reached, "false" if it was
  95. *
  96. * Synopsis:
  97. * sys.start_process::write_pipe()
  98. *
  99. * Description:
  100. * Creates a write interface to the process's standard input. Data is written using the
  101. * write() method on this object. Write errors are reported implicitly by this statement
  102. * going down and the ''is_error variable changing to "true".
  103. * When write_pipe() is initialized for a process, it takes ownership of the write pipe
  104. * to the process. When write_pipe() is requested to terminate, it will close the pipe
  105. * (unless the close() has been used).
  106. * Attempting to initialize write_pipe() on a process which was not started with 'w'
  107. * in the mode argument, or where another write_pipe() object has already taken ownership
  108. * of the write pope, will result in throwing an error to the interpreter.
  109. *
  110. * Variables:
  111. * string is_error - "true" if there was a write error, "false" if not
  112. *
  113. * Synopsis:
  114. * sys.start_process::write_pipe::write(string data)
  115. *
  116. * Description:
  117. * Writes the given data. If a write error occurs, it is reported implicitly via the
  118. * write_pipe() object going down.
  119. * WARNING: if a write() is requested to terminate before it has completed, the
  120. * write_pipe() will become unusable and any write() or close() invocation after
  121. * that will throw an error to the interpreter.
  122. *
  123. * Synopsis:
  124. * sys.start_process::write_pipe::close(string data)
  125. *
  126. * Description:
  127. * Closes the write pipe. This will make whatever is reading the other end of the pipe
  128. * encounter EOF after it has read any pending data. It is guaranteed that after the
  129. * pipe is closed, the write_pipe() object will not go down to report any errors.
  130. * After close() is performed, any further write() or close() calls are disallowed and
  131. * will throw errors to the interpreter.
  132. */
  133. #include <stdlib.h>
  134. #include <string.h>
  135. #include <stdio.h>
  136. #include <inttypes.h>
  137. #include <limits.h>
  138. #include <unistd.h>
  139. #include <misc/offset.h>
  140. #include <structure/LinkedList0.h>
  141. #include <system/BProcess.h>
  142. #include <system/BConnection.h>
  143. #include <ncd/extra/NCDBuf.h>
  144. #include <ncd/extra/build_cmdline.h>
  145. #include <ncd/extra/NCDBProcessOpts.h>
  146. #include <ncd/module_common.h>
  147. #include <generated/blog_channel_ncd_sys_start_process.h>
  148. #define READ_BUF_SIZE 8192
  149. #define PROCESS_STATE_ERROR 1
  150. #define PROCESS_STATE_RUNNING 2
  151. #define PROCESS_STATE_TERMINATED 3
  152. #define PROCESS_STATE_DYING 4
  153. #define READER_STATE_RUNNING 1
  154. #define READER_STATE_EOF 2
  155. #define READER_STATE_ERROR 3
  156. #define READER_STATE_ABORTED 4
  157. #define WRITER_STATE_RUNNING 1
  158. #define WRITER_STATE_CLOSED 2
  159. #define WRITER_STATE_ERROR 3
  160. #define WRITER_STATE_ABORTED 4
  161. struct process_instance {
  162. NCDModuleInst *i;
  163. BProcess process;
  164. BSmallTimer kill_timer;
  165. LinkedList0 waits_list;
  166. btime_t deinit_kill_time;
  167. int term_on_deinit;
  168. int read_fd;
  169. int write_fd;
  170. int exit_status;
  171. int state;
  172. };
  173. struct wait_instance {
  174. NCDModuleInst *i;
  175. struct process_instance *pinst;
  176. LinkedList0Node waits_list_node;
  177. int exit_status;
  178. };
  179. struct read_pipe_instance {
  180. NCDModuleInst *i;
  181. int state;
  182. int read_fd;
  183. BConnection connection;
  184. NCDBufStore store;
  185. struct read_instance *read_inst;
  186. };
  187. struct read_instance {
  188. NCDModuleInst *i;
  189. struct read_pipe_instance *read_pipe_inst;
  190. NCDBuf *buf;
  191. size_t read_size;
  192. };
  193. struct write_pipe_instance {
  194. NCDModuleInst *i;
  195. int state;
  196. int write_fd;
  197. BConnection connection;
  198. struct write_instance *write_inst;
  199. };
  200. struct write_instance {
  201. NCDModuleInst *i;
  202. struct write_pipe_instance *write_pipe_inst;
  203. MemRef data;
  204. };
  205. static int parse_mode (NCDModuleInst *i, NCDValRef mode_arg, int *out_read, int *out_write)
  206. {
  207. if (!NCDVal_IsString(mode_arg)) {
  208. ModuleLog(i, BLOG_ERROR, "mode argument must be a string");
  209. return 0;
  210. }
  211. *out_read = 0;
  212. *out_write = 0;
  213. MEMREF_LOOP_CHARS(NCDVal_StringMemRef(mode_arg), char_pos, ch, {
  214. if (ch == 'r') {
  215. *out_read = 1;
  216. }
  217. else if (ch == 'w') {
  218. *out_write = 1;
  219. }
  220. else {
  221. ModuleLog(i, BLOG_ERROR, "invalid character in mode argument");
  222. return 0;
  223. }
  224. })
  225. return 1;
  226. }
  227. static void process_free (struct process_instance *o)
  228. {
  229. // close write fd
  230. if (o->write_fd != -1) {
  231. if (close(o->write_fd) < 0) {
  232. ModuleLog(o->i, BLOG_ERROR, "close failed");
  233. }
  234. }
  235. // close read fd
  236. if (o->read_fd != -1) {
  237. if (close(o->read_fd) < 0) {
  238. ModuleLog(o->i, BLOG_ERROR, "close failed");
  239. }
  240. }
  241. NCDModuleInst_Backend_Dead(o->i);
  242. }
  243. static void process_handler (void *vo, int normally, uint8_t normally_exit_status)
  244. {
  245. struct process_instance *o = vo;
  246. ASSERT(o->state == PROCESS_STATE_RUNNING || o->state == PROCESS_STATE_DYING)
  247. ModuleLog(o->i, BLOG_INFO, "process terminated");
  248. // free kill timer
  249. BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &o->kill_timer);
  250. // free process
  251. BProcess_Free(&o->process);
  252. // remember exit code
  253. o->exit_status = (!normally ? -1 : normally_exit_status);
  254. // finish waits
  255. LinkedList0Node *ln;
  256. while ((ln = LinkedList0_GetFirst(&o->waits_list))) {
  257. struct wait_instance *winst = UPPER_OBJECT(ln, struct wait_instance, waits_list_node);
  258. ASSERT(winst->pinst == o)
  259. LinkedList0_Remove(&o->waits_list, &winst->waits_list_node);
  260. winst->pinst = NULL;
  261. winst->exit_status = o->exit_status;
  262. NCDModuleInst_Backend_Up(winst->i);
  263. }
  264. // if we have been requested to die, then die now
  265. if (o->state == PROCESS_STATE_DYING) {
  266. process_free(o);
  267. return;
  268. }
  269. // set state
  270. o->state = PROCESS_STATE_TERMINATED;
  271. }
  272. static void process_kill_timer_handler (BSmallTimer *kill_timer)
  273. {
  274. struct process_instance *o = UPPER_OBJECT(kill_timer, struct process_instance, kill_timer);
  275. ASSERT(o->state == PROCESS_STATE_DYING)
  276. ModuleLog(o->i, BLOG_INFO, "killing process after timeout");
  277. BProcess_Kill(&o->process);
  278. }
  279. static int opts_func_unknown (void *user, NCDValRef key, NCDValRef val)
  280. {
  281. struct process_instance *o = user;
  282. if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "term_on_deinit")) {
  283. if (!ncd_read_boolean(val, &o->term_on_deinit)) {
  284. ModuleLog(o->i, BLOG_ERROR, "term_on_deinit: bad value");
  285. return 0;
  286. }
  287. return 1;
  288. }
  289. if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "deinit_kill_time")) {
  290. if (NCDVal_IsString(val) && NCDVal_StringEquals(val, "never")) {
  291. o->deinit_kill_time = -2;
  292. }
  293. else if (NCDVal_IsString(val) && NCDVal_StringEqualsId(val, NCD_STRING_EMPTY)) {
  294. o->deinit_kill_time = -1;
  295. }
  296. else if (!ncd_read_time(val, &o->deinit_kill_time)) {
  297. ModuleLog(o->i, BLOG_ERROR, "wrong value for deinit_kill_time option");
  298. return 0;
  299. }
  300. return 1;
  301. }
  302. return 0;
  303. }
  304. static void process_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  305. {
  306. struct process_instance *o = vo;
  307. o->i = i;
  308. NCDModuleInst_Backend_PassMemToMethods(i);
  309. // check arguments
  310. NCDValRef command_arg;
  311. NCDValRef mode_arg;
  312. NCDValRef options_arg = NCDVal_NewInvalid();
  313. if (!NCDVal_ListRead(params->args, 2, &command_arg, &mode_arg) &&
  314. !NCDVal_ListRead(params->args, 3, &command_arg, &mode_arg, &options_arg)
  315. ) {
  316. ModuleLog(i, BLOG_ERROR, "wrong arity");
  317. goto fail0;
  318. }
  319. // parse mode
  320. int is_read;
  321. int is_write;
  322. if (!parse_mode(i, mode_arg, &is_read, &is_write)) {
  323. goto fail0;
  324. }
  325. // parse options
  326. NCDBProcessOpts opts;
  327. int keep_stdout;
  328. int keep_stderr;
  329. o->deinit_kill_time = -2;
  330. o->term_on_deinit = 1;
  331. if (!NCDBProcessOpts_Init2(&opts, options_arg, opts_func_unknown, o, i, BLOG_CURRENT_CHANNEL, &keep_stdout, &keep_stderr)) {
  332. goto fail0;
  333. }
  334. // keep-stdout option and read mode are not compatible
  335. if (keep_stdout && is_read) {
  336. ModuleLog(i, BLOG_ERROR, "keep-stdout and read mode are not compatible");
  337. goto fail1;
  338. }
  339. // prepare for creating pipes
  340. int fds[4];
  341. int fds_map[3];
  342. int start_num_fds = opts.nfds;
  343. int num_fds = start_num_fds;
  344. memcpy(fds, opts.fds, num_fds * sizeof(int));
  345. memcpy(fds_map, opts.fds_map, num_fds * sizeof(int));
  346. int read_fd = -1;
  347. int write_fd = -1;
  348. // create read pipe
  349. if (is_read) {
  350. int pipefd[2];
  351. if (pipe(pipefd) < 0) {
  352. ModuleLog(i, BLOG_ERROR, "pipe failed");
  353. goto error1;
  354. }
  355. read_fd = pipefd[0];
  356. fds[num_fds] = pipefd[1];
  357. fds_map[num_fds++] = STDOUT_FILENO;
  358. }
  359. // create write pipe
  360. if (is_write) {
  361. int pipefd[2];
  362. if (pipe(pipefd) < 0) {
  363. ModuleLog(i, BLOG_ERROR, "pipe failed");
  364. goto error1;
  365. }
  366. write_fd = pipefd[1];
  367. fds[num_fds] = pipefd[0];
  368. fds_map[num_fds++] = STDIN_FILENO;
  369. }
  370. // terminate fds array
  371. fds[num_fds] = -1;
  372. // build process parameters struct
  373. struct BProcess_params p_params = {};
  374. p_params.fds = fds;
  375. p_params.fds_map = fds_map;
  376. p_params.do_setsid = opts.do_setsid;
  377. p_params.username = opts.username;
  378. // build command line
  379. char *exec;
  380. CmdLine cl;
  381. if (!ncd_build_cmdline(i, BLOG_CURRENT_CHANNEL, command_arg, &exec, &cl)) {
  382. goto error1;
  383. }
  384. // start process
  385. int res = BProcess_Init2(&o->process, i->params->iparams->manager, process_handler, o, exec, CmdLine_Get(&cl), p_params);
  386. CmdLine_Free(&cl);
  387. free(exec);
  388. if (!res) {
  389. ModuleLog(i, BLOG_ERROR, "BProcess_Init failed");
  390. goto error1;
  391. }
  392. // init kill timer
  393. BSmallTimer_Init(&o->kill_timer, process_kill_timer_handler);
  394. // close child fds
  395. while (num_fds-- > start_num_fds) {
  396. if (close(fds[num_fds]) < 0) {
  397. ModuleLog(i, BLOG_ERROR, "close failed");
  398. }
  399. }
  400. // free opts
  401. NCDBProcessOpts_Free(&opts);
  402. // init waits list
  403. LinkedList0_Init(&o->waits_list);
  404. // remember our fds
  405. o->read_fd = read_fd;
  406. o->write_fd = write_fd;
  407. // set state
  408. o->state = PROCESS_STATE_RUNNING;
  409. // go up
  410. NCDModuleInst_Backend_Up(i);
  411. return;
  412. fail1:
  413. NCDBProcessOpts_Free(&opts);
  414. fail0:
  415. NCDModuleInst_Backend_DeadError(i);
  416. return;
  417. error1:
  418. if (write_fd != -1) {
  419. if (close(write_fd) < 0) {
  420. ModuleLog(i, BLOG_ERROR, "close failed");
  421. }
  422. }
  423. if (read_fd != -1) {
  424. if (close(read_fd) < 0) {
  425. ModuleLog(i, BLOG_ERROR, "close failed");
  426. }
  427. }
  428. while (num_fds-- > start_num_fds) {
  429. if (close(fds[num_fds]) < 0) {
  430. ModuleLog(i, BLOG_ERROR, "close failed");
  431. }
  432. }
  433. NCDBProcessOpts_Free(&opts);
  434. o->read_fd = -1;
  435. o->write_fd = -1;
  436. o->state = PROCESS_STATE_ERROR;
  437. NCDModuleInst_Backend_Up(i);
  438. }
  439. static void process_func_die (void *vo)
  440. {
  441. struct process_instance *o = vo;
  442. ASSERT(o->state != PROCESS_STATE_DYING)
  443. // if process is not running, die immediately
  444. if (o->state != PROCESS_STATE_RUNNING) {
  445. process_free(o);
  446. return;
  447. }
  448. if (o->term_on_deinit) {
  449. ModuleLog(o->i, BLOG_INFO, "terminating process");
  450. // send termination signal
  451. BProcess_Terminate(&o->process);
  452. } else {
  453. ModuleLog(o->i, BLOG_INFO, "not terminating process as requested");
  454. }
  455. if (o->deinit_kill_time == -1) {
  456. // user wants SIGKILL immediately
  457. ModuleLog(o->i, BLOG_INFO, "killing process immediately");
  458. BProcess_Kill(&o->process);
  459. } else if (o->deinit_kill_time >= 0) {
  460. // user wants SIGKILL after some time
  461. BReactor_SetSmallTimer(o->i->params->iparams->reactor, &o->kill_timer, BTIMER_SET_RELATIVE, o->deinit_kill_time);
  462. }
  463. // set state
  464. o->state = PROCESS_STATE_DYING;
  465. }
  466. static int process_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
  467. {
  468. struct process_instance *o = vo;
  469. if (name == NCD_STRING_IS_ERROR) {
  470. int is_error = (o->state == PROCESS_STATE_ERROR);
  471. *out = ncd_make_boolean(mem, is_error);
  472. return 1;
  473. }
  474. return 0;
  475. }
  476. static void wait_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  477. {
  478. struct wait_instance *o = vo;
  479. o->i = i;
  480. if (!NCDVal_ListRead(params->args, 0)) {
  481. ModuleLog(i, BLOG_ERROR, "wrong arity");
  482. goto fail0;
  483. }
  484. struct process_instance *pinst = params->method_user;
  485. if (pinst->state == PROCESS_STATE_ERROR) {
  486. ModuleLog(i, BLOG_ERROR, "wait() is disallowed after the process has failed to start");
  487. goto fail0;
  488. }
  489. if (pinst->state == PROCESS_STATE_TERMINATED) {
  490. // not waiting, set no pinst
  491. o->pinst = NULL;
  492. // remember exit code
  493. o->exit_status = pinst->exit_status;
  494. // go up
  495. NCDModuleInst_Backend_Up(i);
  496. } else {
  497. // waitint, set pinst
  498. o->pinst = pinst;
  499. // insert to waits list
  500. LinkedList0_Prepend(&pinst->waits_list, &o->waits_list_node);
  501. }
  502. return;
  503. fail0:
  504. NCDModuleInst_Backend_DeadError(i);
  505. }
  506. static void wait_func_die (void *vo)
  507. {
  508. struct wait_instance *o = vo;
  509. // remove from waits list
  510. if (o->pinst) {
  511. LinkedList0_Remove(&o->pinst->waits_list, &o->waits_list_node);
  512. }
  513. NCDModuleInst_Backend_Dead(o->i);
  514. }
  515. static int wait_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
  516. {
  517. struct wait_instance *o = vo;
  518. ASSERT(!o->pinst)
  519. if (name == NCD_STRING_EXIT_STATUS) {
  520. if (o->exit_status == -1) {
  521. *out = NCDVal_NewString(mem, "-1");
  522. } else {
  523. *out = ncd_make_uintmax(mem, o->exit_status);
  524. }
  525. return 1;
  526. }
  527. return 0;
  528. }
  529. static void terminate_kill_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_kill)
  530. {
  531. if (!NCDVal_ListRead(params->args, 0)) {
  532. ModuleLog(i, BLOG_ERROR, "wrong arity");
  533. goto fail0;
  534. }
  535. struct process_instance *pinst = params->method_user;
  536. if (pinst->state == PROCESS_STATE_ERROR) {
  537. ModuleLog(i, BLOG_ERROR, "terminate()/kill() is disallowed after the process has failed to start");
  538. goto fail0;
  539. }
  540. if (pinst->state != PROCESS_STATE_TERMINATED) {
  541. if (is_kill) {
  542. BProcess_Kill(&pinst->process);
  543. } else {
  544. BProcess_Terminate(&pinst->process);
  545. }
  546. }
  547. NCDModuleInst_Backend_Up(i);
  548. return;
  549. fail0:
  550. NCDModuleInst_Backend_DeadError(i);
  551. }
  552. static void terminate_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  553. {
  554. terminate_kill_new_common(vo, i, params, 0);
  555. }
  556. static void kill_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  557. {
  558. terminate_kill_new_common(vo, i, params, 1);
  559. }
  560. static void read_pipe_free_connection (struct read_pipe_instance *o)
  561. {
  562. // disconnect read instance
  563. if (o->read_inst) {
  564. ASSERT(o->read_inst->read_pipe_inst == o)
  565. o->read_inst->read_pipe_inst = NULL;
  566. }
  567. // free store
  568. NCDBufStore_Free(&o->store);
  569. // free connection read interface
  570. BConnection_RecvAsync_Free(&o->connection);
  571. // free connection
  572. BConnection_Free(&o->connection);
  573. // close fd
  574. if (close(o->read_fd) < 0) {
  575. ModuleLog(o->i, BLOG_ERROR, "close failed");
  576. }
  577. }
  578. static void read_pipe_abort (struct read_pipe_instance *o)
  579. {
  580. ASSERT(o->state == READER_STATE_RUNNING)
  581. // release connection resources
  582. read_pipe_free_connection(o);
  583. // set state
  584. o->state = READER_STATE_ABORTED;
  585. }
  586. static void read_pipe_connection_handler (void *vo, int event)
  587. {
  588. struct read_pipe_instance *o = vo;
  589. ASSERT(o->state == READER_STATE_RUNNING)
  590. if (event == BCONNECTION_EVENT_RECVCLOSED) {
  591. // if we have read operation, make it finish with eof
  592. if (o->read_inst) {
  593. ASSERT(o->read_inst->read_pipe_inst == o)
  594. ASSERT(o->read_inst->buf)
  595. o->read_inst->read_pipe_inst = NULL;
  596. o->read_inst->read_size = 0;
  597. NCDModuleInst_Backend_Up(o->read_inst->i);
  598. o->read_inst = NULL;
  599. }
  600. // free connection resources
  601. read_pipe_free_connection(o);
  602. // set state closed
  603. o->state = READER_STATE_EOF;
  604. return;
  605. }
  606. ModuleLog(o->i, BLOG_ERROR, "read pipe error");
  607. // free connection resources
  608. read_pipe_free_connection(o);
  609. // set state error
  610. o->state = READER_STATE_ERROR;
  611. // backtrack
  612. NCDModuleInst_Backend_DownUp(o->i);
  613. }
  614. static void read_pipe_recv_handler_done (void *vo, int data_len)
  615. {
  616. struct read_pipe_instance *o = vo;
  617. ASSERT(o->state == READER_STATE_RUNNING)
  618. ASSERT(o->read_inst)
  619. ASSERT(o->read_inst->read_pipe_inst == o)
  620. ASSERT(o->read_inst->buf)
  621. ASSERT(data_len > 0)
  622. ASSERT(data_len <= NCDBufStore_BufSize(&o->store))
  623. // finish read operation
  624. o->read_inst->read_pipe_inst = NULL;
  625. o->read_inst->read_size = data_len;
  626. NCDModuleInst_Backend_Up(o->read_inst->i);
  627. o->read_inst = NULL;
  628. }
  629. static void read_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  630. {
  631. struct read_pipe_instance *o = vo;
  632. o->i = i;
  633. NCDModuleInst_Backend_PassMemToMethods(i);
  634. if (!NCDVal_ListRead(params->args, 0)) {
  635. ModuleLog(i, BLOG_ERROR, "wrong arity");
  636. goto fail0;
  637. }
  638. struct process_instance *pinst = params->method_user;
  639. if (pinst->read_fd == -1) {
  640. ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for reading or a read_pipe was already created");
  641. goto fail0;
  642. }
  643. // init connection
  644. if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->read_fd, 0), i->params->iparams->reactor, o, read_pipe_connection_handler)) {
  645. ModuleLog(i, BLOG_ERROR, "BConnection_Init failed");
  646. goto fail0;
  647. }
  648. // init connection read interface
  649. BConnection_RecvAsync_Init(&o->connection);
  650. // set recv done callback
  651. StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), read_pipe_recv_handler_done, o);
  652. // init store
  653. NCDBufStore_Init(&o->store, READ_BUF_SIZE);
  654. // set variables
  655. o->state = READER_STATE_RUNNING;
  656. o->read_fd = pinst->read_fd;
  657. o->read_inst = NULL;
  658. // steal read fd from process instance
  659. pinst->read_fd = -1;
  660. // go up
  661. NCDModuleInst_Backend_Up(i);
  662. return;
  663. fail0:
  664. NCDModuleInst_Backend_DeadError(i);
  665. }
  666. static void read_pipe_func_die (void *vo)
  667. {
  668. struct read_pipe_instance *o = vo;
  669. // free connection resources
  670. if (o->state == READER_STATE_RUNNING) {
  671. read_pipe_free_connection(o);
  672. }
  673. NCDModuleInst_Backend_Dead(o->i);
  674. }
  675. static int read_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
  676. {
  677. struct read_pipe_instance *o = vo;
  678. if (name == NCD_STRING_IS_ERROR) {
  679. int is_error = (o->state == READER_STATE_ERROR);
  680. *out = ncd_make_boolean(mem, is_error);
  681. return 1;
  682. }
  683. return 0;
  684. }
  685. static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  686. {
  687. struct read_instance *o = vo;
  688. o->i = i;
  689. if (!NCDVal_ListRead(params->args, 0)) {
  690. ModuleLog(i, BLOG_ERROR, "wrong arity");
  691. goto fail0;
  692. }
  693. struct read_pipe_instance *read_pipe_inst = params->method_user;
  694. // check if a read error has already occured
  695. if (read_pipe_inst->state == READER_STATE_ERROR) {
  696. ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read error has occured");
  697. goto fail0;
  698. }
  699. // check if the read_pipe has been aborted
  700. if (read_pipe_inst->state == READER_STATE_ABORTED) {
  701. ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read() has been aborted");
  702. goto fail0;
  703. }
  704. // if EOF has already been encountered, complete the read immediately
  705. if (read_pipe_inst->state == READER_STATE_EOF) {
  706. o->buf = NULL;
  707. o->read_pipe_inst = NULL;
  708. o->read_size = 0;
  709. NCDModuleInst_Backend_Up(i);
  710. return;
  711. }
  712. ASSERT(read_pipe_inst->state == READER_STATE_RUNNING)
  713. // check if there's already a read in progress
  714. if (read_pipe_inst->read_inst) {
  715. ModuleLog(i, BLOG_ERROR, "read() is disallowed while another read() is in progress");
  716. goto fail0;
  717. }
  718. // get buffer
  719. o->buf = NCDBufStore_GetBuf(&read_pipe_inst->store);
  720. if (!o->buf) {
  721. ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed");
  722. goto fail0;
  723. }
  724. // set read_pipe
  725. o->read_pipe_inst = read_pipe_inst;
  726. // register read in read_pipe
  727. read_pipe_inst->read_inst = o;
  728. // receive
  729. size_t buf_size = NCDBufStore_BufSize(&read_pipe_inst->store);
  730. int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size);
  731. StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&read_pipe_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read);
  732. return;
  733. fail0:
  734. NCDModuleInst_Backend_DeadError(i);
  735. }
  736. static void read_func_die (void *vo)
  737. {
  738. struct read_instance *o = vo;
  739. // if we're receiving, abort read_pipe
  740. if (o->read_pipe_inst) {
  741. ASSERT(o->read_pipe_inst->state == READER_STATE_RUNNING)
  742. ASSERT(o->read_pipe_inst->read_inst == o)
  743. ASSERT(o->buf)
  744. read_pipe_abort(o->read_pipe_inst);
  745. }
  746. // release buffer
  747. if (o->buf) {
  748. BRefTarget_Deref(NCDBuf_RefTarget(o->buf));
  749. }
  750. NCDModuleInst_Backend_Dead(o->i);
  751. }
  752. static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
  753. {
  754. struct read_instance *o = vo;
  755. ASSERT(!o->read_pipe_inst)
  756. ASSERT(!(o->read_size > 0) || o->buf)
  757. if (name == NCD_STRING_EMPTY) {
  758. if (o->read_size > 0) {
  759. *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf));
  760. } else {
  761. *out = NCDVal_NewIdString(mem, NCD_STRING_EMPTY);
  762. }
  763. return 1;
  764. }
  765. if (name == NCD_STRING_NOT_EOF) {
  766. int not_eof = (o->read_size > 0);
  767. *out = ncd_make_boolean(mem, not_eof);
  768. return 1;
  769. }
  770. return 0;
  771. }
  772. static void write_pipe_free_connection (struct write_pipe_instance *o)
  773. {
  774. // disconnect write instance
  775. if (o->write_inst) {
  776. ASSERT(o->write_inst->write_pipe_inst == o)
  777. o->write_inst->write_pipe_inst = NULL;
  778. }
  779. // free connection send interface
  780. BConnection_SendAsync_Free(&o->connection);
  781. // free connection
  782. BConnection_Free(&o->connection);
  783. // close fd
  784. if (close(o->write_fd) < 0) {
  785. ModuleLog(o->i, BLOG_ERROR, "close failed");
  786. }
  787. }
  788. static void write_pipe_abort (struct write_pipe_instance *o)
  789. {
  790. ASSERT(o->state == WRITER_STATE_RUNNING)
  791. // release connection resources
  792. write_pipe_free_connection(o);
  793. // set state
  794. o->state = WRITER_STATE_ABORTED;
  795. }
  796. static void write_pipe_close (struct write_pipe_instance *o)
  797. {
  798. ASSERT(o->state == WRITER_STATE_RUNNING)
  799. // release connection resources
  800. write_pipe_free_connection(o);
  801. // set state
  802. o->state = WRITER_STATE_CLOSED;
  803. }
  804. static void write_pipe_connection_handler (void *vo, int event)
  805. {
  806. struct write_pipe_instance *o = vo;
  807. ASSERT(o->state == WRITER_STATE_RUNNING)
  808. ModuleLog(o->i, BLOG_ERROR, "write pipe error");
  809. // free connection resources
  810. write_pipe_free_connection(o);
  811. // set state error
  812. o->state = WRITER_STATE_ERROR;
  813. // backtrack
  814. NCDModuleInst_Backend_DownUp(o->i);
  815. }
  816. static void write_pipe_send_handler_done (void *vo, int data_len)
  817. {
  818. struct write_pipe_instance *o = vo;
  819. ASSERT(o->state == WRITER_STATE_RUNNING)
  820. ASSERT(o->write_inst)
  821. ASSERT(o->write_inst->write_pipe_inst == o)
  822. ASSERT(data_len > 0)
  823. ASSERT(data_len <= o->write_inst->data.len)
  824. struct write_instance *wr = o->write_inst;
  825. // update write progress
  826. wr->data = MemRef_SubFrom(wr->data, data_len);
  827. // if there is more data, start another write operation
  828. if (wr->data.len > 0) {
  829. size_t to_send = (wr->data.len > INT_MAX) ? INT_MAX : wr->data.len;
  830. StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)wr->data.ptr, to_send);
  831. return;
  832. }
  833. // finish write operation
  834. wr->write_pipe_inst = NULL;
  835. NCDModuleInst_Backend_Up(wr->i);
  836. o->write_inst = NULL;
  837. }
  838. static void write_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  839. {
  840. struct write_pipe_instance *o = vo;
  841. o->i = i;
  842. NCDModuleInst_Backend_PassMemToMethods(i);
  843. if (!NCDVal_ListRead(params->args, 0)) {
  844. ModuleLog(i, BLOG_ERROR, "wrong arity");
  845. goto fail0;
  846. }
  847. struct process_instance *pinst = params->method_user;
  848. if (pinst->write_fd == -1) {
  849. ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for writing or a write_pipe was already created");
  850. goto fail0;
  851. }
  852. // init connection
  853. if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->write_fd, 0), i->params->iparams->reactor, o, write_pipe_connection_handler)) {
  854. ModuleLog(i, BLOG_ERROR, "BConnection_Init failed");
  855. goto fail0;
  856. }
  857. // init connection send interface
  858. BConnection_SendAsync_Init(&o->connection);
  859. // set send done callback
  860. StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), write_pipe_send_handler_done, o);
  861. // set variables
  862. o->state = WRITER_STATE_RUNNING;
  863. o->write_fd = pinst->write_fd;
  864. o->write_inst = NULL;
  865. // steal write fd from process instance
  866. pinst->write_fd = -1;
  867. // go up
  868. NCDModuleInst_Backend_Up(i);
  869. return;
  870. fail0:
  871. NCDModuleInst_Backend_DeadError(i);
  872. }
  873. static void write_pipe_func_die (void *vo)
  874. {
  875. struct write_pipe_instance *o = vo;
  876. // free connection resources
  877. if (o->state == WRITER_STATE_RUNNING) {
  878. write_pipe_free_connection(o);
  879. }
  880. NCDModuleInst_Backend_Dead(o->i);
  881. }
  882. static int write_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
  883. {
  884. struct write_pipe_instance *o = vo;
  885. if (name == NCD_STRING_IS_ERROR) {
  886. int is_error = (o->state == WRITER_STATE_ERROR);
  887. *out = ncd_make_boolean(mem, is_error);
  888. return 1;
  889. }
  890. return 0;
  891. }
  892. static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  893. {
  894. struct write_instance *o = vo;
  895. o->i = i;
  896. NCDValRef data_arg;
  897. if (!NCDVal_ListRead(params->args, 1, &data_arg)) {
  898. ModuleLog(i, BLOG_ERROR, "wrong arity");
  899. goto fail0;
  900. }
  901. if (!NCDVal_IsString(data_arg)) {
  902. ModuleLog(i, BLOG_ERROR, "wrong type");
  903. goto fail0;
  904. }
  905. struct write_pipe_instance *write_pipe_inst = params->method_user;
  906. // check if a write error has already occured
  907. if (write_pipe_inst->state == WRITER_STATE_ERROR) {
  908. ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write error has occured");
  909. goto fail0;
  910. }
  911. // check if the write_pipe has been aborted
  912. if (write_pipe_inst->state == WRITER_STATE_ABORTED) {
  913. ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write() has been aborted");
  914. goto fail0;
  915. }
  916. // check if the write_pipe has been aborted
  917. if (write_pipe_inst->state == WRITER_STATE_CLOSED) {
  918. ModuleLog(i, BLOG_ERROR, "write() is disallowed after close() has been called");
  919. goto fail0;
  920. }
  921. ASSERT(write_pipe_inst->state == WRITER_STATE_RUNNING)
  922. // check if there's already a write in progress
  923. if (write_pipe_inst->write_inst) {
  924. ModuleLog(i, BLOG_ERROR, "write() is disallowed while another write() is in progress");
  925. goto fail0;
  926. }
  927. // initialize write progress state
  928. o->data = NCDVal_StringMemRef(data_arg);
  929. // if there's nothing to send, go up immediately
  930. if (o->data.len == 0) {
  931. o->write_pipe_inst = NULL;
  932. NCDModuleInst_Backend_Up(i);
  933. return;
  934. }
  935. // set write_pipe
  936. o->write_pipe_inst = write_pipe_inst;
  937. // register write in write_pipe
  938. write_pipe_inst->write_inst = o;
  939. // start send operation
  940. size_t to_send = (o->data.len > INT_MAX) ? INT_MAX : o->data.len;
  941. StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&write_pipe_inst->connection), (uint8_t *)o->data.ptr, to_send);
  942. return;
  943. fail0:
  944. NCDModuleInst_Backend_DeadError(i);
  945. }
  946. static void write_func_die (void *vo)
  947. {
  948. struct write_instance *o = vo;
  949. // if we're sending, abort write_pipe
  950. if (o->write_pipe_inst) {
  951. ASSERT(o->write_pipe_inst->state == WRITER_STATE_RUNNING)
  952. ASSERT(o->write_pipe_inst->write_inst == o)
  953. write_pipe_abort(o->write_pipe_inst);
  954. }
  955. NCDModuleInst_Backend_Dead(o->i);
  956. }
  957. static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  958. {
  959. if (!NCDVal_ListRead(params->args, 0)) {
  960. ModuleLog(i, BLOG_ERROR, "wrong arity");
  961. goto fail0;
  962. }
  963. struct write_pipe_instance *write_pipe_inst = params->method_user;
  964. // check if a write error has already occured
  965. if (write_pipe_inst->state == WRITER_STATE_ERROR) {
  966. ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write error has occured");
  967. goto fail0;
  968. }
  969. // check if the write_pipe has been aborted
  970. if (write_pipe_inst->state == WRITER_STATE_ABORTED) {
  971. ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write() has been aborted");
  972. goto fail0;
  973. }
  974. // check if the write_pipe has been closed
  975. if (write_pipe_inst->state == WRITER_STATE_CLOSED) {
  976. ModuleLog(i, BLOG_ERROR, "close() is disallowed after close() has been called");
  977. goto fail0;
  978. }
  979. // close
  980. write_pipe_close(write_pipe_inst);
  981. // go up
  982. NCDModuleInst_Backend_Up(i);
  983. return;
  984. fail0:
  985. NCDModuleInst_Backend_DeadError(i);
  986. }
  987. static struct NCDModule modules[] = {
  988. {
  989. .type = "sys.start_process",
  990. .func_new2 = process_func_new,
  991. .func_die = process_func_die,
  992. .func_getvar2 = process_func_getvar,
  993. .alloc_size = sizeof(struct process_instance)
  994. }, {
  995. .type = "sys.start_process::wait",
  996. .func_new2 = wait_func_new,
  997. .func_die = wait_func_die,
  998. .func_getvar2 = wait_func_getvar,
  999. .alloc_size = sizeof(struct wait_instance)
  1000. }, {
  1001. .type = "sys.start_process::terminate",
  1002. .func_new2 = terminate_func_new
  1003. }, {
  1004. .type = "sys.start_process::kill",
  1005. .func_new2 = kill_func_new
  1006. }, {
  1007. .type = "sys.start_process::read_pipe",
  1008. .func_new2 = read_pipe_func_new,
  1009. .func_die = read_pipe_func_die,
  1010. .func_getvar2 = read_pipe_func_getvar,
  1011. .alloc_size = sizeof(struct read_pipe_instance)
  1012. }, {
  1013. .type = "sys.start_process::read_pipe::read",
  1014. .func_new2 = read_func_new,
  1015. .func_die = read_func_die,
  1016. .func_getvar2 = read_func_getvar,
  1017. .alloc_size = sizeof(struct read_instance)
  1018. }, {
  1019. .type = "sys.start_process::write_pipe",
  1020. .func_new2 = write_pipe_func_new,
  1021. .func_die = write_pipe_func_die,
  1022. .func_getvar2 = write_pipe_func_getvar,
  1023. .alloc_size = sizeof(struct write_pipe_instance)
  1024. }, {
  1025. .type = "sys.start_process::write_pipe::write",
  1026. .func_new2 = write_func_new,
  1027. .func_die = write_func_die,
  1028. .alloc_size = sizeof(struct write_instance)
  1029. }, {
  1030. .type = "sys.start_process::write_pipe::close",
  1031. .func_new2 = close_func_new
  1032. }, {
  1033. .type = NULL
  1034. }
  1035. };
  1036. const struct NCDModuleGroup ncdmodule_sys_start_process = {
  1037. .modules = modules
  1038. };