summaryrefslogtreecommitdiffstats
path: root/server-client.c
diff options
context:
space:
mode:
authornicm <nicm>2019-12-12 11:39:56 +0000
committernicm <nicm>2019-12-12 11:39:56 +0000
commitc284ebe0ade7cc85ad6c3fe5ce7ed5108119222d (patch)
tree52fa29b04da1e5468bdce23cce61bf0370923c2e /server-client.c
parent64fb7e472a9e627ee486707ab9d30b607d76478a (diff)
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.
Diffstat (limited to 'server-client.c')
-rw-r--r--server-client.c213
1 files changed, 86 insertions, 127 deletions
diff --git a/server-client.c b/server-client.c
index bd256cb8..e6e4d8a9 100644
--- a/server-client.c
+++ b/server-client.c
@@ -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,91 +1997,69 @@ 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;
-
- if (~c->flags & CLIENT_DEAD)
- server_client_push_stdout(c);
- server_client_unref(c);
+ struct msg_write_ready *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+
+ 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);
}
-/* Push stdout to client if possible. */
-void
-server_client_push_stdout(struct client *c)
+/* Handle read data message. */
+static void
+server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
{
- 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);
+ 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 (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->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);
+ 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);
}
}
-/* Event callback to push more stderr data if any left. */
+/* Handle read done message. */
static void
-server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
+server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
{
- 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);
+ 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;
- }
-
- 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;
-
- if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
- break;
- evbuffer_drain(c->stderr_data, sent);
- 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 done", c->name, cf->stream);
+ cf->error = msg->error;
+ file_fire_done(cf);
}
/* Add to client message log. */