summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2008-09-15 13:41:18 +0000
committerMatt Johnston <matt@ucc.asn.au>2008-09-15 13:41:18 +0000
commit185527a1aa012d74533c5129fb208672c4ab7f36 (patch)
tree556d3225dd1c4088dd6af486631ef96e2860a18f
parent9f6f121e0224285b02a0b1bedf48485ed26dc001 (diff)
downloaddropbear-185527a1aa012d74533c5129fb208672c4ab7f36.tar.gz
- Generalise spawn_command function
-rw-r--r--dbutil.c77
-rw-r--r--dbutil.h2
-rw-r--r--svr-chansession.c114
3 files changed, 104 insertions, 89 deletions
diff --git a/dbutil.c b/dbutil.c
index 0967ddc..d10a651 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -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) {
diff --git a/dbutil.h b/dbutil.h
index 856978d..ca09b4a 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -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 */