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