#include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_LANGINFO_CODESET #include #endif #include #include #include #include #include #include #include #include #include "private.h" typedef enum { MODE_CMDLINE, MODE_FILES, MODE_STDIN, MODE_INTERACTIVE } client_mode_e; static bool_t stdout_cb(ktp_session_t *ktp, const char *line, size_t len, void *user_data); static bool_t stderr_cb(ktp_session_t *ktp, const char *line, size_t len, void *user_data); static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type, void *associated_data, void *user_data); static int ktp_sync_cmd(ktp_session_t *ktp, const char *line, const struct options *opts); int main(int argc, char **argv) { int retval = -1; struct options *opts = NULL; int unix_sock = -1; ktp_session_t *ktp = NULL; int retcode = 0; faux_eloop_t *eloop = NULL; int auth_rc = -1; client_mode_e mode = MODE_INTERACTIVE; #ifdef HAVE_LOCALE_H // Set current locale setlocale(LC_ALL, ""); #endif // Parse command line options opts = opts_init(); if (opts_parse(argc, argv, opts)) { fprintf(stderr, "Error: Can't parse command line options\n"); goto err; } // Parse config file if (!access(opts->cfgfile, R_OK)) { if (!config_parse(opts->cfgfile, opts)) goto err; } else if (opts->cfgfile_userdefined) { // User defined config must be found fprintf(stderr, "Error: Can't find config file %s\n", opts->cfgfile); goto err; } // Find out client mode if (faux_list_len(opts->commands) > 0) mode = MODE_CMDLINE; else if (faux_list_len(opts->files) > 0) mode = MODE_FILES; else if (!isatty(STDIN_FILENO)) mode = MODE_STDIN; // Connect to server unix_sock = ktp_connect_unix(opts->unix_socket_path); if (unix_sock < 0) { fprintf(stderr, "Error: Can't connect to server\n"); goto err; } // Eloop object eloop = faux_eloop_new(NULL); faux_eloop_add_signal(eloop, SIGINT, stop_loop_ev, NULL); faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, NULL); faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, NULL); // KTP session ktp = ktp_session_new(unix_sock, eloop); assert(ktp); if (!ktp) { fprintf(stderr, "Error: Can't create klish session\n"); goto err; } // Ignore SIGPIPE from server signal(SIGPIPE, SIG_IGN); // Auth for non-interactive modes. Interactive mode does auth itself if (mode != MODE_INTERACTIVE) { if (!ktp_sync_auth(ktp, &auth_rc) || (auth_rc < 0)) { fprintf(stderr, "Error: Can't auth\n"); goto err; } } // These callback functions is only for batch mode. Interactive // mode can reassign them. ktp_session_set_cb(ktp, KTP_SESSION_CB_STDOUT, stdout_cb, NULL); ktp_session_set_cb(ktp, KTP_SESSION_CB_STDERR, stderr_cb, NULL); // Commands from cmdline if (mode == MODE_CMDLINE) { const char *line = NULL; faux_list_node_t *iter = faux_list_head(opts->commands); while ((line = faux_list_each(&iter))) { // Request to server retcode = ktp_sync_cmd(ktp, line, opts); // Stop-on-error if (opts->stop_on_error && (retcode != 0)) break; } // Commands from files } else if (mode == MODE_FILES) { const char *filename = NULL; faux_list_node_t *iter = faux_list_head(opts->files); while ((filename = (const char *)faux_list_each(&iter))) { char *line = NULL; bool_t stop = BOOL_FALSE; faux_file_t *fd = faux_file_open(filename, O_RDONLY, 0); while ((line = faux_file_getline(fd))) { // Request to server retcode = ktp_sync_cmd(ktp, line, opts); faux_str_free(line); // Stop-on-error if (opts->stop_on_error && (retcode != 0)) { stop = BOOL_TRUE; break; } } faux_file_close(fd); if (stop) break; } // Commands from non-interactive STDIN } else if (mode == MODE_STDIN) { char *line = NULL; faux_file_t *fd = faux_file_fdopen(STDIN_FILENO); while ((line = faux_file_getline(fd))) { // Request to server retcode = ktp_sync_cmd(ktp, line, opts); faux_str_free(line); // Stop-on-error if (opts->stop_on_error && (retcode != 0)) break; } faux_file_close(fd); // Interactive shell } else { // Interactive code is complex so move it to separate file retcode = klish_interactive_shell(ktp, opts); } retval = 0; err: ktp_session_free(ktp); faux_eloop_free(eloop); ktp_disconnect(unix_sock); opts_free(opts); if ((retval < 0) || (retcode != 0)) return -1; return 0; } static int ktp_sync_cmd(ktp_session_t *ktp, const char *line, const struct options *opts) { faux_error_t *error = NULL; int retcode = -1; if (faux_str_is_empty(line)) return 0; // Echo command if (!opts->quiet) fprintf(stderr, "%s\n", line); error = faux_error_new(); if (!ktp_session_cmd(ktp, line, error, opts->dry_run)) { faux_error_free(error); return -1; } faux_eloop_loop(ktp_session_eloop(ktp)); // If retcode is not available then variable will not be changed ktp_session_retcode(ktp, &retcode); if (faux_error_len(error) > 0) { fprintf(stderr, "Error:\n"); faux_error_fshow(error, stderr); } faux_error_free(error); return retcode; } bool_t ktp_sync_auth(ktp_session_t *ktp, int *retcode) { faux_error_t *error = NULL; bool_t rc = BOOL_FALSE; assert(ktp); error = faux_error_new(); if (!ktp_session_auth(ktp, error)) { faux_error_free(error); return BOOL_FALSE; } faux_eloop_loop(ktp_session_eloop(ktp)); rc = ktp_session_retcode(ktp, retcode); if (faux_error_len(error) > 0) { fprintf(stderr, "Auth error:\n"); faux_error_fshow(error, stderr); } faux_error_free(error); return rc; } static bool_t stdout_cb(ktp_session_t *ktp, const char *line, size_t len, void *user_data) { if (faux_write_block(STDOUT_FILENO, line, len) < 0) return BOOL_FALSE; ktp = ktp; user_data = user_data; return BOOL_TRUE; } static bool_t stderr_cb(ktp_session_t *ktp, const char *line, size_t len, void *user_data) { if (faux_write_block(STDERR_FILENO, line, len) < 0) return BOOL_FALSE; ktp = ktp; user_data = user_data; return BOOL_TRUE; } static bool_t stop_loop_ev(faux_eloop_t *eloop, faux_eloop_type_e type, void *associated_data, void *user_data) { // Happy compiler eloop = eloop; type = type; associated_data = associated_data; user_data = user_data; return BOOL_FALSE; // Stop Event Loop }