diff --git a/cmd-if-shell.c b/cmd-if-shell.c index b64599e9..d28faf1a 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -1,4 +1,4 @@ -/* $Id: cmd-if-shell.c,v 1.9 2010-07-17 14:36:40 tcunha Exp $ */ +/* $Id: cmd-if-shell.c,v 1.10 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2009 Tiago Cunha @@ -109,8 +109,7 @@ cmd_if_shell_free(void *data) if (ctx->cmdclient != NULL) { ctx->cmdclient->references--; exitdata.retcode = ctx->cmdclient->retcode; - server_write_client( - ctx->cmdclient, MSG_EXIT, &exitdata, sizeof exitdata); + ctx->cmdclient->flags |= CLIENT_EXIT; } if (ctx->curclient != NULL) ctx->curclient->references--; diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 2a0e4d1d..87a527b6 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -1,4 +1,4 @@ -/* $Id: cmd-load-buffer.c,v 1.16 2010-07-02 02:52:13 tcunha Exp $ */ +/* $Id: cmd-load-buffer.c,v 1.17 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2009 Tiago Cunha @@ -31,6 +31,7 @@ */ int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); +void cmd_load_buffer_callback(struct client *, void *); const struct cmd_entry cmd_load_buffer_entry = { "load-buffer", "loadb", @@ -43,37 +44,55 @@ const struct cmd_entry cmd_load_buffer_entry = { cmd_buffer_print }; +struct cmd_load_buffer_cdata { + struct session *session; + int buffer; +}; + int cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) { - struct cmd_buffer_data *data = self->data; - struct session *s; - FILE *f, *close_f; - char *pdata, *new_pdata; - size_t psize; - u_int limit; - int ch; + struct cmd_buffer_data *data = self->data; + struct cmd_load_buffer_cdata *cdata; + struct session *s; + struct client *c = ctx->cmdclient; + FILE *f; + char *pdata, *new_pdata; + size_t psize; + u_int limit; + int ch; if ((s = cmd_find_session(ctx, data->target)) == NULL) return (-1); - if (strcmp(data->arg, "-") == 0 ) { - if (ctx->cmdclient == NULL) { + if (strcmp(data->arg, "-") == 0) { + if (c == NULL) { ctx->error(ctx, "%s: can't read from stdin", data->arg); return (-1); } - f = ctx->cmdclient->stdin_file; - if (isatty(fileno(ctx->cmdclient->stdin_file))) { + if (c->flags & CLIENT_TERMINAL) { ctx->error(ctx, "%s: stdin is a tty", data->arg); return (-1); } - close_f = NULL; - } else { - if ((f = fopen(data->arg, "rb")) == NULL) { - ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); + if (c->stdin_fd == -1) { + ctx->error(ctx, "%s: can't read from stdin", data->arg); return (-1); } - close_f = f; + + cdata = xmalloc(sizeof *cdata); + cdata->session = s; + cdata->buffer = data->buffer; + c->stdin_data = cdata; + c->stdin_callback = cmd_load_buffer_callback; + + c->references++; + bufferevent_enable(c->stdin_event, EV_READ); + return (1); + } + + if ((f = fopen(data->arg, "rb")) == NULL) { + ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); + return (-1); } pdata = NULL; @@ -94,8 +113,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) if (pdata != NULL) pdata[psize] = '\0'; - if (close_f != NULL) - fclose(close_f); + fclose(f); + f = NULL; limit = options_get_number(&s->options, "buffer-limit"); if (data->buffer == -1) { @@ -104,7 +123,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) } if (paste_replace(&s->buffers, data->buffer, pdata, psize) != 0) { ctx->error(ctx, "no buffer %d", data->buffer); - goto error; + return (-1); } return (0); @@ -112,7 +131,54 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) error: if (pdata != NULL) xfree(pdata); - if (close_f != NULL) - fclose(close_f); + if (f != NULL) + fclose(f); return (-1); } + +void +cmd_load_buffer_callback(struct client *c, void *data) +{ + struct cmd_load_buffer_cdata *cdata = data; + struct session *s = cdata->session; + char *pdata; + size_t psize; + u_int limit; + int idx; + + /* + * Event callback has already checked client is not dead and reduced + * its reference count. But tell it to exit. + */ + c->flags |= CLIENT_EXIT; + + /* Does the target session still exist? */ + if (session_index(s, &idx) != 0) + goto out; + + psize = EVBUFFER_LENGTH(c->stdin_event->input); + if (psize == 0) + goto out; + + pdata = malloc(psize + 1); + if (pdata == NULL) + goto out; + bufferevent_read(c->stdin_event, pdata, psize); + pdata[psize] = '\0'; + + limit = options_get_number(&s->options, "buffer-limit"); + if (cdata->buffer == -1) { + paste_add(&s->buffers, pdata, psize, limit); + goto out; + } + if (paste_replace(&s->buffers, cdata->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", cdata->buffer); + bufferevent_enable(c->stderr_event, EV_WRITE); + goto out; + } + +out: + xfree(cdata); +} diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 5e7701f5..9ec79abc 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -1,4 +1,4 @@ -/* $Id: cmd-run-shell.c,v 1.8 2010-07-17 14:36:40 tcunha Exp $ */ +/* $Id: cmd-run-shell.c,v 1.9 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2009 Tiago Cunha @@ -131,13 +131,10 @@ cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; struct cmd_ctx *ctx = &cdata->ctx; - struct msg_exit_data exitdata; if (ctx->cmdclient != NULL) { ctx->cmdclient->references--; - exitdata.retcode = ctx->cmdclient->retcode; - server_write_client( - ctx->cmdclient, MSG_EXIT, &exitdata, sizeof exitdata); + ctx->cmdclient->flags |= CLIENT_EXIT; } if (ctx->curclient != NULL) ctx->curclient->references--; diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index f6c53821..8470844a 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -1,4 +1,4 @@ -/* $Id: cmd-save-buffer.c,v 1.11 2010-07-02 02:52:13 tcunha Exp $ */ +/* $Id: cmd-save-buffer.c,v 1.12 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2009 Tiago Cunha @@ -47,8 +47,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) struct cmd_buffer_data *data = self->data; struct session *s; struct paste_buffer *pb; - mode_t mask; - FILE *f, *close_f; + mode_t mask; + FILE *f; if ((s = cmd_find_session(ctx, data->target)) == NULL) return (-1); @@ -70,8 +70,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) ctx->error(ctx, "%s: can't write to stdout", data->arg); return (-1); } - f = ctx->cmdclient->stdout_file; - close_f = NULL; + bufferevent_write( + ctx->cmdclient->stdout_event, pb->data, pb->size); } else { mask = umask(S_IRWXG | S_IRWXO); if (cmd_check_flag(data->chflags, 'a')) @@ -83,17 +83,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); return (-1); } - close_f = f; + if (fwrite(pb->data, 1, pb->size, f) != pb->size) { + ctx->error(ctx, "%s: fwrite error", data->arg); + fclose(f); + return (-1); + } + fclose(f); } - if (fwrite(pb->data, 1, pb->size, f) != pb->size) { - ctx->error(ctx, "%s: fwrite error", data->arg); - fclose(f); - return (-1); - } - - if (close_f != NULL) - fclose(close_f); - return (0); } diff --git a/server-client.c b/server-client.c index e3a19a93..b1b2f2db 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $Id: server-client.c,v 1.36 2010-07-20 17:36:03 tcunha Exp $ */ +/* $Id: server-client.c,v 1.37 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -28,9 +28,13 @@ void server_client_handle_key(int, struct mouse_event *, void *); void server_client_repeat_timer(int, short, void *); +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 *); @@ -68,9 +72,9 @@ server_client_create(int fd) ARRAY_INIT(&c->prompt_hdata); - c->stdin_file = NULL; - c->stdout_file = NULL; - c->stderr_file = NULL; + c->stdin_event = NULL; + c->stdout_event = NULL; + c->stderr_event = NULL; c->tty.fd = -1; c->title = NULL; @@ -121,12 +125,18 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); - if (c->stdin_file != NULL) - fclose(c->stdin_file); - if (c->stdout_file != NULL) - fclose(c->stdout_file); - if (c->stderr_file != NULL) - fclose(c->stderr_file); + if (c->stdin_fd != -1) + close(c->stdin_fd); + if (c->stdin_event != NULL) + bufferevent_free(c->stdin_event); + if (c->stdout_fd != -1) + close(c->stdout_fd); + if (c->stdout_event != NULL) + bufferevent_free(c->stdout_event); + if (c->stderr_fd != -1) + close(c->stderr_fd); + if (c->stderr_event != NULL) + bufferevent_free(c->stderr_event); screen_free(&c->status); job_tree_free(&c->status_jobs); @@ -389,11 +399,14 @@ server_client_loop(void) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) + if (c == NULL) continue; - server_client_check_redraw(c); - server_client_reset_state(c); + server_client_check_exit(c); + if (c->session != NULL) { + server_client_check_redraw(c); + server_client_reset_state(c); + } } /* @@ -456,6 +469,28 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); } +/* Check if client should be exited. */ +void +server_client_check_exit(struct client *c) +{ + struct msg_exit_data exitdata; + + if (!(c->flags & CLIENT_EXIT)) + return; + + if (c->stdout_fd != -1 && c->stdout_event != NULL && + EVBUFFER_LENGTH(c->stdout_event->output) != 0) + return; + if (c->stderr_fd != -1 && c->stderr_event != NULL && + EVBUFFER_LENGTH(c->stderr_event->output) != 0) + return; + + exitdata.retcode = c->retcode; + server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); + + c->flags &= ~CLIENT_EXIT; +} + /* Check for client redraws. */ void server_client_check_redraw(struct client *c) @@ -522,6 +557,52 @@ 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); + 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); + 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); + close(c->stderr_fd); + c->stderr_fd = -1; +} + /* Dispatch message from client. */ int server_client_msg_dispatch(struct client *c) @@ -531,6 +612,7 @@ server_client_msg_dispatch(struct client *c) struct msg_identify_data identifydata; struct msg_environ_data environdata; ssize_t n, datalen; + int mode; if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) return (-1); @@ -565,9 +647,17 @@ server_client_msg_dispatch(struct client *c) fatalx("MSG_IDENTIFY missing fd"); memcpy(&identifydata, imsg.data, sizeof identifydata); - c->stdin_file = fdopen(imsg.fd, "r"); - if (c->stdin_file == NULL) - fatal("fdopen(stdin) failed"); + c->stdin_fd = imsg.fd; + c->stdin_event = bufferevent_new(imsg.fd, NULL, NULL, + server_client_in_callback, c); + if (c->stdin_event == NULL) + fatalx("failed to create stdin event"); + + if ((mode = fcntl(imsg.fd, F_GETFL)) != -1) + fcntl(imsg.fd, F_SETFL, mode|O_NONBLOCK); + if (fcntl(imsg.fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); + server_client_msg_identify(c, &identifydata, imsg.fd); break; case MSG_STDOUT: @@ -576,9 +666,16 @@ server_client_msg_dispatch(struct client *c) if (imsg.fd == -1) fatalx("MSG_STDOUT missing fd"); - c->stdout_file = fdopen(imsg.fd, "w"); - if (c->stdout_file == NULL) - fatal("fdopen(stdout) failed"); + c->stdout_fd = imsg.fd; + c->stdout_event = bufferevent_new(imsg.fd, NULL, NULL, + server_client_out_callback, c); + if (c->stdout_event == NULL) + fatalx("failed to create stdout event"); + + if ((mode = fcntl(imsg.fd, F_GETFL)) != -1) + fcntl(imsg.fd, F_SETFL, mode|O_NONBLOCK); + if (fcntl(imsg.fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); break; case MSG_STDERR: if (datalen != 0) @@ -586,9 +683,16 @@ server_client_msg_dispatch(struct client *c) if (imsg.fd == -1) fatalx("MSG_STDERR missing fd"); - c->stderr_file = fdopen(imsg.fd, "w"); - if (c->stderr_file == NULL) - fatal("fdopen(stderr) failed"); + c->stderr_fd = imsg.fd; + c->stderr_event = bufferevent_new(imsg.fd, NULL, NULL, + server_client_err_callback, c); + if (c->stderr_event == NULL) + fatalx("failed to create stderr event"); + + if ((mode = fcntl(imsg.fd, F_GETFL)) != -1) + fcntl(imsg.fd, F_SETFL, mode|O_NONBLOCK); + if (fcntl(imsg.fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); break; case MSG_RESIZE: if (datalen != 0) @@ -658,12 +762,10 @@ server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) va_list ap; va_start(ap, fmt); - vfprintf(ctx->cmdclient->stderr_file, fmt, ap); + evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap); va_end(ap); - fputc('\n', ctx->cmdclient->stderr_file); - fflush(ctx->cmdclient->stderr_file); - + bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1); ctx->cmdclient->retcode = 1; } @@ -674,11 +776,10 @@ server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) va_list ap; va_start(ap, fmt); - vfprintf(ctx->cmdclient->stdout_file, fmt, ap); + evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap); va_end(ap); - fputc('\n', ctx->cmdclient->stdout_file); - fflush(ctx->cmdclient->stdout_file); + bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1); } /* Callback to send print message to client, if not quiet. */ @@ -691,22 +792,20 @@ server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...) return; va_start(ap, fmt); - vfprintf(ctx->cmdclient->stdout_file, fmt, ap); + evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap); va_end(ap); - fputc('\n', ctx->cmdclient->stdout_file); - fflush(ctx->cmdclient->stdout_file); + bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1); } /* Handle command message. */ void server_client_msg_command(struct client *c, struct msg_command_data *data) { - struct cmd_ctx ctx; - struct cmd_list *cmdlist = NULL; - struct msg_exit_data exitdata; - int argc; - char **argv, *cause; + struct cmd_ctx ctx; + struct cmd_list *cmdlist = NULL; + int argc; + char **argv, *cause; ctx.error = server_client_msg_error; ctx.print = server_client_msg_print; @@ -737,18 +836,15 @@ server_client_msg_command(struct client *c, struct msg_command_data *data) } cmd_free_argv(argc, argv); - if (cmd_list_exec(cmdlist, &ctx) != 1) { - exitdata.retcode = c->retcode; - server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); - } + if (cmd_list_exec(cmdlist, &ctx) != 1) + c->flags |= CLIENT_EXIT; cmd_list_free(cmdlist); return; error: if (cmdlist != NULL) cmd_list_free(cmdlist); - exitdata.retcode = c->retcode; - server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); + c->flags |= CLIENT_EXIT; } /* Handle identify message. */ diff --git a/server-fn.c b/server-fn.c index b33ca92d..6c100398 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $Id: server-fn.c,v 1.108 2010-07-02 02:45:52 tcunha Exp $ */ +/* $Id: server-fn.c,v 1.109 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -395,7 +395,7 @@ server_destroy_session(struct session *s) continue; if (s_new == NULL) { c->session = NULL; - server_write_client(c, MSG_EXIT, NULL, 0); + c->flags |= CLIENT_EXIT; } else { c->session = s_new; server_redraw_client(c); diff --git a/tmux.h b/tmux.h index 0cdd5db2..996a9ebe 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.571 2010-07-17 14:38:13 tcunha Exp $ */ +/* $Id: tmux.h,v 1.572 2010-08-09 21:44:25 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1095,9 +1095,17 @@ struct client { char *cwd; struct tty tty; - FILE *stdin_file; - FILE *stdout_file; - FILE *stderr_file; + + 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; struct event repeat_timer; @@ -1107,7 +1115,7 @@ struct client { #define CLIENT_TERMINAL 0x1 #define CLIENT_PREFIX 0x2 -/* 0x4 unused */ +#define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 #define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */