ktpd_session.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <fcntl.h>
  10. #include <sys/socket.h>
  11. #include <sys/un.h>
  12. #include <syslog.h>
  13. #include <poll.h>
  14. #include <sys/wait.h>
  15. #include <faux/str.h>
  16. #include <faux/async.h>
  17. #include <faux/msg.h>
  18. #include <faux/eloop.h>
  19. #include <klish/ksession.h>
  20. #include <klish/ktp.h>
  21. #include <klish/ktp_session.h>
  22. typedef enum {
  23. KTPD_SESSION_STATE_DISCONNECTED = 'd',
  24. KTPD_SESSION_STATE_UNAUTHORIZED = 'a',
  25. KTPD_SESSION_STATE_IDLE = 'i',
  26. KTPD_SESSION_STATE_WAIT_FOR_PROCESS = 'p',
  27. } ktpd_session_state_e;
  28. struct ktpd_session_s {
  29. ksession_t *session;
  30. ktpd_session_state_e state;
  31. uid_t uid;
  32. gid_t gid;
  33. char *user;
  34. faux_async_t *async;
  35. faux_hdr_t *hdr; // Engine will receive header and then msg
  36. faux_eloop_t *eloop; // External link, dont's free()
  37. kexec_t *exec;
  38. };
  39. // Static declarations
  40. static bool_t ktpd_session_read_cb(faux_async_t *async,
  41. faux_buf_t *buf, size_t len, void *user_data);
  42. static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  43. void *associated_data, void *user_data);
  44. static bool_t ktpd_session_exec(ktpd_session_t *ktpd, const char *line,
  45. int *retcode, faux_error_t *error, bool_t dry_run);
  46. ktpd_session_t *ktpd_session_new(int sock, const kscheme_t *scheme,
  47. const char *start_entry, faux_eloop_t *eloop)
  48. {
  49. ktpd_session_t *ktpd = NULL;
  50. if (sock < 0)
  51. return NULL;
  52. if (!eloop)
  53. return NULL;
  54. ktpd = faux_zmalloc(sizeof(*ktpd));
  55. assert(ktpd);
  56. if (!ktpd)
  57. return NULL;
  58. // Init
  59. ktpd->state = KTPD_SESSION_STATE_IDLE;
  60. ktpd->eloop = eloop;
  61. ktpd->session = ksession_new(scheme, start_entry);
  62. assert(ktpd->session);
  63. // Async object
  64. ktpd->async = faux_async_new(sock);
  65. assert(ktpd->async);
  66. // Receive message header first
  67. faux_async_set_read_limits(ktpd->async,
  68. sizeof(faux_hdr_t), sizeof(faux_hdr_t));
  69. faux_async_set_read_cb(ktpd->async, ktpd_session_read_cb, ktpd);
  70. ktpd->hdr = NULL;
  71. faux_async_set_stall_cb(ktpd->async, ktp_stall_cb, ktpd->eloop);
  72. // Eloop callbacks
  73. faux_eloop_add_fd(ktpd->eloop, ktpd_session_fd(ktpd), POLLIN,
  74. ktp_peer_ev, ktpd->async);
  75. faux_eloop_add_signal(ktpd->eloop, SIGCHLD, wait_for_actions_ev, ktpd);
  76. return ktpd;
  77. }
  78. void ktpd_session_free(ktpd_session_t *ktpd)
  79. {
  80. if (!ktpd)
  81. return;
  82. kexec_free(ktpd->exec);
  83. ksession_free(ktpd->session);
  84. faux_free(ktpd->hdr);
  85. close(ktpd_session_fd(ktpd));
  86. faux_async_free(ktpd->async);
  87. faux_free(ktpd);
  88. }
  89. static bool_t ktpd_session_process_cmd(ktpd_session_t *ktpd, faux_msg_t *msg)
  90. {
  91. char *line = NULL;
  92. int retcode = -1;
  93. ktp_cmd_e cmd = KTP_CMD_ACK;
  94. faux_error_t *error = NULL;
  95. bool_t rc = BOOL_FALSE;
  96. bool_t dry_run = BOOL_FALSE;
  97. assert(ktpd);
  98. assert(msg);
  99. // Get line from message
  100. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  101. ktp_send_error(ktpd->async, cmd, "The line is not specified");
  102. return BOOL_FALSE;
  103. }
  104. // Get dry-run flag from message
  105. if (KTP_STATUS_IS_DRY_RUN(faux_msg_get_status(msg)))
  106. dry_run = BOOL_TRUE;
  107. error = faux_error_new();
  108. rc = ktpd_session_exec(ktpd, line, &retcode, error, dry_run);
  109. faux_str_free(line);
  110. // Command is scheduled. Eloop will wait for ACTION completion.
  111. // So inform client about it and about command features like
  112. // interactive/non-interactive.
  113. if (ktpd->exec) {
  114. faux_msg_t *ack = NULL;
  115. ktp_status_e status = KTP_STATUS_INCOMPLETED;
  116. ack = ktp_msg_preform(cmd, status);
  117. faux_msg_send_async(ack, ktpd->async);
  118. faux_msg_free(ack);
  119. faux_error_free(error);
  120. return BOOL_TRUE; // Continue and wait for ACTION
  121. }
  122. // Session status can be changed while parsing
  123. if (ksession_done(ktpd->session)) {
  124. ktp_send_error(ktpd->async, cmd, "Interrupted by system");
  125. faux_error_free(error);
  126. return BOOL_FALSE;
  127. }
  128. if (rc) {
  129. uint8_t retcode8bit = 0;
  130. faux_msg_t *ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  131. retcode8bit = (uint8_t)(retcode & 0xff);
  132. faux_msg_add_param(ack, KTP_PARAM_RETCODE, &retcode8bit, 1);
  133. faux_msg_send_async(ack, ktpd->async);
  134. faux_msg_free(ack);
  135. } else {
  136. char *err = faux_error_cstr(error);
  137. ktp_send_error(ktpd->async, cmd, err);
  138. faux_str_free(err);
  139. return BOOL_FALSE;
  140. }
  141. faux_error_free(error);
  142. return BOOL_TRUE;
  143. }
  144. static bool_t ktpd_session_process_completion(ktpd_session_t *ktpd, faux_msg_t *msg)
  145. {
  146. char *line = NULL;
  147. faux_msg_t *ack = NULL;
  148. kpargv_t *pargv = NULL;
  149. ktp_cmd_e cmd = KTP_COMPLETION_ACK;
  150. assert(ktpd);
  151. assert(msg);
  152. // Get line from message
  153. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  154. ktp_send_error(ktpd->async, cmd, NULL);
  155. return BOOL_FALSE;
  156. }
  157. // Parsing
  158. pargv = ksession_parse_for_completion(ktpd->session, line);
  159. faux_str_free(line);
  160. if (!pargv) {
  161. ktp_send_error(ktpd->async, cmd, NULL);
  162. return BOOL_FALSE;
  163. }
  164. kpargv_debug(pargv);
  165. kpargv_free(pargv);
  166. // Send ACK message
  167. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  168. faux_msg_send_async(ack, ktpd->async);
  169. faux_msg_free(ack);
  170. return BOOL_TRUE;
  171. }
  172. static bool_t ktpd_session_process_help(ktpd_session_t *ktpd, faux_msg_t *msg)
  173. {
  174. char *line = NULL;
  175. faux_msg_t *ack = NULL;
  176. // kpargv_t *pargv = NULL;
  177. ktp_cmd_e cmd = KTP_HELP_ACK;
  178. assert(ktpd);
  179. assert(msg);
  180. // Get line from message
  181. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  182. ktp_send_error(ktpd->async, cmd, NULL);
  183. return BOOL_FALSE;
  184. }
  185. /* // Parsing
  186. pargv = ksession_parse_line(ktpd->session, line, KPURPOSE_HELP);
  187. faux_str_free(line);
  188. kpargv_free(pargv);
  189. */
  190. // Send ACK message
  191. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  192. faux_msg_send_async(ack, ktpd->async);
  193. faux_msg_free(ack);
  194. return BOOL_TRUE;
  195. }
  196. static bool_t ktpd_session_dispatch(ktpd_session_t *ktpd, faux_msg_t *msg)
  197. {
  198. uint16_t cmd = 0;
  199. assert(ktpd);
  200. if (!ktpd)
  201. return BOOL_FALSE;
  202. assert(msg);
  203. if (!msg)
  204. return BOOL_FALSE;
  205. cmd = faux_msg_get_cmd(msg);
  206. switch (cmd) {
  207. case KTP_CMD:
  208. ktpd_session_process_cmd(ktpd, msg);
  209. break;
  210. case KTP_COMPLETION:
  211. ktpd_session_process_completion(ktpd, msg);
  212. break;
  213. case KTP_HELP:
  214. ktpd_session_process_help(ktpd, msg);
  215. break;
  216. default:
  217. syslog(LOG_WARNING, "Unsupported command: 0x%04u\n", cmd);
  218. break;
  219. }
  220. return BOOL_TRUE;
  221. }
  222. /** @brief Low-level function to receive KTP message.
  223. *
  224. * Firstly function gets the header of message. Then it checks and parses
  225. * header and find out the length of whole message. Then it receives the rest
  226. * of message.
  227. */
  228. static bool_t ktpd_session_read_cb(faux_async_t *async,
  229. faux_buf_t *buf, size_t len, void *user_data)
  230. {
  231. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  232. faux_msg_t *completed_msg = NULL;
  233. char *data = NULL;
  234. assert(async);
  235. assert(buf);
  236. assert(ktpd);
  237. // Linearize buffer
  238. data = malloc(len);
  239. faux_buf_read(buf, data, len);
  240. // Receive header
  241. if (!ktpd->hdr) {
  242. size_t whole_len = 0;
  243. size_t msg_wo_hdr = 0;
  244. ktpd->hdr = (faux_hdr_t *)data;
  245. // Check for broken header
  246. if (!ktp_check_header(ktpd->hdr)) {
  247. faux_free(ktpd->hdr);
  248. ktpd->hdr = NULL;
  249. return BOOL_FALSE;
  250. }
  251. whole_len = faux_hdr_len(ktpd->hdr);
  252. // msg_wo_hdr >= 0 because ktp_check_header() validates whole_len
  253. msg_wo_hdr = whole_len - sizeof(faux_hdr_t);
  254. // Plan to receive message body
  255. if (msg_wo_hdr > 0) {
  256. faux_async_set_read_limits(async,
  257. msg_wo_hdr, msg_wo_hdr);
  258. return BOOL_TRUE;
  259. }
  260. // Here message is completed (msg body has zero length)
  261. completed_msg = faux_msg_deserialize_parts(ktpd->hdr, NULL, 0);
  262. // Receive message body
  263. } else {
  264. completed_msg = faux_msg_deserialize_parts(ktpd->hdr, data, len);
  265. faux_free(data);
  266. }
  267. // Plan to receive msg header
  268. faux_async_set_read_limits(ktpd->async,
  269. sizeof(faux_hdr_t), sizeof(faux_hdr_t));
  270. faux_free(ktpd->hdr);
  271. ktpd->hdr = NULL; // Ready to recv new header
  272. // Here message is completed
  273. ktpd_session_dispatch(ktpd, completed_msg);
  274. faux_msg_free(completed_msg);
  275. // Session status can be changed while parsing
  276. if (ksession_done(ktpd->session))
  277. return BOOL_FALSE;
  278. return BOOL_TRUE;
  279. }
  280. bool_t ktpd_session_connected(ktpd_session_t *ktpd)
  281. {
  282. assert(ktpd);
  283. if (!ktpd)
  284. return BOOL_FALSE;
  285. if (KTPD_SESSION_STATE_DISCONNECTED == ktpd->state)
  286. return BOOL_FALSE;
  287. return BOOL_TRUE;
  288. }
  289. int ktpd_session_fd(const ktpd_session_t *ktpd)
  290. {
  291. assert(ktpd);
  292. if (!ktpd)
  293. return BOOL_FALSE;
  294. return faux_async_fd(ktpd->async);
  295. }
  296. static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  297. void *associated_data, void *user_data)
  298. {
  299. int wstatus = 0;
  300. pid_t child_pid = -1;
  301. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  302. int retcode = -1;
  303. uint8_t retcode8bit = 0;
  304. faux_msg_t *ack = NULL;
  305. ktp_cmd_e cmd = KTP_CMD_ACK;
  306. if (!ktpd)
  307. return BOOL_FALSE;
  308. // Wait for any child process. Doesn't block.
  309. while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
  310. if (ktpd->exec)
  311. kexec_continue_command_execution(ktpd->exec, child_pid,
  312. wstatus);
  313. }
  314. if (!ktpd->exec)
  315. return BOOL_TRUE;
  316. // Check if kexec is done now
  317. if (!kexec_retcode(ktpd->exec, &retcode))
  318. return BOOL_TRUE; // Continue
  319. faux_eloop_del_fd(eloop, kexec_stdout(ktpd->exec));
  320. kexec_free(ktpd->exec);
  321. ktpd->exec = NULL;
  322. ktpd->state = KTPD_SESSION_STATE_IDLE;
  323. // Send ACK message
  324. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  325. retcode8bit = (uint8_t)(retcode & 0xff);
  326. faux_msg_add_param(ack, KTP_PARAM_RETCODE, &retcode8bit, 1);
  327. faux_msg_send_async(ack, ktpd->async);
  328. faux_msg_free(ack);
  329. type = type; // Happy compiler
  330. associated_data = associated_data; // Happy compiler
  331. return BOOL_TRUE;
  332. }
  333. static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  334. void *associated_data, void *user_data)
  335. {
  336. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  337. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  338. ssize_t r = -1;
  339. faux_buf_t *faux_buf = NULL;
  340. char *buf = NULL;
  341. ssize_t len = 0;
  342. faux_msg_t *ack = NULL;
  343. if (!ktpd)
  344. return BOOL_TRUE;
  345. if (!ktpd->exec)
  346. return BOOL_TRUE;
  347. faux_buf = kexec_bufout(ktpd->exec);
  348. assert(faux_buf);
  349. do {
  350. void *linear_buf = NULL;
  351. ssize_t really_readed = 0;
  352. ssize_t linear_len =
  353. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  354. // Non-blocked read. The fd became non-blocked while
  355. // kexec_prepare().
  356. r = read(info->fd, linear_buf, linear_len);
  357. if (r > 0)
  358. really_readed = r;
  359. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  360. } while (r > 0);
  361. len = faux_buf_len(faux_buf);
  362. if (0 == len)
  363. return BOOL_TRUE;
  364. buf = malloc(len);
  365. faux_buf_read(faux_buf, buf, len);
  366. // Create KTP_STDOUT message to send to client
  367. ack = ktp_msg_preform(KTP_STDOUT, KTP_STATUS_NONE);
  368. faux_msg_add_param(ack, KTP_PARAM_LINE, buf, len);
  369. faux_msg_send_async(ack, ktpd->async);
  370. faux_msg_free(ack);
  371. free(buf);
  372. // Happy compiler
  373. eloop = eloop;
  374. type = type;
  375. return BOOL_TRUE;
  376. }
  377. static bool_t action_stderr_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  378. void *associated_data, void *user_data)
  379. {
  380. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  381. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  382. ssize_t r = -1;
  383. faux_buf_t *faux_buf = NULL;
  384. char *buf = NULL;
  385. ssize_t len = 0;
  386. faux_msg_t *ack = NULL;
  387. if (!ktpd)
  388. return BOOL_TRUE;
  389. if (!ktpd->exec)
  390. return BOOL_TRUE;
  391. faux_buf = kexec_buferr(ktpd->exec);
  392. assert(faux_buf);
  393. do {
  394. void *linear_buf = NULL;
  395. ssize_t really_readed = 0;
  396. ssize_t linear_len =
  397. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  398. // Non-blocked read. The fd became non-blocked while
  399. // kexec_prepare().
  400. r = read(info->fd, linear_buf, linear_len);
  401. if (r > 0)
  402. really_readed = r;
  403. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  404. } while (r > 0);
  405. len = faux_buf_len(faux_buf);
  406. if (0 == len)
  407. return BOOL_TRUE;
  408. buf = malloc(len);
  409. faux_buf_read(faux_buf, buf, len);
  410. // Create KTP_STDERR message to send to client
  411. ack = ktp_msg_preform(KTP_STDERR, KTP_STATUS_NONE);
  412. faux_msg_add_param(ack, KTP_PARAM_LINE, buf, len);
  413. faux_msg_send_async(ack, ktpd->async);
  414. faux_msg_free(ack);
  415. free(buf);
  416. // Happy compiler
  417. eloop = eloop;
  418. type = type;
  419. return BOOL_TRUE;
  420. }
  421. static bool_t ktpd_session_exec(ktpd_session_t *ktpd, const char *line,
  422. int *retcode, faux_error_t *error, bool_t dry_run)
  423. {
  424. kexec_t *exec = NULL;
  425. assert(ktpd);
  426. if (!ktpd)
  427. return BOOL_FALSE;
  428. // Parsing
  429. exec = ksession_parse_for_exec(ktpd->session, line, error);
  430. if (!exec)
  431. return BOOL_FALSE;
  432. // Set dry-run flag
  433. kexec_set_dry_run(exec, dry_run);
  434. // Session status can be changed while parsing
  435. if (ksession_done(ktpd->session)) {
  436. kexec_free(exec);
  437. return BOOL_FALSE; // Because action is not completed
  438. }
  439. // Execute kexec and then wait for completion using global Eloop
  440. if (!kexec_exec(exec)) {
  441. kexec_free(exec);
  442. return BOOL_FALSE; // Something went wrong
  443. }
  444. // If kexec contains only non-exec (for example dry-run) ACTIONs then
  445. // we don't need event loop and can return here.
  446. if (kexec_retcode(exec, retcode)) {
  447. kexec_free(exec);
  448. return BOOL_TRUE;
  449. }
  450. // Save kexec pointer to use later
  451. ktpd->state = KTPD_SESSION_STATE_WAIT_FOR_PROCESS;
  452. ktpd->exec = exec;
  453. faux_eloop_add_fd(ktpd->eloop, kexec_stdout(exec), POLLIN,
  454. action_stdout_ev, ktpd);
  455. faux_eloop_add_fd(ktpd->eloop, kexec_stderr(exec), POLLIN,
  456. action_stderr_ev, ktpd);
  457. return BOOL_TRUE;
  458. }
  459. #if 0
  460. static void ktpd_session_bad_socket(ktpd_session_t *ktpd)
  461. {
  462. assert(ktpd);
  463. if (!ktpd)
  464. return;
  465. ktpd->state = KTPD_SESSION_STATE_DISCONNECTED;
  466. }
  467. #endif