summaryrefslogtreecommitdiffstats
path: root/qarsh/qarshd.c
diff options
context:
space:
mode:
Diffstat (limited to 'qarsh/qarshd.c')
-rw-r--r--qarsh/qarshd.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/qarsh/qarshd.c b/qarsh/qarshd.c
new file mode 100644
index 0000000..72db068
--- /dev/null
+++ b/qarsh/qarshd.c
@@ -0,0 +1,193 @@
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "sockutil.h"
+#include "qarsh_packet.h"
+
+/*
+ * QA Remote Shell Daemon
+ */
+
+
+/* Globals */
+struct sockaddr_in peername;
+
+
+pid_t
+run_cmd(const char *cmd, int p_in, int p_out, int p_err)
+{
+ pid_t pid;
+ int new_in, new_out, new_err;
+
+ syslog(LOG_INFO, "Running cmdline: %s\n", cmd);
+ if ((pid = fork()) < 0) {
+ syslog(LOG_WARNING,
+ "Could not fork() in run_cmd(): %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (pid == 0) { /* child */
+
+ /* Connect stdin, stdout, and stderr to qarsh */
+ new_in = connect_to_peer(&peername, p_in);
+ if (new_in == -1) syslog(LOG_WARNING, "connect to new_in failed");
+ dup2(new_in, fileno(stdin));
+ new_out = connect_to_peer(&peername, p_out);
+ if (new_out == -1) syslog(LOG_WARNING, "connect to new_out failed");
+ dup2(new_out, fileno(stdout));
+ new_err = connect_to_peer(&peername, p_err);
+ if (new_err == -1) syslog(LOG_WARNING, "connect to new_err failed");
+ dup2(new_err, fileno(stderr));
+
+ /* If there are any shell-type characters in the
+ * cmdline such as '>', '<', '$', '|', '~','*' etc,
+ * then we exec a shell and run the cmd under a shell.
+ *
+ * Otherwise exec the cmd directly.
+ */
+
+ if (strpbrk(cmd, "\"';|<>$\\~*")) {
+ execlp("sh", "sh", "-c", cmd, NULL);
+ } else {
+ char **argv;
+ char *sp, *tmpcmd;
+ int argc = 1;
+
+ /* We need to split the string up */
+ tmpcmd = strdup(cmd);
+ for (sp = tmpcmd; (sp = strchr(sp, ' ')); argc++) {
+ if (*sp == ' ') sp++;
+ }
+ syslog(LOG_INFO, "cmdline split into %d args", argc);
+ argv = calloc(argc+1, sizeof *argv);
+ for (argc = 0; (sp = strsep(&tmpcmd, " ")); argc++) {
+ argv[argc] = sp;
+ }
+ argv[argc] = NULL;
+ execvp(argv[0], argv);
+ printf("exec of %s failed: %d, %s\n", argv[0], errno, strerror(errno));
+ }
+ exit(127);
+ }
+ return pid;
+}
+
+struct qa_packet *
+recv_packet(int fd)
+{
+ char *packetbuf;
+ int packetsize;
+ struct qa_packet *qp = NULL;
+
+ packetbuf = malloc(QARSH_MAX_PACKET_SIZE);
+ memset(packetbuf, 0, QARSH_MAX_PACKET_SIZE);
+
+ packetsize = read(fd, packetbuf, QARSH_MAX_PACKET_SIZE);
+ if (packetsize > 0) {
+ qp = parse_packets(packetbuf, packetsize);
+ }
+ free(packetbuf);
+ return qp;
+}
+
+int
+send_packet(int fd, struct qa_packet *qp)
+{
+ char *packetbuf;
+ int packetsize;
+
+ packetbuf = malloc(1024);
+ memset(packetbuf, 0, 1024);
+ packetbuf = qptostr(qp, &packetbuf, &packetsize);
+
+ return write(fd, packetbuf, packetsize);
+}
+
+void
+handle_packets(int infd)
+{
+ fd_set rfds;
+ int highfd, nfd;
+ struct timeval timeout;
+ struct qa_packet *qp = NULL, *rp = NULL;
+
+ pid_t child_pid = 0;
+ int child_status;
+
+ /* set up the select structures */
+ highfd = infd+1;
+
+ for (;;) {
+ FD_SET(infd, &rfds);
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ nfd = select(highfd, &rfds, NULL, NULL, &timeout);
+ if (nfd < 0) {
+ syslog(LOG_ERR, "select errno %d, %s\n", errno, strerror(errno));
+ } else if (nfd > 0) {
+ qp = recv_packet(infd);
+ if (qp == NULL) {
+ syslog(LOG_INFO, "That's enough\n");
+ break;
+ }
+ switch (qp->qp_type) {
+ case QP_RUNCMD:
+ child_pid = run_cmd(qp->qp_runcmd.qp_cmdline,
+ qp->qp_runcmd.qp_stdin_port,
+ qp->qp_runcmd.qp_stdout_port,
+ qp->qp_runcmd.qp_stderr_port);
+ waitpid(child_pid, &child_status, 0);
+ rp = make_qp_cmdexit(child_pid, child_status);
+ send_packet(fileno(stdout), rp);
+ break;
+ default:
+ syslog(LOG_WARNING,
+ "Packet type %s unimplemented",
+ qp_packet_type(qp->qp_type));
+ }
+ } else {
+ syslog(LOG_DEBUG, "Nothing to do\n");
+ }
+
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ socklen_t peernamelen;
+
+ openlog("qarshd", LOG_PID, LOG_DAEMON);
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ case '?':
+ default:
+ printf("unknown option '%c'\n", optopt);
+ exit(1);
+ }
+ }
+
+ /* daemon initialization */
+
+ getpeername(0, (struct sockaddr *)&peername, &peernamelen);
+ syslog(LOG_INFO, "Talking to peer %s:%d",
+ inet_ntoa(peername.sin_addr), ntohs(peername.sin_port));
+ /* Start reading packets from stdin */
+ handle_packets(0);
+
+ return 0;
+}