Rewrite the code for reading and writing files. Now, if the client is

not attached, the server process asks it to open the file, similar to
how works for stdin, stdout, stderr. This makes special files like
/dev/fd/X work (used by some shells). stdin, stdout and stderr and
control mode are now just special cases of the same mechanism. This will
also make it easier to use for other commands that read files such as
source-file.
This commit is contained in:
nicm
2019-12-12 11:39:56 +00:00
parent 64fb7e472a
commit c284ebe0ad
13 changed files with 955 additions and 501 deletions

View File

@ -50,6 +50,12 @@ static void server_client_dispatch(struct imsg *, void *);
static void server_client_dispatch_command(struct client *, struct imsg *);
static void server_client_dispatch_identify(struct client *, struct imsg *);
static void server_client_dispatch_shell(struct client *);
static void server_client_dispatch_write_ready(struct client *,
struct imsg *);
static void server_client_dispatch_read_data(struct client *,
struct imsg *);
static void server_client_dispatch_read_done(struct client *,
struct imsg *);
/* Number of attached clients. */
u_int
@ -197,16 +203,6 @@ server_client_create(int fd)
TAILQ_INIT(&c->queue);
c->stdin_data = evbuffer_new();
if (c->stdin_data == NULL)
fatalx("out of memory");
c->stdout_data = evbuffer_new();
if (c->stdout_data == NULL)
fatalx("out of memory");
c->stderr_data = evbuffer_new();
if (c->stderr_data == NULL)
fatalx("out of memory");
c->tty.fd = -1;
c->title = NULL;
@ -225,6 +221,8 @@ server_client_create(int fd)
c->prompt_buffer = NULL;
c->prompt_index = 0;
RB_INIT(&c->files);
c->flags |= CLIENT_FOCUSED;
c->keytable = key_bindings_get_table("root", 1);
@ -266,6 +264,7 @@ void
server_client_lost(struct client *c)
{
struct message_entry *msg, *msg1;
struct client_file *cf;
c->flags |= CLIENT_DEAD;
@ -273,8 +272,10 @@ server_client_lost(struct client *c)
status_prompt_clear(c);
status_message_clear(c);
if (c->stdin_callback != NULL)
c->stdin_callback(c, 1, c->stdin_callback_data);
RB_FOREACH(cf, client_files, &c->files) {
cf->error = EINTR;
file_fire_done(cf);
}
TAILQ_REMOVE(&clients, c, entry);
log_debug("lost client %p", c);
@ -288,11 +289,6 @@ server_client_lost(struct client *c)
free(c->ttyname);
free(c->term);
evbuffer_free(c->stdin_data);
evbuffer_free(c->stdout_data);
if (c->stderr_data != c->stdout_data)
evbuffer_free(c->stderr_data);
status_free(c);
free(c->title);
@ -1560,17 +1556,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
static void
server_client_check_exit(struct client *c)
{
struct client_file *cf;
if (~c->flags & CLIENT_EXIT)
return;
if (c->flags & CLIENT_EXITED)
return;
if (EVBUFFER_LENGTH(c->stdin_data) != 0)
return;
if (EVBUFFER_LENGTH(c->stdout_data) != 0)
return;
if (EVBUFFER_LENGTH(c->stderr_data) != 0)
return;
RB_FOREACH(cf, client_files, &c->files) {
if (EVBUFFER_LENGTH(cf->buffer) != 0)
return;
}
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
@ -1709,11 +1705,10 @@ server_client_set_title(struct client *c)
static void
server_client_dispatch(struct imsg *imsg, void *arg)
{
struct client *c = arg;
struct msg_stdin_data stdindata;
const char *data;
ssize_t datalen;
struct session *s;
struct client *c = arg;
const char *data;
ssize_t datalen;
struct session *s;
if (c->flags & CLIENT_DEAD)
return;
@ -1740,21 +1735,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
case MSG_COMMAND:
server_client_dispatch_command(c, imsg);
break;
case MSG_STDIN:
if (datalen != sizeof stdindata)
fatalx("bad MSG_STDIN size");
memcpy(&stdindata, data, sizeof stdindata);
if (c->stdin_callback == NULL)
break;
if (stdindata.size <= 0)
c->stdin_closed = 1;
else {
evbuffer_add(c->stdin_data, stdindata.data,
stdindata.size);
}
c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data);
break;
case MSG_RESIZE:
if (datalen != 0)
fatalx("bad MSG_RESIZE size");
@ -1806,6 +1786,15 @@ server_client_dispatch(struct imsg *imsg, void *arg)
server_client_dispatch_shell(c);
break;
case MSG_WRITE_READY:
server_client_dispatch_write_ready(c, imsg);
break;
case MSG_READ:
server_client_dispatch_read_data(c, imsg);
break;
case MSG_READ_DONE:
server_client_dispatch_read_done(c, imsg);
break;
}
}
@ -1824,7 +1813,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
static void
server_client_dispatch_command(struct client *c, struct imsg *imsg)
{
struct msg_command_data data;
struct msg_command data;
char *buf;
size_t len;
int argc;
@ -1964,19 +1953,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
log_debug("client %p name is %s", c, c->name);
if (c->flags & CLIENT_CONTROL) {
c->stdin_callback = control_callback;
evbuffer_free(c->stderr_data);
c->stderr_data = c->stdout_data;
if (c->flags & CLIENT_CONTROLCONTROL)
evbuffer_add_printf(c->stdout_data, "\033P1000p");
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
c->tty.fd = -1;
close(c->fd);
c->fd = -1;
control_start(c);
c->tty.fd = -1;
} else if (c->fd != -1) {
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
close(c->fd);
@ -2016,93 +1997,71 @@ server_client_dispatch_shell(struct client *c)
proc_kill_peer(c->peer);
}
/* Event callback to push more stdout data if any left. */
/* Handle write ready message. */
static void
server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
{
struct client *c = arg;
struct msg_write_ready *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (~c->flags & CLIENT_DEAD)
server_client_push_stdout(c);
server_client_unref(c);
}
/* Push stdout to client if possible. */
void
server_client_push_stdout(struct client *c)
{
struct msg_stdout_data data;
size_t sent, left;
left = EVBUFFER_LENGTH(c->stdout_data);
while (left != 0) {
sent = left;
if (sent > sizeof data.data)
sent = sizeof data.data;
memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
data.size = sent;
if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
break;
evbuffer_drain(c->stdout_data, sent);
left = EVBUFFER_LENGTH(c->stdout_data);
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
sent, left);
}
if (left != 0) {
c->references++;
event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
log_debug("%s: client %p, queued", __func__, c);
}
}
/* Event callback to push more stderr data if any left. */
static void
server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
{
struct client *c = arg;
if (~c->flags & CLIENT_DEAD)
server_client_push_stderr(c);
server_client_unref(c);
}
/* Push stderr to client if possible. */
void
server_client_push_stderr(struct client *c)
{
struct msg_stderr_data data;
size_t sent, left;
if (c->stderr_data == c->stdout_data) {
server_client_push_stdout(c);
if (msglen != sizeof *msg)
fatalx("bad MSG_WRITE_READY size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
return;
}
if (msg->error != 0) {
cf->error = msg->error;
file_fire_done(cf);
} else
file_push(cf);
}
left = EVBUFFER_LENGTH(c->stderr_data);
while (left != 0) {
sent = left;
if (sent > sizeof data.data)
sent = sizeof data.data;
memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
data.size = sent;
/* Handle read data message. */
static void
server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
{
struct msg_read_data *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
void *bdata = msg->data;
size_t bsize = msg->size;
if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
break;
evbuffer_drain(c->stderr_data, sent);
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_DATA size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
return;
left = EVBUFFER_LENGTH(c->stderr_data);
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
sent, left);
}
if (left != 0) {
c->references++;
event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
log_debug("%s: client %p, queued", __func__, c);
log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
if (cf->error == 0) {
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
cf->error = ENOMEM;
file_fire_done(cf);
} else
file_fire_read(cf);
}
}
/* Handle read done message. */
static void
server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
{
struct msg_read_done *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_DONE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
return;
log_debug("%s: file %d read done", c->name, cf->stream);
cf->error = msg->error;
file_fire_done(cf);
}
/* Add to client message log. */
void
server_client_add_message(struct client *c, const char *fmt, ...)