propagate of 82bb923d0154750ef716b66b498561f882891946 and f51a272341ee12268fe7028bc2f2bad66c603069 from branch 'matt.dbclient.work' to 'matt.dbclient.rez'

--HG--
branch : private-rez
extra : convert_revision : 440ee4177122c8a873ebf8984a90f96a9bd8a56a
This commit is contained in:
Matt Johnston 2004-09-21 10:08:21 +00:00
parent 6766dfae26
commit c2af67efd3
3 changed files with 76 additions and 38 deletions

View File

@ -29,6 +29,14 @@
#include "channel.h" #include "channel.h"
#include "listener.h" #include "listener.h"
struct exitinfo {
int exitpid; /* -1 if not exited */
int exitstatus;
int exitsignal;
int exitcore;
};
struct ChanSess { struct ChanSess {
unsigned char * cmd; /* command to exec */ unsigned char * cmd; /* command to exec */
@ -41,10 +49,7 @@ struct ChanSess {
unsigned char * term; unsigned char * term;
/* exit details */ /* exit details */
int exited; struct exitinfo exit;
int exitstatus;
int exitsignal;
unsigned char exitcore;
#ifndef DISABLE_X11FWD #ifndef DISABLE_X11FWD
struct Listener * x11listener; struct Listener * x11listener;

View File

@ -36,6 +36,7 @@
#include "listener.h" #include "listener.h"
#include "packet.h" #include "packet.h"
#include "tcpfwd.h" #include "tcpfwd.h"
#include "chansession.h"
extern int sessinitdone; /* Is set to 0 somewhere */ extern int sessinitdone; /* Is set to 0 somewhere */
extern int exitflag; extern int exitflag;
@ -176,6 +177,10 @@ struct serversession {
struct ChildPid * childpids; /* array of mappings childpid<->channel */ struct ChildPid * childpids; /* array of mappings childpid<->channel */
unsigned int childpidsize; unsigned int childpidsize;
/* Used to avoid a race in the exit returncode handling - see
* svr-chansession.c for details */
struct exitinfo lastexit;
}; };
typedef enum { typedef enum {

View File

@ -68,17 +68,24 @@ extern char** environ;
static int sesscheckclose(struct Channel *channel) { static int sesscheckclose(struct Channel *channel) {
struct ChanSess *chansess = (struct ChanSess*)channel->typedata; struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
return chansess->exited; return chansess->exit.exitpid >= 0;
} }
/* handler for childs exiting, store the state for return to the client */ /* Handler for childs exiting, store the state for return to the client */
/* There's a particular race we have to watch out for: if the forked child
* executes, exits, and this signal-handler is called, all before the parent
* gets to run, then the childpids[] array won't have the pid in it. Hence we
* use the svr_ses.lastexit struct to hold the exit, which is then compared by
* the parent when it runs. This work correctly at least in the case of a
* single shell spawned (ie the usual case) */
static void sesssigchild_handler(int UNUSED(dummy)) { static void sesssigchild_handler(int UNUSED(dummy)) {
int status; int status;
pid_t pid; pid_t pid;
unsigned int i; unsigned int i;
struct ChanSess * chansess;
struct sigaction sa_chld; struct sigaction sa_chld;
struct exitinfo *exit = NULL;
TRACE(("enter sigchld handler")); TRACE(("enter sigchld handler"));
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
@ -86,25 +93,36 @@ static void sesssigchild_handler(int UNUSED(dummy)) {
for (i = 0; i < svr_ses.childpidsize; i++) { for (i = 0; i < svr_ses.childpidsize; i++) {
if (svr_ses.childpids[i].pid == pid) { if (svr_ses.childpids[i].pid == pid) {
chansess = svr_ses.childpids[i].chansess; exit = &svr_ses.childpids[i].chansess->exit;
chansess->exited = 1; break;
if (WIFEXITED(status)) {
chansess->exitstatus = WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
chansess->exitsignal = WTERMSIG(status);
#if !defined(AIX) && defined(WCOREDUMP)
chansess->exitcore = WCOREDUMP(status);
#else
chansess->exitcore = 0;
#endif
} else {
/* we use this to determine how pid exited */
chansess->exitsignal = -1;
}
} }
} }
/* If the pid wasn't matched, then we might have hit the race mentioned
* above. So we just store the info for the parent to deal with */
if (i == svr_ses.childpidsize) {
exit = &svr_ses.lastexit;
}
exit->exitpid = pid;
if (WIFEXITED(status)) {
exit->exitstatus = WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
exit->exitsignal = WTERMSIG(status);
#if !defined(AIX) && defined(WCOREDUMP)
exit->exitcore = WCOREDUMP(status);
#else
exit->exitcore = 0;
#endif
} else {
/* we use this to determine how pid exited */
exit->exitsignal = -1;
}
exit = NULL;
} }
sa_chld.sa_handler = sesssigchild_handler; sa_chld.sa_handler = sesssigchild_handler;
sa_chld.sa_flags = SA_NOCLDSTOP; sa_chld.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa_chld, NULL); sigaction(SIGCHLD, &sa_chld, NULL);
@ -117,8 +135,8 @@ static void send_exitsignalstatus(struct Channel *channel) {
struct ChanSess *chansess = (struct ChanSess*)channel->typedata; struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
if (chansess->exited) { if (chansess->exit.exitpid >= 0) {
if (chansess->exitsignal > 0) { if (chansess->exit.exitsignal > 0) {
send_msg_chansess_exitsignal(channel, chansess); send_msg_chansess_exitsignal(channel, chansess);
} else { } else {
send_msg_chansess_exitstatus(channel, chansess); send_msg_chansess_exitstatus(channel, chansess);
@ -130,8 +148,8 @@ static void send_exitsignalstatus(struct Channel *channel) {
static void send_msg_chansess_exitstatus(struct Channel * channel, static void send_msg_chansess_exitstatus(struct Channel * channel,
struct ChanSess * chansess) { struct ChanSess * chansess) {
assert(chansess->exited); assert(chansess->exit.exitpid != -1);
assert(chansess->exitsignal == -1); assert(chansess->exit.exitsignal == -1);
CHECKCLEARTOWRITE(); CHECKCLEARTOWRITE();
@ -139,7 +157,7 @@ static void send_msg_chansess_exitstatus(struct Channel * channel,
buf_putint(ses.writepayload, channel->remotechan); buf_putint(ses.writepayload, channel->remotechan);
buf_putstring(ses.writepayload, "exit-status", 11); buf_putstring(ses.writepayload, "exit-status", 11);
buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
buf_putint(ses.writepayload, chansess->exitstatus); buf_putint(ses.writepayload, chansess->exit.exitstatus);
encrypt_packet(); encrypt_packet();
@ -152,15 +170,15 @@ static void send_msg_chansess_exitsignal(struct Channel * channel,
int i; int i;
char* signame = NULL; char* signame = NULL;
assert(chansess->exited); assert(chansess->exit.exitpid != -1);
assert(chansess->exitsignal > 0); assert(chansess->exit.exitsignal > 0);
CHECKCLEARTOWRITE(); CHECKCLEARTOWRITE();
/* we check that we can match a signal name, otherwise /* we check that we can match a signal name, otherwise
* don't send anything */ * don't send anything */
for (i = 0; signames[i].name != NULL; i++) { for (i = 0; signames[i].name != NULL; i++) {
if (signames[i].signal == chansess->exitsignal) { if (signames[i].signal == chansess->exit.exitsignal) {
signame = signames[i].name; signame = signames[i].name;
break; break;
} }
@ -175,7 +193,7 @@ static void send_msg_chansess_exitsignal(struct Channel * channel,
buf_putstring(ses.writepayload, "exit-signal", 11); buf_putstring(ses.writepayload, "exit-signal", 11);
buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
buf_putstring(ses.writepayload, signame, strlen(signame)); buf_putstring(ses.writepayload, signame, strlen(signame));
buf_putbyte(ses.writepayload, chansess->exitcore); buf_putbyte(ses.writepayload, chansess->exit.exitcore);
buf_putstring(ses.writepayload, "", 0); /* error msg */ buf_putstring(ses.writepayload, "", 0); /* error msg */
buf_putstring(ses.writepayload, "", 0); /* lang */ buf_putstring(ses.writepayload, "", 0); /* lang */
@ -199,7 +217,7 @@ static int newchansess(struct Channel *channel) {
chansess->tty = NULL; chansess->tty = NULL;
chansess->term = NULL; chansess->term = NULL;
chansess->exited = 0; chansess->exit.exitpid = -1;
channel->typedata = chansess; channel->typedata = chansess;
@ -263,7 +281,7 @@ static void closechansess(struct Channel *channel) {
if (svr_ses.childpids[i].chansess == chansess) { if (svr_ses.childpids[i].chansess == chansess) {
assert(svr_ses.childpids[i].pid > 0); assert(svr_ses.childpids[i].pid > 0);
TRACE(("closing pid %d", svr_ses.childpids[i].pid)); TRACE(("closing pid %d", svr_ses.childpids[i].pid));
TRACE(("exited = %d", chansess->exited)); TRACE(("exitpid = %d", chansess->exit.exitpid));
svr_ses.childpids[i].pid = -1; svr_ses.childpids[i].pid = -1;
svr_ses.childpids[i].chansess = NULL; svr_ses.childpids[i].chansess = NULL;
} }
@ -592,6 +610,7 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
int outfds[2]; int outfds[2];
int errfds[2]; int errfds[2];
pid_t pid; pid_t pid;
unsigned int i;
TRACE(("enter noptycommand")); TRACE(("enter noptycommand"));
@ -635,12 +654,21 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
TRACE(("continue noptycommand: parent")); TRACE(("continue noptycommand: parent"));
chansess->pid = pid; chansess->pid = pid;
/* add a child pid - Beware: there's a race between this, and the
* exec() called from the child. If the child finishes before we've
* done this (ie if it was a shell builtin and fast), we won't return a
* proper return code. For now, we ignore this case. */
addchildpid(chansess, pid); addchildpid(chansess, pid);
if (svr_ses.lastexit.exitpid != -1) {
/* The child probably exited and the signal handler triggered
* possibly before we got around to adding the childpid. So we fill
* out it's data manually */
for (i = 0; i < svr_ses.childpidsize; i++) {
if (svr_ses.childpids[i].pid == pid) {
svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
svr_ses.lastexit.exitpid = -1;
}
}
}
close(infds[FDIN]); close(infds[FDIN]);
close(outfds[FDOUT]); close(outfds[FDOUT]);
close(errfds[FDOUT]); close(errfds[FDOUT]);