1*9d4bd388Snicm /* $OpenBSD: file.c,v 1.15 2023/04/17 17:58:35 nicm Exp $ */
2f4bc7c7aSnicm
3f4bc7c7aSnicm /*
4f4bc7c7aSnicm * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5f4bc7c7aSnicm *
6f4bc7c7aSnicm * Permission to use, copy, modify, and distribute this software for any
7f4bc7c7aSnicm * purpose with or without fee is hereby granted, provided that the above
8f4bc7c7aSnicm * copyright notice and this permission notice appear in all copies.
9f4bc7c7aSnicm *
10f4bc7c7aSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f4bc7c7aSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f4bc7c7aSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f4bc7c7aSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f4bc7c7aSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15f4bc7c7aSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16f4bc7c7aSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f4bc7c7aSnicm */
18f4bc7c7aSnicm
19f4bc7c7aSnicm #include <sys/types.h>
20dab96b18Snicm #include <sys/queue.h>
2196a20458Snicm #include <sys/uio.h>
22f4bc7c7aSnicm
23f4bc7c7aSnicm #include <errno.h>
24f4bc7c7aSnicm #include <fcntl.h>
25dab96b18Snicm #include <imsg.h>
26f4bc7c7aSnicm #include <stdio.h>
27f4bc7c7aSnicm #include <stdlib.h>
28f4bc7c7aSnicm #include <string.h>
29f4bc7c7aSnicm #include <unistd.h>
30f4bc7c7aSnicm
31f4bc7c7aSnicm #include "tmux.h"
32f4bc7c7aSnicm
330c0c2584Snicm /*
340c0c2584Snicm * IPC file handling. Both client and server use the same data structures
350c0c2584Snicm * (client_file and client_files) to store list of active files. Most functions
360c0c2584Snicm * are for use either in client or server but not both.
370c0c2584Snicm */
380c0c2584Snicm
39f4bc7c7aSnicm static int file_next_stream = 3;
40f4bc7c7aSnicm
41f4bc7c7aSnicm RB_GENERATE(client_files, client_file, entry, file_cmp);
42f4bc7c7aSnicm
430c0c2584Snicm /* Get path for file, either as given or from working directory. */
4480659a0fSnicm static char *
file_get_path(struct client * c,const char * file)4580659a0fSnicm file_get_path(struct client *c, const char *file)
4680659a0fSnicm {
4780659a0fSnicm char *path;
4880659a0fSnicm
4980659a0fSnicm if (*file == '/')
5080659a0fSnicm path = xstrdup(file);
5180659a0fSnicm else
5280659a0fSnicm xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
5380659a0fSnicm return (path);
5480659a0fSnicm }
5580659a0fSnicm
560c0c2584Snicm /* Tree comparison function. */
57f4bc7c7aSnicm int
file_cmp(struct client_file * cf1,struct client_file * cf2)58f4bc7c7aSnicm file_cmp(struct client_file *cf1, struct client_file *cf2)
59f4bc7c7aSnicm {
60f4bc7c7aSnicm if (cf1->stream < cf2->stream)
61f4bc7c7aSnicm return (-1);
62f4bc7c7aSnicm if (cf1->stream > cf2->stream)
63f4bc7c7aSnicm return (1);
64f4bc7c7aSnicm return (0);
65f4bc7c7aSnicm }
66f4bc7c7aSnicm
670c0c2584Snicm /*
680c0c2584Snicm * Create a file object in the client process - the peer is the server to send
690c0c2584Snicm * messages to. Check callback is fired when the file is finished with so the
700c0c2584Snicm * process can decide if it needs to exit (if it is waiting for files to
710c0c2584Snicm * flush).
720c0c2584Snicm */
73f4bc7c7aSnicm struct client_file *
file_create_with_peer(struct tmuxpeer * peer,struct client_files * files,int stream,client_file_cb cb,void * cbdata)740c0c2584Snicm file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
750c0c2584Snicm int stream, client_file_cb cb, void *cbdata)
76f4bc7c7aSnicm {
77f4bc7c7aSnicm struct client_file *cf;
78f4bc7c7aSnicm
79f4bc7c7aSnicm cf = xcalloc(1, sizeof *cf);
800c0c2584Snicm cf->c = NULL;
810c0c2584Snicm cf->references = 1;
820c0c2584Snicm cf->stream = stream;
830c0c2584Snicm
840c0c2584Snicm cf->buffer = evbuffer_new();
850c0c2584Snicm if (cf->buffer == NULL)
860c0c2584Snicm fatalx("out of memory");
870c0c2584Snicm
880c0c2584Snicm cf->cb = cb;
890c0c2584Snicm cf->data = cbdata;
900c0c2584Snicm
910c0c2584Snicm cf->peer = peer;
920c0c2584Snicm cf->tree = files;
930c0c2584Snicm RB_INSERT(client_files, files, cf);
940c0c2584Snicm
950c0c2584Snicm return (cf);
960c0c2584Snicm }
970c0c2584Snicm
980c0c2584Snicm /* Create a file object in the server, communicating with the given client. */
990c0c2584Snicm struct client_file *
file_create_with_client(struct client * c,int stream,client_file_cb cb,void * cbdata)1000c0c2584Snicm file_create_with_client(struct client *c, int stream, client_file_cb cb,
1010c0c2584Snicm void *cbdata)
1020c0c2584Snicm {
1030c0c2584Snicm struct client_file *cf;
1040c0c2584Snicm
1050c0c2584Snicm if (c != NULL && (c->flags & CLIENT_ATTACHED))
1060c0c2584Snicm c = NULL;
1070c0c2584Snicm
1080c0c2584Snicm cf = xcalloc(1, sizeof *cf);
109f4bc7c7aSnicm cf->c = c;
110f4bc7c7aSnicm cf->references = 1;
111f4bc7c7aSnicm cf->stream = stream;
112f4bc7c7aSnicm
113f4bc7c7aSnicm cf->buffer = evbuffer_new();
114f4bc7c7aSnicm if (cf->buffer == NULL)
115f4bc7c7aSnicm fatalx("out of memory");
116f4bc7c7aSnicm
117f4bc7c7aSnicm cf->cb = cb;
118f4bc7c7aSnicm cf->data = cbdata;
119f4bc7c7aSnicm
120f4bc7c7aSnicm if (cf->c != NULL) {
1210c0c2584Snicm cf->peer = cf->c->peer;
1220c0c2584Snicm cf->tree = &cf->c->files;
123f4bc7c7aSnicm RB_INSERT(client_files, &cf->c->files, cf);
124f4bc7c7aSnicm cf->c->references++;
125f4bc7c7aSnicm }
126f4bc7c7aSnicm
127f4bc7c7aSnicm return (cf);
128f4bc7c7aSnicm }
129f4bc7c7aSnicm
1300c0c2584Snicm /* Free a file. */
131f4bc7c7aSnicm void
file_free(struct client_file * cf)132f4bc7c7aSnicm file_free(struct client_file *cf)
133f4bc7c7aSnicm {
134f4bc7c7aSnicm if (--cf->references != 0)
135f4bc7c7aSnicm return;
136f4bc7c7aSnicm
137f4bc7c7aSnicm evbuffer_free(cf->buffer);
138f4bc7c7aSnicm free(cf->path);
139f4bc7c7aSnicm
1400c0c2584Snicm if (cf->tree != NULL)
1410c0c2584Snicm RB_REMOVE(client_files, cf->tree, cf);
1420c0c2584Snicm if (cf->c != NULL)
143f4bc7c7aSnicm server_client_unref(cf->c);
1440c0c2584Snicm
145f4bc7c7aSnicm free(cf);
146f4bc7c7aSnicm }
147f4bc7c7aSnicm
1480c0c2584Snicm /* Event to fire the done callback. */
149f4bc7c7aSnicm static void
file_fire_done_cb(__unused int fd,__unused short events,void * arg)150f4bc7c7aSnicm file_fire_done_cb(__unused int fd, __unused short events, void *arg)
151f4bc7c7aSnicm {
152f4bc7c7aSnicm struct client_file *cf = arg;
153f4bc7c7aSnicm struct client *c = cf->c;
154f4bc7c7aSnicm
15583e2ab13Snicm if (cf->cb != NULL &&
15683e2ab13Snicm (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
157f4bc7c7aSnicm cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
158f4bc7c7aSnicm file_free(cf);
159f4bc7c7aSnicm }
160f4bc7c7aSnicm
1610c0c2584Snicm /* Add an event to fire the done callback (used by the server). */
162f4bc7c7aSnicm void
file_fire_done(struct client_file * cf)163f4bc7c7aSnicm file_fire_done(struct client_file *cf)
164f4bc7c7aSnicm {
165f4bc7c7aSnicm event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
166f4bc7c7aSnicm }
167f4bc7c7aSnicm
1680c0c2584Snicm /* Fire the read callback. */
169f4bc7c7aSnicm void
file_fire_read(struct client_file * cf)170f4bc7c7aSnicm file_fire_read(struct client_file *cf)
171f4bc7c7aSnicm {
172f4bc7c7aSnicm if (cf->cb != NULL)
1730c0c2584Snicm cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
174f4bc7c7aSnicm }
175f4bc7c7aSnicm
1760c0c2584Snicm /* Can this file be printed to? */
177f4bc7c7aSnicm int
file_can_print(struct client * c)178f4bc7c7aSnicm file_can_print(struct client *c)
179f4bc7c7aSnicm {
180*9d4bd388Snicm if (c == NULL ||
181*9d4bd388Snicm (c->flags & CLIENT_ATTACHED) ||
182*9d4bd388Snicm (c->flags & CLIENT_CONTROL))
183f4bc7c7aSnicm return (0);
184f4bc7c7aSnicm return (1);
185f4bc7c7aSnicm }
186f4bc7c7aSnicm
1870c0c2584Snicm /* Print a message to a file. */
188f4bc7c7aSnicm void
file_print(struct client * c,const char * fmt,...)189f4bc7c7aSnicm file_print(struct client *c, const char *fmt, ...)
190f4bc7c7aSnicm {
191f4bc7c7aSnicm va_list ap;
192f4bc7c7aSnicm
193f4bc7c7aSnicm va_start(ap, fmt);
194f4bc7c7aSnicm file_vprint(c, fmt, ap);
195f4bc7c7aSnicm va_end(ap);
196f4bc7c7aSnicm }
197f4bc7c7aSnicm
1980c0c2584Snicm /* Print a message to a file. */
199f4bc7c7aSnicm void
file_vprint(struct client * c,const char * fmt,va_list ap)200f4bc7c7aSnicm file_vprint(struct client *c, const char *fmt, va_list ap)
201f4bc7c7aSnicm {
202f4bc7c7aSnicm struct client_file find, *cf;
203f4bc7c7aSnicm struct msg_write_open msg;
204f4bc7c7aSnicm
205f4bc7c7aSnicm if (!file_can_print(c))
206f4bc7c7aSnicm return;
207f4bc7c7aSnicm
208f4bc7c7aSnicm find.stream = 1;
209f4bc7c7aSnicm if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
2100c0c2584Snicm cf = file_create_with_client(c, 1, NULL, NULL);
211f4bc7c7aSnicm cf->path = xstrdup("-");
212f4bc7c7aSnicm
213f4bc7c7aSnicm evbuffer_add_vprintf(cf->buffer, fmt, ap);
214f4bc7c7aSnicm
215f4bc7c7aSnicm msg.stream = 1;
216f4bc7c7aSnicm msg.fd = STDOUT_FILENO;
217f4bc7c7aSnicm msg.flags = 0;
218f4bc7c7aSnicm proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
219f4bc7c7aSnicm } else {
220f4bc7c7aSnicm evbuffer_add_vprintf(cf->buffer, fmt, ap);
221f4bc7c7aSnicm file_push(cf);
222f4bc7c7aSnicm }
223f4bc7c7aSnicm }
224f4bc7c7aSnicm
2250c0c2584Snicm /* Print a buffer to a file. */
226f4bc7c7aSnicm void
file_print_buffer(struct client * c,void * data,size_t size)227f4bc7c7aSnicm file_print_buffer(struct client *c, void *data, size_t size)
228f4bc7c7aSnicm {
229f4bc7c7aSnicm struct client_file find, *cf;
230f4bc7c7aSnicm struct msg_write_open msg;
231f4bc7c7aSnicm
232f4bc7c7aSnicm if (!file_can_print(c))
233f4bc7c7aSnicm return;
234f4bc7c7aSnicm
235f4bc7c7aSnicm find.stream = 1;
236f4bc7c7aSnicm if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
2370c0c2584Snicm cf = file_create_with_client(c, 1, NULL, NULL);
238f4bc7c7aSnicm cf->path = xstrdup("-");
239f4bc7c7aSnicm
240f4bc7c7aSnicm evbuffer_add(cf->buffer, data, size);
241f4bc7c7aSnicm
242f4bc7c7aSnicm msg.stream = 1;
243f4bc7c7aSnicm msg.fd = STDOUT_FILENO;
244f4bc7c7aSnicm msg.flags = 0;
245f4bc7c7aSnicm proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
246f4bc7c7aSnicm } else {
247f4bc7c7aSnicm evbuffer_add(cf->buffer, data, size);
248f4bc7c7aSnicm file_push(cf);
249f4bc7c7aSnicm }
250f4bc7c7aSnicm }
251f4bc7c7aSnicm
2520c0c2584Snicm /* Report an error to a file. */
253f4bc7c7aSnicm void
file_error(struct client * c,const char * fmt,...)254f4bc7c7aSnicm file_error(struct client *c, const char *fmt, ...)
255f4bc7c7aSnicm {
256f4bc7c7aSnicm struct client_file find, *cf;
257f4bc7c7aSnicm struct msg_write_open msg;
258f4bc7c7aSnicm va_list ap;
259f4bc7c7aSnicm
260f4bc7c7aSnicm if (!file_can_print(c))
261f4bc7c7aSnicm return;
262f4bc7c7aSnicm
263f4bc7c7aSnicm va_start(ap, fmt);
264f4bc7c7aSnicm
265f4bc7c7aSnicm find.stream = 2;
266f4bc7c7aSnicm if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
2670c0c2584Snicm cf = file_create_with_client(c, 2, NULL, NULL);
268f4bc7c7aSnicm cf->path = xstrdup("-");
269f4bc7c7aSnicm
270f4bc7c7aSnicm evbuffer_add_vprintf(cf->buffer, fmt, ap);
271f4bc7c7aSnicm
272f4bc7c7aSnicm msg.stream = 2;
273f4bc7c7aSnicm msg.fd = STDERR_FILENO;
274f4bc7c7aSnicm msg.flags = 0;
275f4bc7c7aSnicm proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
276f4bc7c7aSnicm } else {
277f4bc7c7aSnicm evbuffer_add_vprintf(cf->buffer, fmt, ap);
278f4bc7c7aSnicm file_push(cf);
279f4bc7c7aSnicm }
280f4bc7c7aSnicm
281f4bc7c7aSnicm va_end(ap);
282f4bc7c7aSnicm }
283f4bc7c7aSnicm
2840c0c2584Snicm /* Write data to a file. */
285f4bc7c7aSnicm void
file_write(struct client * c,const char * path,int flags,const void * bdata,size_t bsize,client_file_cb cb,void * cbdata)286f4bc7c7aSnicm file_write(struct client *c, const char *path, int flags, const void *bdata,
287f4bc7c7aSnicm size_t bsize, client_file_cb cb, void *cbdata)
288f4bc7c7aSnicm {
289f4bc7c7aSnicm struct client_file *cf;
290dab96b18Snicm struct msg_write_open *msg;
291dab96b18Snicm size_t msglen;
292f4bc7c7aSnicm int fd = -1;
2930c0c2584Snicm u_int stream = file_next_stream++;
2940c0c2584Snicm FILE *f;
295f4bc7c7aSnicm const char *mode;
296f4bc7c7aSnicm
297f4bc7c7aSnicm if (strcmp(path, "-") == 0) {
2980c0c2584Snicm cf = file_create_with_client(c, stream, cb, cbdata);
299f4bc7c7aSnicm cf->path = xstrdup("-");
300f4bc7c7aSnicm
301f4bc7c7aSnicm fd = STDOUT_FILENO;
30254e3eca9Snicm if (c == NULL ||
30354e3eca9Snicm (c->flags & CLIENT_ATTACHED) ||
30454e3eca9Snicm (c->flags & CLIENT_CONTROL)) {
305f4bc7c7aSnicm cf->error = EBADF;
306f4bc7c7aSnicm goto done;
307f4bc7c7aSnicm }
308f4bc7c7aSnicm goto skip;
309f4bc7c7aSnicm }
310f4bc7c7aSnicm
3110c0c2584Snicm cf = file_create_with_client(c, stream, cb, cbdata);
31280659a0fSnicm cf->path = file_get_path(c, path);
313f4bc7c7aSnicm
314f4bc7c7aSnicm if (c == NULL || c->flags & CLIENT_ATTACHED) {
315f4bc7c7aSnicm if (flags & O_APPEND)
316f4bc7c7aSnicm mode = "ab";
317f4bc7c7aSnicm else
318f4bc7c7aSnicm mode = "wb";
319f4bc7c7aSnicm f = fopen(cf->path, mode);
320f4bc7c7aSnicm if (f == NULL) {
321f4bc7c7aSnicm cf->error = errno;
322f4bc7c7aSnicm goto done;
323f4bc7c7aSnicm }
324f4bc7c7aSnicm if (fwrite(bdata, 1, bsize, f) != bsize) {
325f4bc7c7aSnicm fclose(f);
326f4bc7c7aSnicm cf->error = EIO;
327f4bc7c7aSnicm goto done;
328f4bc7c7aSnicm }
329f4bc7c7aSnicm fclose(f);
330f4bc7c7aSnicm goto done;
331f4bc7c7aSnicm }
332f4bc7c7aSnicm
333f4bc7c7aSnicm skip:
334f4bc7c7aSnicm evbuffer_add(cf->buffer, bdata, bsize);
335f4bc7c7aSnicm
336dab96b18Snicm msglen = strlen(cf->path) + 1 + sizeof *msg;
337dab96b18Snicm if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
338f4bc7c7aSnicm cf->error = E2BIG;
339f4bc7c7aSnicm goto done;
340f4bc7c7aSnicm }
341dab96b18Snicm msg = xmalloc(msglen);
342dab96b18Snicm msg->stream = cf->stream;
343dab96b18Snicm msg->fd = fd;
344dab96b18Snicm msg->flags = flags;
345dab96b18Snicm memcpy(msg + 1, cf->path, msglen - sizeof *msg);
3460c0c2584Snicm if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
347dab96b18Snicm free(msg);
348f4bc7c7aSnicm cf->error = EINVAL;
349f4bc7c7aSnicm goto done;
350f4bc7c7aSnicm }
351dab96b18Snicm free(msg);
352f4bc7c7aSnicm return;
353f4bc7c7aSnicm
354f4bc7c7aSnicm done:
355f4bc7c7aSnicm file_fire_done(cf);
356f4bc7c7aSnicm }
357f4bc7c7aSnicm
3580c0c2584Snicm /* Read a file. */
35983e2ab13Snicm struct client_file *
file_read(struct client * c,const char * path,client_file_cb cb,void * cbdata)360f4bc7c7aSnicm file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
361f4bc7c7aSnicm {
362f4bc7c7aSnicm struct client_file *cf;
363dab96b18Snicm struct msg_read_open *msg;
3640c0c2584Snicm size_t msglen;
365f4bc7c7aSnicm int fd = -1;
3660c0c2584Snicm u_int stream = file_next_stream++;
3670c0c2584Snicm FILE *f;
3680c0c2584Snicm size_t size;
369f4bc7c7aSnicm char buffer[BUFSIZ];
370f4bc7c7aSnicm
371f4bc7c7aSnicm if (strcmp(path, "-") == 0) {
3720c0c2584Snicm cf = file_create_with_client(c, stream, cb, cbdata);
373f4bc7c7aSnicm cf->path = xstrdup("-");
374f4bc7c7aSnicm
375f4bc7c7aSnicm fd = STDIN_FILENO;
37654e3eca9Snicm if (c == NULL ||
37754e3eca9Snicm (c->flags & CLIENT_ATTACHED) ||
37854e3eca9Snicm (c->flags & CLIENT_CONTROL)) {
379f4bc7c7aSnicm cf->error = EBADF;
380f4bc7c7aSnicm goto done;
381f4bc7c7aSnicm }
382f4bc7c7aSnicm goto skip;
383f4bc7c7aSnicm }
384f4bc7c7aSnicm
3850c0c2584Snicm cf = file_create_with_client(c, stream, cb, cbdata);
38680659a0fSnicm cf->path = file_get_path(c, path);
387f4bc7c7aSnicm
388f4bc7c7aSnicm if (c == NULL || c->flags & CLIENT_ATTACHED) {
389f4bc7c7aSnicm f = fopen(cf->path, "rb");
390f4bc7c7aSnicm if (f == NULL) {
391f4bc7c7aSnicm cf->error = errno;
392f4bc7c7aSnicm goto done;
393f4bc7c7aSnicm }
394f4bc7c7aSnicm for (;;) {
395f4bc7c7aSnicm size = fread(buffer, 1, sizeof buffer, f);
396f4bc7c7aSnicm if (evbuffer_add(cf->buffer, buffer, size) != 0) {
397f4bc7c7aSnicm cf->error = ENOMEM;
398f4bc7c7aSnicm goto done;
399f4bc7c7aSnicm }
400f4bc7c7aSnicm if (size != sizeof buffer)
401f4bc7c7aSnicm break;
402f4bc7c7aSnicm }
403f4bc7c7aSnicm if (ferror(f)) {
404f4bc7c7aSnicm cf->error = EIO;
405f4bc7c7aSnicm goto done;
406f4bc7c7aSnicm }
407f4bc7c7aSnicm fclose(f);
408f4bc7c7aSnicm goto done;
409f4bc7c7aSnicm }
410f4bc7c7aSnicm
411f4bc7c7aSnicm skip:
412dab96b18Snicm msglen = strlen(cf->path) + 1 + sizeof *msg;
413dab96b18Snicm if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
414f4bc7c7aSnicm cf->error = E2BIG;
415f4bc7c7aSnicm goto done;
416f4bc7c7aSnicm }
417dab96b18Snicm msg = xmalloc(msglen);
418dab96b18Snicm msg->stream = cf->stream;
419dab96b18Snicm msg->fd = fd;
420dab96b18Snicm memcpy(msg + 1, cf->path, msglen - sizeof *msg);
4210c0c2584Snicm if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
422dab96b18Snicm free(msg);
423f4bc7c7aSnicm cf->error = EINVAL;
424f4bc7c7aSnicm goto done;
425f4bc7c7aSnicm }
426dab96b18Snicm free(msg);
42783e2ab13Snicm return cf;
428f4bc7c7aSnicm
429f4bc7c7aSnicm done:
430f4bc7c7aSnicm file_fire_done(cf);
43183e2ab13Snicm return NULL;
43283e2ab13Snicm }
43383e2ab13Snicm
43483e2ab13Snicm /* Cancel a file read. */
43583e2ab13Snicm void
file_cancel(struct client_file * cf)43683e2ab13Snicm file_cancel(struct client_file *cf)
43783e2ab13Snicm {
43883e2ab13Snicm struct msg_read_cancel msg;
43983e2ab13Snicm
44083e2ab13Snicm log_debug("read cancel file %d", cf->stream);
44183e2ab13Snicm
44283e2ab13Snicm if (cf->closed)
44383e2ab13Snicm return;
44483e2ab13Snicm cf->closed = 1;
44583e2ab13Snicm
44683e2ab13Snicm msg.stream = cf->stream;
44783e2ab13Snicm proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
448f4bc7c7aSnicm }
449f4bc7c7aSnicm
4500c0c2584Snicm /* Push event, fired if there is more writing to be done. */
451f4bc7c7aSnicm static void
file_push_cb(__unused int fd,__unused short events,void * arg)452f4bc7c7aSnicm file_push_cb(__unused int fd, __unused short events, void *arg)
453f4bc7c7aSnicm {
454f4bc7c7aSnicm struct client_file *cf = arg;
455f4bc7c7aSnicm
4560c0c2584Snicm if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
457f4bc7c7aSnicm file_push(cf);
458f4bc7c7aSnicm file_free(cf);
459f4bc7c7aSnicm }
460f4bc7c7aSnicm
4610c0c2584Snicm /* Push uwritten data to the client for a file, if it will accept it. */
462f4bc7c7aSnicm void
file_push(struct client_file * cf)463f4bc7c7aSnicm file_push(struct client_file *cf)
464f4bc7c7aSnicm {
465dab96b18Snicm struct msg_write_data *msg;
466dab96b18Snicm size_t msglen, sent, left;
467f4bc7c7aSnicm struct msg_write_close close;
468f4bc7c7aSnicm
469dab96b18Snicm msg = xmalloc(sizeof *msg);
470f4bc7c7aSnicm left = EVBUFFER_LENGTH(cf->buffer);
471f4bc7c7aSnicm while (left != 0) {
472f4bc7c7aSnicm sent = left;
47357431a22Snicm if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
47457431a22Snicm sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
475f4bc7c7aSnicm
476dab96b18Snicm msglen = (sizeof *msg) + sent;
477dab96b18Snicm msg = xrealloc(msg, msglen);
478dab96b18Snicm msg->stream = cf->stream;
479dab96b18Snicm memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
4800c0c2584Snicm if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
481f4bc7c7aSnicm break;
482f4bc7c7aSnicm evbuffer_drain(cf->buffer, sent);
483f4bc7c7aSnicm
484f4bc7c7aSnicm left = EVBUFFER_LENGTH(cf->buffer);
4850c0c2584Snicm log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
486f4bc7c7aSnicm }
487f4bc7c7aSnicm if (left != 0) {
488f4bc7c7aSnicm cf->references++;
489f4bc7c7aSnicm event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
490f4bc7c7aSnicm } else if (cf->stream > 2) {
491f4bc7c7aSnicm close.stream = cf->stream;
4920c0c2584Snicm proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
493f4bc7c7aSnicm file_fire_done(cf);
494f4bc7c7aSnicm }
495dab96b18Snicm free(msg);
496f4bc7c7aSnicm }
4970c0c2584Snicm
498ab26eabfSnicm /* Check if any files have data left to write. */
499ab26eabfSnicm int
file_write_left(struct client_files * files)500ab26eabfSnicm file_write_left(struct client_files *files)
501ab26eabfSnicm {
502ab26eabfSnicm struct client_file *cf;
503ab26eabfSnicm size_t left;
504ab26eabfSnicm int waiting = 0;
505ab26eabfSnicm
506ab26eabfSnicm RB_FOREACH(cf, client_files, files) {
507ab26eabfSnicm if (cf->event == NULL)
508ab26eabfSnicm continue;
509ab26eabfSnicm left = EVBUFFER_LENGTH(cf->event->output);
510ab26eabfSnicm if (left != 0) {
511ab26eabfSnicm waiting++;
512ab26eabfSnicm log_debug("file %u %zu bytes left", cf->stream, left);
513ab26eabfSnicm }
514ab26eabfSnicm }
515ab26eabfSnicm return (waiting != 0);
516ab26eabfSnicm }
517ab26eabfSnicm
5180c0c2584Snicm /* Client file write error callback. */
5190c0c2584Snicm static void
file_write_error_callback(__unused struct bufferevent * bev,__unused short what,void * arg)5200c0c2584Snicm file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
5210c0c2584Snicm void *arg)
5220c0c2584Snicm {
5230c0c2584Snicm struct client_file *cf = arg;
5240c0c2584Snicm
5250c0c2584Snicm log_debug("write error file %d", cf->stream);
5260c0c2584Snicm
5270c0c2584Snicm bufferevent_free(cf->event);
5280c0c2584Snicm cf->event = NULL;
5290c0c2584Snicm
5300c0c2584Snicm close(cf->fd);
5310c0c2584Snicm cf->fd = -1;
5325f7d4673Snicm
5335f7d4673Snicm if (cf->cb != NULL)
5345f7d4673Snicm cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
5350c0c2584Snicm }
5360c0c2584Snicm
5370c0c2584Snicm /* Client file write callback. */
5380c0c2584Snicm static void
file_write_callback(__unused struct bufferevent * bev,void * arg)5390c0c2584Snicm file_write_callback(__unused struct bufferevent *bev, void *arg)
5400c0c2584Snicm {
5410c0c2584Snicm struct client_file *cf = arg;
5420c0c2584Snicm
5430c0c2584Snicm log_debug("write check file %d", cf->stream);
5440c0c2584Snicm
5450c0c2584Snicm if (cf->cb != NULL)
5460c0c2584Snicm cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
5470c0c2584Snicm
5480c0c2584Snicm if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
5490c0c2584Snicm bufferevent_free(cf->event);
5500c0c2584Snicm close(cf->fd);
5510c0c2584Snicm RB_REMOVE(client_files, cf->tree, cf);
5520c0c2584Snicm file_free(cf);
5530c0c2584Snicm }
5540c0c2584Snicm }
5550c0c2584Snicm
5560c0c2584Snicm /* Handle a file write open message (client). */
5570c0c2584Snicm void
file_write_open(struct client_files * files,struct tmuxpeer * peer,struct imsg * imsg,int allow_streams,int close_received,client_file_cb cb,void * cbdata)5580c0c2584Snicm file_write_open(struct client_files *files, struct tmuxpeer *peer,
5590c0c2584Snicm struct imsg *imsg, int allow_streams, int close_received,
5600c0c2584Snicm client_file_cb cb, void *cbdata)
5610c0c2584Snicm {
5620c0c2584Snicm struct msg_write_open *msg = imsg->data;
5630c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
5640c0c2584Snicm const char *path;
5650c0c2584Snicm struct msg_write_ready reply;
5660c0c2584Snicm struct client_file find, *cf;
5670c0c2584Snicm const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
5680c0c2584Snicm int error = 0;
5690c0c2584Snicm
5700c0c2584Snicm if (msglen < sizeof *msg)
5710c0c2584Snicm fatalx("bad MSG_WRITE_OPEN size");
5720c0c2584Snicm if (msglen == sizeof *msg)
5730c0c2584Snicm path = "-";
5740c0c2584Snicm else
5750c0c2584Snicm path = (const char *)(msg + 1);
5760c0c2584Snicm log_debug("open write file %d %s", msg->stream, path);
5770c0c2584Snicm
5780c0c2584Snicm find.stream = msg->stream;
57980b5a9d1Snicm if (RB_FIND(client_files, files, &find) != NULL) {
5800c0c2584Snicm error = EBADF;
5810c0c2584Snicm goto reply;
5820c0c2584Snicm }
5830c0c2584Snicm cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
5840c0c2584Snicm if (cf->closed) {
5850c0c2584Snicm error = EBADF;
5860c0c2584Snicm goto reply;
5870c0c2584Snicm }
5880c0c2584Snicm
5890c0c2584Snicm cf->fd = -1;
5900c0c2584Snicm if (msg->fd == -1)
5910c0c2584Snicm cf->fd = open(path, msg->flags|flags, 0644);
5920c0c2584Snicm else if (allow_streams) {
5930c0c2584Snicm if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
5940c0c2584Snicm errno = EBADF;
5950c0c2584Snicm else {
5960c0c2584Snicm cf->fd = dup(msg->fd);
5970c0c2584Snicm if (close_received)
5980c0c2584Snicm close(msg->fd); /* can only be used once */
5990c0c2584Snicm }
6000c0c2584Snicm } else
6010c0c2584Snicm errno = EBADF;
6020c0c2584Snicm if (cf->fd == -1) {
6030c0c2584Snicm error = errno;
6040c0c2584Snicm goto reply;
6050c0c2584Snicm }
6060c0c2584Snicm
6070c0c2584Snicm cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
6080c0c2584Snicm file_write_error_callback, cf);
6097c8808e4Snicm if (cf->event == NULL)
6107c8808e4Snicm fatalx("out of memory");
6110c0c2584Snicm bufferevent_enable(cf->event, EV_WRITE);
6120c0c2584Snicm goto reply;
6130c0c2584Snicm
6140c0c2584Snicm reply:
6150c0c2584Snicm reply.stream = msg->stream;
6160c0c2584Snicm reply.error = error;
6170c0c2584Snicm proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
6180c0c2584Snicm }
6190c0c2584Snicm
6200c0c2584Snicm /* Handle a file write data message (client). */
6210c0c2584Snicm void
file_write_data(struct client_files * files,struct imsg * imsg)6220c0c2584Snicm file_write_data(struct client_files *files, struct imsg *imsg)
6230c0c2584Snicm {
6240c0c2584Snicm struct msg_write_data *msg = imsg->data;
6250c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
6260c0c2584Snicm struct client_file find, *cf;
6270c0c2584Snicm size_t size = msglen - sizeof *msg;
6280c0c2584Snicm
6290c0c2584Snicm if (msglen < sizeof *msg)
6300c0c2584Snicm fatalx("bad MSG_WRITE size");
6310c0c2584Snicm find.stream = msg->stream;
6320c0c2584Snicm if ((cf = RB_FIND(client_files, files, &find)) == NULL)
6330c0c2584Snicm fatalx("unknown stream number");
6340c0c2584Snicm log_debug("write %zu to file %d", size, cf->stream);
6350c0c2584Snicm
6360c0c2584Snicm if (cf->event != NULL)
6370c0c2584Snicm bufferevent_write(cf->event, msg + 1, size);
6380c0c2584Snicm }
6390c0c2584Snicm
6400c0c2584Snicm /* Handle a file write close message (client). */
6410c0c2584Snicm void
file_write_close(struct client_files * files,struct imsg * imsg)6420c0c2584Snicm file_write_close(struct client_files *files, struct imsg *imsg)
6430c0c2584Snicm {
6440c0c2584Snicm struct msg_write_close *msg = imsg->data;
6450c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
6460c0c2584Snicm struct client_file find, *cf;
6470c0c2584Snicm
6480c0c2584Snicm if (msglen != sizeof *msg)
6490c0c2584Snicm fatalx("bad MSG_WRITE_CLOSE size");
6500c0c2584Snicm find.stream = msg->stream;
6510c0c2584Snicm if ((cf = RB_FIND(client_files, files, &find)) == NULL)
6520c0c2584Snicm fatalx("unknown stream number");
6530c0c2584Snicm log_debug("close file %d", cf->stream);
6540c0c2584Snicm
6550c0c2584Snicm if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
6560c0c2584Snicm if (cf->event != NULL)
6570c0c2584Snicm bufferevent_free(cf->event);
6580c0c2584Snicm if (cf->fd != -1)
6590c0c2584Snicm close(cf->fd);
6600c0c2584Snicm RB_REMOVE(client_files, files, cf);
6610c0c2584Snicm file_free(cf);
6620c0c2584Snicm }
6630c0c2584Snicm }
6640c0c2584Snicm
6650c0c2584Snicm /* Client file read error callback. */
6660c0c2584Snicm static void
file_read_error_callback(__unused struct bufferevent * bev,__unused short what,void * arg)6670c0c2584Snicm file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
6680c0c2584Snicm void *arg)
6690c0c2584Snicm {
6700c0c2584Snicm struct client_file *cf = arg;
6710c0c2584Snicm struct msg_read_done msg;
6720c0c2584Snicm
6730c0c2584Snicm log_debug("read error file %d", cf->stream);
6740c0c2584Snicm
6750c0c2584Snicm msg.stream = cf->stream;
6760c0c2584Snicm msg.error = 0;
6770c0c2584Snicm proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
6780c0c2584Snicm
6790c0c2584Snicm bufferevent_free(cf->event);
6800c0c2584Snicm close(cf->fd);
6810c0c2584Snicm RB_REMOVE(client_files, cf->tree, cf);
6820c0c2584Snicm file_free(cf);
6830c0c2584Snicm }
6840c0c2584Snicm
6850c0c2584Snicm /* Client file read callback. */
6860c0c2584Snicm static void
file_read_callback(__unused struct bufferevent * bev,void * arg)6870c0c2584Snicm file_read_callback(__unused struct bufferevent *bev, void *arg)
6880c0c2584Snicm {
6890c0c2584Snicm struct client_file *cf = arg;
6900c0c2584Snicm void *bdata;
6910c0c2584Snicm size_t bsize;
6920c0c2584Snicm struct msg_read_data *msg;
6930c0c2584Snicm size_t msglen;
6940c0c2584Snicm
6950c0c2584Snicm msg = xmalloc(sizeof *msg);
6960c0c2584Snicm for (;;) {
6970c0c2584Snicm bdata = EVBUFFER_DATA(cf->event->input);
6980c0c2584Snicm bsize = EVBUFFER_LENGTH(cf->event->input);
6990c0c2584Snicm
7000c0c2584Snicm if (bsize == 0)
7010c0c2584Snicm break;
7020c0c2584Snicm if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
7030c0c2584Snicm bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
7040c0c2584Snicm log_debug("read %zu from file %d", bsize, cf->stream);
7050c0c2584Snicm
7060c0c2584Snicm msglen = (sizeof *msg) + bsize;
7070c0c2584Snicm msg = xrealloc(msg, msglen);
7080c0c2584Snicm msg->stream = cf->stream;
7090c0c2584Snicm memcpy(msg + 1, bdata, bsize);
7100c0c2584Snicm proc_send(cf->peer, MSG_READ, -1, msg, msglen);
7110c0c2584Snicm
7120c0c2584Snicm evbuffer_drain(cf->event->input, bsize);
7130c0c2584Snicm }
7140c0c2584Snicm free(msg);
7150c0c2584Snicm }
7160c0c2584Snicm
7170c0c2584Snicm /* Handle a file read open message (client). */
7180c0c2584Snicm void
file_read_open(struct client_files * files,struct tmuxpeer * peer,struct imsg * imsg,int allow_streams,int close_received,client_file_cb cb,void * cbdata)7190c0c2584Snicm file_read_open(struct client_files *files, struct tmuxpeer *peer,
7200c0c2584Snicm struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
7210c0c2584Snicm void *cbdata)
7220c0c2584Snicm {
7230c0c2584Snicm struct msg_read_open *msg = imsg->data;
7240c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
7250c0c2584Snicm const char *path;
7260c0c2584Snicm struct msg_read_done reply;
7270c0c2584Snicm struct client_file find, *cf;
7280c0c2584Snicm const int flags = O_NONBLOCK|O_RDONLY;
7290c0c2584Snicm int error;
7300c0c2584Snicm
7310c0c2584Snicm if (msglen < sizeof *msg)
7320c0c2584Snicm fatalx("bad MSG_READ_OPEN size");
7330c0c2584Snicm if (msglen == sizeof *msg)
7340c0c2584Snicm path = "-";
7350c0c2584Snicm else
7360c0c2584Snicm path = (const char *)(msg + 1);
7370c0c2584Snicm log_debug("open read file %d %s", msg->stream, path);
7380c0c2584Snicm
7390c0c2584Snicm find.stream = msg->stream;
74080b5a9d1Snicm if (RB_FIND(client_files, files, &find) != NULL) {
7410c0c2584Snicm error = EBADF;
7420c0c2584Snicm goto reply;
7430c0c2584Snicm }
7440c0c2584Snicm cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
7450c0c2584Snicm if (cf->closed) {
7460c0c2584Snicm error = EBADF;
7470c0c2584Snicm goto reply;
7480c0c2584Snicm }
7490c0c2584Snicm
7500c0c2584Snicm cf->fd = -1;
7510c0c2584Snicm if (msg->fd == -1)
7520c0c2584Snicm cf->fd = open(path, flags);
7530c0c2584Snicm else if (allow_streams) {
7540c0c2584Snicm if (msg->fd != STDIN_FILENO)
7550c0c2584Snicm errno = EBADF;
7560c0c2584Snicm else {
7570c0c2584Snicm cf->fd = dup(msg->fd);
7580c0c2584Snicm if (close_received)
7590c0c2584Snicm close(msg->fd); /* can only be used once */
7600c0c2584Snicm }
7610c0c2584Snicm } else
7620c0c2584Snicm errno = EBADF;
7630c0c2584Snicm if (cf->fd == -1) {
7640c0c2584Snicm error = errno;
7650c0c2584Snicm goto reply;
7660c0c2584Snicm }
7670c0c2584Snicm
7680c0c2584Snicm cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
7690c0c2584Snicm file_read_error_callback, cf);
7707c8808e4Snicm if (cf->event == NULL)
7717c8808e4Snicm fatalx("out of memory");
7720c0c2584Snicm bufferevent_enable(cf->event, EV_READ);
7730c0c2584Snicm return;
7740c0c2584Snicm
7750c0c2584Snicm reply:
7760c0c2584Snicm reply.stream = msg->stream;
7770c0c2584Snicm reply.error = error;
7780c0c2584Snicm proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
7790c0c2584Snicm }
7800c0c2584Snicm
78183e2ab13Snicm /* Handle a read cancel message (client). */
78283e2ab13Snicm void
file_read_cancel(struct client_files * files,struct imsg * imsg)78383e2ab13Snicm file_read_cancel(struct client_files *files, struct imsg *imsg)
78483e2ab13Snicm {
78583e2ab13Snicm struct msg_read_cancel *msg = imsg->data;
78683e2ab13Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
78783e2ab13Snicm struct client_file find, *cf;
78883e2ab13Snicm
78983e2ab13Snicm if (msglen != sizeof *msg)
79083e2ab13Snicm fatalx("bad MSG_READ_CANCEL size");
79183e2ab13Snicm find.stream = msg->stream;
79283e2ab13Snicm if ((cf = RB_FIND(client_files, files, &find)) == NULL)
79383e2ab13Snicm fatalx("unknown stream number");
79483e2ab13Snicm log_debug("cancel file %d", cf->stream);
79583e2ab13Snicm
79683e2ab13Snicm file_read_error_callback(NULL, 0, cf);
79783e2ab13Snicm }
79883e2ab13Snicm
7990c0c2584Snicm /* Handle a write ready message (server). */
8000c0c2584Snicm void
file_write_ready(struct client_files * files,struct imsg * imsg)8010c0c2584Snicm file_write_ready(struct client_files *files, struct imsg *imsg)
8020c0c2584Snicm {
8030c0c2584Snicm struct msg_write_ready *msg = imsg->data;
8040c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
8050c0c2584Snicm struct client_file find, *cf;
8060c0c2584Snicm
8070c0c2584Snicm if (msglen != sizeof *msg)
8080c0c2584Snicm fatalx("bad MSG_WRITE_READY size");
8090c0c2584Snicm find.stream = msg->stream;
8100c0c2584Snicm if ((cf = RB_FIND(client_files, files, &find)) == NULL)
8110c0c2584Snicm return;
8120c0c2584Snicm if (msg->error != 0) {
8130c0c2584Snicm cf->error = msg->error;
8140c0c2584Snicm file_fire_done(cf);
8150c0c2584Snicm } else
8160c0c2584Snicm file_push(cf);
8170c0c2584Snicm }
8180c0c2584Snicm
8190c0c2584Snicm /* Handle read data message (server). */
8200c0c2584Snicm void
file_read_data(struct client_files * files,struct imsg * imsg)8210c0c2584Snicm file_read_data(struct client_files *files, struct imsg *imsg)
8220c0c2584Snicm {
8230c0c2584Snicm struct msg_read_data *msg = imsg->data;
8240c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
8250c0c2584Snicm struct client_file find, *cf;
8260c0c2584Snicm void *bdata = msg + 1;
8270c0c2584Snicm size_t bsize = msglen - sizeof *msg;
8280c0c2584Snicm
8290c0c2584Snicm if (msglen < sizeof *msg)
8300c0c2584Snicm fatalx("bad MSG_READ_DATA size");
8310c0c2584Snicm find.stream = msg->stream;
8320c0c2584Snicm if ((cf = RB_FIND(client_files, files, &find)) == NULL)
8330c0c2584Snicm return;
8340c0c2584Snicm
8350c0c2584Snicm log_debug("file %d read %zu bytes", cf->stream, bsize);
83683e2ab13Snicm if (cf->error == 0 && !cf->closed) {
8370c0c2584Snicm if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
8380c0c2584Snicm cf->error = ENOMEM;
8390c0c2584Snicm file_fire_done(cf);
8400c0c2584Snicm } else
8410c0c2584Snicm file_fire_read(cf);
8420c0c2584Snicm }
8430c0c2584Snicm }
8440c0c2584Snicm
8450c0c2584Snicm /* Handle a read done message (server). */
8460c0c2584Snicm void
file_read_done(struct client_files * files,struct imsg * imsg)8470c0c2584Snicm file_read_done(struct client_files *files, struct imsg *imsg)
8480c0c2584Snicm {
8490c0c2584Snicm struct msg_read_done *msg = imsg->data;
8500c0c2584Snicm size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
8510c0c2584Snicm struct client_file find, *cf;
8520c0c2584Snicm
8530c0c2584Snicm if (msglen != sizeof *msg)
8540c0c2584Snicm fatalx("bad MSG_READ_DONE size");
8550c0c2584Snicm find.stream = msg->stream;
8560c0c2584Snicm if ((cf = RB_FIND(client_files, files, &find)) == NULL)
8570c0c2584Snicm return;
8580c0c2584Snicm
8590c0c2584Snicm log_debug("file %d read done", cf->stream);
8600c0c2584Snicm cf->error = msg->error;
8610c0c2584Snicm file_fire_done(cf);
8620c0c2584Snicm }
863