BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 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  
134 #include <stdlib.h>
135 #include <string.h>
136 #include <stdio.h>
137 #include <inttypes.h>
138 #include <limits.h>
139 #include <unistd.h>
140  
141 #include <misc/offset.h>
142 #include <structure/LinkedList0.h>
143 #include <system/BProcess.h>
144 #include <system/BConnection.h>
145 #include <ncd/extra/NCDBuf.h>
146 #include <ncd/extra/build_cmdline.h>
147 #include <ncd/extra/NCDBProcessOpts.h>
148  
149 #include <ncd/module_common.h>
150  
151 #include <generated/blog_channel_ncd_sys_start_process.h>
152  
153 #define READ_BUF_SIZE 8192
154  
155 #define PROCESS_STATE_ERROR 1
156 #define PROCESS_STATE_RUNNING 2
157 #define PROCESS_STATE_TERMINATED 3
158 #define PROCESS_STATE_DYING 4
159  
160 #define READER_STATE_RUNNING 1
161 #define READER_STATE_EOF 2
162 #define READER_STATE_ERROR 3
163 #define READER_STATE_ABORTED 4
164  
165 #define WRITER_STATE_RUNNING 1
166 #define WRITER_STATE_CLOSED 2
167 #define WRITER_STATE_ERROR 3
168 #define WRITER_STATE_ABORTED 4
169  
170 struct process_instance {
171 NCDModuleInst *i;
172 BProcess process;
173 BSmallTimer kill_timer;
174 LinkedList0 waits_list;
175 btime_t deinit_kill_time;
176 int term_on_deinit;
177 int read_fd;
178 int write_fd;
179 int exit_status;
180 int state;
181 };
182  
183 struct wait_instance {
184 NCDModuleInst *i;
185 struct process_instance *pinst;
186 LinkedList0Node waits_list_node;
187 int exit_status;
188 };
189  
190 struct read_pipe_instance {
191 NCDModuleInst *i;
192 int state;
193 int read_fd;
194 BConnection connection;
195 NCDBufStore store;
196 struct read_instance *read_inst;
197 };
198  
199 struct read_instance {
200 NCDModuleInst *i;
201 struct read_pipe_instance *read_pipe_inst;
202 NCDBuf *buf;
203 size_t read_size;
204 };
205  
206 struct write_pipe_instance {
207 NCDModuleInst *i;
208 int state;
209 int write_fd;
210 BConnection connection;
211 struct write_instance *write_inst;
212 };
213  
214 struct write_instance {
215 NCDModuleInst *i;
216 struct write_pipe_instance *write_pipe_inst;
217 MemRef data;
218 };
219  
220 static int parse_mode (NCDModuleInst *i, NCDValRef mode_arg, int *out_read, int *out_write)
221 {
222 if (!NCDVal_IsString(mode_arg)) {
223 ModuleLog(i, BLOG_ERROR, "mode argument must be a string");
224 return 0;
225 }
226  
227 *out_read = 0;
228 *out_write = 0;
229  
230 MEMREF_LOOP_CHARS(NCDVal_StringMemRef(mode_arg), char_pos, ch, {
231 if (ch == 'r') {
232 *out_read = 1;
233 }
234 else if (ch == 'w') {
235 *out_write = 1;
236 }
237 else {
238 ModuleLog(i, BLOG_ERROR, "invalid character in mode argument");
239 return 0;
240 }
241 })
242  
243 return 1;
244 }
245  
246 static void process_free (struct process_instance *o)
247 {
248 // close write fd
249 if (o->write_fd != -1) {
250 if (close(o->write_fd) < 0) {
251 ModuleLog(o->i, BLOG_ERROR, "close failed");
252 }
253 }
254  
255 // close read fd
256 if (o->read_fd != -1) {
257 if (close(o->read_fd) < 0) {
258 ModuleLog(o->i, BLOG_ERROR, "close failed");
259 }
260 }
261  
262 NCDModuleInst_Backend_Dead(o->i);
263 }
264  
265 static void process_handler (void *vo, int normally, uint8_t normally_exit_status)
266 {
267 struct process_instance *o = vo;
268 ASSERT(o->state == PROCESS_STATE_RUNNING || o->state == PROCESS_STATE_DYING)
269  
270 ModuleLog(o->i, BLOG_INFO, "process terminated");
271  
272 // free kill timer
273 BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &o->kill_timer);
274  
275 // free process
276 BProcess_Free(&o->process);
277  
278 // remember exit code
279 o->exit_status = (!normally ? -1 : normally_exit_status);
280  
281 // finish waits
282 LinkedList0Node *ln;
283 while ((ln = LinkedList0_GetFirst(&o->waits_list))) {
284 struct wait_instance *winst = UPPER_OBJECT(ln, struct wait_instance, waits_list_node);
285 ASSERT(winst->pinst == o)
286 LinkedList0_Remove(&o->waits_list, &winst->waits_list_node);
287 winst->pinst = NULL;
288 winst->exit_status = o->exit_status;
289 NCDModuleInst_Backend_Up(winst->i);
290 }
291  
292 // if we have been requested to die, then die now
293 if (o->state == PROCESS_STATE_DYING) {
294 process_free(o);
295 return;
296 }
297  
298 // set state
299 o->state = PROCESS_STATE_TERMINATED;
300 }
301  
302 static void process_kill_timer_handler (BSmallTimer *kill_timer)
303 {
304 struct process_instance *o = UPPER_OBJECT(kill_timer, struct process_instance, kill_timer);
305 ASSERT(o->state == PROCESS_STATE_DYING)
306  
307 ModuleLog(o->i, BLOG_INFO, "killing process after timeout");
308 BProcess_Kill(&o->process);
309 }
310  
311 static int opts_func_unknown (void *user, NCDValRef key, NCDValRef val)
312 {
313 struct process_instance *o = user;
314  
315 if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "term_on_deinit")) {
316 if (!ncd_read_boolean(val, &o->term_on_deinit)) {
317 ModuleLog(o->i, BLOG_ERROR, "term_on_deinit: bad value");
318 return 0;
319 }
320 return 1;
321 }
322  
323 if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "deinit_kill_time")) {
324 if (NCDVal_IsString(val) && NCDVal_StringEquals(val, "never")) {
325 o->deinit_kill_time = -2;
326 }
327 else if (NCDVal_IsString(val) && NCDVal_StringEqualsId(val, NCD_STRING_EMPTY)) {
328 o->deinit_kill_time = -1;
329 }
330 else if (!ncd_read_time(val, &o->deinit_kill_time)) {
331 ModuleLog(o->i, BLOG_ERROR, "wrong value for deinit_kill_time option");
332 return 0;
333 }
334 return 1;
335 }
336  
337 return 0;
338 }
339  
340 static void process_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
341 {
342 struct process_instance *o = vo;
343 o->i = i;
344 NCDModuleInst_Backend_PassMemToMethods(i);
345  
346 // check arguments
347 NCDValRef command_arg;
348 NCDValRef mode_arg;
349 NCDValRef options_arg = NCDVal_NewInvalid();
350 if (!NCDVal_ListRead(params->args, 2, &command_arg, &mode_arg) &&
351 !NCDVal_ListRead(params->args, 3, &command_arg, &mode_arg, &options_arg)
352 ) {
353 ModuleLog(i, BLOG_ERROR, "wrong arity");
354 goto fail0;
355 }
356  
357 // parse mode
358 int is_read;
359 int is_write;
360 if (!parse_mode(i, mode_arg, &is_read, &is_write)) {
361 goto fail0;
362 }
363  
364 // parse options
365 NCDBProcessOpts opts;
366 int keep_stdout;
367 int keep_stderr;
368 o->deinit_kill_time = -2;
369 o->term_on_deinit = 1;
370 if (!NCDBProcessOpts_Init2(&opts, options_arg, opts_func_unknown, o, i, BLOG_CURRENT_CHANNEL, &keep_stdout, &keep_stderr)) {
371 goto fail0;
372 }
373  
374 // keep-stdout option and read mode are not compatible
375 if (keep_stdout && is_read) {
376 ModuleLog(i, BLOG_ERROR, "keep-stdout and read mode are not compatible");
377 goto fail1;
378 }
379  
380 // prepare for creating pipes
381 int fds[4];
382 int fds_map[3];
383 int start_num_fds = opts.nfds;
384 int num_fds = start_num_fds;
385 memcpy(fds, opts.fds, num_fds * sizeof(int));
386 memcpy(fds_map, opts.fds_map, num_fds * sizeof(int));
387 int read_fd = -1;
388 int write_fd = -1;
389  
390 // create read pipe
391 if (is_read) {
392 int pipefd[2];
393 if (pipe(pipefd) < 0) {
394 ModuleLog(i, BLOG_ERROR, "pipe failed");
395 goto error1;
396 }
397 read_fd = pipefd[0];
398 fds[num_fds] = pipefd[1];
399 fds_map[num_fds++] = STDOUT_FILENO;
400 }
401  
402 // create write pipe
403 if (is_write) {
404 int pipefd[2];
405 if (pipe(pipefd) < 0) {
406 ModuleLog(i, BLOG_ERROR, "pipe failed");
407 goto error1;
408 }
409 write_fd = pipefd[1];
410 fds[num_fds] = pipefd[0];
411 fds_map[num_fds++] = STDIN_FILENO;
412 }
413  
414 // terminate fds array
415 fds[num_fds] = -1;
416  
417 // build process parameters struct
418 struct BProcess_params p_params = {};
419 p_params.fds = fds;
420 p_params.fds_map = fds_map;
421 p_params.do_setsid = opts.do_setsid;
422 p_params.username = opts.username;
423  
424 // build command line
425 char *exec;
426 CmdLine cl;
427 if (!ncd_build_cmdline(i, BLOG_CURRENT_CHANNEL, command_arg, &exec, &cl)) {
428 goto error1;
429 }
430  
431 // start process
432 int res = BProcess_Init2(&o->process, i->params->iparams->manager, process_handler, o, exec, CmdLine_Get(&cl), p_params);
433 CmdLine_Free(&cl);
434 free(exec);
435 if (!res) {
436 ModuleLog(i, BLOG_ERROR, "BProcess_Init failed");
437 goto error1;
438 }
439  
440 // init kill timer
441 BSmallTimer_Init(&o->kill_timer, process_kill_timer_handler);
442  
443 // close child fds
444 while (num_fds-- > start_num_fds) {
445 if (close(fds[num_fds]) < 0) {
446 ModuleLog(i, BLOG_ERROR, "close failed");
447 }
448 }
449  
450 // free opts
451 NCDBProcessOpts_Free(&opts);
452  
453 // init waits list
454 LinkedList0_Init(&o->waits_list);
455  
456 // remember our fds
457 o->read_fd = read_fd;
458 o->write_fd = write_fd;
459  
460 // set state
461 o->state = PROCESS_STATE_RUNNING;
462  
463 // go up
464 NCDModuleInst_Backend_Up(i);
465 return;
466  
467 fail1:
468 NCDBProcessOpts_Free(&opts);
469 fail0:
470 NCDModuleInst_Backend_DeadError(i);
471 return;
472  
473 error1:
474 if (write_fd != -1) {
475 if (close(write_fd) < 0) {
476 ModuleLog(i, BLOG_ERROR, "close failed");
477 }
478 }
479 if (read_fd != -1) {
480 if (close(read_fd) < 0) {
481 ModuleLog(i, BLOG_ERROR, "close failed");
482 }
483 }
484 while (num_fds-- > start_num_fds) {
485 if (close(fds[num_fds]) < 0) {
486 ModuleLog(i, BLOG_ERROR, "close failed");
487 }
488 }
489 NCDBProcessOpts_Free(&opts);
490  
491 o->read_fd = -1;
492 o->write_fd = -1;
493 o->state = PROCESS_STATE_ERROR;
494 NCDModuleInst_Backend_Up(i);
495 }
496  
497 static void process_func_die (void *vo)
498 {
499 struct process_instance *o = vo;
500 ASSERT(o->state != PROCESS_STATE_DYING)
501  
502 // if process is not running, die immediately
503 if (o->state != PROCESS_STATE_RUNNING) {
504 process_free(o);
505 return;
506 }
507  
508 if (o->term_on_deinit) {
509 ModuleLog(o->i, BLOG_INFO, "terminating process");
510  
511 // send termination signal
512 BProcess_Terminate(&o->process);
513 } else {
514 ModuleLog(o->i, BLOG_INFO, "not terminating process as requested");
515 }
516  
517 if (o->deinit_kill_time == -1) {
518 // user wants SIGKILL immediately
519 ModuleLog(o->i, BLOG_INFO, "killing process immediately");
520 BProcess_Kill(&o->process);
521 } else if (o->deinit_kill_time >= 0) {
522 // user wants SIGKILL after some time
523 BReactor_SetSmallTimer(o->i->params->iparams->reactor, &o->kill_timer, BTIMER_SET_RELATIVE, o->deinit_kill_time);
524 }
525  
526 // set state
527 o->state = PROCESS_STATE_DYING;
528 }
529  
530 static int process_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
531 {
532 struct process_instance *o = vo;
533  
534 if (name == NCD_STRING_IS_ERROR) {
535 int is_error = (o->state == PROCESS_STATE_ERROR);
536 *out = ncd_make_boolean(mem, is_error);
537 return 1;
538 }
539  
540 return 0;
541 }
542  
543 static void wait_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
544 {
545 struct wait_instance *o = vo;
546 o->i = i;
547  
548 if (!NCDVal_ListRead(params->args, 0)) {
549 ModuleLog(i, BLOG_ERROR, "wrong arity");
550 goto fail0;
551 }
552  
553 struct process_instance *pinst = params->method_user;
554  
555 if (pinst->state == PROCESS_STATE_ERROR) {
556 ModuleLog(i, BLOG_ERROR, "wait() is disallowed after the process has failed to start");
557 goto fail0;
558 }
559  
560 if (pinst->state == PROCESS_STATE_TERMINATED) {
561 // not waiting, set no pinst
562 o->pinst = NULL;
563  
564 // remember exit code
565 o->exit_status = pinst->exit_status;
566  
567 // go up
568 NCDModuleInst_Backend_Up(i);
569 } else {
570 // waitint, set pinst
571 o->pinst = pinst;
572  
573 // insert to waits list
574 LinkedList0_Prepend(&pinst->waits_list, &o->waits_list_node);
575 }
576  
577 return;
578  
579 fail0:
580 NCDModuleInst_Backend_DeadError(i);
581 }
582  
583 static void wait_func_die (void *vo)
584 {
585 struct wait_instance *o = vo;
586  
587 // remove from waits list
588 if (o->pinst) {
589 LinkedList0_Remove(&o->pinst->waits_list, &o->waits_list_node);
590 }
591  
592 NCDModuleInst_Backend_Dead(o->i);
593 }
594  
595 static int wait_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
596 {
597 struct wait_instance *o = vo;
598 ASSERT(!o->pinst)
599  
600 if (name == NCD_STRING_EXIT_STATUS) {
601 if (o->exit_status == -1) {
602 *out = NCDVal_NewString(mem, "-1");
603 } else {
604 *out = ncd_make_uintmax(mem, o->exit_status);
605 }
606 return 1;
607 }
608  
609 return 0;
610 }
611  
612 static void terminate_kill_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_kill)
613 {
614 if (!NCDVal_ListRead(params->args, 0)) {
615 ModuleLog(i, BLOG_ERROR, "wrong arity");
616 goto fail0;
617 }
618  
619 struct process_instance *pinst = params->method_user;
620  
621 if (pinst->state == PROCESS_STATE_ERROR) {
622 ModuleLog(i, BLOG_ERROR, "terminate()/kill() is disallowed after the process has failed to start");
623 goto fail0;
624 }
625  
626 if (pinst->state != PROCESS_STATE_TERMINATED) {
627 if (is_kill) {
628 BProcess_Kill(&pinst->process);
629 } else {
630 BProcess_Terminate(&pinst->process);
631 }
632 }
633  
634 NCDModuleInst_Backend_Up(i);
635 return;
636  
637 fail0:
638 NCDModuleInst_Backend_DeadError(i);
639 }
640  
641 static void terminate_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
642 {
643 terminate_kill_new_common(vo, i, params, 0);
644 }
645  
646 static void kill_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
647 {
648 terminate_kill_new_common(vo, i, params, 1);
649 }
650  
651 static void read_pipe_free_connection (struct read_pipe_instance *o)
652 {
653 // disconnect read instance
654 if (o->read_inst) {
655 ASSERT(o->read_inst->read_pipe_inst == o)
656 o->read_inst->read_pipe_inst = NULL;
657 }
658  
659 // free store
660 NCDBufStore_Free(&o->store);
661  
662 // free connection read interface
663 BConnection_RecvAsync_Free(&o->connection);
664  
665 // free connection
666 BConnection_Free(&o->connection);
667  
668 // close fd
669 if (close(o->read_fd) < 0) {
670 ModuleLog(o->i, BLOG_ERROR, "close failed");
671 }
672 }
673  
674 static void read_pipe_abort (struct read_pipe_instance *o)
675 {
676 ASSERT(o->state == READER_STATE_RUNNING)
677  
678 // release connection resources
679 read_pipe_free_connection(o);
680  
681 // set state
682 o->state = READER_STATE_ABORTED;
683 }
684  
685 static void read_pipe_connection_handler (void *vo, int event)
686 {
687 struct read_pipe_instance *o = vo;
688 ASSERT(o->state == READER_STATE_RUNNING)
689  
690 if (event == BCONNECTION_EVENT_RECVCLOSED) {
691 // if we have read operation, make it finish with eof
692 if (o->read_inst) {
693 ASSERT(o->read_inst->read_pipe_inst == o)
694 ASSERT(o->read_inst->buf)
695 o->read_inst->read_pipe_inst = NULL;
696 o->read_inst->read_size = 0;
697 NCDModuleInst_Backend_Up(o->read_inst->i);
698 o->read_inst = NULL;
699 }
700  
701 // free connection resources
702 read_pipe_free_connection(o);
703  
704 // set state closed
705 o->state = READER_STATE_EOF;
706 return;
707 }
708  
709 ModuleLog(o->i, BLOG_ERROR, "read pipe error");
710  
711 // free connection resources
712 read_pipe_free_connection(o);
713  
714 // set state error
715 o->state = READER_STATE_ERROR;
716  
717 // backtrack
718 NCDModuleInst_Backend_DownUp(o->i);
719 }
720  
721 static void read_pipe_recv_handler_done (void *vo, int data_len)
722 {
723 struct read_pipe_instance *o = vo;
724 ASSERT(o->state == READER_STATE_RUNNING)
725 ASSERT(o->read_inst)
726 ASSERT(o->read_inst->read_pipe_inst == o)
727 ASSERT(o->read_inst->buf)
728 ASSERT(data_len > 0)
729 ASSERT(data_len <= NCDBufStore_BufSize(&o->store))
730  
731 // finish read operation
732 o->read_inst->read_pipe_inst = NULL;
733 o->read_inst->read_size = data_len;
734 NCDModuleInst_Backend_Up(o->read_inst->i);
735 o->read_inst = NULL;
736 }
737  
738 static void read_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
739 {
740 struct read_pipe_instance *o = vo;
741 o->i = i;
742 NCDModuleInst_Backend_PassMemToMethods(i);
743  
744 if (!NCDVal_ListRead(params->args, 0)) {
745 ModuleLog(i, BLOG_ERROR, "wrong arity");
746 goto fail0;
747 }
748  
749 struct process_instance *pinst = params->method_user;
750  
751 if (pinst->read_fd == -1) {
752 ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for reading or a read_pipe was already created");
753 goto fail0;
754 }
755  
756 // init connection
757 if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->read_fd, 0), i->params->iparams->reactor, o, read_pipe_connection_handler)) {
758 ModuleLog(i, BLOG_ERROR, "BConnection_Init failed");
759 goto fail0;
760 }
761  
762 // init connection read interface
763 BConnection_RecvAsync_Init(&o->connection);
764  
765 // set recv done callback
766 StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), read_pipe_recv_handler_done, o);
767  
768 // init store
769 NCDBufStore_Init(&o->store, READ_BUF_SIZE);
770  
771 // set variables
772 o->state = READER_STATE_RUNNING;
773 o->read_fd = pinst->read_fd;
774 o->read_inst = NULL;
775  
776 // steal read fd from process instance
777 pinst->read_fd = -1;
778  
779 // go up
780 NCDModuleInst_Backend_Up(i);
781 return;
782  
783 fail0:
784 NCDModuleInst_Backend_DeadError(i);
785 }
786  
787 static void read_pipe_func_die (void *vo)
788 {
789 struct read_pipe_instance *o = vo;
790  
791 // free connection resources
792 if (o->state == READER_STATE_RUNNING) {
793 read_pipe_free_connection(o);
794 }
795  
796 NCDModuleInst_Backend_Dead(o->i);
797 }
798  
799 static int read_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
800 {
801 struct read_pipe_instance *o = vo;
802  
803 if (name == NCD_STRING_IS_ERROR) {
804 int is_error = (o->state == READER_STATE_ERROR);
805 *out = ncd_make_boolean(mem, is_error);
806 return 1;
807 }
808  
809 return 0;
810 }
811  
812 static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
813 {
814 struct read_instance *o = vo;
815 o->i = i;
816  
817 if (!NCDVal_ListRead(params->args, 0)) {
818 ModuleLog(i, BLOG_ERROR, "wrong arity");
819 goto fail0;
820 }
821  
822 struct read_pipe_instance *read_pipe_inst = params->method_user;
823  
824 // check if a read error has already occured
825 if (read_pipe_inst->state == READER_STATE_ERROR) {
826 ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read error has occured");
827 goto fail0;
828 }
829  
830 // check if the read_pipe has been aborted
831 if (read_pipe_inst->state == READER_STATE_ABORTED) {
832 ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read() has been aborted");
833 goto fail0;
834 }
835  
836 // if EOF has already been encountered, complete the read immediately
837 if (read_pipe_inst->state == READER_STATE_EOF) {
838 o->buf = NULL;
839 o->read_pipe_inst = NULL;
840 o->read_size = 0;
841 NCDModuleInst_Backend_Up(i);
842 return;
843 }
844  
845 ASSERT(read_pipe_inst->state == READER_STATE_RUNNING)
846  
847 // check if there's already a read in progress
848 if (read_pipe_inst->read_inst) {
849 ModuleLog(i, BLOG_ERROR, "read() is disallowed while another read() is in progress");
850 goto fail0;
851 }
852  
853 // get buffer
854 o->buf = NCDBufStore_GetBuf(&read_pipe_inst->store);
855 if (!o->buf) {
856 ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed");
857 goto fail0;
858 }
859  
860 // set read_pipe
861 o->read_pipe_inst = read_pipe_inst;
862  
863 // register read in read_pipe
864 read_pipe_inst->read_inst = o;
865  
866 // receive
867 size_t buf_size = NCDBufStore_BufSize(&read_pipe_inst->store);
868 int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size);
869 StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&read_pipe_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read);
870 return;
871  
872 fail0:
873 NCDModuleInst_Backend_DeadError(i);
874 }
875  
876 static void read_func_die (void *vo)
877 {
878 struct read_instance *o = vo;
879  
880 // if we're receiving, abort read_pipe
881 if (o->read_pipe_inst) {
882 ASSERT(o->read_pipe_inst->state == READER_STATE_RUNNING)
883 ASSERT(o->read_pipe_inst->read_inst == o)
884 ASSERT(o->buf)
885 read_pipe_abort(o->read_pipe_inst);
886 }
887  
888 // release buffer
889 if (o->buf) {
890 BRefTarget_Deref(NCDBuf_RefTarget(o->buf));
891 }
892  
893 NCDModuleInst_Backend_Dead(o->i);
894 }
895  
896 static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
897 {
898 struct read_instance *o = vo;
899 ASSERT(!o->read_pipe_inst)
900 ASSERT(!(o->read_size > 0) || o->buf)
901  
902 if (name == NCD_STRING_EMPTY) {
903 if (o->read_size > 0) {
904 *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf));
905 } else {
906 *out = NCDVal_NewIdString(mem, NCD_STRING_EMPTY);
907 }
908 return 1;
909 }
910  
911 if (name == NCD_STRING_NOT_EOF) {
912 int not_eof = (o->read_size > 0);
913 *out = ncd_make_boolean(mem, not_eof);
914 return 1;
915 }
916  
917 return 0;
918 }
919  
920 static void write_pipe_free_connection (struct write_pipe_instance *o)
921 {
922 // disconnect write instance
923 if (o->write_inst) {
924 ASSERT(o->write_inst->write_pipe_inst == o)
925 o->write_inst->write_pipe_inst = NULL;
926 }
927  
928 // free connection send interface
929 BConnection_SendAsync_Free(&o->connection);
930  
931 // free connection
932 BConnection_Free(&o->connection);
933  
934 // close fd
935 if (close(o->write_fd) < 0) {
936 ModuleLog(o->i, BLOG_ERROR, "close failed");
937 }
938 }
939  
940 static void write_pipe_abort (struct write_pipe_instance *o)
941 {
942 ASSERT(o->state == WRITER_STATE_RUNNING)
943  
944 // release connection resources
945 write_pipe_free_connection(o);
946  
947 // set state
948 o->state = WRITER_STATE_ABORTED;
949 }
950  
951 static void write_pipe_close (struct write_pipe_instance *o)
952 {
953 ASSERT(o->state == WRITER_STATE_RUNNING)
954  
955 // release connection resources
956 write_pipe_free_connection(o);
957  
958 // set state
959 o->state = WRITER_STATE_CLOSED;
960 }
961  
962 static void write_pipe_connection_handler (void *vo, int event)
963 {
964 struct write_pipe_instance *o = vo;
965 ASSERT(o->state == WRITER_STATE_RUNNING)
966  
967 ModuleLog(o->i, BLOG_ERROR, "write pipe error");
968  
969 // free connection resources
970 write_pipe_free_connection(o);
971  
972 // set state error
973 o->state = WRITER_STATE_ERROR;
974  
975 // backtrack
976 NCDModuleInst_Backend_DownUp(o->i);
977 }
978  
979 static void write_pipe_send_handler_done (void *vo, int data_len)
980 {
981 struct write_pipe_instance *o = vo;
982 ASSERT(o->state == WRITER_STATE_RUNNING)
983 ASSERT(o->write_inst)
984 ASSERT(o->write_inst->write_pipe_inst == o)
985 ASSERT(data_len > 0)
986 ASSERT(data_len <= o->write_inst->data.len)
987  
988 struct write_instance *wr = o->write_inst;
989  
990 // update write progress
991 wr->data = MemRef_SubFrom(wr->data, data_len);
992  
993 // if there is more data, start another write operation
994 if (wr->data.len > 0) {
995 size_t to_send = (wr->data.len > INT_MAX) ? INT_MAX : wr->data.len;
996 StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)wr->data.ptr, to_send);
997 return;
998 }
999  
1000 // finish write operation
1001 wr->write_pipe_inst = NULL;
1002 NCDModuleInst_Backend_Up(wr->i);
1003 o->write_inst = NULL;
1004 }
1005  
1006 static void write_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
1007 {
1008 struct write_pipe_instance *o = vo;
1009 o->i = i;
1010 NCDModuleInst_Backend_PassMemToMethods(i);
1011  
1012 if (!NCDVal_ListRead(params->args, 0)) {
1013 ModuleLog(i, BLOG_ERROR, "wrong arity");
1014 goto fail0;
1015 }
1016  
1017 struct process_instance *pinst = params->method_user;
1018  
1019 if (pinst->write_fd == -1) {
1020 ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for writing or a write_pipe was already created");
1021 goto fail0;
1022 }
1023  
1024 // init connection
1025 if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->write_fd, 0), i->params->iparams->reactor, o, write_pipe_connection_handler)) {
1026 ModuleLog(i, BLOG_ERROR, "BConnection_Init failed");
1027 goto fail0;
1028 }
1029  
1030 // init connection send interface
1031 BConnection_SendAsync_Init(&o->connection);
1032  
1033 // set send done callback
1034 StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), write_pipe_send_handler_done, o);
1035  
1036 // set variables
1037 o->state = WRITER_STATE_RUNNING;
1038 o->write_fd = pinst->write_fd;
1039 o->write_inst = NULL;
1040  
1041 // steal write fd from process instance
1042 pinst->write_fd = -1;
1043  
1044 // go up
1045 NCDModuleInst_Backend_Up(i);
1046 return;
1047  
1048 fail0:
1049 NCDModuleInst_Backend_DeadError(i);
1050 }
1051  
1052 static void write_pipe_func_die (void *vo)
1053 {
1054 struct write_pipe_instance *o = vo;
1055  
1056 // free connection resources
1057 if (o->state == WRITER_STATE_RUNNING) {
1058 write_pipe_free_connection(o);
1059 }
1060  
1061 NCDModuleInst_Backend_Dead(o->i);
1062 }
1063  
1064 static int write_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
1065 {
1066 struct write_pipe_instance *o = vo;
1067  
1068 if (name == NCD_STRING_IS_ERROR) {
1069 int is_error = (o->state == WRITER_STATE_ERROR);
1070 *out = ncd_make_boolean(mem, is_error);
1071 return 1;
1072 }
1073  
1074 return 0;
1075 }
1076  
1077 static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
1078 {
1079 struct write_instance *o = vo;
1080 o->i = i;
1081  
1082 NCDValRef data_arg;
1083 if (!NCDVal_ListRead(params->args, 1, &data_arg)) {
1084 ModuleLog(i, BLOG_ERROR, "wrong arity");
1085 goto fail0;
1086 }
1087 if (!NCDVal_IsString(data_arg)) {
1088 ModuleLog(i, BLOG_ERROR, "wrong type");
1089 goto fail0;
1090 }
1091  
1092 struct write_pipe_instance *write_pipe_inst = params->method_user;
1093  
1094 // check if a write error has already occured
1095 if (write_pipe_inst->state == WRITER_STATE_ERROR) {
1096 ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write error has occured");
1097 goto fail0;
1098 }
1099  
1100 // check if the write_pipe has been aborted
1101 if (write_pipe_inst->state == WRITER_STATE_ABORTED) {
1102 ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write() has been aborted");
1103 goto fail0;
1104 }
1105  
1106 // check if the write_pipe has been aborted
1107 if (write_pipe_inst->state == WRITER_STATE_CLOSED) {
1108 ModuleLog(i, BLOG_ERROR, "write() is disallowed after close() has been called");
1109 goto fail0;
1110 }
1111  
1112 ASSERT(write_pipe_inst->state == WRITER_STATE_RUNNING)
1113  
1114 // check if there's already a write in progress
1115 if (write_pipe_inst->write_inst) {
1116 ModuleLog(i, BLOG_ERROR, "write() is disallowed while another write() is in progress");
1117 goto fail0;
1118 }
1119  
1120 // initialize write progress state
1121 o->data = NCDVal_StringMemRef(data_arg);
1122  
1123 // if there's nothing to send, go up immediately
1124 if (o->data.len == 0) {
1125 o->write_pipe_inst = NULL;
1126 NCDModuleInst_Backend_Up(i);
1127 return;
1128 }
1129  
1130 // set write_pipe
1131 o->write_pipe_inst = write_pipe_inst;
1132  
1133 // register write in write_pipe
1134 write_pipe_inst->write_inst = o;
1135  
1136 // start send operation
1137 size_t to_send = (o->data.len > INT_MAX) ? INT_MAX : o->data.len;
1138 StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&write_pipe_inst->connection), (uint8_t *)o->data.ptr, to_send);
1139 return;
1140  
1141 fail0:
1142 NCDModuleInst_Backend_DeadError(i);
1143 }
1144  
1145 static void write_func_die (void *vo)
1146 {
1147 struct write_instance *o = vo;
1148  
1149 // if we're sending, abort write_pipe
1150 if (o->write_pipe_inst) {
1151 ASSERT(o->write_pipe_inst->state == WRITER_STATE_RUNNING)
1152 ASSERT(o->write_pipe_inst->write_inst == o)
1153 write_pipe_abort(o->write_pipe_inst);
1154 }
1155  
1156 NCDModuleInst_Backend_Dead(o->i);
1157 }
1158  
1159 static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
1160 {
1161 if (!NCDVal_ListRead(params->args, 0)) {
1162 ModuleLog(i, BLOG_ERROR, "wrong arity");
1163 goto fail0;
1164 }
1165  
1166 struct write_pipe_instance *write_pipe_inst = params->method_user;
1167  
1168 // check if a write error has already occured
1169 if (write_pipe_inst->state == WRITER_STATE_ERROR) {
1170 ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write error has occured");
1171 goto fail0;
1172 }
1173  
1174 // check if the write_pipe has been aborted
1175 if (write_pipe_inst->state == WRITER_STATE_ABORTED) {
1176 ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write() has been aborted");
1177 goto fail0;
1178 }
1179  
1180 // check if the write_pipe has been closed
1181 if (write_pipe_inst->state == WRITER_STATE_CLOSED) {
1182 ModuleLog(i, BLOG_ERROR, "close() is disallowed after close() has been called");
1183 goto fail0;
1184 }
1185  
1186 // close
1187 write_pipe_close(write_pipe_inst);
1188  
1189 // go up
1190 NCDModuleInst_Backend_Up(i);
1191 return;
1192  
1193 fail0:
1194 NCDModuleInst_Backend_DeadError(i);
1195 }
1196  
1197 static struct NCDModule modules[] = {
1198 {
1199 .type = "sys.start_process",
1200 .func_new2 = process_func_new,
1201 .func_die = process_func_die,
1202 .func_getvar2 = process_func_getvar,
1203 .alloc_size = sizeof(struct process_instance)
1204 }, {
1205 .type = "sys.start_process::wait",
1206 .func_new2 = wait_func_new,
1207 .func_die = wait_func_die,
1208 .func_getvar2 = wait_func_getvar,
1209 .alloc_size = sizeof(struct wait_instance)
1210 }, {
1211 .type = "sys.start_process::terminate",
1212 .func_new2 = terminate_func_new
1213 }, {
1214 .type = "sys.start_process::kill",
1215 .func_new2 = kill_func_new
1216 }, {
1217 .type = "sys.start_process::read_pipe",
1218 .func_new2 = read_pipe_func_new,
1219 .func_die = read_pipe_func_die,
1220 .func_getvar2 = read_pipe_func_getvar,
1221 .alloc_size = sizeof(struct read_pipe_instance)
1222 }, {
1223 .type = "sys.start_process::read_pipe::read",
1224 .func_new2 = read_func_new,
1225 .func_die = read_func_die,
1226 .func_getvar2 = read_func_getvar,
1227 .alloc_size = sizeof(struct read_instance)
1228 }, {
1229 .type = "sys.start_process::write_pipe",
1230 .func_new2 = write_pipe_func_new,
1231 .func_die = write_pipe_func_die,
1232 .func_getvar2 = write_pipe_func_getvar,
1233 .alloc_size = sizeof(struct write_pipe_instance)
1234 }, {
1235 .type = "sys.start_process::write_pipe::write",
1236 .func_new2 = write_func_new,
1237 .func_die = write_func_die,
1238 .alloc_size = sizeof(struct write_instance)
1239 }, {
1240 .type = "sys.start_process::write_pipe::close",
1241 .func_new2 = close_func_new
1242 }, {
1243 .type = NULL
1244 }
1245 };
1246  
1247 const struct NCDModuleGroup ncdmodule_sys_start_process = {
1248 .modules = modules
1249 };