Skip to content

Commit 2ddb021

Browse files
author
tcunha
committed
Sync OpenBSD patchset 741:
When changing so that the client passes its stdout and stderr as well as stdin up to the server, I forgot one essential point - the tmux server could now be both the producer and consumer. This happens when tmux is run inside tmux, as well as when piping tmux commands together. So, using stdio(3) was a bad idea - if sufficient data was written, this could block in write(2). When that happened and the server was both producer and consumer, it deadlocks. Change to use libevent bufferevents for the client stdin, stdout and stderr instead. This is trivial enough for output but requires a callback mechanism to trigger when stdin is finished. This relies on the underlying polling mechanism for libevent to work with whatever devices to which the user could redirect stdin, stdout or stderr, hence the change to use poll(2) over kqueue(2) for tmux.
1 parent 450ba07 commit 2ddb021

7 files changed

+257
-95
lines changed

cmd-if-shell.c

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $Id: cmd-if-shell.c,v 1.9 2010-07-17 14:36:40 tcunha Exp $ */
1+
/* $Id: cmd-if-shell.c,v 1.10 2010-08-09 21:44:25 tcunha Exp $ */
22

33
/*
44
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -109,8 +109,7 @@ cmd_if_shell_free(void *data)
109109
if (ctx->cmdclient != NULL) {
110110
ctx->cmdclient->references--;
111111
exitdata.retcode = ctx->cmdclient->retcode;
112-
server_write_client(
113-
ctx->cmdclient, MSG_EXIT, &exitdata, sizeof exitdata);
112+
ctx->cmdclient->flags |= CLIENT_EXIT;
114113
}
115114
if (ctx->curclient != NULL)
116115
ctx->curclient->references--;

cmd-load-buffer.c

+88-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $Id: cmd-load-buffer.c,v 1.16 2010-07-02 02:52:13 tcunha Exp $ */
1+
/* $Id: cmd-load-buffer.c,v 1.17 2010-08-09 21:44:25 tcunha Exp $ */
22

33
/*
44
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -31,6 +31,7 @@
3131
*/
3232

3333
int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *);
34+
void cmd_load_buffer_callback(struct client *, void *);
3435

3536
const struct cmd_entry cmd_load_buffer_entry = {
3637
"load-buffer", "loadb",
@@ -43,37 +44,55 @@ const struct cmd_entry cmd_load_buffer_entry = {
4344
cmd_buffer_print
4445
};
4546

47+
struct cmd_load_buffer_cdata {
48+
struct session *session;
49+
int buffer;
50+
};
51+
4652
int
4753
cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
4854
{
49-
struct cmd_buffer_data *data = self->data;
50-
struct session *s;
51-
FILE *f, *close_f;
52-
char *pdata, *new_pdata;
53-
size_t psize;
54-
u_int limit;
55-
int ch;
55+
struct cmd_buffer_data *data = self->data;
56+
struct cmd_load_buffer_cdata *cdata;
57+
struct session *s;
58+
struct client *c = ctx->cmdclient;
59+
FILE *f;
60+
char *pdata, *new_pdata;
61+
size_t psize;
62+
u_int limit;
63+
int ch;
5664

5765
if ((s = cmd_find_session(ctx, data->target)) == NULL)
5866
return (-1);
5967

60-
if (strcmp(data->arg, "-") == 0 ) {
61-
if (ctx->cmdclient == NULL) {
68+
if (strcmp(data->arg, "-") == 0) {
69+
if (c == NULL) {
6270
ctx->error(ctx, "%s: can't read from stdin", data->arg);
6371
return (-1);
6472
}
65-
f = ctx->cmdclient->stdin_file;
66-
if (isatty(fileno(ctx->cmdclient->stdin_file))) {
73+
if (c->flags & CLIENT_TERMINAL) {
6774
ctx->error(ctx, "%s: stdin is a tty", data->arg);
6875
return (-1);
6976
}
70-
close_f = NULL;
71-
} else {
72-
if ((f = fopen(data->arg, "rb")) == NULL) {
73-
ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
77+
if (c->stdin_fd == -1) {
78+
ctx->error(ctx, "%s: can't read from stdin", data->arg);
7479
return (-1);
7580
}
76-
close_f = f;
81+
82+
cdata = xmalloc(sizeof *cdata);
83+
cdata->session = s;
84+
cdata->buffer = data->buffer;
85+
c->stdin_data = cdata;
86+
c->stdin_callback = cmd_load_buffer_callback;
87+
88+
c->references++;
89+
bufferevent_enable(c->stdin_event, EV_READ);
90+
return (1);
91+
}
92+
93+
if ((f = fopen(data->arg, "rb")) == NULL) {
94+
ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
95+
return (-1);
7796
}
7897

7998
pdata = NULL;
@@ -94,8 +113,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
94113
if (pdata != NULL)
95114
pdata[psize] = '\0';
96115

97-
if (close_f != NULL)
98-
fclose(close_f);
116+
fclose(f);
117+
f = NULL;
99118

100119
limit = options_get_number(&s->options, "buffer-limit");
101120
if (data->buffer == -1) {
@@ -104,15 +123,62 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
104123
}
105124
if (paste_replace(&s->buffers, data->buffer, pdata, psize) != 0) {
106125
ctx->error(ctx, "no buffer %d", data->buffer);
107-
goto error;
126+
return (-1);
108127
}
109128

110129
return (0);
111130

112131
error:
113132
if (pdata != NULL)
114133
xfree(pdata);
115-
if (close_f != NULL)
116-
fclose(close_f);
134+
if (f != NULL)
135+
fclose(f);
117136
return (-1);
118137
}
138+
139+
void
140+
cmd_load_buffer_callback(struct client *c, void *data)
141+
{
142+
struct cmd_load_buffer_cdata *cdata = data;
143+
struct session *s = cdata->session;
144+
char *pdata;
145+
size_t psize;
146+
u_int limit;
147+
int idx;
148+
149+
/*
150+
* Event callback has already checked client is not dead and reduced
151+
* its reference count. But tell it to exit.
152+
*/
153+
c->flags |= CLIENT_EXIT;
154+
155+
/* Does the target session still exist? */
156+
if (session_index(s, &idx) != 0)
157+
goto out;
158+
159+
psize = EVBUFFER_LENGTH(c->stdin_event->input);
160+
if (psize == 0)
161+
goto out;
162+
163+
pdata = malloc(psize + 1);
164+
if (pdata == NULL)
165+
goto out;
166+
bufferevent_read(c->stdin_event, pdata, psize);
167+
pdata[psize] = '\0';
168+
169+
limit = options_get_number(&s->options, "buffer-limit");
170+
if (cdata->buffer == -1) {
171+
paste_add(&s->buffers, pdata, psize, limit);
172+
goto out;
173+
}
174+
if (paste_replace(&s->buffers, cdata->buffer, pdata, psize) != 0) {
175+
/* No context so can't use server_client_msg_error. */
176+
evbuffer_add_printf(
177+
c->stderr_event->output, "no buffer %d\n", cdata->buffer);
178+
bufferevent_enable(c->stderr_event, EV_WRITE);
179+
goto out;
180+
}
181+
182+
out:
183+
xfree(cdata);
184+
}

cmd-run-shell.c

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $Id: cmd-run-shell.c,v 1.8 2010-07-17 14:36:40 tcunha Exp $ */
1+
/* $Id: cmd-run-shell.c,v 1.9 2010-08-09 21:44:25 tcunha Exp $ */
22

33
/*
44
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -131,13 +131,10 @@ cmd_run_shell_free(void *data)
131131
{
132132
struct cmd_run_shell_data *cdata = data;
133133
struct cmd_ctx *ctx = &cdata->ctx;
134-
struct msg_exit_data exitdata;
135134

136135
if (ctx->cmdclient != NULL) {
137136
ctx->cmdclient->references--;
138-
exitdata.retcode = ctx->cmdclient->retcode;
139-
server_write_client(
140-
ctx->cmdclient, MSG_EXIT, &exitdata, sizeof exitdata);
137+
ctx->cmdclient->flags |= CLIENT_EXIT;
141138
}
142139
if (ctx->curclient != NULL)
143140
ctx->curclient->references--;

cmd-save-buffer.c

+11-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $Id: cmd-save-buffer.c,v 1.11 2010-07-02 02:52:13 tcunha Exp $ */
1+
/* $Id: cmd-save-buffer.c,v 1.12 2010-08-09 21:44:25 tcunha Exp $ */
22

33
/*
44
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -47,8 +47,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
4747
struct cmd_buffer_data *data = self->data;
4848
struct session *s;
4949
struct paste_buffer *pb;
50-
mode_t mask;
51-
FILE *f, *close_f;
50+
mode_t mask;
51+
FILE *f;
5252

5353
if ((s = cmd_find_session(ctx, data->target)) == NULL)
5454
return (-1);
@@ -70,8 +70,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
7070
ctx->error(ctx, "%s: can't write to stdout", data->arg);
7171
return (-1);
7272
}
73-
f = ctx->cmdclient->stdout_file;
74-
close_f = NULL;
73+
bufferevent_write(
74+
ctx->cmdclient->stdout_event, pb->data, pb->size);
7575
} else {
7676
mask = umask(S_IRWXG | S_IRWXO);
7777
if (cmd_check_flag(data->chflags, 'a'))
@@ -83,17 +83,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
8383
ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
8484
return (-1);
8585
}
86-
close_f = f;
87-
}
88-
89-
if (fwrite(pb->data, 1, pb->size, f) != pb->size) {
90-
ctx->error(ctx, "%s: fwrite error", data->arg);
91-
fclose(f);
92-
return (-1);
86+
if (fwrite(pb->data, 1, pb->size, f) != pb->size) {
87+
ctx->error(ctx, "%s: fwrite error", data->arg);
88+
fclose(f);
89+
return (-1);
90+
}
91+
fclose(f);
9392
}
9493

95-
if (close_f != NULL)
96-
fclose(close_f);
97-
9894
return (0);
9995
}

0 commit comments

Comments
 (0)