ktpd_session.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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);
  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. assert(ktpd);
  97. assert(msg);
  98. // Get line from message
  99. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  100. ktp_send_error(ktpd->async, cmd, "The line is not specified");
  101. return BOOL_FALSE;
  102. }
  103. error = faux_error_new();
  104. rc = ktpd_session_exec(ktpd, line, &retcode, error);
  105. faux_str_free(line);
  106. // Command is scheduled. Eloop will wait for ACTION completion.
  107. // So inform client about it and about command features like
  108. // interactive/non-interactive.
  109. if (ktpd->exec) {
  110. faux_msg_t *ack = NULL;
  111. ktp_status_e status = KTP_STATUS_INCOMPLETED;
  112. ack = ktp_msg_preform(cmd, status);
  113. faux_msg_send_async(ack, ktpd->async);
  114. faux_msg_free(ack);
  115. faux_error_free(error);
  116. return BOOL_TRUE; // Continue and wait for ACTION
  117. }
  118. // Session status can be changed while parsing
  119. if (ksession_done(ktpd->session)) {
  120. ktp_send_error(ktpd->async, cmd, "Interrupted by system");
  121. faux_error_free(error);
  122. return BOOL_FALSE;
  123. }
  124. if (rc) {
  125. uint8_t retcode8bit = 0;
  126. faux_msg_t *ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  127. retcode8bit = (uint8_t)(retcode & 0xff);
  128. faux_msg_add_param(ack, KTP_PARAM_RETCODE, &retcode8bit, 1);
  129. faux_msg_send_async(ack, ktpd->async);
  130. faux_msg_free(ack);
  131. } else {
  132. char *err = faux_error_cstr(error);
  133. ktp_send_error(ktpd->async, cmd, err);
  134. faux_str_free(err);
  135. return BOOL_FALSE;
  136. }
  137. faux_error_free(error);
  138. return BOOL_TRUE;
  139. }
  140. static bool_t ktpd_session_process_completion(ktpd_session_t *ktpd, faux_msg_t *msg)
  141. {
  142. char *line = NULL;
  143. faux_msg_t *ack = NULL;
  144. kpargv_t *pargv = NULL;
  145. ktp_cmd_e cmd = KTP_COMPLETION_ACK;
  146. assert(ktpd);
  147. assert(msg);
  148. // Get line from message
  149. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  150. ktp_send_error(ktpd->async, cmd, NULL);
  151. return BOOL_FALSE;
  152. }
  153. // Parsing
  154. pargv = ksession_parse_for_completion(ktpd->session, line);
  155. faux_str_free(line);
  156. if (!pargv) {
  157. ktp_send_error(ktpd->async, cmd, NULL);
  158. return BOOL_FALSE;
  159. }
  160. kpargv_debug(pargv);
  161. kpargv_free(pargv);
  162. // Send ACK message
  163. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  164. faux_msg_send_async(ack, ktpd->async);
  165. faux_msg_free(ack);
  166. return BOOL_TRUE;
  167. }
  168. static bool_t ktpd_session_process_help(ktpd_session_t *ktpd, faux_msg_t *msg)
  169. {
  170. char *line = NULL;
  171. faux_msg_t *ack = NULL;
  172. // kpargv_t *pargv = NULL;
  173. ktp_cmd_e cmd = KTP_HELP_ACK;
  174. assert(ktpd);
  175. assert(msg);
  176. // Get line from message
  177. if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_LINE))) {
  178. ktp_send_error(ktpd->async, cmd, NULL);
  179. return BOOL_FALSE;
  180. }
  181. /* // Parsing
  182. pargv = ksession_parse_line(ktpd->session, line, KPURPOSE_HELP);
  183. faux_str_free(line);
  184. kpargv_free(pargv);
  185. */
  186. // Send ACK message
  187. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  188. faux_msg_send_async(ack, ktpd->async);
  189. faux_msg_free(ack);
  190. return BOOL_TRUE;
  191. }
  192. static bool_t ktpd_session_dispatch(ktpd_session_t *ktpd, faux_msg_t *msg)
  193. {
  194. uint16_t cmd = 0;
  195. assert(ktpd);
  196. if (!ktpd)
  197. return BOOL_FALSE;
  198. assert(msg);
  199. if (!msg)
  200. return BOOL_FALSE;
  201. cmd = faux_msg_get_cmd(msg);
  202. switch (cmd) {
  203. case KTP_CMD:
  204. ktpd_session_process_cmd(ktpd, msg);
  205. break;
  206. case KTP_COMPLETION:
  207. ktpd_session_process_completion(ktpd, msg);
  208. break;
  209. case KTP_HELP:
  210. ktpd_session_process_help(ktpd, msg);
  211. break;
  212. default:
  213. syslog(LOG_WARNING, "Unsupported command: 0x%04u\n", cmd);
  214. break;
  215. }
  216. return BOOL_TRUE;
  217. }
  218. /** @brief Low-level function to receive KTP message.
  219. *
  220. * Firstly function gets the header of message. Then it checks and parses
  221. * header and find out the length of whole message. Then it receives the rest
  222. * of message.
  223. */
  224. static bool_t ktpd_session_read_cb(faux_async_t *async,
  225. faux_buf_t *buf, size_t len, void *user_data)
  226. {
  227. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  228. faux_msg_t *completed_msg = NULL;
  229. char *data = NULL;
  230. assert(async);
  231. assert(buf);
  232. assert(ktpd);
  233. // Linearize buffer
  234. data = malloc(len);
  235. faux_buf_read(buf, data, len);
  236. // Receive header
  237. if (!ktpd->hdr) {
  238. size_t whole_len = 0;
  239. size_t msg_wo_hdr = 0;
  240. ktpd->hdr = (faux_hdr_t *)data;
  241. // Check for broken header
  242. if (!ktp_check_header(ktpd->hdr)) {
  243. faux_free(ktpd->hdr);
  244. ktpd->hdr = NULL;
  245. return BOOL_FALSE;
  246. }
  247. whole_len = faux_hdr_len(ktpd->hdr);
  248. // msg_wo_hdr >= 0 because ktp_check_header() validates whole_len
  249. msg_wo_hdr = whole_len - sizeof(faux_hdr_t);
  250. // Plan to receive message body
  251. if (msg_wo_hdr > 0) {
  252. faux_async_set_read_limits(async,
  253. msg_wo_hdr, msg_wo_hdr);
  254. return BOOL_TRUE;
  255. }
  256. // Here message is completed (msg body has zero length)
  257. completed_msg = faux_msg_deserialize_parts(ktpd->hdr, NULL, 0);
  258. // Receive message body
  259. } else {
  260. completed_msg = faux_msg_deserialize_parts(ktpd->hdr, data, len);
  261. faux_free(data);
  262. }
  263. // Plan to receive msg header
  264. faux_async_set_read_limits(ktpd->async,
  265. sizeof(faux_hdr_t), sizeof(faux_hdr_t));
  266. faux_free(ktpd->hdr);
  267. ktpd->hdr = NULL; // Ready to recv new header
  268. // Here message is completed
  269. ktpd_session_dispatch(ktpd, completed_msg);
  270. faux_msg_free(completed_msg);
  271. // Session status can be changed while parsing
  272. if (ksession_done(ktpd->session))
  273. return BOOL_FALSE;
  274. return BOOL_TRUE;
  275. }
  276. bool_t ktpd_session_connected(ktpd_session_t *ktpd)
  277. {
  278. assert(ktpd);
  279. if (!ktpd)
  280. return BOOL_FALSE;
  281. if (KTPD_SESSION_STATE_DISCONNECTED == ktpd->state)
  282. return BOOL_FALSE;
  283. return BOOL_TRUE;
  284. }
  285. int ktpd_session_fd(const ktpd_session_t *ktpd)
  286. {
  287. assert(ktpd);
  288. if (!ktpd)
  289. return BOOL_FALSE;
  290. return faux_async_fd(ktpd->async);
  291. }
  292. static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  293. void *associated_data, void *user_data)
  294. {
  295. int wstatus = 0;
  296. pid_t child_pid = -1;
  297. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  298. int retcode = -1;
  299. uint8_t retcode8bit = 0;
  300. faux_msg_t *ack = NULL;
  301. ktp_cmd_e cmd = KTP_CMD_ACK;
  302. if (!ktpd)
  303. return BOOL_FALSE;
  304. // Wait for any child process. Doesn't block.
  305. while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
  306. if (ktpd->exec)
  307. kexec_continue_command_execution(ktpd->exec, child_pid,
  308. wstatus);
  309. }
  310. if (!ktpd->exec)
  311. return BOOL_TRUE;
  312. // Check if kexec is done now
  313. if (!kexec_retcode(ktpd->exec, &retcode))
  314. return BOOL_TRUE; // Continue
  315. faux_eloop_del_fd(eloop, kexec_stdout(ktpd->exec));
  316. kexec_free(ktpd->exec);
  317. ktpd->exec = NULL;
  318. ktpd->state = KTPD_SESSION_STATE_IDLE;
  319. // Send ACK message
  320. ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
  321. retcode8bit = (uint8_t)(retcode & 0xff);
  322. faux_msg_add_param(ack, KTP_PARAM_RETCODE, &retcode8bit, 1);
  323. faux_msg_send_async(ack, ktpd->async);
  324. faux_msg_free(ack);
  325. type = type; // Happy compiler
  326. associated_data = associated_data; // Happy compiler
  327. return BOOL_TRUE;
  328. }
  329. static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  330. void *associated_data, void *user_data)
  331. {
  332. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  333. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  334. ssize_t r = -1;
  335. faux_buf_t *faux_buf = NULL;
  336. char *buf = NULL;
  337. ssize_t len = 0;
  338. faux_msg_t *ack = NULL;
  339. if (!ktpd)
  340. return BOOL_TRUE;
  341. if (!ktpd->exec)
  342. return BOOL_TRUE;
  343. faux_buf = kexec_bufout(ktpd->exec);
  344. assert(faux_buf);
  345. do {
  346. void *linear_buf = NULL;
  347. ssize_t really_readed = 0;
  348. ssize_t linear_len =
  349. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  350. // Non-blocked read. The fd became non-blocked while
  351. // kexec_prepare().
  352. r = read(info->fd, linear_buf, linear_len);
  353. if (r > 0)
  354. really_readed = r;
  355. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  356. } while (r > 0);
  357. len = faux_buf_len(faux_buf);
  358. if (0 == len)
  359. return BOOL_TRUE;
  360. buf = malloc(len);
  361. faux_buf_read(faux_buf, buf, len);
  362. // Create KTP_STDOUT message to send to client
  363. ack = ktp_msg_preform(KTP_STDOUT, KTP_STATUS_NONE);
  364. faux_msg_add_param(ack, KTP_PARAM_LINE, buf, len);
  365. faux_msg_send_async(ack, ktpd->async);
  366. faux_msg_free(ack);
  367. free(buf);
  368. // Happy compiler
  369. eloop = eloop;
  370. type = type;
  371. return BOOL_TRUE;
  372. }
  373. static bool_t action_stderr_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  374. void *associated_data, void *user_data)
  375. {
  376. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  377. ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
  378. ssize_t r = -1;
  379. faux_buf_t *faux_buf = NULL;
  380. char *buf = NULL;
  381. ssize_t len = 0;
  382. faux_msg_t *ack = NULL;
  383. if (!ktpd)
  384. return BOOL_TRUE;
  385. if (!ktpd->exec)
  386. return BOOL_TRUE;
  387. faux_buf = kexec_buferr(ktpd->exec);
  388. assert(faux_buf);
  389. do {
  390. void *linear_buf = NULL;
  391. ssize_t really_readed = 0;
  392. ssize_t linear_len =
  393. faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
  394. // Non-blocked read. The fd became non-blocked while
  395. // kexec_prepare().
  396. r = read(info->fd, linear_buf, linear_len);
  397. if (r > 0)
  398. really_readed = r;
  399. faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
  400. } while (r > 0);
  401. len = faux_buf_len(faux_buf);
  402. if (0 == len)
  403. return BOOL_TRUE;
  404. buf = malloc(len);
  405. faux_buf_read(faux_buf, buf, len);
  406. // Create KTP_STDERR message to send to client
  407. ack = ktp_msg_preform(KTP_STDERR, KTP_STATUS_NONE);
  408. faux_msg_add_param(ack, KTP_PARAM_LINE, buf, len);
  409. faux_msg_send_async(ack, ktpd->async);
  410. faux_msg_free(ack);
  411. free(buf);
  412. // Happy compiler
  413. eloop = eloop;
  414. type = type;
  415. return BOOL_TRUE;
  416. }
  417. static bool_t ktpd_session_exec(ktpd_session_t *ktpd, const char *line,
  418. int *retcode, faux_error_t *error)
  419. {
  420. kexec_t *exec = NULL;
  421. assert(ktpd);
  422. if (!ktpd)
  423. return BOOL_FALSE;
  424. // Parsing
  425. exec = ksession_parse_for_exec(ktpd->session, line, error);
  426. if (!exec)
  427. return BOOL_FALSE;
  428. // Session status can be changed while parsing
  429. if (ksession_done(ktpd->session)) {
  430. kexec_free(exec);
  431. return BOOL_FALSE; // Because action is not completed
  432. }
  433. // Execute kexec and then wait for completion using global Eloop
  434. if (!kexec_exec(exec)) {
  435. kexec_free(exec);
  436. return BOOL_FALSE; // Something went wrong
  437. }
  438. // If kexec contains only non-exec (for example dry-run) ACTIONs then
  439. // we don't need event loop and can return here.
  440. if (kexec_retcode(exec, retcode)) {
  441. kexec_free(exec);
  442. return BOOL_TRUE;
  443. }
  444. // Save kexec pointer to use later
  445. ktpd->state = KTPD_SESSION_STATE_WAIT_FOR_PROCESS;
  446. ktpd->exec = exec;
  447. faux_eloop_add_fd(ktpd->eloop, kexec_stdout(exec), POLLIN,
  448. action_stdout_ev, ktpd);
  449. faux_eloop_add_fd(ktpd->eloop, kexec_stderr(exec), POLLIN,
  450. action_stderr_ev, ktpd);
  451. return BOOL_TRUE;
  452. }
  453. #if 0
  454. static void ktpd_session_bad_socket(ktpd_session_t *ktpd)
  455. {
  456. assert(ktpd);
  457. if (!ktpd)
  458. return;
  459. ktpd->state = KTPD_SESSION_STATE_DISCONNECTED;
  460. }
  461. #endif