xref: /openbsd-src/usr.bin/tmux/file.c (revision 9d4bd388c3bd902ea7d15c2cb52783ff424bb337)
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