ktpd_session.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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_NOT_AUTHORIZED = '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. void *data, size_t len, void *user_data);
  42. static bool_t ktpd_session_stall_cb(faux_async_t *async,
  43. size_t len, void *user_data);
  44. static bool_t client_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  45. void *associated_data, void *user_data);
  46. static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  47. void *associated_data, void *user_data);
  48. static bool_t ktpd_session_exec(ktpd_session_t *ktpd, const char *line,
  49. int *retcode, faux_error_t *error);
  50. ktpd_session_t *ktpd_session_new(int sock, const kscheme_t *scheme,
  51. const char *start_entry, faux_eloop_t *eloop)
  52. {
  53. ktpd_session_t *ktpd = NULL;
  54. if (sock < 0)
  55. return NULL;
  56. if (!eloop)
  57. return NULL;
  58. ktpd = faux_zmalloc(sizeof(*ktpd));
  59. assert(ktpd);
  60. if (!ktpd)
  61. return NULL;
  62. // Init
  63. ktpd->state = KTPD_SESSION_STATE_IDLE;
  64. ktpd->eloop = eloop;
  65. ktpd->session = ksession_new(scheme, start_entry);
  66. assert(ktpd->session);
  67. // Async object
  68. ktpd->async = faux_async_new(sock);
  69. assert(ktpd->async);
  70. // Receive message header first
  71. faux_async_set_read_limits(ktpd->async,
  72. sizeof(faux_hdr_t), sizeof(faux_hdr_t));
  73. faux_async_set_read_cb(ktpd->async, ktpd_session_read_cb, ktpd);
  74. ktpd->hdr = NULL;
  75. faux_async_set_stall_cb(ktpd->async, ktpd_session_stall_cb, ktpd);
  76. // Eloop callbacks
  77. faux_eloop_add_fd(ktpd->eloop, ktpd_session_fd(ktpd), POLLIN,
  78. client_ev, ktpd);
  79. faux_eloop_add_signal(ktpd->eloop, SIGCHLD, wait_for_actions_ev, ktpd);
  80. return ktpd;
  81. }
  82. void ktpd_session_free(ktpd_session_t *ktpd)
  83. {
  84. if (!ktpd)
  85. return;
  86. kexec_free(ktpd->exec);
  87. ksession_free(ktpd->session);
  88. faux_free(ktpd->hdr);
  89. close(ktpd_session_fd(ktpd));
  90. faux_async_free(ktpd->async);
  91. faux_free(ktpd);
  92. }
  93. static bool_t check_ktp_header(faux_hdr_t *hdr)
  94. {
  95. assert(hdr);
  96. if (!hdr)
  97. return BOOL_FALSE;
  98. if (faux_hdr_magic(hdr) != KTP_MAGIC)
  99. return BOOL_FALSE;
  100. if (faux_hdr_major(hdr) != KTP_MAJOR)
  101. return BOOL_FALSE;
  102. if (faux_hdr_minor(hdr) != KTP_MINOR)
  103. return BOOL_FALSE;
  104. if (faux_hdr_len(hdr) < (int)sizeof(*hdr))
  105. return BOOL_FALSE;
  106. return BOOL_TRUE;
  107. }
  108. static bool_t ktpd_session_send_error(ktpd_session_t *session,
  109. ktp_cmd_e cmd, const char *error)
  110. {
  111. faux_msg_t *msg = NULL;
  112. assert(session);
  113. if (!session)
  114. return BOOL_FALSE;
  115. msg = ktp_msg_preform(cmd, KTP_STATUS_ERROR);
  116. if (error)
  117. faux_msg_add_param(msg, KTP_PARAM_ERROR, error, strlen(error));
  118. faux_msg_send_async(msg, session->async);
  119. faux_msg_free(msg);
  120. return BOOL_TRUE;
  121. }
  122. static bool_t ktpd_session_process_cmd(ktpd_session_t *ktpd, faux_msg_t *msg)
  123. {
  124. char *line = NULL;
  125. faux_msg_t *ack = NULL;
  126. int retcode = -1;
  127. ktp_cmd_e cmd = KTP_CMD_ACK;
  128. faux_error_t *error = NULL;
  129. bool_t rc = BOOL_FALSE;
  130. assert(ktpd);
  131. assert(msg);
  132. // Get line from message
  133. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  134. ktpd_session_send_error(ktpd, cmd,
  135. "The line is not specified");
  136. return BOOL_FALSE;
  137. }
  138. error = faux_error_new();
  139. rc = ktpd_session_exec(ktpd, line, &retcode, error);
  140. if (ktpd->exec) {
  141. faux_error_free(error);
  142. return BOOL_TRUE; // Continue and wait for ACTION
  143. }
  144. // Session status can be changed while parsing
  145. if (ksession_done(ktpd->session)) {
  146. ktpd_session_send_error(ktpd, cmd, "Interrupted by system");
  147. faux_error_free(error);
  148. return BOOL_FALSE;
  149. }
  150. if (rc) {
  151. char *err = faux_error_cstr(error);
  152. ktpd_session_send_error(ktpd, cmd, err);
  153. faux_str_free(err);
  154. return BOOL_FALSE;
  155. } else {
  156. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  157. faux_msg_send_async(ack, ktpd->async);
  158. faux_msg_free(ack);
  159. }
  160. faux_error_free(error);
  161. return BOOL_TRUE;
  162. }
  163. static bool_t ktpd_session_process_completion(ktpd_session_t *session, faux_msg_t *msg)
  164. {
  165. char *line = NULL;
  166. faux_msg_t *ack = NULL;
  167. kpargv_t *pargv = NULL;
  168. ktp_cmd_e cmd = KTP_COMPLETION_ACK;
  169. assert(session);
  170. assert(msg);
  171. // Get line from message
  172. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  173. ktpd_session_send_error(session, cmd, NULL);
  174. return BOOL_FALSE;
  175. }
  176. // Parsing
  177. pargv = ksession_parse_for_completion(session->session, line);
  178. faux_str_free(line);
  179. if (!pargv) {
  180. ktpd_session_send_error(session, cmd, NULL);
  181. return BOOL_FALSE;
  182. }
  183. kpargv_debug(pargv);
  184. kpargv_free(pargv);
  185. // Send ACK message
  186. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  187. faux_msg_send_async(ack, session->async);
  188. faux_msg_free(ack);
  189. return BOOL_TRUE;
  190. }
  191. static bool_t ktpd_session_process_help(ktpd_session_t *session, faux_msg_t *msg)
  192. {
  193. char *line = NULL;
  194. faux_msg_t *ack = NULL;
  195. // kpargv_t *pargv = NULL;
  196. ktp_cmd_e cmd = KTP_HELP_ACK;
  197. assert(session);
  198. assert(msg);
  199. // Get line from message
  200. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  201. ktpd_session_send_error(session, cmd, NULL);
  202. return BOOL_FALSE;
  203. }
  204. /* // Parsing
  205. pargv = ksession_parse_line(session->session, line, KPURPOSE_HELP);
  206. faux_str_free(line);
  207. kpargv_free(pargv);
  208. */
  209. // Send ACK message
  210. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  211. faux_msg_send_async(ack, session->async);
  212. faux_msg_free(ack);
  213. return BOOL_TRUE;
  214. }
  215. static bool_t ktpd_session_dispatch(ktpd_session_t *session, faux_msg_t *msg)
  216. {
  217. uint16_t cmd = 0;
  218. assert(session);
  219. if (!session)
  220. return BOOL_FALSE;
  221. assert(msg);
  222. if (!msg)
  223. return BOOL_FALSE;
  224. cmd = faux_msg_get_cmd(msg);
  225. switch (cmd) {
  226. case KTP_CMD:
  227. ktpd_session_process_cmd(session, msg);
  228. break;
  229. case KTP_COMPLETION:
  230. ktpd_session_process_completion(session, msg);
  231. break;
  232. case KTP_HELP:
  233. ktpd_session_process_help(session, msg);
  234. break;
  235. default:
  236. syslog(LOG_WARNING, "Unsupported command: 0x%04u\n", cmd);
  237. break;
  238. }
  239. return BOOL_TRUE;
  240. }
  241. /** @brief Low-level function to receive KTP message.
  242. *
  243. * Firstly function gets the header of message. Then it checks and parses
  244. * header and find out the length of whole message. Then it receives the rest
  245. * of message.
  246. */
  247. static bool_t ktpd_session_read_cb(faux_async_t *async,
  248. void *data, size_t len, void *user_data)
  249. {
  250. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  251. faux_msg_t *completed_msg = NULL;
  252. assert(async);
  253. assert(data);
  254. assert(ktpd);
  255. // Receive header
  256. if (!ktpd->hdr) {
  257. size_t whole_len = 0;
  258. size_t msg_wo_hdr = 0;
  259. ktpd->hdr = (faux_hdr_t *)data;
  260. // Check for broken header
  261. if (!check_ktp_header(ktpd->hdr)) {
  262. faux_free(ktpd->hdr);
  263. ktpd->hdr = NULL;
  264. return BOOL_FALSE;
  265. }
  266. whole_len = faux_hdr_len(ktpd->hdr);
  267. // msg_wo_hdr >= 0 because check_ktp_header() validates whole_len
  268. msg_wo_hdr = whole_len - sizeof(faux_hdr_t);
  269. // Plan to receive message body
  270. if (msg_wo_hdr > 0) {
  271. faux_async_set_read_limits(async,
  272. msg_wo_hdr, msg_wo_hdr);
  273. return BOOL_TRUE;
  274. }
  275. // Here message is completed (msg body has zero length)
  276. completed_msg = faux_msg_deserialize_parts(ktpd->hdr, NULL, 0);
  277. // Receive message body
  278. } else {
  279. completed_msg = faux_msg_deserialize_parts(ktpd->hdr, data, len);
  280. faux_free(data);
  281. }
  282. // Plan to receive msg header
  283. faux_async_set_read_limits(ktpd->async,
  284. sizeof(faux_hdr_t), sizeof(faux_hdr_t));
  285. faux_free(ktpd->hdr);
  286. ktpd->hdr = NULL; // Ready to recv new header
  287. // Here message is completed
  288. ktpd_session_dispatch(ktpd, completed_msg);
  289. faux_msg_free(completed_msg);
  290. // Session status can be changed while parsing
  291. if (ksession_done(ktpd->session))
  292. return BOOL_FALSE;
  293. return BOOL_TRUE;
  294. }
  295. static bool_t ktpd_session_stall_cb(faux_async_t *async,
  296. size_t len, void *user_data)
  297. {
  298. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  299. assert(async);
  300. assert(ktpd);
  301. assert(ktpd->eloop);
  302. faux_eloop_include_fd_event(ktpd->eloop, ktpd_session_fd(ktpd), POLLOUT);
  303. async = async; // Happy compiler
  304. len = len; // Happy compiler
  305. return BOOL_TRUE;
  306. }
  307. bool_t ktpd_session_connected(ktpd_session_t *ktpd)
  308. {
  309. assert(ktpd);
  310. if (!ktpd)
  311. return BOOL_FALSE;
  312. if (KTPD_SESSION_STATE_DISCONNECTED == ktpd->state)
  313. return BOOL_FALSE;
  314. return BOOL_TRUE;
  315. }
  316. int ktpd_session_fd(const ktpd_session_t *ktpd)
  317. {
  318. assert(ktpd);
  319. if (!ktpd)
  320. return BOOL_FALSE;
  321. return faux_async_fd(ktpd->async);
  322. }
  323. bool_t ktpd_session_async_in(ktpd_session_t *ktpd)
  324. {
  325. assert(ktpd);
  326. if (!ktpd)
  327. return BOOL_FALSE;
  328. if (!ktpd_session_connected(ktpd))
  329. return BOOL_FALSE;
  330. if (faux_async_in(ktpd->async) < 0)
  331. return BOOL_FALSE;
  332. return BOOL_TRUE;
  333. }
  334. bool_t ktpd_session_async_out(ktpd_session_t *ktpd)
  335. {
  336. assert(ktpd);
  337. if (!ktpd)
  338. return BOOL_FALSE;
  339. if (!ktpd_session_connected(ktpd))
  340. return BOOL_FALSE;
  341. if (faux_async_out(ktpd->async) < 0)
  342. return BOOL_FALSE;
  343. return BOOL_TRUE;
  344. }
  345. static bool_t client_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  346. void *associated_data, void *user_data)
  347. {
  348. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  349. ktpd_session_t *ktpd_session = (ktpd_session_t *)user_data;
  350. assert(ktpd_session);
  351. // Write data
  352. if (info->revents & POLLOUT) {
  353. faux_eloop_exclude_fd_event(eloop, info->fd, POLLOUT);
  354. if (!ktpd_session_async_out(ktpd_session)) {
  355. // Someting went wrong
  356. faux_eloop_del_fd(eloop, info->fd);
  357. syslog(LOG_ERR, "Problem with async output");
  358. return BOOL_FALSE; // Stop event loop
  359. }
  360. }
  361. // Read data
  362. if (info->revents & POLLIN) {
  363. if (!ktpd_session_async_in(ktpd_session)) {
  364. // Someting went wrong
  365. faux_eloop_del_fd(eloop, info->fd);
  366. syslog(LOG_ERR, "Problem with async input");
  367. return BOOL_FALSE; // Stop event loop
  368. }
  369. }
  370. // EOF
  371. if (info->revents & POLLHUP) {
  372. faux_eloop_del_fd(eloop, info->fd);
  373. syslog(LOG_DEBUG, "Close connection %d", info->fd);
  374. return BOOL_FALSE; // Stop event loop
  375. }
  376. type = type; // Happy compiler
  377. return BOOL_TRUE;
  378. }
  379. static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  380. void *associated_data, void *user_data)
  381. {
  382. int wstatus = 0;
  383. pid_t child_pid = -1;
  384. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  385. int retcode = -1;
  386. faux_msg_t *ack = NULL;
  387. ktp_cmd_e cmd = KTP_CMD_ACK;
  388. if (!ktpd)
  389. return BOOL_FALSE;
  390. // Wait for any child process. Doesn't block.
  391. while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
  392. if (ktpd->exec)
  393. kexec_continue_command_execution(ktpd->exec, child_pid,
  394. wstatus);
  395. }
  396. if (!ktpd->exec)
  397. return BOOL_TRUE;
  398. // Check if kexec is done now
  399. if (!kexec_retcode(ktpd->exec, &retcode))
  400. return BOOL_TRUE; // Continue
  401. faux_eloop_del_fd(eloop, kexec_stdout(ktpd->exec));
  402. kexec_free(ktpd->exec);
  403. ktpd->exec = NULL;
  404. ktpd->state = KTPD_SESSION_STATE_IDLE;
  405. // Send ACK message
  406. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  407. faux_msg_send_async(ack, ktpd->async);
  408. faux_msg_free(ack);
  409. type = type; // Happy compiler
  410. associated_data = associated_data; // Happy compiler
  411. return BOOL_TRUE;
  412. }
  413. static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  414. void *associated_data, void *user_data)
  415. {
  416. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  417. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  418. kexec_t *exec = NULL;
  419. ssize_t r = -1;
  420. faux_buf_t *faux_buf = NULL;
  421. void *linear_buf = NULL;
  422. char *buf = NULL;
  423. ssize_t len = 0;
  424. if (!ktpd)
  425. return BOOL_TRUE;
  426. exec = ktpd->exec;
  427. if (!exec)
  428. return BOOL_TRUE;
  429. faux_buf = kexec_bufout(exec);
  430. assert(faux_buf);
  431. do {
  432. ssize_t really_readed = 0;
  433. ssize_t linear_len =
  434. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  435. // Non-blocked read. The fd became non-blocked while
  436. // kexec_prepare().
  437. r = read(info->fd, linear_buf, linear_len);
  438. if (r > 0)
  439. really_readed = r;
  440. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  441. } while (r > 0);
  442. len = faux_buf_len(faux_buf);
  443. if (0 == len)
  444. return BOOL_TRUE;
  445. buf = malloc(len);
  446. faux_buf_read(faux_buf, buf, len);
  447. write(STDOUT_FILENO, buf, len);
  448. free(buf);
  449. // Happy compiler
  450. eloop = eloop;
  451. type = type;
  452. return BOOL_TRUE;
  453. }
  454. static bool_t ktpd_session_exec(ktpd_session_t *ktpd, const char *line,
  455. int *retcode, faux_error_t *error)
  456. {
  457. kexec_t *exec = NULL;
  458. assert(ktpd);
  459. if (!ktpd)
  460. return BOOL_FALSE;
  461. // Parsing
  462. exec = ksession_parse_for_exec(ktpd->session, line, error);
  463. if (!exec)
  464. return BOOL_FALSE;
  465. // Session status can be changed while parsing
  466. if (ksession_done(ktpd->session)) {
  467. kexec_free(exec);
  468. return BOOL_FALSE; // Because action is not completed
  469. }
  470. // Execute kexec and then wait for completion using global Eloop
  471. if (!kexec_exec(exec)) {
  472. kexec_free(exec);
  473. return BOOL_FALSE; // Something went wrong
  474. }
  475. // If kexec contains only non-exec (for example dry-run) ACTIONs then
  476. // we don't need event loop and can return here.
  477. if (kexec_retcode(exec, retcode)) {
  478. kexec_free(exec);
  479. return BOOL_TRUE;
  480. }
  481. // Save kexec pointer to use later
  482. ktpd->state = KTPD_SESSION_STATE_WAIT_FOR_PROCESS;
  483. ktpd->exec = exec;
  484. faux_eloop_add_fd(ktpd->eloop, kexec_stdout(exec), POLLIN,
  485. action_stdout_ev, ktpd);
  486. return BOOL_TRUE;
  487. }
  488. #if 0
  489. static void ktpd_session_bad_socket(ktpd_session_t *session)
  490. {
  491. assert(session);
  492. if (!session)
  493. return;
  494. session->state = KTPD_SESSION_STATE_DISCONNECTED;
  495. }
  496. #endif