ksession_parse.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. /** @file ksession_parse.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 <syslog.h>
  12. #include <faux/eloop.h>
  13. #include <faux/buf.h>
  14. #include <faux/list.h>
  15. #include <faux/argv.h>
  16. #include <faux/error.h>
  17. #include <klish/khelper.h>
  18. #include <klish/kscheme.h>
  19. #include <klish/kpath.h>
  20. #include <klish/kpargv.h>
  21. #include <klish/kexec.h>
  22. #include <klish/ksession.h>
  23. #include <klish/ksession_parse.h>
  24. static bool_t ksession_validate_arg(ksession_t *session, kpargv_t *pargv)
  25. {
  26. char *out = NULL;
  27. int retcode = -1;
  28. const kentry_t *ptype_entry = NULL;
  29. kparg_t *candidate = NULL;
  30. assert(session);
  31. if (!session)
  32. return BOOL_FALSE;
  33. assert(pargv);
  34. if (!pargv)
  35. return BOOL_FALSE;
  36. candidate = kpargv_candidate_parg(pargv);
  37. if (!candidate)
  38. return BOOL_FALSE;
  39. ptype_entry = kentry_nested_by_purpose(kparg_entry(candidate),
  40. KENTRY_PURPOSE_PTYPE);
  41. if (!ptype_entry)
  42. return BOOL_FALSE;
  43. if (!ksession_exec_locally(session, ptype_entry, pargv,
  44. &retcode, &out)) {
  45. return BOOL_FALSE;
  46. }
  47. if (retcode != 0)
  48. return BOOL_FALSE;
  49. if (!faux_str_is_empty(out))
  50. kparg_set_value(candidate, out);
  51. return BOOL_TRUE;
  52. }
  53. static kpargv_status_e ksession_parse_arg(ksession_t *session,
  54. const kentry_t *current_entry, faux_argv_node_t **argv_iter,
  55. kpargv_t *pargv, bool_t entry_is_command, bool_t is_filter)
  56. {
  57. const kentry_t *entry = current_entry;
  58. kentry_mode_e mode = KENTRY_MODE_NONE;
  59. kpargv_status_e retcode = KPARSE_INPROGRESS; // For ENTRY itself
  60. kpargv_status_e rc = KPARSE_NOTFOUND; // For nested ENTRYs
  61. faux_argv_node_t *saved_argv_iter = NULL;
  62. kpargv_purpose_e purpose = KPURPOSE_NONE;
  63. //fprintf(stderr, "PARSE: name=%s, ref=%s, arg=%s\n",
  64. //kentry_name(entry), kentry_ref_str(entry), faux_argv_current(*argv_iter));
  65. assert(current_entry);
  66. if (!current_entry)
  67. return KPARSE_ERROR;
  68. assert(argv_iter);
  69. if (!argv_iter)
  70. return KPARSE_ERROR;
  71. assert(pargv);
  72. if (!pargv)
  73. return KPARSE_ERROR;
  74. purpose = kpargv_purpose(pargv); // Purpose of parsing
  75. // If we know the entry is a command then don't validate it. This
  76. // behaviour is usefull for special purpose entries like PTYPEs, CONDs,
  77. // etc. These entries are the starting point for parsing their args.
  78. // We don't need to parse command itself. Command is predefined.
  79. if (entry_is_command) {
  80. kparg_t *parg = NULL;
  81. // Command is an ENTRY with ACTIONs
  82. if (kentry_actions_len(entry) <= 0)
  83. return KPARSE_ILLEGAL;
  84. parg = kparg_new(entry, NULL);
  85. kpargv_add_pargs(pargv, parg);
  86. kpargv_set_command(pargv, entry);
  87. retcode = KPARSE_INPROGRESS;
  88. // Is entry candidate to resolve current arg?
  89. // Container can't be a candidate.
  90. } else if (!kentry_container(entry)) {
  91. const char *current_arg = NULL;
  92. kparg_t *parg = NULL;
  93. // When purpose is COMPLETION or HELP then fill completion list.
  94. // Additionally if it's last continuable argument then lie to
  95. // engine: make all last arguments NOTFOUND. It's necessary to walk
  96. // through all variants to gether all completions.
  97. if (((KPURPOSE_COMPLETION == purpose) ||
  98. (KPURPOSE_HELP == purpose)) &&
  99. (is_filter == kentry_filter(entry))) {
  100. if (!*argv_iter) {
  101. // That's time to add entry to completions list.
  102. if (!kpargv_continuable(pargv))
  103. kpargv_add_completions(pargv, entry);
  104. return KPARSE_INCOMPLETED;
  105. } else {
  106. // Add entry to completions if it's last incompleted arg.
  107. if (faux_argv_is_last(*argv_iter) &&
  108. kpargv_continuable(pargv)) {
  109. kpargv_add_completions(pargv, entry);
  110. return KPARSE_NOTFOUND;
  111. }
  112. }
  113. }
  114. // If all arguments are resolved already then return INCOMPLETED
  115. if (!*argv_iter)
  116. return KPARSE_INCOMPLETED;
  117. // Validate argument
  118. current_arg = faux_argv_current(*argv_iter);
  119. parg = kparg_new(entry, current_arg);
  120. kpargv_set_candidate_parg(pargv, parg);
  121. if (ksession_validate_arg(session, pargv)) {
  122. kpargv_accept_candidate_parg(pargv);
  123. // Command is an ENTRY with ACTIONs or NAVigation
  124. if (kentry_actions_len(entry) > 0)
  125. kpargv_set_command(pargv, entry);
  126. faux_argv_each(argv_iter); // Next argument
  127. retcode = KPARSE_INPROGRESS;
  128. } else {
  129. // It's not a container and is not validated so
  130. // no chance to find anything here.
  131. kpargv_decline_candidate_parg(pargv);
  132. kparg_free(parg);
  133. return KPARSE_NOTFOUND;
  134. }
  135. }
  136. // ENTRY has no nested ENTRYs so return
  137. if (kentry_entrys_is_empty(entry))
  138. return retcode;
  139. // Walk through the nested entries:
  140. saved_argv_iter = *argv_iter;
  141. // EMPTY mode
  142. mode = kentry_mode(entry);
  143. if (KENTRY_MODE_EMPTY == mode)
  144. return retcode;
  145. // Following code (SWITCH or SEQUENCE cases) sometimes don's set rc.
  146. // It happens when entry has nested entries but purposes of all entries
  147. // are not COMMON so they will be ignored. So return code of function
  148. // will be the code of ENTRY itself processing.
  149. rc = retcode;
  150. // SWITCH mode
  151. // Entries within SWITCH can't has 'min'/'max' else than 1.
  152. // So these attributes will be ignored. Note SWITCH itself can have
  153. // 'min'/'max'.
  154. if (KENTRY_MODE_SWITCH == mode) {
  155. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  156. const kentry_t *nested = NULL;
  157. while ((nested = kentry_entrys_each(&iter))) {
  158. //printf("SWITCH arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
  159. // Ignore entries with non-COMMON purpose.
  160. if (kentry_purpose(nested) != KENTRY_PURPOSE_COMMON)
  161. continue;
  162. rc = ksession_parse_arg(session, nested, argv_iter,
  163. pargv, BOOL_FALSE, is_filter);
  164. //printf("%s\n", kpargv_status_decode(rc));
  165. // If some arguments was consumed then we will not check
  166. // next SWITCH's entries in any case.
  167. if (saved_argv_iter != *argv_iter)
  168. break;
  169. // Try next entries if current status is NOTFOUND.
  170. // The INCOMPLETED status is for completion list. In this
  171. // case all next statuses will be INCOMPLETED too.
  172. if ((rc != KPARSE_NOTFOUND) && (rc != KPARSE_INCOMPLETED))
  173. break;
  174. }
  175. // SEQUENCE mode
  176. } else if (KENTRY_MODE_SEQUENCE == mode) {
  177. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  178. kentry_entrys_node_t *saved_iter = iter;
  179. const kentry_t *nested = NULL;
  180. while ((nested = kentry_entrys_each(&iter))) {
  181. kpargv_status_e nrc = KPARSE_NOTFOUND;
  182. size_t num = 0;
  183. size_t min = kentry_min(nested);
  184. //fprintf(stderr, "SEQ arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
  185. // Ignore entries with non-COMMON purpose.
  186. if (kentry_purpose(nested) != KENTRY_PURPOSE_COMMON)
  187. continue;
  188. // Filter out double parsing for optional entries.
  189. if (kpargv_entry_exists(pargv, nested))
  190. continue;
  191. // Try to match argument and current entry
  192. // (from 'min' to 'max' times)
  193. for (num = 0; num < kentry_max(nested); num++) {
  194. nrc = ksession_parse_arg(session, nested,
  195. argv_iter, pargv, BOOL_FALSE, is_filter);
  196. //fprintf(stderr, "%s: %s\n", kentry_name(nested), kpargv_status_decode(nrc));
  197. if (nrc != KPARSE_INPROGRESS)
  198. break;
  199. }
  200. // All errors will break the loop
  201. if ((KPARSE_ERROR == nrc) ||
  202. (KPARSE_ILLEGAL == nrc) ||
  203. (KPARSE_NONE == nrc)) {
  204. rc = nrc;
  205. break;
  206. }
  207. // Not found necessary number of mandatory instances
  208. if (num < min) {
  209. if (KPARSE_INPROGRESS == nrc)
  210. rc = KPARSE_NOTFOUND;
  211. else
  212. rc = nrc; // NOTFOUND or INCOMPLETED
  213. break;
  214. }
  215. // It's not an error if optional parameter is absend
  216. rc = KPARSE_INPROGRESS;
  217. // Mandatory or ordered parameter
  218. if ((min > 0) || kentry_order(nested))
  219. saved_iter = iter;
  220. // If optional entry is found then go back to nearest
  221. // non-optional (or ordered) entry to try to find
  222. // another optional entries.
  223. if ((0 == min) && (num > 0))
  224. iter = saved_iter;
  225. }
  226. }
  227. // If nested result is NOTFOUND but argument was consumed
  228. // within nested entries or by entry itself then whole sequence
  229. // is ILLEGAL.
  230. if ((KPARSE_NOTFOUND == rc) &&
  231. ((saved_argv_iter != *argv_iter) || !kentry_container(entry)))
  232. rc = KPARSE_ILLEGAL;
  233. return rc;
  234. }
  235. kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
  236. kpargv_purpose_e purpose, bool_t is_filter)
  237. {
  238. faux_argv_node_t *argv_iter = NULL;
  239. kpargv_t *pargv = NULL;
  240. kpargv_status_e pstatus = KPARSE_NONE;
  241. kpath_levels_node_t *levels_iterr = NULL;
  242. klevel_t *level = NULL;
  243. size_t level_found = 0; // Level where command was found
  244. kpath_t *path = NULL;
  245. assert(session);
  246. if (!session)
  247. return NULL;
  248. assert(argv);
  249. if (!argv)
  250. return NULL;
  251. argv_iter = faux_argv_iter(argv);
  252. // Initialize kpargv_t
  253. pargv = kpargv_new();
  254. assert(pargv);
  255. kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
  256. kpargv_set_purpose(pargv, purpose);
  257. // Iterate levels of path from higher to lower. Note the reversed
  258. // iterator will be used.
  259. path = ksession_path(session);
  260. levels_iterr = kpath_iterr(path);
  261. level_found = kpath_len(path) - 1; // Levels begin with '0'
  262. while ((level = kpath_eachr(&levels_iterr))) {
  263. const kentry_t *current_entry = klevel_entry(level);
  264. // Ignore entries with non-COMMON purpose. These entries are for
  265. // special processing and will be ignored here.
  266. if (kentry_purpose(current_entry) != KENTRY_PURPOSE_COMMON)
  267. continue;
  268. // Parsing
  269. pstatus = ksession_parse_arg(session, current_entry, &argv_iter,
  270. pargv, BOOL_FALSE, is_filter);
  271. if (pstatus != KPARSE_NOTFOUND)
  272. break;
  273. // NOTFOUND but some args were parsed.
  274. // When it's completion for first argument (that can be continued)
  275. // len == 0 and engine will search for completions on higher
  276. // levels of path.
  277. if (kpargv_pargs_len(pargv) > 0)
  278. break;
  279. level_found--;
  280. }
  281. // Save last argument
  282. if (argv_iter)
  283. kpargv_set_last_arg(pargv, faux_argv_current(argv_iter));
  284. // It's a higher level of parsing, so some statuses can have different
  285. // meanings
  286. if (KPARSE_NONE == pstatus)
  287. pstatus = KPARSE_ERROR; // Strange case
  288. else if (KPARSE_INPROGRESS == pstatus) {
  289. if (NULL == argv_iter) // All args are parsed
  290. pstatus = KPARSE_OK;
  291. else
  292. pstatus = KPARSE_ILLEGAL; // Additional not parsable args
  293. } else if (KPARSE_NOTFOUND == pstatus)
  294. pstatus = KPARSE_ILLEGAL; // Unknown command
  295. // If no ACTIONs were found i.e. command was not found
  296. if ((KPARSE_OK == pstatus) && !kpargv_command(pargv))
  297. pstatus = KPARSE_NOACTION;
  298. kpargv_set_status(pargv, pstatus);
  299. kpargv_set_level(pargv, level_found);
  300. return pargv;
  301. }
  302. // Delimeter of commands is '|' (pipe)
  303. faux_list_t *ksession_split_pipes(const char *raw_line, faux_error_t *error)
  304. {
  305. faux_list_t *list = NULL;
  306. faux_argv_t *argv = NULL;
  307. faux_argv_node_t *argv_iter = NULL;
  308. faux_argv_t *cur_argv = NULL; // Current argv
  309. const char *delimeter = "|";
  310. const char *arg = NULL;
  311. assert(raw_line);
  312. if (!raw_line)
  313. return NULL;
  314. // Split raw line to arguments
  315. argv = faux_argv_new();
  316. assert(argv);
  317. if (!argv)
  318. return NULL;
  319. if (faux_argv_parse(argv, raw_line) < 0) {
  320. faux_argv_free(argv);
  321. return NULL;
  322. }
  323. list = faux_list_new(FAUX_LIST_UNSORTED, FAUX_LIST_NONUNIQUE,
  324. NULL, NULL, (void (*)(void *))faux_argv_free);
  325. assert(list);
  326. if (!list) {
  327. faux_argv_free(argv);
  328. return NULL;
  329. }
  330. argv_iter = faux_argv_iter(argv);
  331. cur_argv = faux_argv_new();
  332. assert(cur_argv);
  333. while ((arg = faux_argv_each(&argv_iter))) {
  334. if (strcmp(arg, delimeter) == 0) {
  335. // End of current line (from "|" to "|")
  336. // '|' in a first position is an error
  337. if (faux_argv_len(cur_argv) == 0) {
  338. faux_argv_free(argv);
  339. faux_list_free(list);
  340. faux_error_sprintf(error, "The pipe '|' can't "
  341. "be at the first position");
  342. return NULL;
  343. }
  344. // Add argv to argv's list
  345. faux_list_add(list, cur_argv);
  346. cur_argv = faux_argv_new();
  347. assert(cur_argv);
  348. } else {
  349. faux_argv_add(cur_argv, arg);
  350. }
  351. }
  352. // Continuable flag is usefull for last argv
  353. faux_argv_set_continuable(cur_argv, faux_argv_is_continuable(argv));
  354. // Empty cur_argv is not an error. It's usefull for completion and help.
  355. // But empty cur_argv and continuable is abnormal.
  356. if ((faux_argv_len(cur_argv) == 0) &&
  357. faux_argv_is_continuable(cur_argv)) {
  358. faux_argv_free(argv);
  359. faux_list_free(list);
  360. faux_error_sprintf(error, "The pipe '|' can't "
  361. "be the last argument");
  362. return NULL;
  363. }
  364. faux_list_add(list, cur_argv);
  365. faux_argv_free(argv);
  366. return list;
  367. }
  368. // is_piped means full command contains more than one piped components
  369. static bool_t ksession_check_line(const kpargv_t *pargv, faux_error_t *error,
  370. bool_t is_first, bool_t is_piped)
  371. {
  372. kpargv_purpose_e purpose = KPURPOSE_EXEC;
  373. const kentry_t *cmd = NULL;
  374. if (!pargv)
  375. return BOOL_FALSE;
  376. purpose = kpargv_purpose(pargv);
  377. cmd = kpargv_command(pargv);
  378. // For execution pargv must be fully correct but for completion
  379. // it's not a case
  380. if ((KPURPOSE_EXEC == purpose) && (kpargv_status(pargv) != KPARSE_OK)) {
  381. faux_error_sprintf(error, "%s", kpargv_status_str(pargv));
  382. return BOOL_FALSE;
  383. }
  384. // Can't check following conditions without cmd
  385. if (!cmd)
  386. return BOOL_TRUE;
  387. // First component
  388. if (is_first) {
  389. // First component can't be filter
  390. if (kentry_filter(cmd)) {
  391. faux_error_sprintf(error, "The filter \"%s\" "
  392. "can't be used without previous pipeline",
  393. kentry_name(cmd));
  394. return BOOL_FALSE;
  395. }
  396. // Interactive command can't have filters
  397. if (kentry_interactive(cmd) && is_piped) {
  398. faux_error_sprintf(error, "The interactive command \"%s\" "
  399. "can't have filters",
  400. kentry_name(cmd));
  401. return BOOL_FALSE;
  402. }
  403. // Components after pipe "|"
  404. } else {
  405. // Only the first component can be non-filter
  406. if (!kentry_filter(cmd)) {
  407. faux_error_sprintf(error, "The non-filter command \"%s\" "
  408. "can't be destination of pipe",
  409. kentry_name(cmd));
  410. return BOOL_FALSE;
  411. }
  412. // Only the first component can have 'restore=true' attribute
  413. if (kentry_restore(cmd)) {
  414. faux_error_sprintf(error, "The command \"%s\" "
  415. "can't be destination of pipe",
  416. kentry_name(cmd));
  417. return BOOL_FALSE;
  418. }
  419. // Only the first component can have 'interactive=true' attribute
  420. if (kentry_interactive(cmd)) {
  421. faux_error_sprintf(error, "The filter \"%s\" "
  422. "can't be interactive",
  423. kentry_name(cmd));
  424. return BOOL_FALSE;
  425. }
  426. }
  427. return BOOL_TRUE;
  428. }
  429. // All components except last one must be legal for execution but last
  430. // component must be parsed for completion.
  431. // Completion is a "back-end" operation so it doesn't need detailed error
  432. // reporting.
  433. kpargv_t *ksession_parse_for_completion(ksession_t *session,
  434. const char *raw_line)
  435. {
  436. faux_list_t *split = NULL;
  437. faux_list_node_t *iter = NULL;
  438. kpargv_t *pargv = NULL;
  439. bool_t is_piped = BOOL_FALSE;
  440. assert(session);
  441. if (!session)
  442. return NULL;
  443. assert(raw_line);
  444. if (!raw_line)
  445. return NULL;
  446. // Split raw line (with '|') to components
  447. split = ksession_split_pipes(raw_line, NULL);
  448. if (!split || (faux_list_len(split) < 1)) {
  449. faux_list_free(split);
  450. return NULL;
  451. }
  452. is_piped = (faux_list_len(split) > 1);
  453. iter = faux_list_head(split);
  454. while (iter) {
  455. faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
  456. bool_t is_last = (iter == faux_list_tail(split));
  457. bool_t is_first = (iter == faux_list_head(split));
  458. kpargv_purpose_e purpose = is_last ? KPURPOSE_COMPLETION : KPURPOSE_EXEC;
  459. pargv = ksession_parse_line(session, argv, purpose, !is_first);
  460. if (!ksession_check_line(pargv, NULL, is_first, is_piped)) {
  461. kpargv_free(pargv);
  462. pargv = NULL;
  463. break;
  464. }
  465. if (!is_last)
  466. kpargv_free(pargv);
  467. iter = faux_list_next_node(iter);
  468. }
  469. faux_list_free(split);
  470. return pargv;
  471. }
  472. kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
  473. faux_error_t *error)
  474. {
  475. faux_list_t *split = NULL;
  476. faux_list_node_t *iter = NULL;
  477. kpargv_t *pargv = NULL;
  478. kexec_t *exec = NULL;
  479. bool_t is_piped = BOOL_FALSE;
  480. assert(session);
  481. if (!session)
  482. return NULL;
  483. assert(raw_line);
  484. if (!raw_line)
  485. return NULL;
  486. // Split raw line (with '|') to components
  487. split = ksession_split_pipes(raw_line, error);
  488. if (!split || (faux_list_len(split) < 1)) {
  489. faux_list_free(split);
  490. return NULL;
  491. }
  492. is_piped = (faux_list_len(split) > 1);
  493. // Create exec list
  494. exec = kexec_new();
  495. assert(exec);
  496. if (!exec) {
  497. faux_list_free(split);
  498. return NULL;
  499. }
  500. iter = faux_list_head(split);
  501. while (iter) {
  502. faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
  503. kcontext_t *context = NULL;
  504. bool_t is_first = (iter == faux_list_head(split));
  505. pargv = ksession_parse_line(session, argv, KPURPOSE_EXEC, !is_first);
  506. // All components must be ready for execution
  507. if (!ksession_check_line(pargv, error, is_first, is_piped)) {
  508. kpargv_free(pargv);
  509. kexec_free(exec);
  510. faux_list_free(split);
  511. return NULL;
  512. }
  513. // Fill the kexec_t
  514. context = kcontext_new(KCONTEXT_TYPE_ACTION);
  515. assert(context);
  516. kcontext_set_scheme(context, ksession_scheme(session));
  517. kcontext_set_pargv(context, pargv);
  518. // Context for ACTION execution contains session
  519. kcontext_set_session(context, session);
  520. kexec_add_contexts(exec, context);
  521. // Next component
  522. iter = faux_list_next_node(iter);
  523. }
  524. faux_list_free(split);
  525. return exec;
  526. }
  527. kexec_t *ksession_parse_for_local_exec(ksession_t *session,
  528. const kentry_t *entry, const kpargv_t *parent_pargv)
  529. {
  530. faux_argv_node_t *argv_iter = NULL;
  531. kpargv_t *pargv = NULL;
  532. kexec_t *exec = NULL;
  533. faux_argv_t *argv = NULL;
  534. kcontext_t *context = NULL;
  535. kpargv_status_e pstatus = KPARSE_NONE;
  536. const char *line = NULL; // TODO: Must be 'line' field of ENTRY
  537. assert(entry);
  538. if (!entry)
  539. return NULL;
  540. exec = kexec_new();
  541. assert(exec);
  542. argv = faux_argv_new();
  543. assert(argv);
  544. faux_argv_parse(argv, line);
  545. argv_iter = faux_argv_iter(argv);
  546. pargv = kpargv_new();
  547. assert(pargv);
  548. kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
  549. kpargv_set_purpose(pargv, KPURPOSE_EXEC);
  550. pstatus = ksession_parse_arg(session, entry, &argv_iter, pargv,
  551. BOOL_TRUE, BOOL_FALSE);
  552. // Parsing problems
  553. if ((pstatus != KPARSE_INPROGRESS) || (argv_iter != NULL)) {
  554. kexec_free(exec);
  555. faux_argv_free(argv);
  556. kpargv_free(pargv);
  557. return NULL;
  558. }
  559. context = kcontext_new(KCONTEXT_TYPE_SERVICE_ACTION);
  560. assert(context);
  561. kcontext_set_scheme(context, ksession_scheme(session));
  562. kcontext_set_pargv(context, pargv);
  563. kcontext_set_parent_pargv(context, parent_pargv);
  564. kcontext_set_session(context, session);
  565. kexec_add_contexts(exec, context);
  566. faux_argv_free(argv);
  567. return exec;
  568. }
  569. static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  570. void *associated_data, void *user_data)
  571. {
  572. ksession_t *session = (ksession_t *)user_data;
  573. if (!session)
  574. return BOOL_FALSE;
  575. ksession_set_done(session, BOOL_TRUE); // Stop the whole session
  576. // Happy compiler
  577. eloop = eloop;
  578. type = type;
  579. associated_data = associated_data;
  580. return BOOL_FALSE; // Stop Event Loop
  581. }
  582. static bool_t get_stdout(kexec_t *exec)
  583. {
  584. ssize_t r = -1;
  585. faux_buf_t *faux_buf = NULL;
  586. void *linear_buf = NULL;
  587. int fd = -1;
  588. if (!exec)
  589. return BOOL_FALSE;
  590. fd = kexec_stdout(exec);
  591. assert(fd != -1);
  592. faux_buf = kexec_bufout(exec);
  593. assert(faux_buf);
  594. do {
  595. ssize_t really_readed = 0;
  596. ssize_t linear_len =
  597. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  598. // Non-blocked read. The fd became non-blocked while
  599. // kexec_prepare().
  600. r = read(fd, linear_buf, linear_len);
  601. if (r > 0)
  602. really_readed = r;
  603. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  604. } while (r > 0);
  605. return BOOL_TRUE;
  606. }
  607. static bool_t action_terminated_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  608. void *associated_data, void *user_data)
  609. {
  610. int wstatus = 0;
  611. pid_t child_pid = -1;
  612. kexec_t *exec = (kexec_t *)user_data;
  613. if (!exec)
  614. return BOOL_FALSE;
  615. // Wait for any child process. Doesn't block.
  616. while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0)
  617. kexec_continue_command_execution(exec, child_pid, wstatus);
  618. // Check if kexec is done now
  619. if (kexec_done(exec)) {
  620. // May be buffer still contains data
  621. get_stdout(exec);
  622. return BOOL_FALSE; // To break a loop
  623. }
  624. // Happy compiler
  625. eloop = eloop;
  626. type = type;
  627. associated_data = associated_data;
  628. return BOOL_TRUE;
  629. }
  630. static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  631. void *associated_data, void *user_data)
  632. {
  633. kexec_t *exec = (kexec_t *)user_data;
  634. // Happy compiler
  635. eloop = eloop;
  636. type = type;
  637. associated_data = associated_data;
  638. return get_stdout(exec);
  639. }
  640. bool_t ksession_exec_locally(ksession_t *session, const kentry_t *entry,
  641. kpargv_t *parent_pargv, int *retcode, char **out)
  642. {
  643. kexec_t *exec = NULL;
  644. faux_eloop_t *eloop = NULL;
  645. faux_buf_t *buf = NULL;
  646. char *cstr = NULL;
  647. ssize_t len = 0;
  648. assert(entry);
  649. if (!entry)
  650. return BOOL_FALSE;
  651. // Parsing
  652. exec = ksession_parse_for_local_exec(session, entry, parent_pargv);
  653. if (!exec)
  654. return BOOL_FALSE;
  655. // Session status can be changed while parsing because it can execute
  656. // nested ksession_exec_locally() to check for PTYPEs, CONDitions etc.
  657. // So check for 'done' flag to propagate it.
  658. // NOTE: Don't interrupt single kexec_t. Let's it to complete.
  659. // if (ksession_done(session)) {
  660. // kexec_free(exec);
  661. // return BOOL_FALSE; // Because action is not completed
  662. // }
  663. // Execute kexec and then wait for completion using local Eloop
  664. if (!kexec_exec(exec)) {
  665. kexec_free(exec);
  666. return BOOL_FALSE; // Something went wrong
  667. }
  668. // If kexec contains only non-exec (for example dry-run) ACTIONs then
  669. // we don't need event loop and can return here.
  670. if (kexec_retcode(exec, retcode)) {
  671. kexec_free(exec);
  672. return BOOL_TRUE;
  673. }
  674. // Local service loop
  675. eloop = faux_eloop_new(NULL);
  676. faux_eloop_add_signal(eloop, SIGINT, stop_loop_ev, session);
  677. faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, session);
  678. faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, session);
  679. faux_eloop_add_signal(eloop, SIGCHLD, action_terminated_ev, exec);
  680. faux_eloop_add_fd(eloop, kexec_stdout(exec), POLLIN,
  681. action_stdout_ev, exec);
  682. faux_eloop_loop(eloop);
  683. faux_eloop_free(eloop);
  684. kexec_retcode(exec, retcode);
  685. if (!out) {
  686. kexec_free(exec);
  687. return BOOL_TRUE;
  688. }
  689. buf = kexec_bufout(exec);
  690. if ((len = faux_buf_len(buf)) <= 0) {
  691. kexec_free(exec);
  692. return BOOL_TRUE;
  693. }
  694. cstr = faux_malloc(len + 1);
  695. faux_buf_read(buf, cstr, len);
  696. cstr[len] = '\0';
  697. *out = cstr;
  698. kexec_free(exec);
  699. return BOOL_TRUE;
  700. }