diff options
author | Nate Straz <nstraz@redhat.com> | 2005-09-13 16:01:28 +0000 |
---|---|---|
committer | Nathan Straz <nstraz@redhat.com> | 2008-09-23 09:37:44 -0400 |
commit | bf489daffc4902db3a9eb95d485fd867ac1ea524 (patch) | |
tree | c022f12c9d1bb8b56b566976cf5127b37112bbf1 /qarsh.c | |
parent | c38e0fba0a10d5af0bb25f674177cb6404f73991 (diff) | |
download | qarsh-bf489daffc4902db3a9eb95d485fd867ac1ea524.tar.gz qarsh-bf489daffc4902db3a9eb95d485fd867ac1ea524.tar.xz qarsh-bf489daffc4902db3a9eb95d485fd867ac1ea524.zip |
Flatten the qarsh tree.
Diffstat (limited to 'qarsh.c')
-rw-r--r-- | qarsh.c | 365 |
1 files changed, 365 insertions, 0 deletions
@@ -0,0 +1,365 @@ +/* + * QA Remote Shell - client side + * + * Run a command on the server with lots of wizz-bang options + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <netdb.h> +#include <syslog.h> +#include <pwd.h> + + +#include "sockutil.h" +#include "qarsh_packet.h" + +#define QARSH_MINPORT 5010 + +/* Globals */ +int qarsh_fd = -1; /* The control connection to qarshd */ +int signal_to_send = 0; +int sigs_to_propogate[] = { SIGINT, SIGTERM, SIGHUP, SIGUSR1, SIGUSR2 }; +sigset_t pselect_sigmask; + +void +usage() +{ + printf("qarsh: [user[.group]@]hostname cmdline ...\n"); +} + +char * +copyargs(char **argv) +{ + int cc; + char **ap, *p; + char *args; + + cc = 0; + for (ap = argv; *ap; ++ap) + cc += strlen(*ap) + 1; + if (cc == 0) return NULL; + args = malloc(cc); + if (!args) { + perror("qarsh: malloc failed in copyargs"); + exit(1); + } + for (p = args, ap = argv; *ap; ++ap) { + for (p = strcpy(p, *ap); *p; ++p); + if (ap[1]) + *p++ = ' '; + } + return args; +} + +void +sig_handler(int sig) +{ + signal_to_send = sig; +} + +void +setup_signals(void) +{ + struct sigaction sa; + sigset_t sigmask; + int i, n; + + n = sizeof sigs_to_propogate / sizeof *sigs_to_propogate; + sigemptyset(&sigmask); + for (i = 0; i < n; i++) { + sigaddset(&sigmask, sigs_to_propogate[i]); + } + sigprocmask(SIG_BLOCK, &sigmask, &pselect_sigmask); + sa.sa_handler = sig_handler; + sa.sa_mask = sigmask; + sa.sa_flags = SA_RESTART; + for (i = 0; i < n; i++) { + sigaction(sigs_to_propogate[i], &sa, NULL); + } +} + +void +set_remote_user(char *user, char *group) +{ + struct qa_packet *qp; + + qp = make_qp_setuser(user, group); + qp->qp_seq = 1; + send_packet(qarsh_fd, qp); + qpfree(qp); + qp = recv_packet(qarsh_fd); + if (qp && qp->qp_type == QP_RETURNCODE + && qp->qp_returncode.qp_rc == -1) { + + fprintf(stderr, "Remote side failed, %s\n", + qp->qp_returncode.qp_strerror); + close(qarsh_fd); + exit(125); + } +} + +int +run_remote_cmd(char *cmdline) +{ + struct qa_packet *qp; + char *buf; + int bufsize; + int rc; + int p_in, p_out, p_err; /* Port numbers */ + int l_in, l_out, l_err; /* listening sockets */ + int c_in, c_out, c_err; /* client sockets */ + fd_set readfds, testfds; + int nset; + struct sockaddr_in caddr; + socklen_t clen; + + l_in = bind_any(QARSH_MINPORT); + p_in = getsockport(l_in); + l_out = bind_any(QARSH_MINPORT); + p_out = getsockport(l_out); + l_err = bind_any(QARSH_MINPORT); + p_err = getsockport(l_err); + + qp = make_qp_runcmd(cmdline, p_in, p_out, p_err); + qp->qp_seq = 1; + send_packet(qarsh_fd, qp); + qpfree(qp); + + /* Get the stdin, stdout, and stderr connections up before we do work */ + FD_ZERO(&readfds); + FD_SET(l_in, &readfds); + FD_SET(l_out, &readfds); + FD_SET(l_err, &readfds); + c_in = c_out = c_err = 0; + + do { + testfds = readfds; + nset = select(FD_SETSIZE, &testfds, NULL, NULL, NULL); + + if (FD_ISSET(l_in, &testfds)) { + clen = sizeof caddr; + c_in = accept(l_in, (struct sockaddr *)&caddr, &clen); + if (c_in == -1) { + fprintf(stderr, + "accept of l_in failed, %d: %s\n", + errno, strerror(errno)); + continue; + } + } + if (FD_ISSET(l_out, &testfds)) { + clen = sizeof caddr; + c_out = accept(l_out, (struct sockaddr *)&caddr, &clen); + if (c_out == -1) { + fprintf(stderr, + "accept of l_out failed, %d: %s\n", + errno, strerror(errno)); + continue; + } + } + if (FD_ISSET(l_err, &testfds)) { + clen = sizeof caddr; + c_err = accept(l_err, (struct sockaddr *)&caddr, &clen); + if (c_err == -1) { + fprintf(stderr, + "accept of l_err failed, %d: %s\n", + errno, strerror(errno)); + continue; + } + } + } while (c_in == 0 || c_out == 0 || c_err == 0); + close(l_in); + close(l_out); + close(l_err); + l_in = l_out = l_err = -1; + + /* Now we can start doing some real work */ + + FD_ZERO(&readfds); + FD_SET(qarsh_fd, &readfds); + FD_SET(c_out, &readfds); + FD_SET(c_err, &readfds); + FD_SET(fileno(stdin), &readfds); + /* Setup signal handling stuff so we can propogate signals */ + setup_signals(); + + if (fcntl(fileno(stdin), F_SETFL, O_NONBLOCK) != 0) { + fprintf(stderr, + "fcntl stdin O_NONBLOCK failed, %d: %s\n", + errno, strerror(errno)); + } + buf = malloc(1024); + memset(buf, 0, 1024); + + for (;;) { + testfds = readfds; + memset(buf, 0, 1024); + + nset = pselect(FD_SETSIZE, &testfds, NULL, NULL, NULL, + &pselect_sigmask); + if (nset == -1 && errno == EINTR) { + /* Only test signals */ + if (signal_to_send) { + qp = make_qp_kill(signal_to_send); + send_packet(qarsh_fd, qp); + qpfree(qp); + signal_to_send = 0; + } + } else if (nset > 0) { + if (nset && FD_ISSET(fileno(stdin), &testfds)) { + bufsize = read(fileno(stdin), buf, 1024); + if (bufsize > 0) { + write(c_in, buf, bufsize); + } else if (bufsize == 0) { + FD_CLR(fileno(stdin), &readfds); + close(fileno(stdin)); + close(c_in); + c_in = 0; + } + nset--; + } + if (nset && c_out && FD_ISSET(c_out, &testfds)) { + bufsize = read(c_out, buf, 1024); + if (bufsize > 0) { + write(fileno(stdout), buf, bufsize); + } else if (bufsize == 0) { + FD_CLR(c_out, &readfds); + close(c_out); + c_out = 0; + } + nset--; + } + if (nset && c_err && FD_ISSET(c_err, &testfds)) { + bufsize = read(c_err, buf, 1024); + if (bufsize > 0) { + write(fileno(stderr), buf, bufsize); + } else if (bufsize == 0) { + FD_CLR(c_err, &readfds); + close(c_err); + c_err = 0; + } + nset--; + } + if (nset && FD_ISSET(qarsh_fd, &testfds)) { + qp = recv_packet(qarsh_fd); + + /* dump_qp(qp); */ + if (qp && qp->qp_type == QP_CMDEXIT) { + break; + } + nset--; + } + } + } + if (c_out) close(c_out); + if (c_err) close(c_err); + if (qp == NULL) { + fprintf(stderr, "Remote command exited with unknown state\n"); + free(buf); + return 127; + } + if (WIFSIGNALED(qp->qp_cmdexit.qp_status)) { + rc = 128 + WTERMSIG(qp->qp_cmdexit.qp_status); + } else { + rc = WEXITSTATUS(qp->qp_cmdexit.qp_status); + } + qpfree(qp); + free(buf); + return rc; +} + +int +main(int argc, char *argv[]) +{ + + int c; + int port = 5008; + char *host; + char *remuser = NULL; + char *remgroup = NULL; + char *args; + struct passwd *pw; + int ret; + + openlog("qarsh", LOG_PID, LOG_DAEMON); + + while ((c = getopt(argc, argv, "+p:l:g:")) != -1) { + switch (c) { + case 'l': + remuser = strdup(optarg); + break; + case 'g': + remgroup = strdup(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case '?': + default: + printf("Unknown option %c\n", (char)optopt); + usage(); + exit(1); + } } + + if ((host = argv[optind++]) == NULL) { + usage(); + exit(1); + } + /* check for user and group in form [user[.group]@]hostname */ + { + char *sp; + + if ((sp = strchr(host, '@'))) { + remuser = host; + host = sp+1; + *sp = '\0'; + } + if (remuser && (sp = strchr(remuser, '.'))) { + remgroup = sp+1; + *sp = '\0'; + } + } + if (!(pw = getpwuid(getuid()))) { + fprintf(stderr, "qarsh: unknown user id.\n"); + exit(1); + } + if (remuser == NULL) { + remuser = strdup(pw->pw_name); + } + + argc -= optind; + argv += optind; + if ((args = copyargs(argv)) == NULL) { + usage(); + exit(1); + } + qarsh_fd = connect_to_host(host, port); + if (qarsh_fd == -1) { + if (errno == 0) { + fprintf(stderr, "Could not connect to %s:%d, %d: %s\n", + host, port, h_errno, hstrerror(h_errno)); + } else { + fprintf(stderr, "Could not connect to %s:%d, %d: %s\n", + host, port, errno, strerror(errno)); + } + return 127; + } + + set_remote_user(remuser, remgroup); + + ret = run_remote_cmd(args); + close(qarsh_fd); + free(args); + return ret; +} |