diff options
author | Matt Johnston <matt@ucc.asn.au> | 2008-09-15 13:41:18 +0000 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2008-09-15 13:41:18 +0000 |
commit | 185527a1aa012d74533c5129fb208672c4ab7f36 (patch) | |
tree | 556d3225dd1c4088dd6af486631ef96e2860a18f | |
parent | 9f6f121e0224285b02a0b1bedf48485ed26dc001 (diff) | |
download | dropbear-185527a1aa012d74533c5129fb208672c4ab7f36.tar.gz |
- Generalise spawn_command function
-rw-r--r-- | dbutil.c | 77 | ||||
-rw-r--r-- | dbutil.h | 2 | ||||
-rw-r--r-- | svr-chansession.c | 114 |
3 files changed, 104 insertions, 89 deletions
@@ -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) { @@ -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); diff --git a/svr-chansession.c b/svr-chansession.c index 364daf0..6c99e70 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -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; - -#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")) - 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 */ + if (ret == DROPBEAR_FAILURE) { + return ret; + } - } else { - /* parent */ - TRACE(("continue noptycommand: parent")) - chansess->pid = pid; - TRACE(("child pid is %d", pid)) + ses.maxfd = MAX(ses.maxfd, channel->writefd); + ses.maxfd = MAX(ses.maxfd, channel->readfd); + ses.maxfd = MAX(ses.maxfd, channel->errfd); - addchildpid(chansess, pid); + addchildpid(chansess, 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 */ |