Commit 4f6d271f authored by Niels Möller's avatar Niels Möller

New function close_fd.

Rev: src/client.c:1.32
Rev: src/io.c:1.24
Rev: src/server.c:1.20
parent 8cd3db51
......@@ -41,11 +41,18 @@
#include "read_packet.h"
#include "service.h"
#include "ssh.h"
#include "translate_signal.h"
#include "unpad.h"
#include "version.h"
#include "werror.h"
#include "xalloc.h"
/* For strsignal */
#ifndef _GNU_SOURCE
#warning _GNU_SOURCE undefined
#endif
#include <string.h>
/* Handle connection and initial handshaking. */
struct client_callback
{
......@@ -317,8 +324,24 @@ struct client_session
struct io_fd *in;
struct io_fd *out;
struct io_fd *err;
/* Where to save the exit code. */
int *exit_status;
};
static int close_client_session(struct ssh_channel *c)
{
struct client_session *session = (struct client_session *) c;
MDEBUG(session);
close_fd(session->in);
close_fd(session->out);
close_fd(session->err);
return LSH_OK | LSH_CHANNEL_PENDING_CLOSE;
}
static int client_session_die(struct ssh_channel *c)
{
struct client_session *closure = (struct client_session *) c;
......@@ -333,6 +356,111 @@ static int client_session_die(struct ssh_channel *c)
exit(EXIT_FAILURE);
}
struct exit_handler
{
struct channel_request super;
int *exit_status;
};
static int do_exit_status(struct channel_request *c,
struct ssh_channel *channel,
int want_reply,
struct simple_buffer *args)
{
struct exit_handler *closure = (struct exit_handler *) c;
int status;
MDEBUG(closure);
if (!want_reply
&& parse_uint32(args, &status)
&& parse_eod(args))
{
*closure->exit_status = status;
ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);;
ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);;
return close_client_session(channel);
}
/* Invalid request */
return LSH_FAIL | LSH_DIE;
}
static int do_exit_signal(struct channel_request *c,
struct ssh_channel *channel,
int want_reply,
struct simple_buffer *args)
{
int signal;
int core;
UINT8 *msg;
UINT32 length;
UINT8 *language;
UINT32 language_length;
struct exit_handler *closure = (struct exit_handler *) c;
MDEBUG(closure);
if (!want_reply
&& parse_uint32(args, &signal)
&& parse_boolean(args, &core)
&& parse_string(args, &length, &msg)
&& parse_string(args, &language_length, &language)
&& parse_eod(args))
{
/* FIXME: What exit status should be returned when the remote
* process dies violently? */
*closure->exit_status = 7;
signal = signal_network_to_local(signal);
werror_utf8(length, msg);
werror("Remote process was killed by %s.\n",
signal ? strsignal(signal) : "an unknown signal");
if (core)
werror("(core dumped remotely)\n");
ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);;
ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);;
return close_client_session(channel);
}
/* Invalid request */
return LSH_FAIL | LSH_DIE;
}
struct channel_request *make_handle_exit_status(int *exit_status)
{
struct exit_handler *self;
NEW(self);
self->super.handler = do_exit_status;
self->exit_status = exit_status;
return &self->super;
}
struct channel_request *make_handle_exit_signal(int *exit_status)
{
struct exit_handler *self;
NEW(self);
self->super.handler = do_exit_signal;
self->exit_status = exit_status;
return &self->super;
}
/* Recieve channel data */
static int do_recieve(struct ssh_channel *c,
int type, struct lsh_string *data)
......@@ -378,6 +506,11 @@ static int do_io(struct ssh_channel *channel)
closure->in->handler = make_channel_read_data(&closure->super);
channel->send = do_send;
ALIST_SET(channel->request_types, ATOM_EXIT_STATUS,
make_handle_exit_status(closure->exit_status));
ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL,
make_handle_exit_signal(closure->exit_status));
return LSH_OK | LSH_GOON;
}
......@@ -409,7 +542,8 @@ static struct ssh_channel *make_client_session(struct io_fd *in,
struct io_fd *err,
UINT32 max_window,
int final_request,
struct lsh_string *args)
struct lsh_string *args,
int *exit_status)
{
struct client_session *self;
......@@ -422,6 +556,8 @@ static struct ssh_channel *make_client_session(struct io_fd *in,
/* FIXME: Make maximum packet size configurable */
self->super.rec_max_packet = SSH_MAX_PACKET;
self->super.request_types = make_alist(0, -1);
/* self->expect_close = 0; */
self->in = in;
......@@ -430,6 +566,8 @@ static struct ssh_channel *make_client_session(struct io_fd *in,
self->final_request = final_request;
self->args = args;
self->exit_status = exit_status;
return &self->super;
}
......@@ -480,7 +618,8 @@ struct connection_startup *make_client_startup(struct io_fd *in,
struct io_fd *out,
struct io_fd *err,
int final_request,
struct lsh_string *args)
struct lsh_string *args,
int *exit_status)
{
struct client_startup *closure;
......@@ -488,7 +627,8 @@ struct connection_startup *make_client_startup(struct io_fd *in,
closure->super.start = do_client_startup;
closure->session = make_client_session(in, out, err,
WINDOW_SIZE,
final_request, args);
final_request, args,
exit_status);
return &closure->super;
}
......
......@@ -71,6 +71,9 @@ static int do_read(struct abstract_read **r, UINT32 length, UINT8 *buffer)
* instead of in the select loop? */
case EWOULDBLOCK: /* aka EAGAIN */
return 0;
case EPIPE:
werror("io.c: read() returned EPIPE! Treating it as EOF.\n");
return A_EOF;
default:
werror("io.c: do_read: read() failed (errno %d), %s\n",
errno, strerror(errno));
......@@ -93,7 +96,7 @@ static int do_read(struct abstract_read **r, UINT32 length, UINT8 *buffer)
/* UNLINK_FD must be followed by a continue, to avoid updating _fd */
#define UNLINK_FD (*_fd = (*_fd)->next)
static void close_fd(struct io_fd *fd)
static void really_close_fd(struct io_fd *fd)
{
/* FIXME: The value returned from the close callback could be used
* to choose an exit code. */
......@@ -121,7 +124,7 @@ static void close_fd(struct io_fd *fd)
#endif
}
static int io_iter(struct io_backend *b)
int io_iter(struct io_backend *b)
{
struct pollfd *fds;
int i;
......@@ -213,7 +216,7 @@ static int io_iter(struct io_backend *b)
case EINTR:
return 1;
default:
fatal("io_run:poll failed: %s", strerror(errno));
fatal("io_iter:poll failed: %s", strerror(errno));
}
}
else
......@@ -290,6 +293,9 @@ static int io_iter(struct io_backend *b)
* this case? */
}
#endif
/* This condition must be taken care of earlier. */
assert(!(res & LSH_CHANNEL_FINISHED));
if (res & LSH_HOLD)
{
/* This flag should not be combined with anything else */
......@@ -333,13 +339,13 @@ static int io_iter(struct io_backend *b)
/* In this case, it should be safe to
* deallocate the buffer immediately */
lsh_object_free(p->buffer);
close_fd(p);
really_close_fd(p);
}
}
if (fd->close_now)
{
/* Some error occured. So close this fd too! */
close_fd(fd);
really_close_fd(fd);
b->io = NULL;
b->nio = 0;
}
......@@ -384,10 +390,11 @@ static int io_iter(struct io_backend *b)
}
b->callouts = NULL;
}
/* Skip the rest od this iteration */
/* Skip the rest of this iteration */
return 1;
}
}
#if 0
if (fd->close_now)
{
/* FIXME: Cleanup properly...
......@@ -395,16 +402,36 @@ static int io_iter(struct io_backend *b)
* After a write error, read state must be freed,
* and vice versa. */
close_fd(fd);
really_close_fd(fd);
UNLINK_FD;
b->nio--;
continue;
}
#endif
}
END_FOR_FDS;
/* Close files */
i = 0; /* Start over */
FOR_FDS(struct io_fd, fd, b->io, i++)
if (fd->close_now)
{
/* FIXME: Cleanup properly...
*
* After a write error, read state must be freed,
* and vice versa. */
really_close_fd(fd);
UNLINK_FD;
b->nio--;
continue;
}
END_FOR_FDS;
FOR_FDS(struct listen_fd, fd, b->listen, i++)
{
if (fds[i].revents & POLLIN)
......@@ -458,7 +485,15 @@ static int io_iter(struct io_backend *b)
/* FIXME: Prehaps this function should return a suitable exit code? */
void io_run(struct io_backend *b)
{
signal(SIGPIPE, SIG_IGN);
struct sigaction pipe;
pipe.sa_handler = SIG_IGN;
sigemptyset(&pipe.sa_mask);
pipe.sa_flags = 0;
pipe.sa_restorer = NULL;
if (sigaction(SIGPIPE, &pipe, NULL) < 0)
fatal("Failed to ignore SIGPIPE.\n");
while(io_iter(b))
;
......@@ -754,3 +789,11 @@ struct io_fd *io_write(struct io_backend *b,
return f;
}
/* Marks a file for closing, at the end of the current iteration.
* FIXME: Could be generalized for other fd:s than read-write fds. */
void close_fd(struct io_fd *fd)
{
fd->close_now = 1;
}
......@@ -33,12 +33,18 @@
#include "keyexchange.h"
#include "read_line.h"
#include "read_packet.h"
#include "reaper.h"
#include "ssh.h"
#include "translate_signal.h"
#include "unpad.h"
#include "version.h"
#include "werror.h"
#include "xalloc.h"
#ifndef _GNU_SOURCE
#warning _GNU_SOURCE undefined
#endif
#include <assert.h>
#include <string.h>
#include <errno.h>
......@@ -226,7 +232,7 @@ struct server_session
int running;
/* Child process's stdio */
struct abstract_write *in;
struct io_fd *in;
struct io_fd *out;
struct io_fd *err;
};
......@@ -242,7 +248,7 @@ static int do_recieve(struct ssh_channel *c,
switch(type)
{
case CHANNEL_DATA:
return A_WRITE(closure->in, data);
return A_WRITE(&closure->in->buffer->super, data);
case CHANNEL_STDERR_DATA:
werror("Ignoring unexpected stderr data.\n");
lsh_string_free(data);
......@@ -318,7 +324,8 @@ static struct ssh_channel *do_open_session(struct channel_open *c,
if (!parse_eod(args))
return 0;
return make_server_session(closure->user, WINDOW_SIZE, closure->session_requests);
return make_server_session(closure->user, WINDOW_SIZE,
closure->session_requests);
}
struct channel_open *make_open_session(struct unix_user *user,
......@@ -382,11 +389,92 @@ struct unix_service *make_server_session_service(struct alist *global_requests,
return &closure->super;
}
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
int core, int signal)
{
struct lsh_string *msg = ssh_format("Process killed by %z.", strsignal(signal));
return format_channel_request(ATOM_EXIT_SIGNAL,
channel,
0,
"%i%c%fS%z",
signal_local_to_network(signal),
core,
msg, "");
}
struct lsh_string *format_exit(struct ssh_channel *channel, int value)
{
return format_channel_request(ATOM_EXIT_STATUS,
channel,
0,
"%i", value);
}
struct exit_shell
{
struct exit_callback super;
struct server_session *session;
};
static void do_exit_shell(struct exit_callback *c, int signaled,
int core, int value)
{
struct exit_shell *closure = (struct exit_shell *) c;
struct server_session *session = closure->session;
struct ssh_channel *channel = &session->super;
MDEBUG(closure);
MDEBUG(session);
close_fd(session->in);
close_fd(session->out);
close_fd(session->err);
if (!(channel->flags & CHANNEL_SENT_EOF)
/* Don't send eof if the process died violently. */
&& !signaled)
{
int res = channel_eof(channel);
if (LSH_CLOSEDP(res))
/* FIXME: Can we do anything better with the return code than
* ignore it? */
return;
}
if (!(channel->flags & CHANNEL_SENT_CLOSE))
{
int res = A_WRITE(channel->write,
signaled
? format_exit_signal(channel, core, value)
: format_exit(channel, value));
res = channel_close(channel);
/* FIXME: Can we do anything better with the return code than
* ignore it? */
return;
}
}
static struct exit_callback *make_exit_shell(struct server_session *session)
{
struct exit_shell *self;
NEW(self);
self->super.exit = do_exit_shell;
self->session = session;
return &self->super;
}
struct shell_request
{
struct channel_request super;
struct io_backend *backend;
struct reap *reap;
};
/* Creates a one-way socket connection. Returns 1 on successm 0 on
......@@ -469,6 +557,8 @@ static int do_spawn_shell(struct channel_request *c,
break;
case 0:
/* Child */
debug("do_spawn_shell: Child process\n");
if (!session->user->shell)
{
werror("No login shell!\n");
......@@ -549,20 +639,21 @@ static int do_spawn_shell(struct channel_request *c,
}
default:
/* Parent */
/* FIXME: Install a callback to catch dying children */
debug("Parent process\n");
REAP(closure->reap, child, make_exit_shell(session));
/* Close the child's fd:s */
close(in[0]);
close(out[1]);
close(err[1]);
session->in
= &io_write(closure->backend, in[1],
= io_write(closure->backend, in[1],
SSH_MAX_PACKET,
/* FIXME: Use a proper close callback */
make_channel_close(channel))
->buffer->super;
make_channel_close(channel));
session->out
= io_read(closure->backend, out[0],
make_channel_read_data(channel),
......@@ -597,13 +688,15 @@ static int do_spawn_shell(struct channel_request *c,
: LSH_OK | LSH_GOON;
}
struct channel_request *make_shell_handler(struct io_backend *backend)
struct channel_request *make_shell_handler(struct io_backend *backend,
struct reap *reap)
{
struct shell_request *closure;
NEW(closure);
closure->super.handler = do_spawn_shell;
closure->backend = backend;
closure->reap = reap;
return &closure->super;
}
......
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