klish.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. #include <stdlib.h>
  2. #include <stdint.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <assert.h>
  6. #include <unistd.h>
  7. #include <errno.h>
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <fcntl.h>
  11. #include <getopt.h>
  12. #include <sys/socket.h>
  13. #include <sys/un.h>
  14. #include <syslog.h>
  15. #include <sys/wait.h>
  16. #include <errno.h>
  17. #ifdef HAVE_LOCALE_H
  18. #include <locale.h>
  19. #endif
  20. #ifdef HAVE_LANGINFO_CODESET
  21. #include <langinfo.h>
  22. #endif
  23. #include <faux/faux.h>
  24. #include <faux/str.h>
  25. #include <faux/msg.h>
  26. #include <faux/list.h>
  27. #include <faux/file.h>
  28. #include <faux/eloop.h>
  29. #include <klish/ktp.h>
  30. #include <klish/ktp_session.h>
  31. #include <tinyrl/tinyrl.h>
  32. #include "private.h"
  33. // Client mode
  34. typedef enum {
  35. MODE_CMDLINE,
  36. MODE_FILES,
  37. MODE_STDIN,
  38. MODE_INTERACTIVE
  39. } client_mode_e;
  40. // Context for main loop
  41. typedef struct ctx_s {
  42. ktp_session_t *ktp;
  43. tinyrl_t *tinyrl;
  44. struct options *opts;
  45. char *hotkeys[VT100_HOTKEY_MAP_LEN]; // MODE_INTERACTIVE
  46. // pager_working flag values:
  47. // TRI_UNDEFINED - Not started yet or not necessary
  48. // TRI_TRUE - Pager is working
  49. // TRI_FALSE - Can't start pager or pager has exited
  50. tri_t pager_working;
  51. FILE *pager_pipe;
  52. client_mode_e mode;
  53. // Parsing state vars
  54. faux_list_node_t *cmdline_iter; // MODE_CMDLINE
  55. faux_list_node_t *files_iter; // MODE_FILES
  56. faux_file_t *files_fd; // MODE_FILES
  57. faux_file_t *stdin_fd; // MODE_STDIN
  58. } ctx_t;
  59. // KTP session static functions
  60. static bool_t async_stdin_sent_cb(ktp_session_t *ktp, size_t len,
  61. void *user_data);
  62. static bool_t stdout_cb(ktp_session_t *ktp, const char *line, size_t len,
  63. void *user_data);
  64. static bool_t stderr_cb(ktp_session_t *ktp, const char *line, size_t len,
  65. void *user_data);
  66. static bool_t auth_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
  67. static bool_t cmd_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
  68. static bool_t cmd_incompleted_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
  69. static bool_t completion_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
  70. static bool_t help_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
  71. static bool_t notification_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
  72. // Eloop callbacks
  73. //static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  74. // void *associated_data, void *user_data);
  75. static bool_t stdin_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
  76. void *associated_data, void *user_data);
  77. static bool_t sigwinch_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
  78. void *associated_data, void *user_data);
  79. static bool_t ctrl_c_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
  80. void *associated_data, void *user_data);
  81. // Service functions
  82. static void reset_hotkey_table(ctx_t *ctx);
  83. static bool_t send_winch_notification(ctx_t *ctx);
  84. static bool_t send_next_command(ctx_t *ctx);
  85. static void signal_handler_empty(int signo);
  86. // Keys
  87. static bool_t tinyrl_key_enter(tinyrl_t *tinyrl, unsigned char key);
  88. static bool_t tinyrl_key_tab(tinyrl_t *tinyrl, unsigned char key);
  89. static bool_t tinyrl_key_help(tinyrl_t *tinyrl, unsigned char key);
  90. static bool_t tinyrl_key_hotkey(tinyrl_t *tinyrl, unsigned char key);
  91. int main(int argc, char **argv)
  92. {
  93. int retval = -1;
  94. struct options *opts = NULL;
  95. int unix_sock = -1;
  96. ktp_session_t *ktp = NULL;
  97. int retcode = 0;
  98. faux_eloop_t *eloop = NULL;
  99. ctx_t ctx = {};
  100. tinyrl_t *tinyrl = NULL;
  101. char *hist_path = NULL;
  102. int stdin_flags = 0;
  103. struct sigaction sig_act = {};
  104. sigset_t sig_set = {};
  105. #ifdef HAVE_LOCALE_H
  106. // Set current locale
  107. setlocale(LC_ALL, "");
  108. #endif
  109. // Parse command line options
  110. opts = opts_init();
  111. if (opts_parse(argc, argv, opts)) {
  112. fprintf(stderr, "Error: Can't parse command line options\n");
  113. goto err;
  114. }
  115. // Parse config file
  116. if (!access(opts->cfgfile, R_OK)) {
  117. if (!config_parse(opts->cfgfile, opts))
  118. goto err;
  119. } else if (opts->cfgfile_userdefined) {
  120. // User defined config must be found
  121. fprintf(stderr, "Error: Can't find config file %s\n",
  122. opts->cfgfile);
  123. goto err;
  124. }
  125. // Init context
  126. faux_bzero(&ctx, sizeof(ctx));
  127. // Find out client mode
  128. ctx.mode = MODE_INTERACTIVE; // Default
  129. if (faux_list_len(opts->commands) > 0)
  130. ctx.mode = MODE_CMDLINE;
  131. else if (faux_list_len(opts->files) > 0)
  132. ctx.mode = MODE_FILES;
  133. else if (!isatty(STDIN_FILENO))
  134. ctx.mode = MODE_STDIN;
  135. // Connect to server
  136. unix_sock = ktp_connect_unix(opts->unix_socket_path);
  137. if (unix_sock < 0) {
  138. fprintf(stderr, "Error: Can't connect to server\n");
  139. goto err;
  140. }
  141. // Eloop object
  142. eloop = faux_eloop_new(NULL);
  143. // Handlers are used to send SIGINT
  144. // to non-interactive commands
  145. faux_eloop_add_signal(eloop, SIGINT, ctrl_c_cb, &ctx);
  146. faux_eloop_add_signal(eloop, SIGTERM, ctrl_c_cb, &ctx);
  147. faux_eloop_add_signal(eloop, SIGQUIT, ctrl_c_cb, &ctx);
  148. // To don't stop klish client on exit. SIGTSTP can be pended
  149. faux_eloop_add_signal(eloop, SIGTSTP, ctrl_c_cb, &ctx);
  150. // Notify server about terminal window size change
  151. faux_eloop_add_signal(eloop, SIGWINCH, sigwinch_cb, &ctx);
  152. // Ignore SIGINT etc. Don't use SIG_IGN because it will
  153. // not interrupt syscall. It necessary because in MODE_STDIN
  154. // there is a blocking read and eloop doesn't process signals
  155. // while blocking read
  156. sigemptyset(&sig_set);
  157. sig_act.sa_flags = 0;
  158. sig_act.sa_mask = sig_set;
  159. sig_act.sa_handler = &signal_handler_empty;
  160. sigaction(SIGINT, &sig_act, NULL);
  161. sigaction(SIGTERM, &sig_act, NULL);
  162. sigaction(SIGQUIT, &sig_act, NULL);
  163. // Ignore SIGPIPE from server. Don't use SIG_IGN because it will not
  164. // break syscall
  165. sigaction(SIGPIPE, &sig_act, NULL);
  166. // KTP session
  167. ktp = ktp_session_new(unix_sock, eloop);
  168. assert(ktp);
  169. if (!ktp) {
  170. fprintf(stderr, "Error: Can't create klish session\n");
  171. goto err;
  172. }
  173. // Don't stop loop on each answer
  174. ktp_session_set_stop_on_answer(ktp, BOOL_FALSE);
  175. // Set stdin to O_NONBLOCK mode
  176. stdin_flags = fcntl(STDIN_FILENO, F_GETFL, 0);
  177. if (ctx.mode != MODE_STDIN)
  178. fcntl(STDIN_FILENO, F_SETFL, stdin_flags | O_NONBLOCK);
  179. // TiniRL
  180. if (ctx.mode == MODE_INTERACTIVE)
  181. hist_path = faux_expand_tilde("~/.klish_history");
  182. tinyrl = tinyrl_new(stdin, stdout, hist_path, 100);
  183. if (ctx.mode == MODE_INTERACTIVE)
  184. faux_str_free(hist_path);
  185. tinyrl_set_prompt(tinyrl, "$ ");
  186. tinyrl_set_udata(tinyrl, &ctx);
  187. // Populate context
  188. ctx.ktp = ktp;
  189. ctx.tinyrl = tinyrl;
  190. ctx.opts = opts;
  191. ctx.pager_working = TRI_UNDEFINED;
  192. ktp_session_set_cb(ktp, KTP_SESSION_CB_STDIN, async_stdin_sent_cb, &ctx);
  193. ktp_session_set_cb(ktp, KTP_SESSION_CB_STDOUT, stdout_cb, &ctx);
  194. ktp_session_set_cb(ktp, KTP_SESSION_CB_STDERR, stderr_cb, &ctx);
  195. ktp_session_set_cb(ktp, KTP_SESSION_CB_AUTH_ACK, auth_ack_cb, &ctx);
  196. ktp_session_set_cb(ktp, KTP_SESSION_CB_CMD_ACK, cmd_ack_cb, &ctx);
  197. ktp_session_set_cb(ktp, KTP_SESSION_CB_CMD_ACK_INCOMPLETED,
  198. cmd_incompleted_ack_cb, &ctx);
  199. ktp_session_set_cb(ktp, KTP_SESSION_CB_COMPLETION_ACK,
  200. completion_ack_cb, &ctx);
  201. ktp_session_set_cb(ktp, KTP_SESSION_CB_HELP_ACK, help_ack_cb, &ctx);
  202. ktp_session_set_cb(ktp, KTP_SESSION_CB_NOTIFICATION,
  203. notification_cb, &ctx);
  204. // Commands from cmdline
  205. if (ctx.mode == MODE_CMDLINE) {
  206. // "-c" options iterator
  207. ctx.cmdline_iter = faux_list_head(opts->commands);
  208. // Commands from files
  209. } else if (ctx.mode == MODE_FILES) {
  210. // input files iterator
  211. ctx.files_iter = faux_list_head(opts->files);
  212. // Commands from non-interactive STDIN
  213. } else if (ctx.mode == MODE_STDIN) {
  214. // Interactive shell
  215. } else {
  216. // Interactive keys
  217. tinyrl_set_hotkey_fn(tinyrl, tinyrl_key_hotkey);
  218. tinyrl_bind_key(tinyrl, '\n', tinyrl_key_enter);
  219. tinyrl_bind_key(tinyrl, '\r', tinyrl_key_enter);
  220. tinyrl_bind_key(tinyrl, '\t', tinyrl_key_tab);
  221. tinyrl_bind_key(tinyrl, '?', tinyrl_key_help);
  222. }
  223. // Send AUTH message to server
  224. if (!ktp_session_auth(ktp, NULL))
  225. goto err;
  226. // Main loop
  227. faux_eloop_loop(eloop);
  228. if (ctx.mode != MODE_INTERACTIVE)
  229. ktp_session_retcode(ktp, &retcode);
  230. retval = 0;
  231. err:
  232. // Restore stdin mode
  233. fcntl(STDIN_FILENO, F_SETFL, stdin_flags);
  234. reset_hotkey_table(&ctx);
  235. if (tinyrl) {
  236. if (tinyrl_busy(tinyrl))
  237. faux_error_free(ktp_session_error(ktp));
  238. tinyrl_free(tinyrl);
  239. }
  240. ktp_session_free(ktp);
  241. faux_eloop_free(eloop);
  242. ktp_disconnect(unix_sock);
  243. opts_free(opts);
  244. if ((retval < 0) || (retcode != 0))
  245. return -1;
  246. return 0;
  247. }
  248. static bool_t send_next_command(ctx_t *ctx)
  249. {
  250. char *line = NULL;
  251. faux_error_t *error = NULL;
  252. bool_t rc = BOOL_FALSE;
  253. // User must type next interactive command. So just return
  254. if (ctx->mode == MODE_INTERACTIVE)
  255. return BOOL_TRUE;
  256. // Commands from cmdline
  257. if (ctx->mode == MODE_CMDLINE) {
  258. line = faux_str_dup(faux_list_each(&ctx->cmdline_iter));
  259. // Commands from input files
  260. } else if (ctx->mode == MODE_FILES) {
  261. do {
  262. if (!ctx->files_fd) {
  263. const char *fn = (const char *)faux_list_each(&ctx->files_iter);
  264. if (!fn)
  265. break; // No more files
  266. ctx->files_fd = faux_file_open(fn, O_RDONLY, 0);
  267. }
  268. if (!ctx->files_fd) // Can't open file. Try next file
  269. continue;
  270. line = faux_file_getline(ctx->files_fd);
  271. if (!line) { // EOF
  272. faux_file_close(ctx->files_fd);
  273. ctx->files_fd = NULL;
  274. }
  275. } while (!line);
  276. // Commands from stdin
  277. } else if (ctx->mode == MODE_STDIN) {
  278. if (!ctx->stdin_fd)
  279. ctx->stdin_fd = faux_file_fdopen(STDIN_FILENO);
  280. if (ctx->stdin_fd)
  281. line = faux_file_getline(ctx->stdin_fd);
  282. if (!line) // EOF
  283. faux_file_close(ctx->stdin_fd);
  284. }
  285. if (!line) {
  286. ktp_session_set_done(ctx->ktp, BOOL_TRUE);
  287. return BOOL_TRUE;
  288. }
  289. if (ctx->opts->verbose) {
  290. const char *prompt = tinyrl_prompt(ctx->tinyrl);
  291. printf("%s%s\n", prompt ? prompt : "", line);
  292. fflush(stdout);
  293. }
  294. error = faux_error_new();
  295. rc = ktp_session_cmd(ctx->ktp, line, error, ctx->opts->dry_run);
  296. faux_str_free(line);
  297. if (!rc) {
  298. faux_error_free(error);
  299. return BOOL_FALSE;
  300. }
  301. // Suppose non-interactive command by default
  302. tinyrl_enable_isig(ctx->tinyrl);
  303. return BOOL_TRUE;
  304. }
  305. static bool_t stderr_cb(ktp_session_t *ktp, const char *line, size_t len,
  306. void *user_data)
  307. {
  308. if (faux_write_block(STDERR_FILENO, line, len) < 0)
  309. return BOOL_FALSE;
  310. ktp = ktp;
  311. user_data = user_data;
  312. return BOOL_TRUE;
  313. }
  314. static bool_t process_prompt_param(tinyrl_t *tinyrl, const faux_msg_t *msg)
  315. {
  316. char *prompt = NULL;
  317. if (!tinyrl)
  318. return BOOL_FALSE;
  319. if (!msg)
  320. return BOOL_FALSE;
  321. prompt = faux_msg_get_str_param_by_type(msg, KTP_PARAM_PROMPT);
  322. if (prompt) {
  323. tinyrl_set_prompt(tinyrl, prompt);
  324. faux_str_free(prompt);
  325. }
  326. return BOOL_TRUE;
  327. }
  328. static void reset_hotkey_table(ctx_t *ctx)
  329. {
  330. size_t i = 0;
  331. assert(ctx);
  332. for (i = 0; i < VT100_HOTKEY_MAP_LEN; i++)
  333. faux_str_free(ctx->hotkeys[i]);
  334. faux_bzero(ctx->hotkeys, sizeof(ctx->hotkeys));
  335. }
  336. static bool_t process_hotkey_param(ctx_t *ctx, const faux_msg_t *msg)
  337. {
  338. faux_list_node_t *iter = NULL;
  339. uint32_t param_len = 0;
  340. char *param_data = NULL;
  341. uint16_t param_type = 0;
  342. if (!ctx)
  343. return BOOL_FALSE;
  344. if (!msg)
  345. return BOOL_FALSE;
  346. if (!faux_msg_get_param_by_type(msg, KTP_PARAM_HOTKEY,
  347. (void **)&param_data, &param_len))
  348. return BOOL_TRUE;
  349. // If there is HOTKEY parameter then reinitialize whole hotkey table
  350. reset_hotkey_table(ctx);
  351. iter = faux_msg_init_param_iter(msg);
  352. while (faux_msg_get_param_each(
  353. &iter, &param_type, (void **)&param_data, &param_len)) {
  354. char *cmd = NULL;
  355. ssize_t code = -1;
  356. size_t key_len = 0;
  357. if (param_len < 3) // <key>'\0'<cmd>
  358. continue;
  359. if (KTP_PARAM_HOTKEY != param_type)
  360. continue;
  361. key_len = strlen(param_data); // Length of <key>
  362. if (key_len < 1)
  363. continue;
  364. code = vt100_hotkey_decode(param_data);
  365. if ((code < 0) || (code > VT100_HOTKEY_MAP_LEN))
  366. continue;
  367. cmd = faux_str_dupn(param_data + key_len + 1,
  368. param_len - key_len - 1);
  369. if (!cmd)
  370. continue;
  371. ctx->hotkeys[code] = cmd;
  372. }
  373. return BOOL_TRUE;
  374. }
  375. bool_t auth_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
  376. {
  377. ctx_t *ctx = (ctx_t *)udata;
  378. int rc = -1;
  379. faux_error_t *error = NULL;
  380. process_prompt_param(ctx->tinyrl, msg);
  381. process_hotkey_param(ctx, msg);
  382. if (!ktp_session_retcode(ktp, &rc))
  383. rc = -1;
  384. error = ktp_session_error(ktp);
  385. if ((rc < 0) && (faux_error_len(error) > 0)) {
  386. faux_error_node_t *err_iter = faux_error_iter(error);
  387. const char *err = NULL;
  388. while ((err = faux_error_each(&err_iter)))
  389. fprintf(stderr, "Error: auth: %s\n", err);
  390. return BOOL_FALSE;
  391. }
  392. send_winch_notification(ctx);
  393. if (ctx->mode == MODE_INTERACTIVE) {
  394. // Start getting stdin
  395. faux_eloop_add_fd(ktp_session_eloop(ktp), STDIN_FILENO, POLLIN,
  396. stdin_cb, ctx);
  397. // Print prompt for interactive command
  398. tinyrl_redisplay(ctx->tinyrl);
  399. } else {
  400. // Send first command for non-interactive modes
  401. send_next_command(ctx);
  402. }
  403. // Happy compiler
  404. msg = msg;
  405. return BOOL_TRUE;
  406. }
  407. bool_t cmd_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
  408. {
  409. ctx_t *ctx = (ctx_t *)udata;
  410. int rc = -1;
  411. faux_error_t *error = NULL;
  412. bool_t it_was_pager = BOOL_FALSE;
  413. // Wait for pager
  414. if (ctx->pager_working != TRI_UNDEFINED) {
  415. pclose(ctx->pager_pipe);
  416. ctx->pager_working = TRI_UNDEFINED;
  417. ctx->pager_pipe = NULL;
  418. it_was_pager = BOOL_TRUE;
  419. }
  420. // Set tinyrl native mode for interactive command line
  421. tinyrl_native_mode(ctx->tinyrl);
  422. // Disable SIGINT caught for non-interactive commands.
  423. // Do it after pager exit. Else it can restore wrong tty mode after
  424. // ISIG disabling
  425. tinyrl_disable_isig(ctx->tinyrl);
  426. // Sometimes output stream from server doesn't contain final crlf so
  427. // goto newline itself
  428. if (ktp_session_last_stream(ktp) == STDERR_FILENO) {
  429. if (ktp_session_stderr_need_newline(ktp))
  430. fprintf(stderr, "\n");
  431. } else {
  432. // Pager adds newline itself
  433. if (ktp_session_stdout_need_newline(ktp) && !it_was_pager)
  434. tinyrl_crlf(ctx->tinyrl);
  435. }
  436. process_prompt_param(ctx->tinyrl, msg);
  437. process_hotkey_param(ctx, msg);
  438. if (!ktp_session_retcode(ktp, &rc))
  439. rc = -1;
  440. error = ktp_session_error(ktp);
  441. if (rc != 0) {
  442. if (faux_error_len(error) > 0) {
  443. faux_error_node_t *err_iter = faux_error_iter(error);
  444. const char *err = NULL;
  445. while ((err = faux_error_each(&err_iter)))
  446. fprintf(stderr, "Error: %s\n", err);
  447. }
  448. // Stop-on-error
  449. if (ctx->opts->stop_on_error) {
  450. faux_error_free(error);
  451. ktp_session_set_done(ktp, BOOL_TRUE);
  452. return BOOL_TRUE;
  453. }
  454. }
  455. faux_error_free(error);
  456. if (ctx->mode == MODE_INTERACTIVE) {
  457. tinyrl_set_busy(ctx->tinyrl, BOOL_FALSE);
  458. if (!ktp_session_done(ktp))
  459. tinyrl_redisplay(ctx->tinyrl);
  460. // Operation is finished so restore stdin handler
  461. faux_eloop_add_fd(ktp_session_eloop(ktp), STDIN_FILENO, POLLIN,
  462. stdin_cb, ctx);
  463. }
  464. // Send next command for non-interactive modes
  465. send_next_command(ctx);
  466. // Happy compiler
  467. msg = msg;
  468. return BOOL_TRUE;
  469. }
  470. bool_t cmd_incompleted_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
  471. {
  472. ctx_t *ctx = (ctx_t *)udata;
  473. // It can't get data from stdin, it gets commands from stdin instead.
  474. // So don't process incompleted ack but just return
  475. if (ctx->mode == MODE_STDIN)
  476. return BOOL_TRUE;
  477. if (ktp_session_state(ktp) == KTP_SESSION_STATE_WAIT_FOR_CMD) {
  478. // Make raw terminal for commands that need terminal as output
  479. if (KTP_STATUS_IS_INTERACTIVE(ktp_session_cmd_features(ktp)))
  480. tinyrl_raw_mode(ctx->tinyrl);
  481. // Cmd need stdin so restore stdin handler
  482. if (KTP_STATUS_IS_NEED_STDIN(ktp_session_cmd_features(ktp))) {
  483. // Disable SIGINT signal (it is used for commands that
  484. // don't need stdin. Commands with stdin can get ^C
  485. // themself interactively.)
  486. tinyrl_disable_isig(ctx->tinyrl);
  487. faux_eloop_add_fd(ktp_session_eloop(ktp), STDIN_FILENO, POLLIN,
  488. stdin_cb, ctx);
  489. } else {
  490. // Raw mode setting can disable ISIG internally.
  491. // So restore it
  492. tinyrl_enable_isig(ctx->tinyrl);
  493. }
  494. }
  495. // Happy compiler
  496. msg = msg;
  497. return BOOL_TRUE;
  498. }
  499. static bool_t stdin_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
  500. void *associated_data, void *udata)
  501. {
  502. bool_t rc = BOOL_TRUE;
  503. ctx_t *ctx = (ctx_t *)udata;
  504. ktp_session_state_e state = KTP_SESSION_STATE_ERROR;
  505. faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
  506. bool_t close_stdin = BOOL_FALSE;
  507. size_t obuf_len = 0;
  508. if (!ctx)
  509. return BOOL_FALSE;
  510. // Some errors or fd is closed so stop interactive session
  511. // Non-interactive session just removes stdin callback
  512. if (info->revents & (POLLHUP | POLLERR | POLLNVAL))
  513. close_stdin = BOOL_TRUE;
  514. // Temporarily stop stdin reading because too much data is buffered
  515. // and all data can't be sent to server yet
  516. obuf_len = faux_buf_len(faux_async_obuf(ktp_session_async(ctx->ktp)));
  517. if (obuf_len > OBUF_LIMIT) {
  518. faux_eloop_del_fd(eloop, STDIN_FILENO);
  519. return BOOL_TRUE;
  520. }
  521. state = ktp_session_state(ctx->ktp);
  522. // Standard klish command line
  523. if ((state == KTP_SESSION_STATE_IDLE) &&
  524. (ctx->mode == MODE_INTERACTIVE)) {
  525. tinyrl_read(ctx->tinyrl);
  526. if (close_stdin) {
  527. faux_eloop_del_fd(eloop, STDIN_FILENO);
  528. rc = BOOL_FALSE;
  529. }
  530. // Command needs stdin
  531. } else if ((state == KTP_SESSION_STATE_WAIT_FOR_CMD) &&
  532. KTP_STATUS_IS_NEED_STDIN(ktp_session_cmd_features(ctx->ktp))) {
  533. int fd = fileno(tinyrl_istream(ctx->tinyrl));
  534. char buf[1024] = {};
  535. ssize_t bytes_readed = 0;
  536. // Don't read all data from stdin to don't overfill out buffer.
  537. // Allow another handlers to push already received data to
  538. // server
  539. if ((bytes_readed = read(fd, buf, sizeof(buf))) > 0)
  540. ktp_session_stdin(ctx->ktp, buf, bytes_readed);
  541. // Actually close stdin only when all data is read
  542. if (close_stdin && (bytes_readed <= 0)) {
  543. ktp_session_stdin_close(ctx->ktp);
  544. faux_eloop_del_fd(eloop, STDIN_FILENO);
  545. }
  546. // Input is not needed
  547. } else {
  548. // Here the situation when input is not allowed. Remove stdin from
  549. // eloop waiting list. Else klish will get 100% CPU. Callbacks on
  550. // operation completions will restore this handler.
  551. faux_eloop_del_fd(eloop, STDIN_FILENO);
  552. }
  553. // Happy compiler
  554. type = type;
  555. return rc;
  556. }
  557. static bool_t async_stdin_sent_cb(ktp_session_t *ktp, size_t len,
  558. void *user_data)
  559. {
  560. ctx_t *ctx = (ctx_t *)user_data;
  561. assert(ktp);
  562. // This callbacks is executed when any number of bytes is really written
  563. // to server socket. So if stdin transmit was stopped due to obuf
  564. // overflow it's time to rearm transmission
  565. faux_eloop_add_fd(ktp_session_eloop(ktp), STDIN_FILENO, POLLIN,
  566. stdin_cb, ctx);
  567. len = len; // Happy compiler
  568. return BOOL_TRUE;
  569. }
  570. static bool_t send_winch_notification(ctx_t *ctx)
  571. {
  572. size_t width = 0;
  573. size_t height = 0;
  574. char *winsize = NULL;
  575. faux_msg_t *req = NULL;
  576. ktp_status_e status = KTP_STATUS_NONE;
  577. if (!ctx->tinyrl)
  578. return BOOL_FALSE;
  579. if (!ctx->ktp)
  580. return BOOL_FALSE;
  581. tinyrl_winsize(ctx->tinyrl, &width, &height);
  582. winsize = faux_str_sprintf("%lu %lu", width, height);
  583. req = ktp_msg_preform(KTP_NOTIFICATION, status);
  584. faux_msg_add_param(req, KTP_PARAM_WINCH, winsize, strlen(winsize));
  585. faux_str_free(winsize);
  586. faux_msg_send_async(req, ktp_session_async(ctx->ktp));
  587. faux_msg_free(req);
  588. return BOOL_TRUE;
  589. }
  590. static bool_t sigwinch_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
  591. void *associated_data, void *udata)
  592. {
  593. ctx_t *ctx = (ctx_t *)udata;
  594. if (!ctx)
  595. return BOOL_FALSE;
  596. send_winch_notification(ctx);
  597. // Happy compiler
  598. eloop = eloop;
  599. type = type;
  600. associated_data = associated_data;
  601. return BOOL_TRUE;
  602. }
  603. static bool_t ctrl_c_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
  604. void *associated_data, void *udata)
  605. {
  606. ctx_t *ctx = (ctx_t *)udata;
  607. char ctrl_c = KEY_ETX;
  608. ktp_session_state_e state = KTP_SESSION_STATE_ERROR;
  609. if (!ctx)
  610. return BOOL_FALSE;
  611. if (ctx->mode != MODE_INTERACTIVE)
  612. return BOOL_FALSE;
  613. state = ktp_session_state(ctx->ktp);
  614. if (state == KTP_SESSION_STATE_WAIT_FOR_CMD)
  615. ktp_session_stdin(ctx->ktp, &ctrl_c, sizeof(ctrl_c));
  616. // Happy compiler
  617. eloop = eloop;
  618. type = type;
  619. associated_data = associated_data;
  620. return BOOL_TRUE;
  621. }
  622. static bool_t tinyrl_key_enter(tinyrl_t *tinyrl, unsigned char key)
  623. {
  624. const char *line = NULL;
  625. ctx_t *ctx = (ctx_t *)tinyrl_udata(tinyrl);
  626. faux_error_t *error = faux_error_new();
  627. tinyrl_line_to_hist(tinyrl);
  628. tinyrl_multi_crlf(tinyrl);
  629. tinyrl_reset_line_state(tinyrl);
  630. line = tinyrl_line(tinyrl);
  631. ktp_session_cmd(ctx->ktp, line, error, ctx->opts->dry_run);
  632. tinyrl_reset_line(tinyrl);
  633. tinyrl_set_busy(tinyrl, BOOL_TRUE);
  634. // Suppose non-interactive command by default
  635. // Caught SIGINT for non-interactive commands
  636. tinyrl_enable_isig(tinyrl);
  637. key = key; // Happy compiler
  638. return BOOL_TRUE;
  639. }
  640. static bool_t tinyrl_key_hotkey(tinyrl_t *tinyrl, unsigned char key)
  641. {
  642. const char *line = NULL;
  643. ctx_t *ctx = (ctx_t *)tinyrl_udata(tinyrl);
  644. faux_error_t *error = NULL;
  645. if (key >= VT100_HOTKEY_MAP_LEN)
  646. return BOOL_TRUE;
  647. line = ctx->hotkeys[key];
  648. if (faux_str_is_empty(line))
  649. return BOOL_TRUE;
  650. error = faux_error_new();
  651. tinyrl_multi_crlf(tinyrl);
  652. tinyrl_reset_line_state(tinyrl);
  653. tinyrl_reset_line(tinyrl);
  654. ktp_session_cmd(ctx->ktp, line, error, ctx->opts->dry_run);
  655. tinyrl_set_busy(tinyrl, BOOL_TRUE);
  656. return BOOL_TRUE;
  657. }
  658. static bool_t tinyrl_key_tab(tinyrl_t *tinyrl, unsigned char key)
  659. {
  660. char *line = NULL;
  661. ctx_t *ctx = (ctx_t *)tinyrl_udata(tinyrl);
  662. line = tinyrl_line_to_pos(tinyrl);
  663. ktp_session_completion(ctx->ktp, line, ctx->opts->dry_run);
  664. faux_str_free(line);
  665. tinyrl_set_busy(tinyrl, BOOL_TRUE);
  666. key = key; // Happy compiler
  667. return BOOL_TRUE;
  668. }
  669. static bool_t tinyrl_key_help(tinyrl_t *tinyrl, unsigned char key)
  670. {
  671. char *line = NULL;
  672. ctx_t *ctx = (ctx_t *)tinyrl_udata(tinyrl);
  673. line = tinyrl_line_to_pos(tinyrl);
  674. // If "?" is quoted then it's not special hotkey.
  675. // Just insert it into the line.
  676. if (faux_str_unclosed_quotes(line, NULL)) {
  677. faux_str_free(line);
  678. return tinyrl_key_default(tinyrl, key);
  679. }
  680. ktp_session_help(ctx->ktp, line);
  681. faux_str_free(line);
  682. tinyrl_set_busy(tinyrl, BOOL_TRUE);
  683. key = key; // Happy compiler
  684. return BOOL_TRUE;
  685. }
  686. static void display_completions(const tinyrl_t *tinyrl, faux_list_t *completions,
  687. const char *prefix, size_t max)
  688. {
  689. size_t width = tinyrl_width(tinyrl);
  690. size_t cols = 0;
  691. faux_list_node_t *iter = NULL;
  692. faux_list_node_t *node = NULL;
  693. size_t prefix_len = 0;
  694. size_t cols_filled = 0;
  695. if (prefix)
  696. prefix_len = strlen(prefix);
  697. // Find out column and rows number
  698. if (max < width)
  699. cols = (width + 1) / (prefix_len + max + 1); // For a space between words
  700. else
  701. cols = 1;
  702. iter = faux_list_head(completions);
  703. while ((node = faux_list_each_node(&iter))) {
  704. char *compl = (char *)faux_list_data(node);
  705. tinyrl_printf(tinyrl, "%*s%s",
  706. (prefix_len + max + 1 - strlen(compl)),
  707. prefix ? prefix : "",
  708. compl);
  709. cols_filled++;
  710. if ((cols_filled >= cols) || (node == faux_list_tail(completions))) {
  711. cols_filled = 0;
  712. tinyrl_crlf(tinyrl);
  713. }
  714. }
  715. }
  716. bool_t completion_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
  717. {
  718. ctx_t *ctx = (ctx_t *)udata;
  719. faux_list_node_t *iter = NULL;
  720. uint32_t param_len = 0;
  721. char *param_data = NULL;
  722. uint16_t param_type = 0;
  723. char *prefix = NULL;
  724. faux_list_t *completions = NULL;
  725. size_t completions_num = 0;
  726. size_t max_compl_len = 0;
  727. tinyrl_set_busy(ctx->tinyrl, BOOL_FALSE);
  728. process_prompt_param(ctx->tinyrl, msg);
  729. prefix = faux_msg_get_str_param_by_type(msg, KTP_PARAM_PREFIX);
  730. completions = faux_list_new(FAUX_LIST_UNSORTED, FAUX_LIST_NONUNIQUE,
  731. NULL, NULL, (void (*)(void *))faux_str_free);
  732. iter = faux_msg_init_param_iter(msg);
  733. while (faux_msg_get_param_each(&iter, &param_type, (void **)&param_data, &param_len)) {
  734. char *compl = NULL;
  735. if (KTP_PARAM_LINE != param_type)
  736. continue;
  737. compl = faux_str_dupn(param_data, param_len);
  738. faux_list_add(completions, compl);
  739. if (param_len > max_compl_len)
  740. max_compl_len = param_len;
  741. }
  742. completions_num = faux_list_len(completions);
  743. // Single possible completion
  744. if (1 == completions_num) {
  745. char *compl = (char *)faux_list_data(faux_list_head(completions));
  746. tinyrl_line_insert(ctx->tinyrl, compl, strlen(compl));
  747. // Add space after completion
  748. tinyrl_line_insert(ctx->tinyrl, " ", 1);
  749. tinyrl_redisplay(ctx->tinyrl);
  750. // Multi possible completions
  751. } else if (completions_num > 1) {
  752. faux_list_node_t *eq_iter = NULL;
  753. size_t eq_part = 0;
  754. char *str = NULL;
  755. char *compl = NULL;
  756. // Try to find equal part for all possible completions
  757. eq_iter = faux_list_head(completions);
  758. str = (char *)faux_list_data(eq_iter);
  759. eq_part = strlen(str);
  760. eq_iter = faux_list_next_node(eq_iter);
  761. while ((compl = (char *)faux_list_each(&eq_iter)) && (eq_part > 0)) {
  762. size_t cur_eq = 0;
  763. cur_eq = tinyrl_equal_part(ctx->tinyrl, str, compl);
  764. if (cur_eq < eq_part)
  765. eq_part = cur_eq;
  766. }
  767. // The equal part was found
  768. if (eq_part > 0) {
  769. tinyrl_line_insert(ctx->tinyrl, str, eq_part);
  770. tinyrl_redisplay(ctx->tinyrl);
  771. // There is no equal part for all completions
  772. } else {
  773. tinyrl_multi_crlf(ctx->tinyrl);
  774. tinyrl_reset_line_state(ctx->tinyrl);
  775. display_completions(ctx->tinyrl, completions,
  776. prefix, max_compl_len);
  777. tinyrl_redisplay(ctx->tinyrl);
  778. }
  779. }
  780. faux_list_free(completions);
  781. faux_str_free(prefix);
  782. // Operation is finished so restore stdin handler
  783. faux_eloop_add_fd(ktp_session_eloop(ktp), STDIN_FILENO, POLLIN,
  784. stdin_cb, ctx);
  785. // Happy compiler
  786. ktp = ktp;
  787. msg = msg;
  788. return BOOL_TRUE;
  789. }
  790. static void display_help(const tinyrl_t *tinyrl, faux_list_t *help_list,
  791. size_t max)
  792. {
  793. faux_list_node_t *iter = NULL;
  794. faux_list_node_t *node = NULL;
  795. iter = faux_list_head(help_list);
  796. while ((node = faux_list_each_node(&iter))) {
  797. help_t *help = (help_t *)faux_list_data(node);
  798. tinyrl_printf(tinyrl, " %s%*s%s\n",
  799. help->prefix,
  800. (max + 2 - strlen(help->prefix)),
  801. " ",
  802. help->line);
  803. }
  804. }
  805. bool_t help_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
  806. {
  807. ctx_t *ctx = (ctx_t *)udata;
  808. faux_list_t *help_list = NULL;
  809. faux_list_node_t *iter = NULL;
  810. uint32_t param_len = 0;
  811. char *param_data = NULL;
  812. uint16_t param_type = 0;
  813. size_t max_prefix_len = 0;
  814. tinyrl_set_busy(ctx->tinyrl, BOOL_FALSE);
  815. process_prompt_param(ctx->tinyrl, msg);
  816. help_list = faux_list_new(FAUX_LIST_SORTED, FAUX_LIST_UNIQUE,
  817. help_compare, NULL, help_free);
  818. // Wait for PREFIX - LINE pairs
  819. iter = faux_msg_init_param_iter(msg);
  820. while (faux_msg_get_param_each(&iter, &param_type, (void **)&param_data,
  821. &param_len)) {
  822. char *prefix_str = NULL;
  823. char *line_str = NULL;
  824. help_t *help = NULL;
  825. size_t prefix_len = 0;
  826. // Get PREFIX
  827. if (KTP_PARAM_PREFIX != param_type)
  828. continue;
  829. prefix_str = faux_str_dupn(param_data, param_len);
  830. prefix_len = param_len;
  831. // Get LINE
  832. if (!faux_msg_get_param_each(&iter, &param_type,
  833. (void **)&param_data, &param_len) ||
  834. (KTP_PARAM_LINE != param_type)) {
  835. faux_str_free(prefix_str);
  836. break;
  837. }
  838. line_str = faux_str_dupn(param_data, param_len);
  839. help = help_new(prefix_str, line_str);
  840. if (!faux_list_add(help_list, help)) {
  841. help_free(help);
  842. continue;
  843. }
  844. if (prefix_len > max_prefix_len)
  845. max_prefix_len = prefix_len;
  846. }
  847. if (faux_list_len(help_list) > 0) {
  848. tinyrl_multi_crlf(ctx->tinyrl);
  849. tinyrl_reset_line_state(ctx->tinyrl);
  850. display_help(ctx->tinyrl, help_list, max_prefix_len);
  851. tinyrl_redisplay(ctx->tinyrl);
  852. }
  853. faux_list_free(help_list);
  854. // Operation is finished so restore stdin handler
  855. faux_eloop_add_fd(ktp_session_eloop(ktp), STDIN_FILENO, POLLIN,
  856. stdin_cb, ctx);
  857. ktp = ktp; // happy compiler
  858. return BOOL_TRUE;
  859. }
  860. //size_t max_stdout_len = 0;
  861. static bool_t stdout_cb(ktp_session_t *ktp, const char *line, size_t len,
  862. void *udata)
  863. {
  864. ctx_t *ctx = (ctx_t *)udata;
  865. assert(ctx);
  866. //if (len > max_stdout_len) {
  867. //max_stdout_len = len;
  868. //fprintf(stderr, "max_stdout_len=%ld\n", max_stdout_len);
  869. //}
  870. // Start pager if necessary
  871. if (
  872. ctx->opts->pager_enabled && // Pager enabled within config file
  873. (ctx->pager_working == TRI_UNDEFINED) && // Pager is not working
  874. !KTP_STATUS_IS_INTERACTIVE(ktp_session_cmd_features(ktp)) // Non interactive command
  875. ) {
  876. ctx->pager_pipe = popen(ctx->opts->pager, "we");
  877. if (!ctx->pager_pipe)
  878. ctx->pager_working = TRI_FALSE; // Indicates can't start
  879. else
  880. ctx->pager_working = TRI_TRUE;
  881. }
  882. // Write to pager's pipe if pager is really working
  883. // Don't do anything if pager state is TRI_FALSE
  884. if (ctx->pager_working == TRI_TRUE) {
  885. if (faux_write_block(fileno(ctx->pager_pipe), line, len) <= 0) {
  886. // If we can't write to pager pipe then send
  887. // "SIGPIPE" to server. Pager is finished or broken.
  888. ktp_session_stdout_close(ktp);
  889. ctx->pager_working = TRI_FALSE;
  890. return BOOL_TRUE; // Don't break the loop
  891. }
  892. // TRI_UNDEFINED here means that pager is not needed
  893. } else if (ctx->pager_working == TRI_UNDEFINED) {
  894. if (faux_write_block(STDOUT_FILENO, line, len) < 0)
  895. return BOOL_FALSE;
  896. }
  897. return BOOL_TRUE;
  898. }
  899. bool_t notification_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
  900. {
  901. char *str = NULL;
  902. ctx_t *ctx = (ctx_t *)udata;
  903. str = faux_msg_get_str_param_by_type(msg, KTP_PARAM_ERROR);
  904. if (!str)
  905. return BOOL_TRUE;
  906. if (ctx->mode == MODE_INTERACTIVE) {
  907. tinyrl_multi_crlf(ctx->tinyrl);
  908. tinyrl_reset_line_state(ctx->tinyrl);
  909. }
  910. fprintf(stderr, "Note: %s\n", str);
  911. fflush(stderr);
  912. if (ctx->mode == MODE_INTERACTIVE)
  913. tinyrl_redisplay(ctx->tinyrl);
  914. faux_str_free(str);
  915. ktp = ktp; // Happy compiler
  916. return BOOL_TRUE;
  917. }
  918. static void signal_handler_empty(int signo)
  919. {
  920. signo = signo; // Happy compiler
  921. }