summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client.c64
-rw-r--r--cmd-load-buffer.c51
-rw-r--r--cmd-save-buffer.c3
-rw-r--r--server-client.c159
-rw-r--r--server-fn.c80
-rw-r--r--tmux.h41
6 files changed, 213 insertions, 185 deletions
diff --git a/client.c b/client.c
index 113083fd..4ebcf2ad 100644
--- a/client.c
+++ b/client.c
@@ -35,6 +35,7 @@
struct imsgbuf client_ibuf;
struct event client_event;
+struct event client_stdin;
enum {
CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED,
@@ -56,6 +57,7 @@ void client_send_environ(void);
void client_write_server(enum msgtype, void *, size_t);
void client_update_event(void);
void client_signal(int, short, void *);
+void client_stdin_callback(int, short, void *);
void client_callback(int, short, void *);
int client_dispatch_attached(void);
int client_dispatch_wait(void *);
@@ -227,6 +229,11 @@ client_main(int argc, char **argv, int flags)
imsg_init(&client_ibuf, fd);
event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
+ /* Create stdin handler. */
+ setblocking(STDIN_FILENO, 0);
+ event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
+ client_stdin_callback, NULL);
+
/* Establish signal handlers. */
set_signals(client_signal);
@@ -255,6 +262,7 @@ client_main(int argc, char **argv, int flags)
/* Set the event and dispatch. */
client_update_event();
+ event_add (&client_stdin, NULL);
event_dispatch();
/* Print the exit message, if any, and exit. */
@@ -266,6 +274,7 @@ client_main(int argc, char **argv, int flags)
if (client_exittype == MSG_DETACHKILL && ppid > 1)
kill(ppid, SIGHUP);
}
+ setblocking(STDIN_FILENO, 1);
return (client_exitval);
}
@@ -287,20 +296,11 @@ client_send_identify(int flags)
strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
*data.term = '\0';
- if ((fd = dup(STDOUT_FILENO)) == -1)
- fatal("dup failed");
- imsg_compose(&client_ibuf,
- MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
-
- if ((fd = dup(STDERR_FILENO)) == -1)
- fatal("dup failed");
- imsg_compose(&client_ibuf,
- MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
-
if ((fd = dup(STDIN_FILENO)) == -1)
fatal("dup failed");
imsg_compose(&client_ibuf,
MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
+ client_update_event();
}
/* Forward entire environment to server. */
@@ -322,6 +322,7 @@ void
client_write_server(enum msgtype type, void *buf, size_t len)
{
imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
+ client_update_event();
}
/* Update client event based on whether it needs to read or read and write. */
@@ -421,6 +422,23 @@ lost_server:
event_loopexit(NULL);
}
+/* Callback for client stdin read events. */
+/* ARGSUSED */
+void
+client_stdin_callback(unused int fd, unused short events, unused void *data1)
+{
+ struct msg_stdin_data data;
+
+ data.size = read(STDIN_FILENO, data.data, sizeof data.data);
+ if (data.size < 0 && (errno == EINTR || errno == EAGAIN))
+ return;
+
+ client_write_server(MSG_STDIN, &data, sizeof data);
+ if (data.size <= 0)
+ event_del(&client_stdin);
+ client_update_event();
+}
+
/* Dispatch imsgs when in wait state (before MSG_READY). */
int
client_dispatch_wait(void *data)
@@ -429,11 +447,10 @@ client_dispatch_wait(void *data)
ssize_t n, datalen;
struct msg_shell_data shelldata;
struct msg_exit_data exitdata;
+ struct msg_stdout_data stdoutdata;
+ struct msg_stderr_data stderrdata;
const char *shellcmd = data;
- if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
- fatalx("imsg_read failed");
-
for (;;) {
if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
fatalx("imsg_get failed");
@@ -441,6 +458,7 @@ client_dispatch_wait(void *data)
return (0);
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+ log_debug("got %d from server", imsg.hdr.type);
switch (imsg.hdr.type) {
case MSG_EXIT:
case MSG_SHUTDOWN:
@@ -457,14 +475,30 @@ client_dispatch_wait(void *data)
if (datalen != 0)
fatalx("bad MSG_READY size");
+ event_del(&client_stdin);
client_attached = 1;
break;
+ case MSG_STDOUT:
+ if (datalen != sizeof stdoutdata)
+ fatalx("bad MSG_STDOUT");
+ memcpy(&stdoutdata, imsg.data, sizeof stdoutdata);
+
+ fwrite(stdoutdata.data, stdoutdata.size, 1, stdout);
+ break;
+ case MSG_STDERR:
+ if (datalen != sizeof stderrdata)
+ fatalx("bad MSG_STDERR");
+ memcpy(&stderrdata, imsg.data, sizeof stderrdata);
+
+ fwrite(stderrdata.data, stderrdata.size, 1, stderr);
+ break;
case MSG_VERSION:
if (datalen != 0)
fatalx("bad MSG_VERSION size");
- log_warnx("protocol version mismatch (client %u, "
- "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
+ fprintf(stderr, "protocol version mismatch "
+ "(client %u, server %u)\n", PROTOCOL_VERSION,
+ imsg.hdr.peerid);
client_exitval = 1;
imsg_free(&imsg);
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 09fddbf7..1e6b6024 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -31,7 +31,7 @@
*/
int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *);
-void cmd_load_buffer_callback(struct client *, void *);
+void cmd_load_buffer_callback(struct client *, int, void *);
const struct cmd_entry cmd_load_buffer_entry = {
"load-buffer", "loadb",
@@ -54,8 +54,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
char *pdata, *new_pdata, *cause;
size_t psize;
u_int limit;
- int ch, buffer;
- int *buffer_ptr;
+ int ch, error, buffer, *buffer_ptr;
if (!args_has(args, 'b'))
buffer = -1;
@@ -70,27 +69,16 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
path = args->argv[0];
if (strcmp(path, "-") == 0) {
- if (c == NULL) {
- ctx->error(ctx, "%s: can't read from stdin", path);
- return (-1);
- }
- if (c->flags & CLIENT_TERMINAL) {
- ctx->error(ctx, "%s: stdin is a tty", path);
- return (-1);
- }
- if (c->stdin_fd == -1) {
- ctx->error(ctx, "%s: can't read from stdin", path);
- return (-1);
- }
-
buffer_ptr = xmalloc(sizeof *buffer_ptr);
*buffer_ptr = buffer;
- c->stdin_data = buffer_ptr;
- c->stdin_callback = cmd_load_buffer_callback;
-
- c->references++;
- bufferevent_enable(c->stdin_event, EV_READ);
+ error = server_set_stdin_callback (c, cmd_load_buffer_callback,
+ buffer_ptr, &cause);
+ if (error != 0) {
+ ctx->error(ctx, "%s: %s", path, cause);
+ xfree(cause);
+ return (-1);
+ }
return (1);
}
@@ -154,35 +142,36 @@ error:
}
void
-cmd_load_buffer_callback(struct client *c, void *data)
+cmd_load_buffer_callback(struct client *c, int closed, void *data)
{
int *buffer = data;
char *pdata;
size_t psize;
u_int limit;
- /*
- * Event callback has already checked client is not dead and reduced
- * its reference count. But tell it to exit.
- */
+ if (!closed)
+ return;
+ c->stdin_callback = NULL;
+
+ c->references--;
c->flags |= CLIENT_EXIT;
- psize = EVBUFFER_LENGTH(c->stdin_event->input);
+ psize = EVBUFFER_LENGTH(c->stdin_data);
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) {
xfree(data);
return;
}
- bufferevent_read(c->stdin_event, pdata, psize);
+ memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
pdata[psize] = '\0';
+ evbuffer_drain(c->stdin_data, psize);
limit = options_get_number(&global_options, "buffer-limit");
if (*buffer == -1)
paste_add(&global_buffers, pdata, psize, limit);
else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
/* No context so can't use server_client_msg_error. */
- evbuffer_add_printf(
- c->stderr_event->output, "no buffer %d\n", *buffer);
- bufferevent_enable(c->stderr_event, EV_WRITE);
+ evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer);
+ server_push_stderr(c);
}
xfree(data);
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 93ec4b0b..80f9d572 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -79,7 +79,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
ctx->error(ctx, "%s: can't write to stdout", path);
return (-1);
}
- bufferevent_write(c->stdout_event, pb->data, pb->size);
+ evbuffer_add(c->stdout_data, pb->data, pb->size);
+ server_push_stdout(c);
} else {
if (c != NULL)
wd = c->cwd;
diff --git a/server-client.c b/server-client.c
index 2cbb5bf3..25f17f06 100644
--- a/server-client.c
+++ b/server-client.c
@@ -35,9 +35,6 @@ void server_client_check_exit(struct client *);
void server_client_check_redraw(struct client *);
void server_client_set_title(struct client *);
void server_client_reset_state(struct client *);
-void server_client_in_callback(struct bufferevent *, short, void *);
-void server_client_out_callback(struct bufferevent *, short, void *);
-void server_client_err_callback(struct bufferevent *, short, void *);
int server_client_msg_dispatch(struct client *);
void server_client_msg_command(struct client *, struct msg_command_data *);
@@ -67,9 +64,9 @@ server_client_create(int fd)
fatal("gettimeofday failed");
memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
- c->stdin_event = NULL;
- c->stdout_event = NULL;
- c->stderr_event = NULL;
+ c->stdin_data = evbuffer_new ();
+ c->stdout_data = evbuffer_new ();
+ c->stderr_data = evbuffer_new ();
c->tty.fd = -1;
c->title = NULL;
@@ -144,24 +141,9 @@ server_client_lost(struct client *c)
if (c->flags & CLIENT_TERMINAL)
tty_free(&c->tty);
- if (c->stdin_event != NULL)
- bufferevent_free(c->stdin_event);
- if (c->stdin_fd != -1) {
- setblocking(c->stdin_fd, 1);
- close(c->stdin_fd);
- }
- if (c->stdout_event != NULL)
- bufferevent_free(c->stdout_event);
- if (c->stdout_fd != -1) {
- setblocking(c->stdout_fd, 1);
- close(c->stdout_fd);
- }
- if (c->stderr_event != NULL)
- bufferevent_free(c->stderr_event);
- if (c->stderr_fd != -1) {
- setblocking(c->stderr_fd, 1);
- close(c->stderr_fd);
- }
+ evbuffer_free (c->stdin_data);
+ evbuffer_free (c->stdout_data);
+ evbuffer_free (c->stderr_data);
status_free_jobs(&c->status_new);
status_free_jobs(&c->status_old);
@@ -240,6 +222,9 @@ server_client_callback(int fd, short events, void *data)
goto client_lost;
}
+ server_push_stdout(c);
+ server_push_stderr(c);
+
server_update_event(c);
return;
@@ -603,11 +588,11 @@ server_client_check_exit(struct client *c)
if (!(c->flags & CLIENT_EXIT))
return;
- if (c->stdout_fd != -1 && c->stdout_event != NULL &&
- EVBUFFER_LENGTH(c->stdout_event->output) != 0)
+ if (EVBUFFER_LENGTH(c->stdin_data) != 0)
+ return;
+ if (EVBUFFER_LENGTH(c->stdout_data) != 0)
return;
- if (c->stderr_fd != -1 && c->stderr_event != NULL &&
- EVBUFFER_LENGTH(c->stderr_event->output) != 0)
+ if (EVBUFFER_LENGTH(c->stderr_data) != 0)
return;
exitdata.retcode = c->retcode;
@@ -686,55 +671,6 @@ server_client_set_title(struct client *c)
xfree(title);
}
-/*
- * Error callback for client stdin. Caller must increase reference count when
- * enabling event!
- */
-void
-server_client_in_callback(
- unused struct bufferevent *bufev, unused short what, void *data)
-{
- struct client *c = data;
-
- c->references--;
- if (c->flags & CLIENT_DEAD)
- return;
-
- bufferevent_disable(c->stdin_event, EV_READ|EV_WRITE);
- setblocking(c->stdin_fd, 1);
- close(c->stdin_fd);
- c->stdin_fd = -1;
-
- if (c->stdin_callback != NULL)
- c->stdin_callback(c, c->stdin_data);
-}
-
-/* Error callback for client stdout. */
-void
-server_client_out_callback(
- unused struct bufferevent *bufev, unused short what, unused void *data)
-{
- struct client *c = data;
-
- bufferevent_disable(c->stdout_event, EV_READ|EV_WRITE);
- setblocking(c->stdout_fd, 1);
- close(c->stdout_fd);
- c->stdout_fd = -1;
-}
-
-/* Error callback for client stderr. */
-void
-server_client_err_callback(
- unused struct bufferevent *bufev, unused short what, unused void *data)
-{
- struct client *c = data;
-
- bufferevent_disable(c->stderr_event, EV_READ|EV_WRITE);
- setblocking(c->stderr_fd, 1);
- close(c->stderr_fd);
- c->stderr_fd = -1;
-}
-
/* Dispatch message from client. */
int
server_client_msg_dispatch(struct client *c)
@@ -743,6 +679,7 @@ server_client_msg_dispatch(struct client *c)
struct msg_command_data commanddata;
struct msg_identify_data identifydata;
struct msg_environ_data environdata;
+ struct msg_stdin_data stdindata;
ssize_t n, datalen;
if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
@@ -778,42 +715,23 @@ server_client_msg_dispatch(struct client *c)
fatalx("MSG_IDENTIFY missing fd");
memcpy(&identifydata, imsg.data, sizeof identifydata);
- c->stdin_fd = imsg.fd;
- c->stdin_event = bufferevent_new(c->stdin_fd,
- NULL, NULL, server_client_in_callback, c);
- if (c->stdin_event == NULL)
- fatalx("failed to create stdin event");
- setblocking(c->stdin_fd, 0);
-
server_client_msg_identify(c, &identifydata, imsg.fd);
break;
- case MSG_STDOUT:
- if (datalen != 0)
- fatalx("bad MSG_STDOUT size");
- if (imsg.fd == -1)
- fatalx("MSG_STDOUT missing fd");
-
- c->stdout_fd = imsg.fd;
- c->stdout_event = bufferevent_new(c->stdout_fd,
- NULL, NULL, server_client_out_callback, c);
- if (c->stdout_event == NULL)
- fatalx("failed to create stdout event");
- setblocking(c->stdout_fd, 0);
-
- break;
- case MSG_STDERR:
- if (datalen != 0)
- fatalx("bad MSG_STDERR size");
- if (imsg.fd == -1)
- fatalx("MSG_STDERR missing fd");
-
- c->stderr_fd = imsg.fd;
- c->stderr_event = bufferevent_new(c->stderr_fd,
- NULL, NULL, server_client_err_callback, c);
- if (c->stderr_event == NULL)
- fatalx("failed to create stderr event");
- setblocking(c->stderr_fd, 0);
+ case MSG_STDIN:
+ if (datalen != sizeof stdindata)
+ fatalx("bad MSG_STDIN size");
+ memcpy(&stdindata, imsg.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)
@@ -880,10 +798,11 @@ server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
- evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap);
+ evbuffer_add_vprintf(ctx->cmdclient->stderr_data, fmt, ap);
va_end(ap);
- bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1);
+ evbuffer_add(ctx->cmdclient->stderr_data, "\n", 1);
+ server_push_stderr(ctx->cmdclient);
ctx->cmdclient->retcode = 1;
}
@@ -894,10 +813,11 @@ server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
- evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
+ evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap);
va_end(ap);
- bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
+ evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1);
+ server_push_stdout(ctx->cmdclient);
}
/* Callback to send print message to client, if not quiet. */
@@ -910,10 +830,11 @@ server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
return;
va_start(ap, fmt);
- evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
+ evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap);
va_end(ap);
- bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
+ evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1);
+ server_push_stdout(ctx->cmdclient);
}
/* Handle command message. */
@@ -970,8 +891,6 @@ void
server_client_msg_identify(
struct client *c, struct msg_identify_data *data, int fd)
{
- int tty_fd;
-
c->cwd = NULL;
data->cwd[(sizeof data->cwd) - 1] = '\0';
if (*data->cwd != '\0')
@@ -979,10 +898,8 @@ server_client_msg_identify(
if (!isatty(fd))
return;
- if ((tty_fd = dup(fd)) == -1)
- fatal("dup failed");
data->term[(sizeof data->term) - 1] = '\0';
- tty_init(&c->tty, tty_fd, data->term);
+ tty_init(&c->tty, fd, data->term);
if (data->flags & IDENTIFY_UTF8)
c->tty.flags |= TTY_UTF8;
if (data->flags & IDENTIFY_256COLOURS)
diff --git a/server-fn.c b/server-fn.c
index 600955af..791aab1c 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -46,17 +46,21 @@ server_fill_environ(struct session *s, struct environ *env)
environ_set(env, "TMUX", var);
}
-void
+int
server_write_client(
struct client *c, enum msgtype type, const void *buf, size_t len)
{
struct imsgbuf *ibuf = &c->ibuf;
+ int error;
if (c->flags & CLIENT_BAD)
- return;
+ return (-1);
log_debug("writing %d to client %d", type, c->ibuf.fd);
- imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len);
- server_update_event(c);
+ error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1,
+ (void *) buf, len);
+ if (error == 1)
+ server_update_event(c);
+ return (error == 1 ? 0 : -1);
}
void
@@ -502,3 +506,71 @@ server_update_event(struct client *c)
event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
event_add(&c->event, NULL);
}
+
+/* Push stdout to client if possible. */
+void
+server_push_stdout(struct client *c)
+{
+ struct msg_stdout_data data;
+ size_t size;
+
+ size = EVBUFFER_LENGTH(c->stdout_data);
+ if (size == 0)
+ return;
+ if (size > sizeof data.data)
+ size = sizeof data.data;
+
+ memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size);
+ data.size = size;
+
+ if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0)
+ evbuffer_drain(c->stdout_data, size);
+}
+
+/* Push stderr to client if possible. */
+void
+server_push_stderr(struct client *c)
+{
+ struct msg_stderr_data data;
+ size_t size;
+
+ size = EVBUFFER_LENGTH(c->stderr_data);
+ if (size == 0)
+ return;
+ if (size > sizeof data.data)
+ size = sizeof data.data;
+
+ memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size);
+ data.size = size;
+
+ if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0)
+ evbuffer_drain(c->stderr_data, size);
+}
+
+/* Set stdin callback. */
+int
+server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
+ void *), void *cb_data, char **cause)
+{
+ if (c == NULL) {
+ *cause = xstrdup("no client with stdin");
+ return (-1);
+ }
+ if (c->flags & CLIENT_TERMINAL) {
+ *cause = xstrdup("stdin is a tty");
+ return (-1);
+ }
+ if (c->stdin_callback != NULL) {
+ *cause = xstrdup("stdin in use");
+ return (-1);
+ }
+
+ c->stdin_callback_data = cb_data;
+ c->stdin_callback = cb;
+
+ c->references++;
+
+ if (c->stdin_closed)
+ c->stdin_callback (c, 1, c->stdin_callback_data);
+ return (0);
+}
diff --git a/tmux.h b/tmux.h
index 8c66bca5..96efb661 100644
--- a/tmux.h
+++ b/tmux.h
@@ -19,7 +19,7 @@
#ifndef TMUX_H
#define TMUX_H
-#define PROTOCOL_VERSION 6
+#define PROTOCOL_VERSION 7
#include <sys/param.h>
#include <sys/time.h>
@@ -369,7 +369,7 @@ enum msgtype {
MSG_EXITED,
MSG_EXITING,
MSG_IDENTIFY,
- MSG_PRINT,
+ MSG_STDIN,
MSG_READY,
MSG_RESIZE,
MSG_SHUTDOWN,
@@ -425,6 +425,21 @@ struct msg_exit_data {
int retcode;
};
+struct msg_stdin_data {
+ ssize_t size;
+ char data[BUFSIZ];
+};
+
+struct msg_stdout_data {
+ ssize_t size;
+ char data[BUFSIZ];
+};
+
+struct msg_stderr_data {
+ ssize_t size;
+ char data[BUFSIZ];
+};
+
/* Mode key commands. */
enum mode_key_cmd {
MODEKEY_NONE,
@@ -1161,16 +1176,12 @@ struct client {
struct tty tty;
- int stdin_fd;
- void *stdin_data;
- void (*stdin_callback)(struct client *, void *);
- struct bufferevent *stdin_event;
-
- int stdout_fd;
- struct bufferevent *stdout_event;
-
- int stderr_fd;
- struct bufferevent *stderr_event;
+ void (*stdin_callback)(struct client *, int, void *);
+ void *stdin_callback_data;
+ struct evbuffer *stdin_data;
+ int stdin_closed;
+ struct evbuffer *stdout_data;
+ struct evbuffer *stderr_data;
struct event repeat_timer;
@@ -1734,7 +1745,7 @@ void server_window_loop(void);
/* server-fn.c */
void server_fill_environ(struct session *, struct environ *);
-void server_write_client(
+int server_write_client(
struct client *, enum msgtype, const void *, size_t);
void server_write_session(
struct session *, enum msgtype, const void *, size_t);
@@ -1762,6 +1773,10 @@ void server_check_unattached (void);
void server_set_identify(struct client *);
void server_clear_identify(struct client *);
void server_update_event(struct client *);
+void server_push_stdout(struct client *);
+void server_push_stderr(struct client *);
+int server_set_stdin_callback(struct client *, void (*)(struct client *,
+ int, void *), void *, char **);
/* status.c */
int status_out_cmp(struct status_out *, struct status_out *);