ksession.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /** @file ksession.c
  2. */
  3. #include <assert.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <signal.h>
  8. #include <sys/types.h>
  9. #include <sys/wait.h>
  10. #include <unistd.h>
  11. #include <faux/argv.h>
  12. #include <faux/eloop.h>
  13. #include <faux/buf.h>
  14. #include <klish/khelper.h>
  15. #include <klish/kview.h>
  16. #include <klish/kscheme.h>
  17. #include <klish/kpath.h>
  18. #include <klish/kpargv.h>
  19. #include <klish/ksession.h>
  20. #include <klish/kexec.h>
  21. struct ksession_s {
  22. const kscheme_t *scheme;
  23. kpath_t *path;
  24. bool_t done; // Indicates that session is over and must be closed
  25. };
  26. // Scheme
  27. KGET(session, const kscheme_t *, scheme);
  28. // Path
  29. KGET(session, kpath_t *, path);
  30. // Done
  31. KGET_BOOL(session, done);
  32. KSET_BOOL(session, done);
  33. ksession_t *ksession_new(const kscheme_t *scheme, const char *start_entry)
  34. {
  35. ksession_t *session = NULL;
  36. kentry_t *entry = NULL;
  37. const char *entry_to_search = NULL;
  38. klevel_t *level = NULL;
  39. assert(scheme);
  40. if (!scheme)
  41. return NULL;
  42. // Before real session allocation we will try to find starting entry.
  43. // Starting entry can be get from function argument, from STARTUP tag or
  44. // default name 'main' can be used. Don't create session if we can't get
  45. // starting entry at all. Priorities are (from higher) argument, STARTUP,
  46. // default name.
  47. if (start_entry)
  48. entry_to_search = start_entry;
  49. // STARTUP is not implemented yet
  50. else
  51. entry_to_search = KSESSION_STARTING_ENTRY;
  52. entry = kscheme_find_entry_by_path(scheme, entry_to_search);
  53. if (!entry)
  54. return NULL; // Can't find starting entry
  55. session = faux_zmalloc(sizeof(*session));
  56. assert(session);
  57. if (!session)
  58. return NULL;
  59. // Initialization
  60. session->scheme = scheme;
  61. // Create kpath_t stack
  62. session->path = kpath_new();
  63. assert(session->path);
  64. level = klevel_new(entry);
  65. assert(level);
  66. kpath_push(session->path, level);
  67. session->done = BOOL_FALSE;
  68. return session;
  69. }
  70. void ksession_free(ksession_t *session)
  71. {
  72. if (!session)
  73. return;
  74. kpath_free(session->path);
  75. free(session);
  76. }
  77. static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  78. void *associated_data, void *user_data)
  79. {
  80. ksession_t *session = (ksession_t *)user_data;
  81. if (!session)
  82. return BOOL_FALSE;
  83. ksession_set_done(session, BOOL_TRUE); // Stop the whole session
  84. // Happy compiler
  85. eloop = eloop;
  86. type = type;
  87. associated_data = associated_data;
  88. return BOOL_FALSE; // Stop Event Loop
  89. }
  90. static bool_t action_terminated_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  91. void *associated_data, void *user_data)
  92. {
  93. int wstatus = 0;
  94. pid_t child_pid = -1;
  95. kexec_t *exec = (kexec_t *)user_data;
  96. if (!exec)
  97. return BOOL_FALSE;
  98. // Wait for any child process. Doesn't block.
  99. while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0)
  100. kexec_continue_command_execution(exec, child_pid, wstatus);
  101. // Check if kexec is done now
  102. if (kexec_done(exec))
  103. return BOOL_FALSE; // To break a loop
  104. // Happy compiler
  105. eloop = eloop;
  106. type = type;
  107. associated_data = associated_data;
  108. return BOOL_TRUE;
  109. }
  110. static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  111. void *associated_data, void *user_data)
  112. {
  113. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  114. kexec_t *exec = (kexec_t *)user_data;
  115. ssize_t r = -1;
  116. faux_buf_t *faux_buf = NULL;
  117. void *linear_buf = NULL;
  118. if (!exec)
  119. return BOOL_FALSE;
  120. faux_buf = kexec_bufout(exec);
  121. assert(faux_buf);
  122. do {
  123. ssize_t really_readed = 0;
  124. ssize_t linear_len =
  125. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  126. // Non-blocked read. The fd became non-blocked while
  127. // kexec_prepare().
  128. r = read(info->fd, linear_buf, linear_len);
  129. if (r > 0)
  130. really_readed = r;
  131. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  132. } while (r > 0);
  133. // Happy compiler
  134. eloop = eloop;
  135. type = type;
  136. return BOOL_TRUE;
  137. }
  138. bool_t ksession_exec_locally(ksession_t *session, kentry_t *entry,
  139. int *retcode, const char **out)
  140. {
  141. kexec_t *exec = NULL;
  142. faux_eloop_t *eloop = NULL;
  143. faux_buf_t *buf = NULL;
  144. char *cstr = NULL;
  145. ssize_t len = 0;
  146. assert(entry);
  147. if (!entry)
  148. return BOOL_FALSE;
  149. // Parsing
  150. exec = ksession_parse_for_local_exec(entry);
  151. if (!exec)
  152. return BOOL_FALSE;
  153. // Session status can be changed while parsing because it can execute
  154. // nested ksession_exec_locally() to check for PTYPEs, CONDitions etc.
  155. // So check for 'done' flag to propagate it.
  156. if (ksession_done(session)) {
  157. kexec_free(exec);
  158. return BOOL_FALSE; // Because action is not completed
  159. }
  160. // Execute kexec and then wait for completion using local Eloop
  161. if (!kexec_exec(exec)) {
  162. kexec_free(exec);
  163. return BOOL_FALSE; // Something went wrong
  164. }
  165. // If kexec contains only non-exec (for example dry-run) ACTIONs then
  166. // we don't need event loop and can return here.
  167. if (kexec_retcode(exec, retcode)) {
  168. kexec_free(exec);
  169. return BOOL_TRUE;
  170. }
  171. // Local service loop
  172. eloop = faux_eloop_new(NULL);
  173. faux_eloop_add_signal(eloop, SIGINT, stop_loop_ev, session);
  174. faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, session);
  175. faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, session);
  176. faux_eloop_add_signal(eloop, SIGCHLD, action_terminated_ev, exec);
  177. faux_eloop_add_fd(eloop, kexec_stdout(exec), POLLIN,
  178. action_stdout_ev, exec);
  179. faux_eloop_loop(eloop);
  180. faux_eloop_free(eloop);
  181. kexec_retcode(exec, retcode);
  182. if (!out) {
  183. kexec_free(exec);
  184. return BOOL_TRUE;
  185. }
  186. buf = kexec_bufout(exec);
  187. if ((len = faux_buf_len(buf)) <= 0) {
  188. kexec_free(exec);
  189. return BOOL_TRUE;
  190. }
  191. cstr = faux_malloc(len + 1);
  192. faux_buf_read(buf, cstr, len);
  193. cstr[len] = '\0';
  194. *out = cstr;
  195. kexec_free(exec);
  196. return BOOL_TRUE;
  197. }