summaryrefslogtreecommitdiffstats
path: root/qarsh.c
diff options
context:
space:
mode:
authorNate Straz <nstraz@redhat.com>2005-09-13 16:01:28 +0000
committerNathan Straz <nstraz@redhat.com>2008-09-23 09:37:44 -0400
commitbf489daffc4902db3a9eb95d485fd867ac1ea524 (patch)
treec022f12c9d1bb8b56b566976cf5127b37112bbf1 /qarsh.c
parentc38e0fba0a10d5af0bb25f674177cb6404f73991 (diff)
downloadqarsh-bf489daffc4902db3a9eb95d485fd867ac1ea524.tar.gz
qarsh-bf489daffc4902db3a9eb95d485fd867ac1ea524.tar.xz
qarsh-bf489daffc4902db3a9eb95d485fd867ac1ea524.zip
Flatten the qarsh tree.
Diffstat (limited to 'qarsh.c')
-rw-r--r--qarsh.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/qarsh.c b/qarsh.c
new file mode 100644
index 0000000..b0c4aab
--- /dev/null
+++ b/qarsh.c
@@ -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;
+}