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 && (c == NULL || (~c->flags & CLIENT_DEAD)))
153 cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
154 file_free(cf);
155 }
156
157 /* Add an event to fire the done callback (used by the server). */
158 void
file_fire_done(struct client_file * cf)159 file_fire_done(struct client_file *cf)
160 {
161 event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
162 }
163
164 /* Fire the read callback. */
165 void
file_fire_read(struct client_file * cf)166 file_fire_read(struct client_file *cf)
167 {
168 if (cf->cb != NULL)
169 cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
170 }
171
172 /* Can this file be printed to? */
173 int
file_can_print(struct client * c)174 file_can_print(struct client *c)
175 {
176 if (c == NULL)
177 return (0);
178 if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
179 return (0);
180 return (1);
181 }
182
183 /* Print a message to a file. */
184 void
file_print(struct client * c,const char * fmt,...)185 file_print(struct client *c, const char *fmt, ...)
186 {
187 va_list ap;
188
189 va_start(ap, fmt);
190 file_vprint(c, fmt, ap);
191 va_end(ap);
192 }
193
194 /* Print a message to a file. */
195 void
file_vprint(struct client * c,const char * fmt,va_list ap)196 file_vprint(struct client *c, const char *fmt, va_list ap)
197 {
198 struct client_file find, *cf;
199 struct msg_write_open msg;
200
201 if (!file_can_print(c))
202 return;
203
204 find.stream = 1;
205 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
206 cf = file_create_with_client(c, 1, NULL, NULL);
207 cf->path = xstrdup("-");
208
209 evbuffer_add_vprintf(cf->buffer, fmt, ap);
210
211 msg.stream = 1;
212 msg.fd = STDOUT_FILENO;
213 msg.flags = 0;
214 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
215 } else {
216 evbuffer_add_vprintf(cf->buffer, fmt, ap);
217 file_push(cf);
218 }
219 }
220
221 /* Print a buffer to a file. */
222 void
file_print_buffer(struct client * c,void * data,size_t size)223 file_print_buffer(struct client *c, void *data, size_t size)
224 {
225 struct client_file find, *cf;
226 struct msg_write_open msg;
227
228 if (!file_can_print(c))
229 return;
230
231 find.stream = 1;
232 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
233 cf = file_create_with_client(c, 1, NULL, NULL);
234 cf->path = xstrdup("-");
235
236 evbuffer_add(cf->buffer, data, size);
237
238 msg.stream = 1;
239 msg.fd = STDOUT_FILENO;
240 msg.flags = 0;
241 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
242 } else {
243 evbuffer_add(cf->buffer, data, size);
244 file_push(cf);
245 }
246 }
247
248 /* Report an error to a file. */
249 void
file_error(struct client * c,const char * fmt,...)250 file_error(struct client *c, const char *fmt, ...)
251 {
252 struct client_file find, *cf;
253 struct msg_write_open msg;
254 va_list ap;
255
256 if (!file_can_print(c))
257 return;
258
259 va_start(ap, fmt);
260
261 find.stream = 2;
262 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
263 cf = file_create_with_client(c, 2, NULL, NULL);
264 cf->path = xstrdup("-");
265
266 evbuffer_add_vprintf(cf->buffer, fmt, ap);
267
268 msg.stream = 2;
269 msg.fd = STDERR_FILENO;
270 msg.flags = 0;
271 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
272 } else {
273 evbuffer_add_vprintf(cf->buffer, fmt, ap);
274 file_push(cf);
275 }
276
277 va_end(ap);
278 }
279
280 /* Write data to a file. */
281 void
file_write(struct client * c,const char * path,int flags,const void * bdata,size_t bsize,client_file_cb cb,void * cbdata)282 file_write(struct client *c, const char *path, int flags, const void *bdata,
283 size_t bsize, client_file_cb cb, void *cbdata)
284 {
285 struct client_file *cf;
286 struct msg_write_open *msg;
287 size_t msglen;
288 int fd = -1;
289 u_int stream = file_next_stream++;
290 FILE *f;
291 const char *mode;
292
293 if (strcmp(path, "-") == 0) {
294 cf = file_create_with_client(c, stream, cb, cbdata);
295 cf->path = xstrdup("-");
296
297 fd = STDOUT_FILENO;
298 if (c == NULL ||
299 (c->flags & CLIENT_ATTACHED) ||
300 (c->flags & CLIENT_CONTROL)) {
301 cf->error = EBADF;
302 goto done;
303 }
304 goto skip;
305 }
306
307 cf = file_create_with_client(c, stream, cb, cbdata);
308 cf->path = file_get_path(c, path);
309
310 if (c == NULL || c->flags & CLIENT_ATTACHED) {
311 if (flags & O_APPEND)
312 mode = "ab";
313 else
314 mode = "wb";
315 f = fopen(cf->path, mode);
316 if (f == NULL) {
317 cf->error = errno;
318 goto done;
319 }
320 if (fwrite(bdata, 1, bsize, f) != bsize) {
321 fclose(f);
322 cf->error = EIO;
323 goto done;
324 }
325 fclose(f);
326 goto done;
327 }
328
329 skip:
330 evbuffer_add(cf->buffer, bdata, bsize);
331
332 msglen = strlen(cf->path) + 1 + sizeof *msg;
333 if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
334 cf->error = E2BIG;
335 goto done;
336 }
337 msg = xmalloc(msglen);
338 msg->stream = cf->stream;
339 msg->fd = fd;
340 msg->flags = flags;
341 memcpy(msg + 1, cf->path, msglen - sizeof *msg);
342 if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
343 free(msg);
344 cf->error = EINVAL;
345 goto done;
346 }
347 free(msg);
348 return;
349
350 done:
351 file_fire_done(cf);
352 }
353
354 /* Read a file. */
355 void
file_read(struct client * c,const char * path,client_file_cb cb,void * cbdata)356 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
357 {
358 struct client_file *cf;
359 struct msg_read_open *msg;
360 size_t msglen;
361 int fd = -1;
362 u_int stream = file_next_stream++;
363 FILE *f;
364 size_t size;
365 char buffer[BUFSIZ];
366
367 if (strcmp(path, "-") == 0) {
368 cf = file_create_with_client(c, stream, cb, cbdata);
369 cf->path = xstrdup("-");
370
371 fd = STDIN_FILENO;
372 if (c == NULL ||
373 (c->flags & CLIENT_ATTACHED) ||
374 (c->flags & CLIENT_CONTROL)) {
375 cf->error = EBADF;
376 goto done;
377 }
378 goto skip;
379 }
380
381 cf = file_create_with_client(c, stream, cb, cbdata);
382 cf->path = file_get_path(c, path);
383
384 if (c == NULL || c->flags & CLIENT_ATTACHED) {
385 f = fopen(cf->path, "rb");
386 if (f == NULL) {
387 cf->error = errno;
388 goto done;
389 }
390 for (;;) {
391 size = fread(buffer, 1, sizeof buffer, f);
392 if (evbuffer_add(cf->buffer, buffer, size) != 0) {
393 cf->error = ENOMEM;
394 goto done;
395 }
396 if (size != sizeof buffer)
397 break;
398 }
399 if (ferror(f)) {
400 cf->error = EIO;
401 goto done;
402 }
403 fclose(f);
404 goto done;
405 }
406
407 skip:
408 msglen = strlen(cf->path) + 1 + sizeof *msg;
409 if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
410 cf->error = E2BIG;
411 goto done;
412 }
413 msg = xmalloc(msglen);
414 msg->stream = cf->stream;
415 msg->fd = fd;
416 memcpy(msg + 1, cf->path, msglen - sizeof *msg);
417 if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
418 free(msg);
419 cf->error = EINVAL;
420 goto done;
421 }
422 free(msg);
423 return;
424
425 done:
426 file_fire_done(cf);
427 }
428
429 /* Push event, fired if there is more writing to be done. */
430 static void
file_push_cb(__unused int fd,__unused short events,void * arg)431 file_push_cb(__unused int fd, __unused short events, void *arg)
432 {
433 struct client_file *cf = arg;
434
435 if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
436 file_push(cf);
437 file_free(cf);
438 }
439
440 /* Push uwritten data to the client for a file, if it will accept it. */
441 void
file_push(struct client_file * cf)442 file_push(struct client_file *cf)
443 {
444 struct msg_write_data *msg;
445 size_t msglen, sent, left;
446 struct msg_write_close close;
447
448 msg = xmalloc(sizeof *msg);
449 left = EVBUFFER_LENGTH(cf->buffer);
450 while (left != 0) {
451 sent = left;
452 if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
453 sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
454
455 msglen = (sizeof *msg) + sent;
456 msg = xrealloc(msg, msglen);
457 msg->stream = cf->stream;
458 memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
459 if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
460 break;
461 evbuffer_drain(cf->buffer, sent);
462
463 left = EVBUFFER_LENGTH(cf->buffer);
464 log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
465 }
466 if (left != 0) {
467 cf->references++;
468 event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
469 } else if (cf->stream > 2) {
470 close.stream = cf->stream;
471 proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
472 file_fire_done(cf);
473 }
474 free(msg);
475 }
476
477 /* Check if any files have data left to write. */
478 int
file_write_left(struct client_files * files)479 file_write_left(struct client_files *files)
480 {
481 struct client_file *cf;
482 size_t left;
483 int waiting = 0;
484
485 RB_FOREACH(cf, client_files, files) {
486 if (cf->event == NULL)
487 continue;
488 left = EVBUFFER_LENGTH(cf->event->output);
489 if (left != 0) {
490 waiting++;
491 log_debug("file %u %zu bytes left", cf->stream, left);
492 }
493 }
494 return (waiting != 0);
495 }
496
497 /* Client file write error callback. */
498 static void
file_write_error_callback(__unused struct bufferevent * bev,__unused short what,void * arg)499 file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
500 void *arg)
501 {
502 struct client_file *cf = arg;
503
504 log_debug("write error file %d", cf->stream);
505
506 bufferevent_free(cf->event);
507 cf->event = NULL;
508
509 close(cf->fd);
510 cf->fd = -1;
511
512 if (cf->cb != NULL)
513 cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
514 }
515
516 /* Client file write callback. */
517 static void
file_write_callback(__unused struct bufferevent * bev,void * arg)518 file_write_callback(__unused struct bufferevent *bev, void *arg)
519 {
520 struct client_file *cf = arg;
521
522 log_debug("write check file %d", cf->stream);
523
524 if (cf->cb != NULL)
525 cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
526
527 if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
528 bufferevent_free(cf->event);
529 close(cf->fd);
530 RB_REMOVE(client_files, cf->tree, cf);
531 file_free(cf);
532 }
533 }
534
535 /* Handle a file write open message (client). */
536 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)537 file_write_open(struct client_files *files, struct tmuxpeer *peer,
538 struct imsg *imsg, int allow_streams, int close_received,
539 client_file_cb cb, void *cbdata)
540 {
541 struct msg_write_open *msg = imsg->data;
542 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
543 const char *path;
544 struct msg_write_ready reply;
545 struct client_file find, *cf;
546 const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
547 int error = 0;
548
549 if (msglen < sizeof *msg)
550 fatalx("bad MSG_WRITE_OPEN size");
551 if (msglen == sizeof *msg)
552 path = "-";
553 else
554 path = (const char *)(msg + 1);
555 log_debug("open write file %d %s", msg->stream, path);
556
557 find.stream = msg->stream;
558 if (RB_FIND(client_files, files, &find) != NULL) {
559 error = EBADF;
560 goto reply;
561 }
562 cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
563 if (cf->closed) {
564 error = EBADF;
565 goto reply;
566 }
567
568 cf->fd = -1;
569 if (msg->fd == -1)
570 cf->fd = open(path, msg->flags|flags, 0644);
571 else if (allow_streams) {
572 if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
573 errno = EBADF;
574 else {
575 cf->fd = dup(msg->fd);
576 if (close_received)
577 close(msg->fd); /* can only be used once */
578 }
579 } else
580 errno = EBADF;
581 if (cf->fd == -1) {
582 error = errno;
583 goto reply;
584 }
585
586 cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
587 file_write_error_callback, cf);
588 bufferevent_enable(cf->event, EV_WRITE);
589 goto reply;
590
591 reply:
592 reply.stream = msg->stream;
593 reply.error = error;
594 proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
595 }
596
597 /* Handle a file write data message (client). */
598 void
file_write_data(struct client_files * files,struct imsg * imsg)599 file_write_data(struct client_files *files, struct imsg *imsg)
600 {
601 struct msg_write_data *msg = imsg->data;
602 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
603 struct client_file find, *cf;
604 size_t size = msglen - sizeof *msg;
605
606 if (msglen < sizeof *msg)
607 fatalx("bad MSG_WRITE size");
608 find.stream = msg->stream;
609 if ((cf = RB_FIND(client_files, files, &find)) == NULL)
610 fatalx("unknown stream number");
611 log_debug("write %zu to file %d", size, cf->stream);
612
613 if (cf->event != NULL)
614 bufferevent_write(cf->event, msg + 1, size);
615 }
616
617 /* Handle a file write close message (client). */
618 void
file_write_close(struct client_files * files,struct imsg * imsg)619 file_write_close(struct client_files *files, struct imsg *imsg)
620 {
621 struct msg_write_close *msg = imsg->data;
622 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
623 struct client_file find, *cf;
624
625 if (msglen != sizeof *msg)
626 fatalx("bad MSG_WRITE_CLOSE size");
627 find.stream = msg->stream;
628 if ((cf = RB_FIND(client_files, files, &find)) == NULL)
629 fatalx("unknown stream number");
630 log_debug("close file %d", cf->stream);
631
632 if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
633 if (cf->event != NULL)
634 bufferevent_free(cf->event);
635 if (cf->fd != -1)
636 close(cf->fd);
637 RB_REMOVE(client_files, files, cf);
638 file_free(cf);
639 }
640 }
641
642 /* Client file read error callback. */
643 static void
file_read_error_callback(__unused struct bufferevent * bev,__unused short what,void * arg)644 file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
645 void *arg)
646 {
647 struct client_file *cf = arg;
648 struct msg_read_done msg;
649
650 log_debug("read error file %d", cf->stream);
651
652 msg.stream = cf->stream;
653 msg.error = 0;
654 proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
655
656 bufferevent_free(cf->event);
657 close(cf->fd);
658 RB_REMOVE(client_files, cf->tree, cf);
659 file_free(cf);
660 }
661
662 /* Client file read callback. */
663 static void
file_read_callback(__unused struct bufferevent * bev,void * arg)664 file_read_callback(__unused struct bufferevent *bev, void *arg)
665 {
666 struct client_file *cf = arg;
667 void *bdata;
668 size_t bsize;
669 struct msg_read_data *msg;
670 size_t msglen;
671
672 msg = xmalloc(sizeof *msg);
673 for (;;) {
674 bdata = EVBUFFER_DATA(cf->event->input);
675 bsize = EVBUFFER_LENGTH(cf->event->input);
676
677 if (bsize == 0)
678 break;
679 if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
680 bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
681 log_debug("read %zu from file %d", bsize, cf->stream);
682
683 msglen = (sizeof *msg) + bsize;
684 msg = xrealloc(msg, msglen);
685 msg->stream = cf->stream;
686 memcpy(msg + 1, bdata, bsize);
687 proc_send(cf->peer, MSG_READ, -1, msg, msglen);
688
689 evbuffer_drain(cf->event->input, bsize);
690 }
691 free(msg);
692 }
693
694 /* Handle a file read open message (client). */
695 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)696 file_read_open(struct client_files *files, struct tmuxpeer *peer,
697 struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
698 void *cbdata)
699 {
700 struct msg_read_open *msg = imsg->data;
701 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
702 const char *path;
703 struct msg_read_done reply;
704 struct client_file find, *cf;
705 const int flags = O_NONBLOCK|O_RDONLY;
706 int error;
707
708 if (msglen < sizeof *msg)
709 fatalx("bad MSG_READ_OPEN size");
710 if (msglen == sizeof *msg)
711 path = "-";
712 else
713 path = (const char *)(msg + 1);
714 log_debug("open read file %d %s", msg->stream, path);
715
716 find.stream = msg->stream;
717 if (RB_FIND(client_files, files, &find) != NULL) {
718 error = EBADF;
719 goto reply;
720 }
721 cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
722 if (cf->closed) {
723 error = EBADF;
724 goto reply;
725 }
726
727 cf->fd = -1;
728 if (msg->fd == -1)
729 cf->fd = open(path, flags);
730 else if (allow_streams) {
731 if (msg->fd != STDIN_FILENO)
732 errno = EBADF;
733 else {
734 cf->fd = dup(msg->fd);
735 if (close_received)
736 close(msg->fd); /* can only be used once */
737 }
738 } else
739 errno = EBADF;
740 if (cf->fd == -1) {
741 error = errno;
742 goto reply;
743 }
744
745 cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
746 file_read_error_callback, cf);
747 bufferevent_enable(cf->event, EV_READ);
748 return;
749
750 reply:
751 reply.stream = msg->stream;
752 reply.error = error;
753 proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
754 }
755
756 /* Handle a write ready message (server). */
757 void
file_write_ready(struct client_files * files,struct imsg * imsg)758 file_write_ready(struct client_files *files, struct imsg *imsg)
759 {
760 struct msg_write_ready *msg = imsg->data;
761 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
762 struct client_file find, *cf;
763
764 if (msglen != sizeof *msg)
765 fatalx("bad MSG_WRITE_READY size");
766 find.stream = msg->stream;
767 if ((cf = RB_FIND(client_files, files, &find)) == NULL)
768 return;
769 if (msg->error != 0) {
770 cf->error = msg->error;
771 file_fire_done(cf);
772 } else
773 file_push(cf);
774 }
775
776 /* Handle read data message (server). */
777 void
file_read_data(struct client_files * files,struct imsg * imsg)778 file_read_data(struct client_files *files, struct imsg *imsg)
779 {
780 struct msg_read_data *msg = imsg->data;
781 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
782 struct client_file find, *cf;
783 void *bdata = msg + 1;
784 size_t bsize = msglen - sizeof *msg;
785
786 if (msglen < sizeof *msg)
787 fatalx("bad MSG_READ_DATA size");
788 find.stream = msg->stream;
789 if ((cf = RB_FIND(client_files, files, &find)) == NULL)
790 return;
791
792 log_debug("file %d read %zu bytes", cf->stream, bsize);
793 if (cf->error == 0) {
794 if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
795 cf->error = ENOMEM;
796 file_fire_done(cf);
797 } else
798 file_fire_read(cf);
799 }
800 }
801
802 /* Handle a read done message (server). */
803 void
file_read_done(struct client_files * files,struct imsg * imsg)804 file_read_done(struct client_files *files, struct imsg *imsg)
805 {
806 struct msg_read_done *msg = imsg->data;
807 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
808 struct client_file find, *cf;
809
810 if (msglen != sizeof *msg)
811 fatalx("bad MSG_READ_DONE size");
812 find.stream = msg->stream;
813 if ((cf = RB_FIND(client_files, files, &find)) == NULL)
814 return;
815
816 log_debug("file %d read done", cf->stream);
817 cf->error = msg->error;
818 file_fire_done(cf);
819 }
820