diff options
Diffstat (limited to 'qarsh/qarshd.c')
-rw-r--r-- | qarsh/qarshd.c | 193 |
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; +} |