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