klish.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. #ifdef HAVE_LOCALE_H
  15. #include <locale.h>
  16. #endif
  17. #ifdef HAVE_LANGINFO_CODESET
  18. #include <langinfo.h>
  19. #endif
  20. #include <faux/faux.h>
  21. #include <faux/str.h>
  22. #include <faux/msg.h>
  23. #include <faux/list.h>
  24. #include <faux/file.h>
  25. #include <faux/eloop.h>
  26. #include <klish/ktp.h>
  27. #include <klish/ktp_session.h>
  28. #include "private.h"
  29. typedef enum {
  30. MODE_CMDLINE,
  31. MODE_FILES,
  32. MODE_STDIN,
  33. MODE_INTERACTIVE
  34. } client_mode_e;
  35. static bool_t stdout_cb(ktp_session_t *ktp, const char *line, size_t len,
  36. void *user_data);
  37. static bool_t stderr_cb(ktp_session_t *ktp, const char *line, size_t len,
  38. void *user_data);
  39. static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  40. void *associated_data, void *user_data);
  41. static int ktp_sync_cmd(ktp_session_t *ktp, const char *line,
  42. const struct options *opts);
  43. int main(int argc, char **argv)
  44. {
  45. int retval = -1;
  46. struct options *opts = NULL;
  47. int unix_sock = -1;
  48. ktp_session_t *ktp = NULL;
  49. int retcode = 0;
  50. faux_eloop_t *eloop = NULL;
  51. int auth_rc = -1;
  52. client_mode_e mode = MODE_INTERACTIVE;
  53. #ifdef HAVE_LOCALE_H
  54. // Set current locale
  55. setlocale(LC_ALL, "");
  56. #endif
  57. // Parse command line options
  58. opts = opts_init();
  59. if (opts_parse(argc, argv, opts)) {
  60. fprintf(stderr, "Error: Can't parse command line options\n");
  61. goto err;
  62. }
  63. // Parse config file
  64. if (!access(opts->cfgfile, R_OK)) {
  65. if (!config_parse(opts->cfgfile, opts))
  66. goto err;
  67. } else if (opts->cfgfile_userdefined) {
  68. // User defined config must be found
  69. fprintf(stderr, "Error: Can't find config file %s\n",
  70. opts->cfgfile);
  71. goto err;
  72. }
  73. // Find out client mode
  74. if (faux_list_len(opts->commands) > 0)
  75. mode = MODE_CMDLINE;
  76. else if (faux_list_len(opts->files) > 0)
  77. mode = MODE_FILES;
  78. else if (!isatty(STDIN_FILENO))
  79. mode = MODE_STDIN;
  80. // Connect to server
  81. unix_sock = ktp_connect_unix(opts->unix_socket_path);
  82. if (unix_sock < 0) {
  83. fprintf(stderr, "Error: Can't connect to server\n");
  84. goto err;
  85. }
  86. // Eloop object
  87. eloop = faux_eloop_new(NULL);
  88. faux_eloop_add_signal(eloop, SIGINT, stop_loop_ev, NULL);
  89. faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, NULL);
  90. faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, NULL);
  91. // KTP session
  92. ktp = ktp_session_new(unix_sock, eloop);
  93. assert(ktp);
  94. if (!ktp) {
  95. fprintf(stderr, "Error: Can't create klish session\n");
  96. goto err;
  97. }
  98. // Ignore SIGPIPE from server
  99. signal(SIGPIPE, SIG_IGN);
  100. // Auth for non-interactive modes. Interactive mode does auth itself
  101. if (mode != MODE_INTERACTIVE) {
  102. if (!ktp_sync_auth(ktp, &auth_rc) || (auth_rc < 0)) {
  103. fprintf(stderr, "Error: Can't auth\n");
  104. goto err;
  105. }
  106. }
  107. // These callback functions is only for batch mode. Interactive
  108. // mode can reassign them.
  109. ktp_session_set_cb(ktp, KTP_SESSION_CB_STDOUT, stdout_cb, NULL);
  110. ktp_session_set_cb(ktp, KTP_SESSION_CB_STDERR, stderr_cb, NULL);
  111. // Commands from cmdline
  112. if (mode == MODE_CMDLINE) {
  113. const char *line = NULL;
  114. faux_list_node_t *iter = faux_list_head(opts->commands);
  115. while ((line = faux_list_each(&iter))) {
  116. // Request to server
  117. retcode = ktp_sync_cmd(ktp, line, opts);
  118. // Stop-on-error
  119. if (opts->stop_on_error && (retcode != 0))
  120. break;
  121. }
  122. // Commands from files
  123. } else if (mode == MODE_FILES) {
  124. const char *filename = NULL;
  125. faux_list_node_t *iter = faux_list_head(opts->files);
  126. while ((filename = (const char *)faux_list_each(&iter))) {
  127. char *line = NULL;
  128. bool_t stop = BOOL_FALSE;
  129. faux_file_t *fd = faux_file_open(filename, O_RDONLY, 0);
  130. while ((line = faux_file_getline(fd))) {
  131. // Request to server
  132. retcode = ktp_sync_cmd(ktp, line, opts);
  133. faux_str_free(line);
  134. // Stop-on-error
  135. if (opts->stop_on_error && (retcode != 0)) {
  136. stop = BOOL_TRUE;
  137. break;
  138. }
  139. }
  140. faux_file_close(fd);
  141. if (stop)
  142. break;
  143. }
  144. // Commands from non-interactive STDIN
  145. } else if (mode == MODE_STDIN) {
  146. char *line = NULL;
  147. faux_file_t *fd = faux_file_fdopen(STDIN_FILENO);
  148. while ((line = faux_file_getline(fd))) {
  149. // Request to server
  150. retcode = ktp_sync_cmd(ktp, line, opts);
  151. faux_str_free(line);
  152. // Stop-on-error
  153. if (opts->stop_on_error && (retcode != 0))
  154. break;
  155. }
  156. faux_file_close(fd);
  157. // Interactive shell
  158. } else {
  159. // Interactive code is complex so move it to separate file
  160. retcode = klish_interactive_shell(ktp, opts);
  161. }
  162. retval = 0;
  163. err:
  164. ktp_session_free(ktp);
  165. faux_eloop_free(eloop);
  166. ktp_disconnect(unix_sock);
  167. opts_free(opts);
  168. if ((retval < 0) || (retcode != 0))
  169. return -1;
  170. return 0;
  171. }
  172. static int ktp_sync_cmd(ktp_session_t *ktp, const char *line,
  173. const struct options *opts)
  174. {
  175. faux_error_t *error = NULL;
  176. int retcode = -1;
  177. if (faux_str_is_empty(line))
  178. return 0;
  179. // Echo command
  180. if (!opts->quiet)
  181. fprintf(stderr, "%s\n", line);
  182. error = faux_error_new();
  183. if (!ktp_session_cmd(ktp, line, error, opts->dry_run)) {
  184. faux_error_free(error);
  185. return -1;
  186. }
  187. faux_eloop_loop(ktp_session_eloop(ktp));
  188. // If retcode is not available then variable will not be changed
  189. ktp_session_retcode(ktp, &retcode);
  190. if (faux_error_len(error) > 0) {
  191. fprintf(stderr, "Error:\n");
  192. faux_error_fshow(error, stderr);
  193. }
  194. faux_error_free(error);
  195. return retcode;
  196. }
  197. bool_t ktp_sync_auth(ktp_session_t *ktp, int *retcode)
  198. {
  199. faux_error_t *error = NULL;
  200. bool_t rc = BOOL_FALSE;
  201. assert(ktp);
  202. error = faux_error_new();
  203. if (!ktp_session_auth(ktp, error)) {
  204. faux_error_free(error);
  205. return BOOL_FALSE;
  206. }
  207. faux_eloop_loop(ktp_session_eloop(ktp));
  208. rc = ktp_session_retcode(ktp, retcode);
  209. if (faux_error_len(error) > 0) {
  210. fprintf(stderr, "Auth error:\n");
  211. faux_error_fshow(error, stderr);
  212. }
  213. faux_error_free(error);
  214. return rc;
  215. }
  216. static bool_t stdout_cb(ktp_session_t *ktp, const char *line, size_t len,
  217. void *user_data)
  218. {
  219. if (faux_write_block(STDOUT_FILENO, line, len) < 0)
  220. return BOOL_FALSE;
  221. ktp = ktp;
  222. user_data = user_data;
  223. return BOOL_TRUE;
  224. }
  225. static bool_t stderr_cb(ktp_session_t *ktp, const char *line, size_t len,
  226. void *user_data)
  227. {
  228. if (faux_write_block(STDERR_FILENO, line, len) < 0)
  229. return BOOL_FALSE;
  230. ktp = ktp;
  231. user_data = user_data;
  232. return BOOL_TRUE;
  233. }
  234. static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
  235. void *associated_data, void *user_data)
  236. {
  237. // Happy compiler
  238. eloop = eloop;
  239. type = type;
  240. associated_data = associated_data;
  241. user_data = user_data;
  242. return BOOL_FALSE; // Stop Event Loop
  243. }