# HG changeset patch # User Matt Johnston # Date 1221486078 0 # Node ID 357a2e2e9bccf17caed1cbf0678bcf4660a2d3fa # Parent c302c7383282d287fe1f9da8acc493e67026ed44 - Generalise spawn_command function diff -r c302c7383282 -r 357a2e2e9bcc dbutil.c --- a/dbutil.c Mon Sep 15 13:06:40 2008 +0000 +++ b/dbutil.c Mon Sep 15 13:41:18 2008 +0000 @@ -389,6 +389,83 @@ 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) { diff -r c302c7383282 -r 357a2e2e9bcc dbutil.h --- a/dbutil.h Mon Sep 15 13:06:40 2008 +0000 +++ b/dbutil.h Mon Sep 15 13:41:18 2008 +0000 @@ -49,6 +49,8 @@ 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); diff -r c302c7383282 -r 357a2e2e9bcc svr-chansession.c --- a/svr-chansession.c Mon Sep 15 13:06:40 2008 +0000 +++ b/svr-chansession.c Mon Sep 15 13:41:18 2008 +0000 @@ -47,7 +47,7 @@ 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 @@ * 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")) - - /* 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; + ret = spawn_command(execchild, chansess, + &channel->writefd, &channel->readfd, &channel->errfd, + &chansess->pid); -#ifdef __uClinux__ - pid = vfork(); -#else - pid = fork(); -#endif - - if (pid < 0) - return DROPBEAR_FAILURE; + if (ret == DROPBEAR_FAILURE) { + return ret; + } - 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 */ + ses.maxfd = MAX(ses.maxfd, channel->writefd); + ses.maxfd = MAX(ses.maxfd, channel->readfd); + ses.maxfd = MAX(ses.maxfd, channel->errfd); - 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 */ + addchildpid(chansess, chansess->pid); - } 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 @@ /* 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 */