123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /** @file ksession.c
- */
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <faux/argv.h>
- #include <faux/eloop.h>
- #include <faux/buf.h>
- #include <klish/khelper.h>
- #include <klish/kview.h>
- #include <klish/kscheme.h>
- #include <klish/kpath.h>
- #include <klish/kpargv.h>
- #include <klish/ksession.h>
- #include <klish/kexec.h>
- struct ksession_s {
- const kscheme_t *scheme;
- kpath_t *path;
- bool_t done; // Indicates that session is over and must be closed
- };
- // Scheme
- KGET(session, const kscheme_t *, scheme);
- // Path
- KGET(session, kpath_t *, path);
- // Done
- KGET_BOOL(session, done);
- KSET_BOOL(session, done);
- ksession_t *ksession_new(const kscheme_t *scheme, const char *start_entry)
- {
- ksession_t *session = NULL;
- const kentry_t *entry = NULL;
- const char *entry_to_search = NULL;
- klevel_t *level = NULL;
- assert(scheme);
- if (!scheme)
- return NULL;
- // Before real session allocation we will try to find starting entry.
- // Starting entry can be get from function argument, from STARTUP tag or
- // default name 'main' can be used. Don't create session if we can't get
- // starting entry at all. Priorities are (from higher) argument, STARTUP,
- // default name.
- if (start_entry)
- entry_to_search = start_entry;
- // STARTUP is not implemented yet
- else
- entry_to_search = KSESSION_STARTING_ENTRY;
- entry = kscheme_find_entry_by_path(scheme, entry_to_search);
- if (!entry)
- return NULL; // Can't find starting entry
- session = faux_zmalloc(sizeof(*session));
- assert(session);
- if (!session)
- return NULL;
- // Initialization
- session->scheme = scheme;
- // Create kpath_t stack
- session->path = kpath_new();
- assert(session->path);
- level = klevel_new(entry);
- assert(level);
- kpath_push(session->path, level);
- session->done = BOOL_FALSE;
- return session;
- }
- void ksession_free(ksession_t *session)
- {
- if (!session)
- return;
- kpath_free(session->path);
- free(session);
- }
- static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
- void *associated_data, void *user_data)
- {
- ksession_t *session = (ksession_t *)user_data;
- if (!session)
- return BOOL_FALSE;
- ksession_set_done(session, BOOL_TRUE); // Stop the whole session
- // Happy compiler
- eloop = eloop;
- type = type;
- associated_data = associated_data;
- return BOOL_FALSE; // Stop Event Loop
- }
- static bool_t action_terminated_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
- void *associated_data, void *user_data)
- {
- int wstatus = 0;
- pid_t child_pid = -1;
- kexec_t *exec = (kexec_t *)user_data;
- if (!exec)
- return BOOL_FALSE;
- // Wait for any child process. Doesn't block.
- while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0)
- kexec_continue_command_execution(exec, child_pid, wstatus);
- // Check if kexec is done now
- if (kexec_done(exec))
- return BOOL_FALSE; // To break a loop
- // Happy compiler
- eloop = eloop;
- type = type;
- associated_data = associated_data;
- return BOOL_TRUE;
- }
- static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
- void *associated_data, void *user_data)
- {
- faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
- kexec_t *exec = (kexec_t *)user_data;
- ssize_t r = -1;
- faux_buf_t *faux_buf = NULL;
- void *linear_buf = NULL;
- if (!exec)
- return BOOL_FALSE;
- faux_buf = kexec_bufout(exec);
- assert(faux_buf);
- do {
- ssize_t really_readed = 0;
- ssize_t linear_len =
- faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
- // Non-blocked read. The fd became non-blocked while
- // kexec_prepare().
- r = read(info->fd, linear_buf, linear_len);
- if (r > 0)
- really_readed = r;
- faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
- } while (r > 0);
- // Happy compiler
- eloop = eloop;
- type = type;
- return BOOL_TRUE;
- }
- bool_t ksession_exec_locally(ksession_t *session, const kentry_t *entry,
- kpargv_t *parent_pargv, int *retcode, const char **out)
- {
- kexec_t *exec = NULL;
- faux_eloop_t *eloop = NULL;
- faux_buf_t *buf = NULL;
- char *cstr = NULL;
- ssize_t len = 0;
- assert(entry);
- if (!entry)
- return BOOL_FALSE;
- // Parsing
- exec = ksession_parse_for_local_exec(session, entry, parent_pargv);
- if (!exec)
- return BOOL_FALSE;
- // Session status can be changed while parsing because it can execute
- // nested ksession_exec_locally() to check for PTYPEs, CONDitions etc.
- // So check for 'done' flag to propagate it.
- if (ksession_done(session)) {
- kexec_free(exec);
- return BOOL_FALSE; // Because action is not completed
- }
- // Execute kexec and then wait for completion using local Eloop
- if (!kexec_exec(exec)) {
- kexec_free(exec);
- return BOOL_FALSE; // Something went wrong
- }
- // If kexec contains only non-exec (for example dry-run) ACTIONs then
- // we don't need event loop and can return here.
- if (kexec_retcode(exec, retcode)) {
- kexec_free(exec);
- return BOOL_TRUE;
- }
- // Local service loop
- eloop = faux_eloop_new(NULL);
- faux_eloop_add_signal(eloop, SIGINT, stop_loop_ev, session);
- faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, session);
- faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, session);
- faux_eloop_add_signal(eloop, SIGCHLD, action_terminated_ev, exec);
- faux_eloop_add_fd(eloop, kexec_stdout(exec), POLLIN,
- action_stdout_ev, exec);
- faux_eloop_loop(eloop);
- faux_eloop_free(eloop);
- kexec_retcode(exec, retcode);
- if (!out) {
- kexec_free(exec);
- return BOOL_TRUE;
- }
- buf = kexec_bufout(exec);
- if ((len = faux_buf_len(buf)) <= 0) {
- kexec_free(exec);
- return BOOL_TRUE;
- }
- cstr = faux_malloc(len + 1);
- faux_buf_read(buf, cstr, len);
- cstr[len] = '\0';
- *out = cstr;
- kexec_free(exec);
- return BOOL_TRUE;
- }
|