Commit 0252985f authored by Niels Möller's avatar Niels Möller
Browse files

(spawn_process): Let the child process open

the slave side of the pty.
(do_alloc_pty): Don't open or touch the slave side of the pty,
just open the master side and store the mode of the client,a nd
the window dimensions, in the pty_info struct for use later.

Rev: src/server_session.c:1.77
parent 3461f3f9
...@@ -337,6 +337,9 @@ make_pipes(int *in, int *out, int *err) ...@@ -337,6 +337,9 @@ make_pipes(int *in, int *out, int *err)
#if WITH_PTY_SUPPORT #if WITH_PTY_SUPPORT
/* Sets certain fd:s to -1, which means that the slave tty should be
* used (for the child), or that the stdout fd should be duplicated
* (for the parent). */
static int static int
make_pty(struct pty_info *pty, int *in, int *out, int *err) make_pty(struct pty_info *pty, int *in, int *out, int *err)
{ {
...@@ -347,9 +350,8 @@ make_pty(struct pty_info *pty, int *in, int *out, int *err) ...@@ -347,9 +350,8 @@ make_pty(struct pty_info *pty, int *in, int *out, int *err)
debug("exists: \n" debug("exists: \n"
" alive = %i\n" " alive = %i\n"
" master = %i\n" " master = %i\n"
" slave = %i\n"
"... ", "... ",
pty->super.alive, pty->master, pty->slave); pty->super.alive, pty->master);
debug("\n"); debug("\n");
if (pty) if (pty)
...@@ -358,9 +360,9 @@ make_pty(struct pty_info *pty, int *in, int *out, int *err) ...@@ -358,9 +360,9 @@ make_pty(struct pty_info *pty, int *in, int *out, int *err)
debug("make_pty: Using allocated pty.\n"); debug("make_pty: Using allocated pty.\n");
/* Ownership of the master fd:s is passed on to some file /* Ownership of the master fd is passed on to some file
* objects. We need an fd for window_change_request, but we have * object. We need an fd for window_change_request, but we have
* to use one of our regular fd:s to the master side, or we're * to use our regular fd:s to the master side, or we're
* disrupt EOF handling on either side. */ * disrupt EOF handling on either side. */
pty->super.alive = 0; pty->super.alive = 0;
...@@ -369,57 +371,36 @@ make_pty(struct pty_info *pty, int *in, int *out, int *err) ...@@ -369,57 +371,36 @@ make_pty(struct pty_info *pty, int *in, int *out, int *err)
* could use a single lsh_fd object for the master side of the * could use a single lsh_fd object for the master side of the
* pty. */ * pty. */
in[0] = pty->slave; /* -1 means opening deferred to the child */
in[0] = -1;
in[1] = pty->master; in[1] = pty->master;
pty->master = -1;
if ((out[0] = dup(pty->master)) < 0) if ((out[0] = dup(pty->master)) < 0)
{ {
werror("make_pty: duping master pty to stdout failed (errno = %i): %z\n", werror("make_pty: duping master pty to stdout failed (errno = %i): %z\n",
errno, STRERROR(errno)); errno, STRERROR(errno));
close(in[0]); close(in[0]);
close(in[1]);
return 0; return 0;
} }
if ((out[1] = dup(pty->slave)) < 0) out[1] = -1;
{
werror("make_pty: duping slave pty to stdout failed (errno = %i): %z\n",
errno, STRERROR(errno));
close(in[0]);
close(in[1]);
close(out[0]);
return 0;
}
#if BASH_WORKAROUND #if BASH_WORKAROUND
/* Don't use a separate stderr channel; just dup the /* Don't use a separate stderr channel; just dup the
* stdout pty to stderr. */ * stdout pty to stderr. */
if ((err[1] = dup(pty->slave)) < 0)
{
werror("make_pty: duping slave pty to stdout failed (errno = %i): %z\n",
errno, STRERROR(errno));
close(in[0]);
close(in[1]);
close(out[0]);
close(out[1]);
return 0;
}
err[0] = -1; err[0] = -1;
err[1] = -1;
#else /* !BASH_WORKAROUND */ #else /* !BASH_WORKAROUND */
if (!lsh_make_pipe(err)) if (!lsh_make_pipe(err))
{ {
close(in[0]);
close(in[1]); close(in[1]);
close(out[0]); close(out[0]);
close(out[1]);
return 0; return 0;
} }
#endif /* !BASH_WORKAROUND */ #endif /* !BASH_WORKAROUND */
...@@ -440,6 +421,8 @@ spawn_process(struct server_session *session, ...@@ -440,6 +421,8 @@ spawn_process(struct server_session *session,
struct lsh_user *user, struct lsh_user *user,
struct address_info *peer) struct address_info *peer)
{ {
struct lsh_process *child;
int in[2]; int in[2];
int out[2]; int out[2];
int err[2]; int err[2];
...@@ -453,6 +436,7 @@ spawn_process(struct server_session *session, ...@@ -453,6 +436,7 @@ spawn_process(struct server_session *session,
if (session->pty && !make_pty(session->pty, in, out, err)) if (session->pty && !make_pty(session->pty, in, out, err))
{ {
KILL_RESOURCE(&session->pty->super);
KILL(session->pty); KILL(session->pty);
session->pty = NULL; session->pty = NULL;
} }
...@@ -460,169 +444,171 @@ spawn_process(struct server_session *session, ...@@ -460,169 +444,171 @@ spawn_process(struct server_session *session,
if (!session->pty && !make_pipes(in, out, err)) if (!session->pty && !make_pipes(in, out, err))
return -1; return -1;
{ if (USER_FORK(user, &child,
struct lsh_process *child; make_exit_shell(session),
peer, session->pty ? session->pty->tty_name : NULL))
if (USER_FORK(user, &child, {
make_exit_shell(session), if (child)
peer, session->pty ? session->pty->tty_name : NULL)) { /* Parent */
{ struct ssh_channel *channel = &session->super;
if (child) trace("spawn_process: Parent process\n");
{ /* Parent */
struct ssh_channel *channel = &session->super;
trace("spawn_process: Parent process\n");
session->process = child; session->process = child;
/* Close the child's fd:s */ /* Close the child's fd:s */
close(in[0]); close(in[0]);
close(out[1]); close(out[1]);
close(err[1]); close(err[1]);
{ {
/* Exception handlers */ /* Exception handlers */
struct exception_handler *io_exception_handler struct exception_handler *io_exception_handler
= make_channel_io_exception_handler(channel, = make_channel_io_exception_handler(channel,
"lshd: Child stdio: ", "lshd: Child stdio: ",
&default_exception_handler, &default_exception_handler,
HANDLER_CONTEXT); HANDLER_CONTEXT);
/* Close callback for stderr and stdout */ /* Close callback for stderr and stdout */
struct lsh_callback *read_close_callback struct lsh_callback *read_close_callback
= make_channel_read_close_callback(channel); = make_channel_read_close_callback(channel);
session->in session->in
= io_write(make_lsh_fd(in[1], "child stdin", = io_write(make_lsh_fd(in[1], "child stdin",
io_exception_handler), io_exception_handler),
SSH_MAX_PACKET, NULL); SSH_MAX_PACKET, NULL);
/* Flow control */ /* Flow control */
session->in->write_buffer->report = &session->super.super; session->in->write_buffer->report = &session->super.super;
/* FIXME: Should we really use the same exception handler, /* FIXME: Should we really use the same exception handler,
* which will close the channel on read errors, or is it * which will close the channel on read errors, or is it
* better to just send EOF on read errors? */ * better to just send EOF on read errors? */
session->out session->out
= io_read(make_lsh_fd(out[0], "child stdout", = io_read(make_lsh_fd(out[0], "child stdout",
io_exception_handler), io_exception_handler),
make_channel_read_data(channel), make_channel_read_data(channel),
read_close_callback); read_close_callback);
session->err session->err
= ( (err[0] != -1) = ( (err[0] != -1)
? io_read(make_lsh_fd(err[0], "child stderr", ? io_read(make_lsh_fd(err[0], "child stderr",
io_exception_handler), io_exception_handler),
make_channel_read_stderr(channel), make_channel_read_stderr(channel),
read_close_callback) read_close_callback)
: NULL); : NULL);
} }
channel->receive = do_receive; channel->receive = do_receive;
channel->send_adjust = do_send_adjust; channel->send_adjust = do_send_adjust;
channel->eof = do_eof; channel->eof = do_eof;
/* Make sure that the process and it's stdio is /* Make sure that the process and it's stdio is
* cleaned up if the channel or connection dies. */ * cleaned up if the channel or connection dies. */
REMEMBER_RESOURCE
(channel->resources, &child->super);
/* FIXME: How to do this properly if in and out may use the
* same fd? */
REMEMBER_RESOURCE
(channel->resources, &session->in->super);
REMEMBER_RESOURCE
(channel->resources, &session->out->super);
if (session->err)
REMEMBER_RESOURCE REMEMBER_RESOURCE
(channel->resources, &child->super); (channel->resources, &session->err->super);
/* FIXME: How to do this properly if in and out may use the /* Don't close channel immediately at EOF, as we want to
* same fd? */ * get a chance to send exit-status or exit-signal. */
REMEMBER_RESOURCE session->super.flags &= ~CHANNEL_CLOSE_AT_EOF;
(channel->resources, &session->in->super); return 1;
REMEMBER_RESOURCE }
(channel->resources, &session->out->super); else
if (session->err) { /* Child */
REMEMBER_RESOURCE int tty = -1;
(channel->resources, &session->err->super); trace("spawn_process: Child process\n");
assert(getuid() == user->uid);
/* Don't close channel immediately at EOF, as we want to
* get a chance to send exit-status or exit-signal. */
session->super.flags &= ~CHANNEL_CLOSE_AT_EOF;
return 1;
}
else
{ /* Child */
trace("spawn_process: Child process\n");
assert(getuid() == user->uid);
#if 0 #if 0
/* Debug timing problems */ /* Debug timing problems */
if (sleep(5)) if (sleep(5))
{ {
trace("server_session.c: sleep interrupted\n"); trace("server_session.c: sleep interrupted\n");
sleep(5); sleep(5);
} }
#endif #endif
if (!USER_CHDIR_HOME(user)) if (!USER_CHDIR_HOME(user))
{ {
werror("Could not change to home (or root) directory!\n"); werror("Could not change to home (or root) directory!\n");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
#if WITH_PTY_SUPPORT #if WITH_PTY_SUPPORT
if (session->pty) if (session->pty)
{ {
debug("lshd: server.c: Setting controlling tty...\n"); debug("lshd: server.c: Opening slave tty...\n");
if (!tty_setctty(session->pty)) if ( (tty = pty_open_slave(session->pty)) < 0)
{ {
debug("lshd: server.c: " debug("lshd: server.c: "
"Setting controlling tty... Failed!\n"); "Opening slave tty... Failed!\n");
werror("lshd: Can't set controlling tty for child!\n"); werror("lshd: Can't open controlling tty for child!\n");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
else else
debug("lshd: server.c: Setting controlling tty... Ok.\n"); debug("lshd: server.c: Opening slave tty... Ok.\n");
} }
#endif /* WITH_PTY_SUPPORT */ #endif /* WITH_PTY_SUPPORT */
/* Close all descriptors but those used for communicationg /* Close all descriptors but those used for communicationg
* with parent. We rely on the close-on-exec flag for all * with parent. We rely on the close-on-exec flag for all
* other fd:s. */ * other fd:s. */
if (dup2(in[0], STDIN_FILENO) < 0)
{
werror("Can't dup stdin!\n");
_exit(EXIT_FAILURE);
}
close(in[0]);
close(in[1]);
if (dup2(out[1], STDOUT_FILENO) < 0)
{
werror("Can't dup stdout!\n");
_exit(EXIT_FAILURE);
}
close(out[0]);
close(out[1]);
if (!dup_error_stream()) if (dup2(in[0] >= 0 ? in[0] : tty, STDIN_FILENO) < 0)
{ {
werror("server_session: Failed to dup old stderr. Bye.\n"); werror("Can't dup stdin!\n");
set_error_ignore(); _exit(EXIT_FAILURE);
} }
if (dup2(err[1], STDERR_FILENO) < 0) if (dup2(out[1] >= 0 ? out[1] : tty, STDOUT_FILENO) < 0)
{ {
werror("Can't dup stderr!\n"); werror("Can't dup stdout!\n");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
close(err[0]);
close(err[1]); if (!dup_error_stream())
{
werror("server_session: Failed to dup old stderr. Bye.\n");
set_error_ignore();
}
if (dup2(err[1] >= 0 ? err[1] : tty, STDERR_FILENO) < 0)
{
werror("Can't dup stderr!\n");
_exit(EXIT_FAILURE);
}
/* Unconditionally close all the fd:s, no matter if some
* of them are -1. */
close(in[0]);
close(in[1]);
close(out[0]);
close(out[1]);
close(err[0]);
close(err[1]);
close(tty);
return 0;
}
}
/* fork failed */
/* Close all fd:s */
close(err[0]);
close(err[1]);
close(out[0]);
close(out[1]);
close(in[0]);
close(in[1]);
return 0;
}
}
/* fork failed */
/* Close and return channel_failure */
close(err[0]);
close(err[1]);
close(out[0]);
close(out[1]);
close(in[0]);
close(in[1]);
}
return -1; return -1;
} }
...@@ -932,84 +918,57 @@ do_alloc_pty(struct channel_request *c UNUSED, ...@@ -932,84 +918,57 @@ do_alloc_pty(struct channel_request *c UNUSED,
struct command_continuation *s, struct command_continuation *s,
struct exception_handler *e) struct exception_handler *e)
{ {
struct terminal_dimensions dims;
const UINT8 *mode;
UINT32 mode_length;
struct lsh_string *term = NULL; struct lsh_string *term = NULL;
static struct exception pty_request_failed = static struct exception pty_request_failed =
STATIC_EXCEPTION(EXC_CHANNEL_REQUEST, "pty request failed"); STATIC_EXCEPTION(EXC_CHANNEL_REQUEST, "pty request failed");
struct pty_info *pty = make_pty_info();
CAST(server_session, session, channel); CAST(server_session, session, channel);
verbose("Client requesting a tty...\n"); verbose("Client requesting a tty...\n");
if ((term = parse_string_copy(args)) if ((term = parse_string_copy(args))
&& parse_uint32(args, &dims.char_width) && parse_uint32(args, &pty->dims.char_width)
&& parse_uint32(args, &dims.char_height) && parse_uint32(args, &pty->dims.char_height)
&& parse_uint32(args, &dims.pixel_width) && parse_uint32(args, &pty->dims.pixel_width)
&& parse_uint32(args, &dims.pixel_height) && parse_uint32(args, &pty->dims.pixel_height)
&& parse_string(args, &mode_length, &mode) && (pty->mode = parse_string_copy(args))
&& parse_eod(args)) && parse_eod(args))
{ {
/* The client may only request one tty, and only before /* The client may only request one tty, and only before
* starting a process. */ * starting a process. */
if (session->pty || session->process) if (session->pty || session->process
|| !pty_open_master(pty, channel->connection->user->uid))
{ {
lsh_string_free(term); verbose("Pty allocation failed.\n");
EXCEPTION_RAISE(e, &pty_request_failed); EXCEPTION_RAISE(e, &pty_request_failed);
} }
else else
{ {
struct pty_info *pty = make_pty_info(); /* FIXME: Perhaps we can set the window dimensions directly
* on the master pty? */
if (pty_allocate(pty, channel->connection->user->uid)) session->term = term;
{ REMEMBER_RESOURCE(channel->resources, &pty->super);
struct termios ios;
verbose(" granted.\n");
if (tty_getattr(pty->slave, &ios)) COMMAND_RETURN(s, NULL);
{
assert(pty->super.alive); /* Success */
session->pty = pty; return;
/* Don't set TERM if the value is empty. */
if (!term->length)
{
lsh_string_free(term);
term = NULL;
}
session->term = term;
tty_decode_term_mode(&ios, mode_length, mode);
if (tty_setattr(pty->slave, &ios) &&
tty_setwinsize(pty->slave, &dims))
{
REMEMBER_RESOURCE(channel->resources, &pty->super);
verbose(" granted.\n");
COMMAND_RETURN(s, NULL);
return;
}
}
}
/* Close fd:s and mark the pty-struct as dead */
KILL_RESOURCE(&pty->super);
KILL(pty);
} }
verbose("Pty allocation failed.\n");
lsh_string_free(term);
EXCEPTION_RAISE(e, &pty_request_failed);
} }
else else
{ {
werror("Invalid pty request.\n"); werror("Invalid pty request.\n");
lsh_string_free(term);
PROTOCOL_ERROR(e, "Invalid pty request."); PROTOCOL_ERROR(e, "Invalid pty request.");
} }
/* Cleanup for failure cases. */
lsh_string_free(term);
KILL_RESOURCE(&pty->super);
KILL(pty);
} }
struct channel_request struct channel_request
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment