From 468d9d0d5dd436b6280639850449a38f48715894 Mon Sep 17 00:00:00 2001 From: Nate Straz Date: Wed, 24 Aug 2005 23:12:22 +0000 Subject: Initial pass at signal propogation. Only SIGINT is passed along at this point. --- qarsh/qarsh.c | 56 +++++++++++++++++++++++++++++++++++++++++++--------- qarsh/qarsh_packet.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ qarsh/qarsh_packet.h | 10 +++++++++- qarsh/qarshd.c | 48 ++++++++++++++++++++++++++++++++++++-------- 4 files changed, 144 insertions(+), 18 deletions(-) diff --git a/qarsh/qarsh.c b/qarsh/qarsh.c index 0b91f5d..2c709c7 100644 --- a/qarsh/qarsh.c +++ b/qarsh/qarsh.c @@ -5,6 +5,7 @@ * */ +#define _GNU_SOURCE #include #include #include @@ -28,6 +29,7 @@ /* Globals */ int qarsh_fd = -1; /* The control connection to qarshd */ +int signal_to_send = 0; void usage() @@ -59,6 +61,12 @@ copyargs(char **argv) return args; } +void +sig_handler(int sig) +{ + signal_to_send = sig; +} + void set_remote_user(char *user, char *group) { @@ -90,6 +98,8 @@ run_remote_cmd(char *cmdline) int l_in, l_out, l_err; /* listening sockets */ int c_in, c_out, c_err; /* client sockets */ fd_set readfds, testfds; + sigset_t sigmask, orig_sigmask; + struct sigaction sa; int nset; struct sockaddr_in caddr; socklen_t clen; @@ -160,9 +170,19 @@ run_remote_cmd(char *cmdline) FD_SET(c_out, &readfds); FD_SET(c_err, &readfds); FD_SET(fileno(stdin), &readfds); + /* Setup signal handling stuff so we can propogate signals */ + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigprocmask(SIG_BLOCK, &sigmask, &orig_sigmask); + sa.sa_handler = sig_handler; + sa.sa_mask = sigmask; + sa.sa_flags = SA_RESTART; + sigaction(SIGINT, &sa, NULL); if (fcntl(fileno(stdin), F_SETFL, O_NONBLOCK) != 0) { - fprintf(stderr, "fcntl stdin O_NONBLOCK failed, %d: %s\n", errno, strerror(errno)); + fprintf(stderr, + "fcntl stdin O_NONBLOCK failed, %d: %s\n", + errno, strerror(errno)); } buf = malloc(1024); memset(buf, 0, 1024); @@ -171,47 +191,65 @@ run_remote_cmd(char *cmdline) testfds = readfds; memset(buf, 0, 1024); - nset = select(FD_SETSIZE, &testfds, NULL, NULL, NULL); - if (FD_ISSET(fileno(stdin), &testfds)) { + nset = pselect(FD_SETSIZE, &testfds, NULL, NULL, NULL, + &orig_sigmask); + if (nset == -1 && errno == EINTR) { + nset = 0; + } + + if (signal_to_send) { + qp = make_qp_kill(signal_to_send); + send_packet(qarsh_fd, qp); + qpfree(qp); + signal_to_send = 0; + } + if (nset && FD_ISSET(fileno(stdin), &testfds)) { do { bufsize = read(fileno(stdin), buf, 1024); - write(c_in, buf, bufsize); + if (bufsize > 0) + write(c_in, buf, bufsize); } while (bufsize == 1024); if (bufsize == 0) { FD_CLR(fileno(stdin), &readfds); close(c_in); c_in = 0; } + nset--; } - if (c_out && FD_ISSET(c_out, &testfds)) { + if (nset && c_out && FD_ISSET(c_out, &testfds)) { do { bufsize = read(c_out, buf, 1024); - write(fileno(stdout), buf, bufsize); + if (bufsize > 0) + write(fileno(stdout), buf, bufsize); } while (bufsize == 1024); if (bufsize == 0) { FD_CLR(c_out, &readfds); close(c_out); c_out = 0; } + nset--; } - if (c_err && FD_ISSET(c_err, &testfds)) { + if (nset && c_err && FD_ISSET(c_err, &testfds)) { do { bufsize = read(c_err, buf, 1024); - write(fileno(stderr), buf, bufsize); + if (bufsize > 0) + write(fileno(stderr), buf, bufsize); } while (bufsize == 1024); if (bufsize == 0) { FD_CLR(c_err, &readfds); close(c_err); c_err = 0; } + nset--; } - if (FD_ISSET(qarsh_fd, &testfds)) { + if (nset && FD_ISSET(qarsh_fd, &testfds)) { qp = recv_packet(qarsh_fd); /* dump_qp(qp); */ if (qp && qp->qp_type == QP_CMDEXIT) { break; } + nset--; } } diff --git a/qarsh/qarsh_packet.c b/qarsh/qarsh_packet.c index cc4f082..bad614f 100644 --- a/qarsh/qarsh_packet.c +++ b/qarsh/qarsh_packet.c @@ -17,6 +17,7 @@ int parse_qp_ack(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_runcmd(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_cmdexit(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp); +int parse_qp_kill(xmlXPathContextPtr ctxt, struct qa_packet *qp); void string_qp_hello(xmlNodePtr node, struct qa_packet *qp); void string_qp_returncode(xmlNodePtr node, struct qa_packet *qp); @@ -24,6 +25,7 @@ void string_qp_runcmd(xmlNodePtr node, struct qa_packet *qp); void string_qp_ack(xmlNodePtr node, struct qa_packet *qp); void string_qp_cmdexit(xmlNodePtr node, struct qa_packet *qp); void string_qp_setuser(xmlNodePtr node, struct qa_packet *qp); +void string_qp_kill(xmlNodePtr node, struct qa_packet *qp); void free_qp_hello(struct qa_packet *qp); void free_qp_returncode(struct qa_packet *qp); @@ -35,6 +37,7 @@ void dump_qp_runcmd(struct qa_packet *qp); void dump_qp_returncode(struct qa_packet *qp); void dump_qp_cmdexit(struct qa_packet *qp); void dump_qp_setuser(struct qa_packet *qp); +void dump_qp_kill(struct qa_packet *qp); struct packet_internals { @@ -86,6 +89,12 @@ struct packet_internals { .pi_string = string_qp_setuser, .pi_free = free_qp_setuser, .pi_dump = dump_qp_setuser + }, { + .pi_name = "kill", + .pi_parse = parse_qp_kill, + .pi_string = string_qp_kill, + .pi_free = NULL, + .pi_dump = dump_qp_kill } }; @@ -196,6 +205,17 @@ parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp) return 0; } +int +parse_qp_kill(xmlXPathContextPtr ctxt, struct qa_packet *qp) +{ + char *s; + + s = get_xpath_string(ctxt, "param[@name='signal']"); + qp->qp_kill.qp_sig = atoi(s); + free(s); + return 0; +} + struct qa_packet * parse_packet(xmlXPathContextPtr ctxt) { @@ -349,6 +369,14 @@ void string_qp_setuser(xmlNodePtr node, struct qa_packet *qp) } } +void string_qp_kill(xmlNodePtr node, struct qa_packet *qp) +{ + char tmpstr[32]; + + snprintf(tmpstr, 32, "%d", qp->qp_kill.qp_sig); + xmlAddChild(node, make_param("signal", tmpstr)); +} + /* Must pass in a pointer, but not a malloc'ed pointer */ char * qptostr(struct qa_packet *qp, char **qpstr, int *qpsize) @@ -476,6 +504,20 @@ make_qp_setuser(char *user, char *group) return qp; } +struct qa_packet * +make_qp_kill(int sig) +{ + struct qa_packet *qp; + qp = malloc(sizeof *qp); + assert(qp); + memset(qp, 0, sizeof *qp); + + qp->qp_type = QP_KILL; + qp->qp_kill.qp_sig = sig; + + return qp; +} + /* * Packet deallocation functions */ @@ -559,6 +601,12 @@ dump_qp_setuser(struct qa_packet *qp) printf("\tgroup: %s\n", qp->qp_setuser.qp_group); } +void +dump_qp_kill(struct qa_packet *qp) +{ + printf("\tsig: %d\n", qp->qp_kill.qp_sig); +} + void dump_qp(struct qa_packet *qp) { diff --git a/qarsh/qarsh_packet.h b/qarsh/qarsh_packet.h index b6d821b..64b2b2a 100644 --- a/qarsh/qarsh_packet.h +++ b/qarsh/qarsh_packet.h @@ -12,7 +12,8 @@ enum qa_packet_type { QP_RUNCMD = 3, QP_ACK = 4, QP_CMDEXIT = 5, - QP_SETUSER = 6 + QP_SETUSER = 6, + QP_KILL = 7 }; struct qp_hello_pkt { @@ -48,6 +49,10 @@ struct qp_setuser_pkt { char *qp_group; }; +struct qp_kill_pkt { + int qp_sig; +}; + #define QP_VERSION 1 struct qa_packet { @@ -60,6 +65,7 @@ struct qa_packet { struct qp_ack_pkt ack; struct qp_cmdexit_pkt cmdexit; struct qp_setuser_pkt setuser; + struct qp_kill_pkt kill; } qp_u; }; @@ -69,6 +75,7 @@ struct qa_packet { #define qp_ack qp_u.ack #define qp_cmdexit qp_u.cmdexit #define qp_setuser qp_u.setuser +#define qp_kill qp_u.kill /* Prototypes */ char *qp_packet_type(enum qa_packet_type t); @@ -79,6 +86,7 @@ struct qa_packet *make_qp_ack(enum qa_packet_type t, int i); struct qa_packet *make_qp_runcmd(char *cmdline, int p_in, int p_out, int p_err); struct qa_packet *make_qp_cmdexit(pid_t pid, int status); struct qa_packet *make_qp_setuser(char *user, char *group); +struct qa_packet *make_qp_kill(int sig); char *qptostr(struct qa_packet *qp, char **qpstr, int *qpsize); void qpfree(struct qa_packet *qp); void dump_qp(struct qa_packet *qp); diff --git a/qarsh/qarshd.c b/qarsh/qarshd.c index d443704..bf8353b 100644 --- a/qarsh/qarshd.c +++ b/qarsh/qarshd.c @@ -1,4 +1,5 @@ +#define _GNU_SOURCE #include #include #include @@ -25,6 +26,7 @@ /* Globals */ struct sockaddr_in peername; +int child_exitted = 0; int setup_user(char *user, char *group) @@ -49,6 +51,12 @@ setup_user(char *user, char *group) return 1; } +void +sig_handler(int sig) +{ + if (sig == SIGCHLD) child_exitted++; +} + pid_t run_cmd(const char *cmd, int p_in, int p_out, int p_err) { @@ -113,20 +121,40 @@ handle_packets(int infd) { fd_set rfds; int nfd; - struct timeval timeout; + struct timespec timeout; struct qa_packet *qp = NULL, *rp = NULL; + sigset_t sigmask, orig_sigmask; + struct sigaction sa; pid_t child_pid = 0; int child_status; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigmask, &orig_sigmask); + sa.sa_handler = sig_handler; + sa.sa_mask = sigmask; + sa.sa_flags = SA_RESTART; + sigaction(SIGCHLD, &sa, NULL); + for (;;) { FD_SET(infd, &rfds); timeout.tv_sec = 5; - timeout.tv_usec = 0; - nfd = select(infd+1, &rfds, NULL, NULL, &timeout); + nfd = pselect(infd+1, &rfds, NULL, NULL, &timeout, + &orig_sigmask); if (nfd < 0) { - syslog(LOG_ERR, "select errno %d, %s\n", errno, strerror(errno)); + if (errno == EINTR) { + /* Handle signals here */ + if (child_exitted) { + waitpid(child_pid, &child_status, 0); + rp = make_qp_cmdexit(child_pid, child_status); + send_packet(fileno(stdout), rp); + qpfree(rp); + } + } else { + syslog(LOG_ERR, "select errno %d, %s\n", errno, strerror(errno)); + } } else if (nfd > 0) { qp = recv_packet(infd); if (qp == NULL) { @@ -134,6 +162,14 @@ handle_packets(int infd) break; } switch (qp->qp_type) { + case QP_KILL: + if (child_pid) { + + syslog(LOG_INFO, "Sending child %d signal %d", + child_pid, qp->qp_kill.qp_sig); + kill(child_pid, qp->qp_kill.qp_sig); + } + break; case QP_SETUSER: if (setup_user(qp->qp_setuser.qp_user, qp->qp_setuser.qp_group) == 0) { @@ -149,10 +185,6 @@ handle_packets(int infd) 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); - qpfree(rp); break; default: syslog(LOG_WARNING, -- cgit