ksession.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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 <faux/argv.h>
  11. #include <faux/eloop.h>
  12. #include <klish/khelper.h>
  13. #include <klish/kview.h>
  14. #include <klish/kscheme.h>
  15. #include <klish/kpath.h>
  16. #include <klish/kpargv.h>
  17. #include <klish/ksession.h>
  18. #include <klish/kexec.h>
  19. struct ksession_s {
  20. const kscheme_t *scheme;
  21. kpath_t *path;
  22. bool_t done; // Indicates that session is over and must be closed
  23. };
  24. // Scheme
  25. KGET(session, const kscheme_t *, scheme);
  26. // Path
  27. KGET(session, kpath_t *, path);
  28. // Done
  29. KGET_BOOL(session, done);
  30. KSET_BOOL(session, done);
  31. ksession_t *ksession_new(const kscheme_t *scheme, const char *start_entry)
  32. {
  33. ksession_t *session = NULL;
  34. kentry_t *entry = NULL;
  35. const char *entry_to_search = NULL;
  36. klevel_t *level = NULL;
  37. assert(scheme);
  38. if (!scheme)
  39. return NULL;
  40. // Before real session allocation we will try to find starting entry.
  41. // Starting entry can be get from function argument, from STARTUP tag or
  42. // default name 'main' can be used. Don't create session if we can't get
  43. // starting entry at all. Priorities are (from higher) argument, STARTUP,
  44. // default name.
  45. if (start_entry)
  46. entry_to_search = start_entry;
  47. // STARTUP is not implemented yet
  48. else
  49. entry_to_search = KSESSION_STARTING_ENTRY;
  50. entry = kscheme_find_entry_by_path(scheme, entry_to_search);
  51. if (!entry)
  52. return NULL; // Can't find starting entry
  53. session = faux_zmalloc(sizeof(*session));
  54. assert(session);
  55. if (!session)
  56. return NULL;
  57. // Initialization
  58. session->scheme = scheme;
  59. // Create kpath_t stack
  60. session->path = kpath_new();
  61. assert(session->path);
  62. level = klevel_new(entry);
  63. assert(level);
  64. kpath_push(session->path, level);
  65. session->done = BOOL_FALSE;
  66. return session;
  67. }
  68. void ksession_free(ksession_t *session)
  69. {
  70. if (!session)
  71. return;
  72. kpath_free(session->path);
  73. free(session);
  74. }
  75. static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  76. void *associated_data, void *user_data)
  77. {
  78. ksession_t *session = (ksession_t *)user_data;
  79. if (!session)
  80. return BOOL_FALSE;
  81. ksession_set_done(session, BOOL_TRUE); // Stop the whole session
  82. // Happy compiler
  83. eloop = eloop;
  84. type = type;
  85. associated_data = associated_data;
  86. return BOOL_FALSE; // Stop Event Loop
  87. }
  88. static bool_t action_terminated_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  89. void *associated_data, void *user_data)
  90. {
  91. int wstatus = 0;
  92. pid_t child_pid = -1;
  93. kexec_t *exec = (kexec_t *)user_data;
  94. if (!exec)
  95. return BOOL_FALSE;
  96. // Wait for any child process. Doesn't block.
  97. while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0)
  98. kexec_continue_command_execution(exec, child_pid, wstatus);
  99. // Check if kexec is done now
  100. if (kexec_done(exec))
  101. return BOOL_FALSE; // To break a loop
  102. // Happy compiler
  103. eloop = eloop;
  104. type = type;
  105. associated_data = associated_data;
  106. return BOOL_TRUE;
  107. }
  108. bool_t ksession_exec_locally(ksession_t *session, const char *line,
  109. int *retcode, faux_error_t *error)
  110. {
  111. kexec_t *exec = NULL;
  112. faux_eloop_t *eloop = NULL;
  113. assert(session);
  114. if (!session)
  115. return BOOL_FALSE;
  116. // Parsing
  117. exec = ksession_parse_for_exec(session, line, error);
  118. if (!exec)
  119. return BOOL_FALSE;
  120. // Session status can be changed while parsing because it can execute
  121. // nested ksession_exec_locally() to check for PTYPEs, CONDitions etc.
  122. // So check for 'done' flag to propagate it.
  123. if (ksession_done(session))
  124. return BOOL_FALSE; // Because action is not completed
  125. // Execute kexec and then wait for completion using local Eloop
  126. if (!kexec_exec(exec))
  127. return BOOL_FALSE; // Something went wrong
  128. // If kexec contains only sync ACTIONs then we don't need event loop
  129. // and can return here.
  130. if (kexec_retcode(exec, retcode))
  131. return BOOL_TRUE;
  132. // Local service loop
  133. eloop = faux_eloop_new(NULL);
  134. faux_eloop_add_signal(eloop, SIGINT, stop_loop_ev, session);
  135. faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, session);
  136. faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, session);
  137. faux_eloop_add_signal(eloop, SIGCHLD, action_terminated_ev, exec);
  138. faux_eloop_loop(eloop);
  139. faux_eloop_free(eloop);
  140. kexec_retcode(exec, retcode);
  141. return BOOL_TRUE;
  142. }