xref: /netbsd-src/external/bsd/tmux/dist/file.c (revision c23f9150cad51fdd442fa1806fac769ae26a1fdd)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "tmux.h"
29 
30 /*
31  * IPC file handling. Both client and server use the same data structures
32  * (client_file and client_files) to store list of active files. Most functions
33  * are for use either in client or server but not both.
34  */
35 
36 static int	file_next_stream = 3;
37 
38 RB_GENERATE(client_files, client_file, entry, file_cmp);
39 
40 /* Get path for file, either as given or from working directory. */
41 static char *
file_get_path(struct client * c,const char * file)42 file_get_path(struct client *c, const char *file)
43 {
44 	char	*path;
45 
46 	if (*file == '/')
47 		path = xstrdup(file);
48 	else
49 		xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
50 	return (path);
51 }
52 
53 /* Tree comparison function. */
54 int
file_cmp(struct client_file * cf1,struct client_file * cf2)55 file_cmp(struct client_file *cf1, struct client_file *cf2)
56 {
57 	if (cf1->stream < cf2->stream)
58 		return (-1);
59 	if (cf1->stream > cf2->stream)
60 		return (1);
61 	return (0);
62 }
63 
64 /*
65  * Create a file object in the client process - the peer is the server to send
66  * messages to. Check callback is fired when the file is finished with so the
67  * process can decide if it needs to exit (if it is waiting for files to
68  * flush).
69  */
70 struct client_file *
file_create_with_peer(struct tmuxpeer * peer,struct client_files * files,int stream,client_file_cb cb,void * cbdata)71 file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
72     int stream, client_file_cb cb, void *cbdata)
73 {
74 	struct client_file	*cf;
75 
76 	cf = xcalloc(1, sizeof *cf);
77 	cf->c = NULL;
78 	cf->references = 1;
79 	cf->stream = stream;
80 
81 	cf->buffer = evbuffer_new();
82 	if (cf->buffer == NULL)
83 		fatalx("out of memory");
84 
85 	cf->cb = cb;
86 	cf->data = cbdata;
87 
88 	cf->peer = peer;
89 	cf->tree = files;
90 	RB_INSERT(client_files, files, cf);
91 
92 	return (cf);
93 }
94 
95 /* Create a file object in the server, communicating with the given client. */
96 struct client_file *
file_create_with_client(struct client * c,int stream,client_file_cb cb,void * cbdata)97 file_create_with_client(struct client *c, int stream, client_file_cb cb,
98     void *cbdata)
99 {
100 	struct client_file	*cf;
101 
102 	if (c != NULL && (c->flags & CLIENT_ATTACHED))
103 		c = NULL;
104 
105 	cf = xcalloc(1, sizeof *cf);
106 	cf->c = c;
107 	cf->references = 1;
108 	cf->stream = stream;
109 
110 	cf->buffer = evbuffer_new();
111 	if (cf->buffer == NULL)
112 		fatalx("out of memory");
113 
114 	cf->cb = cb;
115 	cf->data = cbdata;
116 
117 	if (cf->c != NULL) {
118 		cf->peer = cf->c->peer;
119 		cf->tree = &cf->c->files;
120 		RB_INSERT(client_files, &cf->c->files, cf);
121 		cf->c->references++;
122 	}
123 
124 	return (cf);
125 }
126 
127 /* Free a file. */
128 void
file_free(struct client_file * cf)129 file_free(struct client_file *cf)
130 {
131 	if (--cf->references != 0)
132 		return;
133 
134 	evbuffer_free(cf->buffer);
135 	free(cf->path);
136 
137 	if (cf->tree != NULL)
138 		RB_REMOVE(client_files, cf->tree, cf);
139 	if (cf->c != NULL)
140 		server_client_unref(cf->c);
141 
142 	free(cf);
143 }
144 
145 /* Event to fire the done callback. */
146 static void
file_fire_done_cb(__unused int fd,__unused short events,void * arg)147 file_fire_done_cb(__unused int fd, __unused short events, void *arg)
148 {
149 	struct client_file	*cf = arg;
150 	struct client		*c = cf->c;
151 
152 	if (cf->cb != NULL &&
153 	    (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
154 		cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
155 	file_free(cf);
156 }
157 
158 /* Add an event to fire the done callback (used by the server). */
159 void
file_fire_done(struct client_file * cf)160 file_fire_done(struct client_file *cf)
161 {
162 	event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
163 }
164 
165 /* Fire the read callback. */
166 void
file_fire_read(struct client_file * cf)167 file_fire_read(struct client_file *cf)
168 {
169 	if (cf->cb != NULL)
170 		cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
171 }
172 
173 /* Can this file be printed to? */
174 int
file_can_print(struct client * c)175 file_can_print(struct client *c)
176 {
177 	if (c == NULL ||
178 	    (c->flags & CLIENT_ATTACHED) ||
179 	    (c->flags & CLIENT_CONTROL))
180 		return (0);
181 	return (1);
182 }
183 
184 /* Print a message to a file. */
185 void
file_print(struct client * c,const char * fmt,...)186 file_print(struct client *c, const char *fmt, ...)
187 {
188 	va_list	ap;
189 
190 	va_start(ap, fmt);
191 	file_vprint(c, fmt, ap);
192 	va_end(ap);
193 }
194 
195 /* Print a message to a file. */
196 void
file_vprint(struct client * c,const char * fmt,va_list ap)197 file_vprint(struct client *c, const char *fmt, va_list ap)
198 {
199 	struct client_file	 find, *cf;
200 	struct msg_write_open	 msg;
201 
202 	if (!file_can_print(c))
203 		return;
204 
205 	find.stream = 1;
206 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
207 		cf = file_create_with_client(c, 1, NULL, NULL);
208 		cf->path = xstrdup("-");
209 
210 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
211 
212 		msg.stream = 1;
213 		msg.fd = STDOUT_FILENO;
214 		msg.flags = 0;
215 		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
216 	} else {
217 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
218 		file_push(cf);
219 	}
220 }
221 
222 /* Print a buffer to a file. */
223 void
file_print_buffer(struct client * c,void * data,size_t size)224 file_print_buffer(struct client *c, void *data, size_t size)
225 {
226 	struct client_file	 find, *cf;
227 	struct msg_write_open	 msg;
228 
229 	if (!file_can_print(c))
230 		return;
231 
232 	find.stream = 1;
233 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
234 		cf = file_create_with_client(c, 1, NULL, NULL);
235 		cf->path = xstrdup("-");
236 
237 		evbuffer_add(cf->buffer, data, size);
238 
239 		msg.stream = 1;
240 		msg.fd = STDOUT_FILENO;
241 		msg.flags = 0;
242 		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
243 	} else {
244 		evbuffer_add(cf->buffer, data, size);
245 		file_push(cf);
246 	}
247 }
248 
249 /* Report an error to a file. */
250 void
file_error(struct client * c,const char * fmt,...)251 file_error(struct client *c, const char *fmt, ...)
252 {
253 	struct client_file	 find, *cf;
254 	struct msg_write_open	 msg;
255 	va_list			 ap;
256 
257 	if (!file_can_print(c))
258 		return;
259 
260 	va_start(ap, fmt);
261 
262 	find.stream = 2;
263 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
264 		cf = file_create_with_client(c, 2, NULL, NULL);
265 		cf->path = xstrdup("-");
266 
267 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
268 
269 		msg.stream = 2;
270 		msg.fd = STDERR_FILENO;
271 		msg.flags = 0;
272 		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
273 	} else {
274 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
275 		file_push(cf);
276 	}
277 
278 	va_end(ap);
279 }
280 
281 /* Write data to a file. */
282 void
file_write(struct client * c,const char * path,int flags,const void * bdata,size_t bsize,client_file_cb cb,void * cbdata)283 file_write(struct client *c, const char *path, int flags, const void *bdata,
284     size_t bsize, client_file_cb cb, void *cbdata)
285 {
286 	struct client_file	*cf;
287 	struct msg_write_open	*msg;
288 	size_t			 msglen;
289 	int			 fd = -1;
290 	u_int			 stream = file_next_stream++;
291 	FILE			*f;
292 	const char		*mode;
293 
294 	if (strcmp(path, "-") == 0) {
295 		cf = file_create_with_client(c, stream, cb, cbdata);
296 		cf->path = xstrdup("-");
297 
298 		fd = STDOUT_FILENO;
299 		if (c == NULL ||
300 		    (c->flags & CLIENT_ATTACHED) ||
301 		    (c->flags & CLIENT_CONTROL)) {
302 			cf->error = EBADF;
303 			goto done;
304 		}
305 		goto skip;
306 	}
307 
308 	cf = file_create_with_client(c, stream, cb, cbdata);
309 	cf->path = file_get_path(c, path);
310 
311 	if (c == NULL || c->flags & CLIENT_ATTACHED) {
312 		if (flags & O_APPEND)
313 			mode = "ab";
314 		else
315 			mode = "wb";
316 		f = fopen(cf->path, mode);
317 		if (f == NULL) {
318 			cf->error = errno;
319 			goto done;
320 		}
321 		if (fwrite(bdata, 1, bsize, f) != bsize) {
322 			fclose(f);
323 			cf->error = EIO;
324 			goto done;
325 		}
326 		fclose(f);
327 		goto done;
328 	}
329 
330 skip:
331 	evbuffer_add(cf->buffer, bdata, bsize);
332 
333 	msglen = strlen(cf->path) + 1 + sizeof *msg;
334 	if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
335 		cf->error = E2BIG;
336 		goto done;
337 	}
338 	msg = xmalloc(msglen);
339 	msg->stream = cf->stream;
340 	msg->fd = fd;
341 	msg->flags = flags;
342 	memcpy(msg + 1, cf->path, msglen - sizeof *msg);
343 	if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
344 		free(msg);
345 		cf->error = EINVAL;
346 		goto done;
347 	}
348 	free(msg);
349 	return;
350 
351 done:
352 	file_fire_done(cf);
353 }
354 
355 /* Read a file. */
356 struct client_file *
file_read(struct client * c,const char * path,client_file_cb cb,void * cbdata)357 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
358 {
359 	struct client_file	*cf;
360 	struct msg_read_open	*msg;
361 	size_t			 msglen;
362 	int			 fd = -1;
363 	u_int			 stream = file_next_stream++;
364 	FILE			*f;
365 	size_t			 size;
366 	char			 buffer[BUFSIZ];
367 
368 	if (strcmp(path, "-") == 0) {
369 		cf = file_create_with_client(c, stream, cb, cbdata);
370 		cf->path = xstrdup("-");
371 
372 		fd = STDIN_FILENO;
373 		if (c == NULL ||
374 		    (c->flags & CLIENT_ATTACHED) ||
375 		    (c->flags & CLIENT_CONTROL)) {
376 			cf->error = EBADF;
377 			goto done;
378 		}
379 		goto skip;
380 	}
381 
382 	cf = file_create_with_client(c, stream, cb, cbdata);
383 	cf->path = file_get_path(c, path);
384 
385 	if (c == NULL || c->flags & CLIENT_ATTACHED) {
386 		f = fopen(cf->path, "rb");
387 		if (f == NULL) {
388 			cf->error = errno;
389 			goto done;
390 		}
391 		for (;;) {
392 			size = fread(buffer, 1, sizeof buffer, f);
393 			if (evbuffer_add(cf->buffer, buffer, size) != 0) {
394 				cf->error = ENOMEM;
395 				goto done;
396 			}
397 			if (size != sizeof buffer)
398 				break;
399 		}
400 		if (ferror(f)) {
401 			cf->error = EIO;
402 			goto done;
403 		}
404 		fclose(f);
405 		goto done;
406 	}
407 
408 skip:
409 	msglen = strlen(cf->path) + 1 + sizeof *msg;
410 	if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
411 		cf->error = E2BIG;
412 		goto done;
413 	}
414 	msg = xmalloc(msglen);
415 	msg->stream = cf->stream;
416 	msg->fd = fd;
417 	memcpy(msg + 1, cf->path, msglen - sizeof *msg);
418 	if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
419 		free(msg);
420 		cf->error = EINVAL;
421 		goto done;
422 	}
423 	free(msg);
424 	return cf;
425 
426 done:
427 	file_fire_done(cf);
428 	return NULL;
429 }
430 
431 /* Cancel a file read. */
432 void
file_cancel(struct client_file * cf)433 file_cancel(struct client_file *cf)
434 {
435 	struct msg_read_cancel	 msg;
436 
437 	log_debug("read cancel file %d", cf->stream);
438 
439 	if (cf->closed)
440 		return;
441 	cf->closed = 1;
442 
443 	msg.stream = cf->stream;
444 	proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
445 }
446 
447 /* Push event, fired if there is more writing to be done. */
448 static void
file_push_cb(__unused int fd,__unused short events,void * arg)449 file_push_cb(__unused int fd, __unused short events, void *arg)
450 {
451 	struct client_file	*cf = arg;
452 
453 	if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
454 		file_push(cf);
455 	file_free(cf);
456 }
457 
458 /* Push uwritten data to the client for a file, if it will accept it. */
459 void
file_push(struct client_file * cf)460 file_push(struct client_file *cf)
461 {
462 	struct msg_write_data	*msg;
463 	size_t			 msglen, sent, left;
464 	struct msg_write_close	 close;
465 
466 	msg = xmalloc(sizeof *msg);
467 	left = EVBUFFER_LENGTH(cf->buffer);
468 	while (left != 0) {
469 		sent = left;
470 		if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
471 			sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
472 
473 		msglen = (sizeof *msg) + sent;
474 		msg = xrealloc(msg, msglen);
475 		msg->stream = cf->stream;
476 		memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
477 		if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
478 			break;
479 		evbuffer_drain(cf->buffer, sent);
480 
481 		left = EVBUFFER_LENGTH(cf->buffer);
482 		log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
483 	}
484 	if (left != 0) {
485 		cf->references++;
486 		event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
487 	} else if (cf->stream > 2) {
488 		close.stream = cf->stream;
489 		proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
490 		file_fire_done(cf);
491 	}
492 	free(msg);
493 }
494 
495 /* Check if any files have data left to write. */
496 int
file_write_left(struct client_files * files)497 file_write_left(struct client_files *files)
498 {
499 	struct client_file	*cf;
500 	size_t			 left;
501 	int			 waiting = 0;
502 
503 	RB_FOREACH(cf, client_files, files) {
504 		if (cf->event == NULL)
505 			continue;
506 		left = EVBUFFER_LENGTH(cf->event->output);
507 		if (left != 0) {
508 			waiting++;
509 			log_debug("file %u %zu bytes left", cf->stream, left);
510 		}
511 	}
512 	return (waiting != 0);
513 }
514 
515 /* Client file write error callback. */
516 static void
file_write_error_callback(__unused struct bufferevent * bev,__unused short what,void * arg)517 file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
518     void *arg)
519 {
520 	struct client_file	*cf = arg;
521 
522 	log_debug("write error file %d", cf->stream);
523 
524 	bufferevent_free(cf->event);
525 	cf->event = NULL;
526 
527 	close(cf->fd);
528 	cf->fd = -1;
529 
530 	if (cf->cb != NULL)
531 		cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
532 }
533 
534 /* Client file write callback. */
535 static void
file_write_callback(__unused struct bufferevent * bev,void * arg)536 file_write_callback(__unused struct bufferevent *bev, void *arg)
537 {
538 	struct client_file	*cf = arg;
539 
540 	log_debug("write check file %d", cf->stream);
541 
542 	if (cf->cb != NULL)
543 		cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
544 
545 	if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
546 		bufferevent_free(cf->event);
547 		close(cf->fd);
548 		RB_REMOVE(client_files, cf->tree, cf);
549 		file_free(cf);
550 	}
551 }
552 
553 /* Handle a file write open message (client). */
554 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)555 file_write_open(struct client_files *files, struct tmuxpeer *peer,
556     struct imsg *imsg, int allow_streams, int close_received,
557     client_file_cb cb, void *cbdata)
558 {
559 	struct msg_write_open	*msg = imsg->data;
560 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
561 	const char		*path;
562 	struct msg_write_ready	 reply;
563 	struct client_file	 find, *cf;
564 	const int		 flags = O_NONBLOCK|O_WRONLY|O_CREAT;
565 	int			 error = 0;
566 
567 	if (msglen < sizeof *msg)
568 		fatalx("bad MSG_WRITE_OPEN size");
569 	if (msglen == sizeof *msg)
570 		path = "-";
571 	else
572 		path = (const char *)(msg + 1);
573 	log_debug("open write file %d %s", msg->stream, path);
574 
575 	find.stream = msg->stream;
576 	if (RB_FIND(client_files, files, &find) != NULL) {
577 		error = EBADF;
578 		goto reply;
579 	}
580 	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
581 	if (cf->closed) {
582 		error = EBADF;
583 		goto reply;
584 	}
585 
586 	cf->fd = -1;
587 	if (msg->fd == -1)
588 		cf->fd = open(path, msg->flags|flags, 0644);
589 	else if (allow_streams) {
590 		if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
591 			errno = EBADF;
592 		else {
593 			cf->fd = dup(msg->fd);
594 			if (close_received)
595 				close(msg->fd); /* can only be used once */
596 		}
597 	} else
598 	      errno = EBADF;
599 	if (cf->fd == -1) {
600 		error = errno;
601 		goto reply;
602 	}
603 
604 	cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
605 	    file_write_error_callback, cf);
606 	if (cf->event == NULL)
607 		fatalx("out of memory");
608 	bufferevent_enable(cf->event, EV_WRITE);
609 	goto reply;
610 
611 reply:
612 	reply.stream = msg->stream;
613 	reply.error = error;
614 	proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
615 }
616 
617 /* Handle a file write data message (client). */
618 void
file_write_data(struct client_files * files,struct imsg * imsg)619 file_write_data(struct client_files *files, struct imsg *imsg)
620 {
621 	struct msg_write_data	*msg = imsg->data;
622 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
623 	struct client_file	 find, *cf;
624 	size_t			 size = msglen - sizeof *msg;
625 
626 	if (msglen < sizeof *msg)
627 		fatalx("bad MSG_WRITE size");
628 	find.stream = msg->stream;
629 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
630 		fatalx("unknown stream number");
631 	log_debug("write %zu to file %d", size, cf->stream);
632 
633 	if (cf->event != NULL)
634 		bufferevent_write(cf->event, msg + 1, size);
635 }
636 
637 /* Handle a file write close message (client). */
638 void
file_write_close(struct client_files * files,struct imsg * imsg)639 file_write_close(struct client_files *files, struct imsg *imsg)
640 {
641 	struct msg_write_close	*msg = imsg->data;
642 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
643 	struct client_file	 find, *cf;
644 
645 	if (msglen != sizeof *msg)
646 		fatalx("bad MSG_WRITE_CLOSE size");
647 	find.stream = msg->stream;
648 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
649 		fatalx("unknown stream number");
650 	log_debug("close file %d", cf->stream);
651 
652 	if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
653 		if (cf->event != NULL)
654 			bufferevent_free(cf->event);
655 		if (cf->fd != -1)
656 			close(cf->fd);
657 		RB_REMOVE(client_files, files, cf);
658 		file_free(cf);
659 	}
660 }
661 
662 /* Client file read error callback. */
663 static void
file_read_error_callback(__unused struct bufferevent * bev,__unused short what,void * arg)664 file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
665     void *arg)
666 {
667 	struct client_file	*cf = arg;
668 	struct msg_read_done	 msg;
669 
670 	log_debug("read error file %d", cf->stream);
671 
672 	msg.stream = cf->stream;
673 	msg.error = 0;
674 	proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
675 
676 	bufferevent_free(cf->event);
677 	close(cf->fd);
678 	RB_REMOVE(client_files, cf->tree, cf);
679 	file_free(cf);
680 }
681 
682 /* Client file read callback. */
683 static void
file_read_callback(__unused struct bufferevent * bev,void * arg)684 file_read_callback(__unused struct bufferevent *bev, void *arg)
685 {
686 	struct client_file	*cf = arg;
687 	void			*bdata;
688 	size_t			 bsize;
689 	struct msg_read_data	*msg;
690 	size_t			 msglen;
691 
692 	msg = xmalloc(sizeof *msg);
693 	for (;;) {
694 		bdata = EVBUFFER_DATA(cf->event->input);
695 		bsize = EVBUFFER_LENGTH(cf->event->input);
696 
697 		if (bsize == 0)
698 			break;
699 		if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
700 			bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
701 		log_debug("read %zu from file %d", bsize, cf->stream);
702 
703 		msglen = (sizeof *msg) + bsize;
704 		msg = xrealloc(msg, msglen);
705 		msg->stream = cf->stream;
706 		memcpy(msg + 1, bdata, bsize);
707 		proc_send(cf->peer, MSG_READ, -1, msg, msglen);
708 
709 		evbuffer_drain(cf->event->input, bsize);
710 	}
711 	free(msg);
712 }
713 
714 /* Handle a file read open message (client). */
715 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)716 file_read_open(struct client_files *files, struct tmuxpeer *peer,
717     struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
718     void *cbdata)
719 {
720 	struct msg_read_open	*msg = imsg->data;
721 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
722 	const char		*path;
723 	struct msg_read_done	 reply;
724 	struct client_file	 find, *cf;
725 	const int		 flags = O_NONBLOCK|O_RDONLY;
726 	int			 error;
727 
728 	if (msglen < sizeof *msg)
729 		fatalx("bad MSG_READ_OPEN size");
730 	if (msglen == sizeof *msg)
731 		path = "-";
732 	else
733 		path = (const char *)(msg + 1);
734 	log_debug("open read file %d %s", msg->stream, path);
735 
736 	find.stream = msg->stream;
737 	if (RB_FIND(client_files, files, &find) != NULL) {
738 		error = EBADF;
739 		goto reply;
740 	}
741 	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
742 	if (cf->closed) {
743 		error = EBADF;
744 		goto reply;
745 	}
746 
747 	cf->fd = -1;
748 	if (msg->fd == -1)
749 		cf->fd = open(path, flags);
750 	else if (allow_streams) {
751 		if (msg->fd != STDIN_FILENO)
752 			errno = EBADF;
753 		else {
754 			cf->fd = dup(msg->fd);
755 			if (close_received)
756 				close(msg->fd); /* can only be used once */
757 		}
758 	} else
759 		errno = EBADF;
760 	if (cf->fd == -1) {
761 		error = errno;
762 		goto reply;
763 	}
764 
765 	cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
766 	    file_read_error_callback, cf);
767 	if (cf->event == NULL)
768 		fatalx("out of memory");
769 	bufferevent_enable(cf->event, EV_READ);
770 	return;
771 
772 reply:
773 	reply.stream = msg->stream;
774 	reply.error = error;
775 	proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
776 }
777 
778 /* Handle a read cancel message (client). */
779 void
file_read_cancel(struct client_files * files,struct imsg * imsg)780 file_read_cancel(struct client_files *files, struct imsg *imsg)
781 {
782 	struct msg_read_cancel	*msg = imsg->data;
783 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
784 	struct client_file	 find, *cf;
785 
786 	if (msglen != sizeof *msg)
787 		fatalx("bad MSG_READ_CANCEL size");
788 	find.stream = msg->stream;
789 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
790 		fatalx("unknown stream number");
791 	log_debug("cancel file %d", cf->stream);
792 
793 	file_read_error_callback(NULL, 0, cf);
794 }
795 
796 /* Handle a write ready message (server). */
797 void
file_write_ready(struct client_files * files,struct imsg * imsg)798 file_write_ready(struct client_files *files, struct imsg *imsg)
799 {
800 	struct msg_write_ready	*msg = imsg->data;
801 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
802 	struct client_file	 find, *cf;
803 
804 	if (msglen != sizeof *msg)
805 		fatalx("bad MSG_WRITE_READY size");
806 	find.stream = msg->stream;
807 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
808 		return;
809 	if (msg->error != 0) {
810 		cf->error = msg->error;
811 		file_fire_done(cf);
812 	} else
813 		file_push(cf);
814 }
815 
816 /* Handle read data message (server). */
817 void
file_read_data(struct client_files * files,struct imsg * imsg)818 file_read_data(struct client_files *files, struct imsg *imsg)
819 {
820 	struct msg_read_data	*msg = imsg->data;
821 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
822 	struct client_file	 find, *cf;
823 	void			*bdata = msg + 1;
824 	size_t			 bsize = msglen - sizeof *msg;
825 
826 	if (msglen < sizeof *msg)
827 		fatalx("bad MSG_READ_DATA size");
828 	find.stream = msg->stream;
829 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
830 		return;
831 
832 	log_debug("file %d read %zu bytes", cf->stream, bsize);
833 	if (cf->error == 0 && !cf->closed) {
834 		if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
835 			cf->error = ENOMEM;
836 			file_fire_done(cf);
837 		} else
838 			file_fire_read(cf);
839 	}
840 }
841 
842 /* Handle a read done message (server). */
843 void
file_read_done(struct client_files * files,struct imsg * imsg)844 file_read_done(struct client_files *files, struct imsg *imsg)
845 {
846 	struct msg_read_done	*msg = imsg->data;
847 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
848 	struct client_file	 find, *cf;
849 
850 	if (msglen != sizeof *msg)
851 		fatalx("bad MSG_READ_DONE size");
852 	find.stream = msg->stream;
853 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
854 		return;
855 
856 	log_debug("file %d read done", cf->stream);
857 	cf->error = msg->error;
858 	file_fire_done(cf);
859 }
860