- Generalise spawn_command function

--HG--
extra : convert_revision : 9927a5fe084c8053c747a40515f0213141ef8139
This commit is contained in:
Matt Johnston 2008-09-15 13:41:18 +00:00
parent d7c1e58217
commit 7ac24b10b9
3 changed files with 104 additions and 89 deletions

View File

@ -389,6 +389,83 @@ int connect_remote(const char* remotehost, const char* remoteport,
return sock;
}
/* Sets up a pipe for a, returning three non-blocking file descriptors
* and the pid. exec_fn is the function that will actually execute the child process,
* it will be run after the child has fork()ed, and is passed exec_data. */
int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid)
{
int infds[2];
int outfds[2];
int errfds[2];
pid_t pid;
const int FDIN = 0;
const int FDOUT = 1;
/* redirect stdin/stdout/stderr */
if (pipe(infds) != 0)
return DROPBEAR_FAILURE;
if (pipe(outfds) != 0)
return DROPBEAR_FAILURE;
if (pipe(errfds) != 0)
return DROPBEAR_FAILURE;
#ifdef __uClinux__
pid = vfork();
#else
pid = fork();
#endif
if (pid < 0)
return DROPBEAR_FAILURE;
if (!pid) {
/* child */
TRACE(("back to normal sigchld"))
/* Revert to normal sigchld handling */
if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
dropbear_exit("signal() error");
}
/* redirect stdin/stdout */
if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
TRACE(("leave noptycommand: error redirecting FDs"))
dropbear_exit("child dup2() failure");
}
close(infds[FDOUT]);
close(infds[FDIN]);
close(outfds[FDIN]);
close(outfds[FDOUT]);
close(errfds[FDIN]);
close(errfds[FDOUT]);
exec_fn(exec_data);
/* not reached */
return DROPBEAR_FAILURE;
} else {
/* parent */
close(infds[FDIN]);
close(outfds[FDOUT]);
close(errfds[FDOUT]);
setnonblocking(errfds[FDIN]);
setnonblocking(outfds[FDIN]);
setnonblocking(infds[FDOUT]);
*ret_pid = pid;
*ret_writefd = infds[FDOUT];
*ret_readfd = outfds[FDIN];
*ret_errfd = errfds[FDIN];
return DROPBEAR_SUCCESS;
}
}
/* Return a string representation of the socket address passed. The return
* value is allocated with malloc() */
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {

View File

@ -49,6 +49,8 @@ char * stripcontrol(const char * text);
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
int dropbear_listen(const char* address, const char* port,
int *socks, unsigned int sockcount, char **errstring, int *maxfd);
int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
int *writefd, int *readfd, int *errfd, pid_t *pid);
int connect_remote(const char* remotehost, const char* remoteport,
int nonblocking, char ** errstring);
char* getaddrhostname(struct sockaddr_storage * addr);

View File

@ -47,7 +47,7 @@ static int sessionsignal(struct ChanSess *chansess);
static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
static int sessionwinchange(struct ChanSess *chansess);
static void execchild(struct ChanSess *chansess);
static void execchild(void *user_data_chansess);
static void addchildpid(struct ChanSess *chansess, pid_t pid);
static void sesssigchild_handler(int val);
static void closechansess(struct Channel *channel);
@ -633,102 +633,37 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
* pty.
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
int infds[2];
int outfds[2];
int errfds[2];
pid_t pid;
unsigned int i;
const int FDIN = 0;
const int FDOUT = 1;
int ret;
TRACE(("enter noptycommand"))
ret = spawn_command(execchild, chansess,
&channel->writefd, &channel->readfd, &channel->errfd,
&chansess->pid);
/* redirect stdin/stdout/stderr */
if (pipe(infds) != 0)
return DROPBEAR_FAILURE;
if (pipe(outfds) != 0)
return DROPBEAR_FAILURE;
if (pipe(errfds) != 0)
return DROPBEAR_FAILURE;
if (ret == DROPBEAR_FAILURE) {
return ret;
}
#ifdef __uClinux__
pid = vfork();
#else
pid = fork();
#endif
ses.maxfd = MAX(ses.maxfd, channel->writefd);
ses.maxfd = MAX(ses.maxfd, channel->readfd);
ses.maxfd = MAX(ses.maxfd, channel->errfd);
if (pid < 0)
return DROPBEAR_FAILURE;
addchildpid(chansess, chansess->pid);
if (!pid) {
/* child */
TRACE(("back to normal sigchld"))
/* Revert to normal sigchld handling */
if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
dropbear_exit("signal() error");
}
/* redirect stdin/stdout */
if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
TRACE(("leave noptycommand: error redirecting FDs"))
return DROPBEAR_FAILURE;
}
close(infds[FDOUT]);
close(infds[FDIN]);
close(outfds[FDIN]);
close(outfds[FDOUT]);
close(errfds[FDIN]);
close(errfds[FDOUT]);
execchild(chansess);
/* not reached */
} else {
/* parent */
TRACE(("continue noptycommand: parent"))
chansess->pid = pid;
TRACE(("child pid is %d", pid))
addchildpid(chansess, pid);
if (svr_ses.lastexit.exitpid != -1) {
TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
/* The child probably exited and the signal handler triggered
* possibly before we got around to adding the childpid. So we fill
* out its data manually */
for (i = 0; i < svr_ses.childpidsize; i++) {
if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
TRACE(("found match for lastexitpid"))
svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
svr_ses.lastexit.exitpid = -1;
}
if (svr_ses.lastexit.exitpid != -1) {
TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
/* The child probably exited and the signal handler triggered
* possibly before we got around to adding the childpid. So we fill
* out its data manually */
int i;
for (i = 0; i < svr_ses.childpidsize; i++) {
if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
TRACE(("found match for lastexitpid"))
svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
svr_ses.lastexit.exitpid = -1;
}
}
close(infds[FDIN]);
close(outfds[FDOUT]);
close(errfds[FDOUT]);
channel->writefd = infds[FDOUT];
channel->readfd = outfds[FDIN];
channel->errfd = errfds[FDIN];
ses.maxfd = MAX(ses.maxfd, channel->writefd);
ses.maxfd = MAX(ses.maxfd, channel->readfd);
ses.maxfd = MAX(ses.maxfd, channel->errfd);
setnonblocking(channel->readfd);
setnonblocking(channel->writefd);
setnonblocking(channel->errfd);
}
#undef FDIN
#undef FDOUT
TRACE(("leave noptycommand"))
return DROPBEAR_SUCCESS;
@ -873,12 +808,13 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) {
/* Clean up, drop to user privileges, set up the environment and execute
* the command/shell. This function does not return. */
static void execchild(struct ChanSess *chansess) {
static void execchild(void *user_data) {
char *argv[4];
char * usershell = NULL;
char * baseshell = NULL;
unsigned int i;
struct ChanSess *chansess = user_data;
/* with uClinux we'll have vfork()ed, so don't want to overwrite the
* hostkey. can't think of a workaround to clear it */