summaryrefslogtreecommitdiffstats
path: root/qarsh/qarsh.c
diff options
context:
space:
mode:
Diffstat (limited to 'qarsh/qarsh.c')
-rw-r--r--qarsh/qarsh.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/qarsh/qarsh.c b/qarsh/qarsh.c
new file mode 100644
index 0000000..fcfda10
--- /dev/null
+++ b/qarsh/qarsh.c
@@ -0,0 +1,255 @@
+/*
+ * QA Remote Shell - client side
+ *
+ * Run a command on the server with lots of wizz-bang options
+ *
+ */
+
+#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 "sockutil.h"
+#include "qarsh_packet.h"
+
+#define QARSH_MINPORT 5010
+
+/* Globals */
+int qarsh_fd = -1; /* The control connection to qarshd */
+
+
+char *
+copyargs(char **argv)
+{
+ int cc;
+ char **ap, *p;
+ char *args;
+
+ cc = 0;
+ for (ap = argv; *ap; ++ap)
+ cc += strlen(*ap) + 1;
+ 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);
+}
+
+
+int
+run_remote_cmd(char *cmdline)
+{
+ struct qa_packet *qp;
+ char *packetbuf;
+ int packetsize;
+ 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;
+ packetbuf = qptostr(qp, &packetbuf, &packetsize);
+ qpfree(qp);
+ write(qarsh_fd, packetbuf, packetsize);
+ free(packetbuf);
+
+ /* 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);
+
+ if (fcntl(fileno(stdin), F_SETFL, O_NONBLOCK) != 0) {
+ fprintf(stderr, "fcntl stdin O_NONBLOCK failed, %d: %s\n", errno, strerror(errno));
+ }
+ packetbuf = malloc(1024);
+ memset(packetbuf, 0, 1024);
+
+ for (;;) {
+ testfds = readfds;
+ memset(packetbuf, 0, 1024);
+
+ nset = select(FD_SETSIZE, &testfds, NULL, NULL, NULL);
+ if (FD_ISSET(fileno(stdin), &testfds)) {
+ do {
+ packetsize = read(fileno(stdin), packetbuf, 1024);
+ write(c_in, packetbuf, packetsize);
+ } while (packetsize == 1024);
+ if (packetsize == 0) {
+ FD_CLR(fileno(stdin), &readfds);
+ close(c_in);
+ c_in = 0;
+ }
+ }
+ if (c_out && FD_ISSET(c_out, &testfds)) {
+ do {
+ packetsize = read(c_out, packetbuf, 1024);
+ write(fileno(stdout), packetbuf, packetsize);
+ } while (packetsize == 1024);
+ if (packetsize == 0) {
+ FD_CLR(c_out, &readfds);
+ close(c_out);
+ c_out = 0;
+ }
+ }
+ if (c_err && FD_ISSET(c_err, &testfds)) {
+ do {
+ packetsize = read(c_err, packetbuf, 1024);
+ write(fileno(stderr), packetbuf, packetsize);
+ } while (packetsize == 1024);
+ if (packetsize == 0) {
+ FD_CLR(c_err, &readfds);
+ close(c_err);
+ c_err = 0;
+ }
+ }
+ if (FD_ISSET(qarsh_fd, &testfds)) {
+ packetsize = read(qarsh_fd, packetbuf, 1024);
+ if (packetsize == 0) {
+ qp = NULL;
+ break;
+ }
+ qp = parse_packets(packetbuf, packetsize);
+
+ /* dump_qp(qp); */
+ if (qp && qp->qp_type == QP_CMDEXIT) {
+ break;
+ }
+ }
+
+ }
+ 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(packetbuf);
+ 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(packetbuf);
+ return rc;
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ int c;
+ int port = 5008;
+ char *host;
+ char *args;
+ int ret;
+
+ openlog("qarsh", LOG_PID, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, "+p:")) != -1) {
+ switch (c) {
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case '?':
+ default:
+ printf("Unknown option %c\n", (char)c);
+ } }
+
+ host = argv[optind++];
+
+ argc -= optind;
+ argv += optind;
+ args = copyargs(argv);
+
+ qarsh_fd = connect_to_host(host, port);
+ if (qarsh_fd == -1) {
+ fprintf(stderr, "Could not connect to %s:%d, %d: %s\n",
+ host, port, errno, strerror(errno));
+ return 127;
+ }
+
+ ret = run_remote_cmd(args);
+ close(qarsh_fd);
+ free(args);
+ return ret;
+}