4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
20 #include <sys/queue.h>
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.
39 static int file_next_stream
= 3;
41 RB_GENERATE(client_files
, client_file
, entry
, file_cmp
);
43 /* Get path for file, either as given or from working directory. */
45 file_get_path(struct client
*c
, const char *file
)
52 xasprintf(&path
, "%s/%s", server_client_get_cwd(c
, NULL
), file
);
56 /* Tree comparison function. */
58 file_cmp(struct client_file
*cf1
, struct client_file
*cf2
)
60 if (cf1
->stream
< cf2
->stream
)
62 if (cf1
->stream
> cf2
->stream
)
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
74 file_create_with_peer(struct tmuxpeer
*peer
, struct client_files
*files
,
75 int stream
, client_file_cb cb
, void *cbdata
)
77 struct client_file
*cf
;
79 cf
= xcalloc(1, sizeof *cf
);
84 cf
->buffer
= evbuffer_new();
85 if (cf
->buffer
== NULL
)
86 fatalx("out of memory");
93 RB_INSERT(client_files
, files
, cf
);
98 /* Create a file object in the server, communicating with the given client. */
100 file_create_with_client(struct client
*c
, int stream
, client_file_cb cb
,
103 struct client_file
*cf
;
105 if (c
!= NULL
&& (c
->flags
& CLIENT_ATTACHED
))
108 cf
= xcalloc(1, sizeof *cf
);
113 cf
->buffer
= evbuffer_new();
114 if (cf
->buffer
== NULL
)
115 fatalx("out of memory");
121 cf
->peer
= cf
->c
->peer
;
122 cf
->tree
= &cf
->c
->files
;
123 RB_INSERT(client_files
, &cf
->c
->files
, cf
);
132 file_free(struct client_file
*cf
)
134 if (--cf
->references
!= 0)
137 evbuffer_free(cf
->buffer
);
140 if (cf
->tree
!= NULL
)
141 RB_REMOVE(client_files
, cf
->tree
, cf
);
143 server_client_unref(cf
->c
);
148 /* Event to fire the done callback. */
150 file_fire_done_cb(__unused
int fd
, __unused
short events
, void *arg
)
152 struct client_file
*cf
= arg
;
153 struct client
*c
= cf
->c
;
155 if (cf
->cb
!= NULL
&&
156 (cf
->closed
|| c
== NULL
|| (~c
->flags
& CLIENT_DEAD
)))
157 cf
->cb(c
, cf
->path
, cf
->error
, 1, cf
->buffer
, cf
->data
);
161 /* Add an event to fire the done callback (used by the server). */
163 file_fire_done(struct client_file
*cf
)
165 event_once(-1, EV_TIMEOUT
, file_fire_done_cb
, cf
, NULL
);
168 /* Fire the read callback. */
170 file_fire_read(struct client_file
*cf
)
173 cf
->cb(cf
->c
, cf
->path
, cf
->error
, 0, cf
->buffer
, cf
->data
);
176 /* Can this file be printed to? */
178 file_can_print(struct client
*c
)
181 (c
->flags
& CLIENT_ATTACHED
) ||
182 (c
->flags
& CLIENT_CONTROL
))
187 /* Print a message to a file. */
189 file_print(struct client
*c
, const char *fmt
, ...)
194 file_vprint(c
, fmt
, ap
);
198 /* Print a message to a file. */
200 file_vprint(struct client
*c
, const char *fmt
, va_list ap
)
202 struct client_file find
, *cf
;
203 struct msg_write_open msg
;
205 if (!file_can_print(c
))
209 if ((cf
= RB_FIND(client_files
, &c
->files
, &find
)) == NULL
) {
210 cf
= file_create_with_client(c
, 1, NULL
, NULL
);
211 cf
->path
= xstrdup("-");
213 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
216 msg
.fd
= STDOUT_FILENO
;
218 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
220 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
225 /* Print a buffer to a file. */
227 file_print_buffer(struct client
*c
, void *data
, size_t size
)
229 struct client_file find
, *cf
;
230 struct msg_write_open msg
;
232 if (!file_can_print(c
))
236 if ((cf
= RB_FIND(client_files
, &c
->files
, &find
)) == NULL
) {
237 cf
= file_create_with_client(c
, 1, NULL
, NULL
);
238 cf
->path
= xstrdup("-");
240 evbuffer_add(cf
->buffer
, data
, size
);
243 msg
.fd
= STDOUT_FILENO
;
245 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
247 evbuffer_add(cf
->buffer
, data
, size
);
252 /* Report an error to a file. */
254 file_error(struct client
*c
, const char *fmt
, ...)
256 struct client_file find
, *cf
;
257 struct msg_write_open msg
;
260 if (!file_can_print(c
))
266 if ((cf
= RB_FIND(client_files
, &c
->files
, &find
)) == NULL
) {
267 cf
= file_create_with_client(c
, 2, NULL
, NULL
);
268 cf
->path
= xstrdup("-");
270 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
273 msg
.fd
= STDERR_FILENO
;
275 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
277 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
284 /* Write data to a file. */
286 file_write(struct client
*c
, const char *path
, int flags
, const void *bdata
,
287 size_t bsize
, client_file_cb cb
, void *cbdata
)
289 struct client_file
*cf
;
290 struct msg_write_open
*msg
;
293 u_int stream
= file_next_stream
++;
297 if (strcmp(path
, "-") == 0) {
298 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
299 cf
->path
= xstrdup("-");
303 (c
->flags
& CLIENT_ATTACHED
) ||
304 (c
->flags
& CLIENT_CONTROL
)) {
311 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
312 cf
->path
= file_get_path(c
, path
);
314 if (c
== NULL
|| c
->flags
& CLIENT_ATTACHED
) {
315 if (flags
& O_APPEND
)
319 f
= fopen(cf
->path
, mode
);
324 if (fwrite(bdata
, 1, bsize
, f
) != bsize
) {
334 evbuffer_add(cf
->buffer
, bdata
, bsize
);
336 msglen
= strlen(cf
->path
) + 1 + sizeof *msg
;
337 if (msglen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
) {
341 msg
= xmalloc(msglen
);
342 msg
->stream
= cf
->stream
;
345 memcpy(msg
+ 1, cf
->path
, msglen
- sizeof *msg
);
346 if (proc_send(cf
->peer
, MSG_WRITE_OPEN
, -1, msg
, msglen
) != 0) {
360 file_read(struct client
*c
, const char *path
, client_file_cb cb
, void *cbdata
)
362 struct client_file
*cf
;
363 struct msg_read_open
*msg
;
366 u_int stream
= file_next_stream
++;
371 if (strcmp(path
, "-") == 0) {
372 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
373 cf
->path
= xstrdup("-");
377 (c
->flags
& CLIENT_ATTACHED
) ||
378 (c
->flags
& CLIENT_CONTROL
)) {
385 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
386 cf
->path
= file_get_path(c
, path
);
388 if (c
== NULL
|| c
->flags
& CLIENT_ATTACHED
) {
389 f
= fopen(cf
->path
, "rb");
395 size
= fread(buffer
, 1, sizeof buffer
, f
);
396 if (evbuffer_add(cf
->buffer
, buffer
, size
) != 0) {
400 if (size
!= sizeof buffer
)
412 msglen
= strlen(cf
->path
) + 1 + sizeof *msg
;
413 if (msglen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
) {
417 msg
= xmalloc(msglen
);
418 msg
->stream
= cf
->stream
;
420 memcpy(msg
+ 1, cf
->path
, msglen
- sizeof *msg
);
421 if (proc_send(cf
->peer
, MSG_READ_OPEN
, -1, msg
, msglen
) != 0) {
434 /* Cancel a file read. */
436 file_cancel(struct client_file
*cf
)
438 struct msg_read_cancel msg
;
440 log_debug("read cancel file %d", cf
->stream
);
446 msg
.stream
= cf
->stream
;
447 proc_send(cf
->peer
, MSG_READ_CANCEL
, -1, &msg
, sizeof msg
);
450 /* Push event, fired if there is more writing to be done. */
452 file_push_cb(__unused
int fd
, __unused
short events
, void *arg
)
454 struct client_file
*cf
= arg
;
456 if (cf
->c
== NULL
|| ~cf
->c
->flags
& CLIENT_DEAD
)
461 /* Push uwritten data to the client for a file, if it will accept it. */
463 file_push(struct client_file
*cf
)
465 struct msg_write_data
*msg
;
466 size_t msglen
, sent
, left
;
467 struct msg_write_close close
;
469 msg
= xmalloc(sizeof *msg
);
470 left
= EVBUFFER_LENGTH(cf
->buffer
);
473 if (sent
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
)
474 sent
= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
;
476 msglen
= (sizeof *msg
) + sent
;
477 msg
= xrealloc(msg
, msglen
);
478 msg
->stream
= cf
->stream
;
479 memcpy(msg
+ 1, EVBUFFER_DATA(cf
->buffer
), sent
);
480 if (proc_send(cf
->peer
, MSG_WRITE
, -1, msg
, msglen
) != 0)
482 evbuffer_drain(cf
->buffer
, sent
);
484 left
= EVBUFFER_LENGTH(cf
->buffer
);
485 log_debug("file %d sent %zu, left %zu", cf
->stream
, sent
, left
);
489 event_once(-1, EV_TIMEOUT
, file_push_cb
, cf
, NULL
);
490 } else if (cf
->stream
> 2) {
491 close
.stream
= cf
->stream
;
492 proc_send(cf
->peer
, MSG_WRITE_CLOSE
, -1, &close
, sizeof close
);
498 /* Check if any files have data left to write. */
500 file_write_left(struct client_files
*files
)
502 struct client_file
*cf
;
506 RB_FOREACH(cf
, client_files
, files
) {
507 if (cf
->event
== NULL
)
509 left
= EVBUFFER_LENGTH(cf
->event
->output
);
512 log_debug("file %u %zu bytes left", cf
->stream
, left
);
515 return (waiting
!= 0);
518 /* Client file write error callback. */
520 file_write_error_callback(__unused
struct bufferevent
*bev
, __unused
short what
,
523 struct client_file
*cf
= arg
;
525 log_debug("write error file %d", cf
->stream
);
527 bufferevent_free(cf
->event
);
534 cf
->cb(NULL
, NULL
, 0, -1, NULL
, cf
->data
);
537 /* Client file write callback. */
539 file_write_callback(__unused
struct bufferevent
*bev
, void *arg
)
541 struct client_file
*cf
= arg
;
543 log_debug("write check file %d", cf
->stream
);
546 cf
->cb(NULL
, NULL
, 0, -1, NULL
, cf
->data
);
548 if (cf
->closed
&& EVBUFFER_LENGTH(cf
->event
->output
) == 0) {
549 bufferevent_free(cf
->event
);
551 RB_REMOVE(client_files
, cf
->tree
, cf
);
556 /* Handle a file write open message (client). */
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
)
562 struct msg_write_open
*msg
= imsg
->data
;
563 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
565 struct msg_write_ready reply
;
566 struct client_file find
, *cf
;
567 const int flags
= O_NONBLOCK
|O_WRONLY
|O_CREAT
;
570 if (msglen
< sizeof *msg
)
571 fatalx("bad MSG_WRITE_OPEN size");
572 if (msglen
== sizeof *msg
)
575 path
= (const char *)(msg
+ 1);
576 log_debug("open write file %d %s", msg
->stream
, path
);
578 find
.stream
= msg
->stream
;
579 if (RB_FIND(client_files
, files
, &find
) != NULL
) {
583 cf
= file_create_with_peer(peer
, files
, msg
->stream
, cb
, cbdata
);
591 cf
->fd
= open(path
, msg
->flags
|flags
, 0644);
592 else if (allow_streams
) {
593 if (msg
->fd
!= STDOUT_FILENO
&& msg
->fd
!= STDERR_FILENO
)
596 cf
->fd
= dup(msg
->fd
);
598 close(msg
->fd
); /* can only be used once */
607 cf
->event
= bufferevent_new(cf
->fd
, NULL
, file_write_callback
,
608 file_write_error_callback
, cf
);
609 if (cf
->event
== NULL
)
610 fatalx("out of memory");
611 bufferevent_enable(cf
->event
, EV_WRITE
);
615 reply
.stream
= msg
->stream
;
617 proc_send(peer
, MSG_WRITE_READY
, -1, &reply
, sizeof reply
);
620 /* Handle a file write data message (client). */
622 file_write_data(struct client_files
*files
, struct imsg
*imsg
)
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
;
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
);
636 if (cf
->event
!= NULL
)
637 bufferevent_write(cf
->event
, msg
+ 1, size
);
640 /* Handle a file write close message (client). */
642 file_write_close(struct client_files
*files
, struct imsg
*imsg
)
644 struct msg_write_close
*msg
= imsg
->data
;
645 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
646 struct client_file find
, *cf
;
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
);
655 if (cf
->event
== NULL
|| EVBUFFER_LENGTH(cf
->event
->output
) == 0) {
656 if (cf
->event
!= NULL
)
657 bufferevent_free(cf
->event
);
660 RB_REMOVE(client_files
, files
, cf
);
665 /* Client file read error callback. */
667 file_read_error_callback(__unused
struct bufferevent
*bev
, __unused
short what
,
670 struct client_file
*cf
= arg
;
671 struct msg_read_done msg
;
673 log_debug("read error file %d", cf
->stream
);
675 msg
.stream
= cf
->stream
;
677 proc_send(cf
->peer
, MSG_READ_DONE
, -1, &msg
, sizeof msg
);
679 bufferevent_free(cf
->event
);
681 RB_REMOVE(client_files
, cf
->tree
, cf
);
685 /* Client file read callback. */
687 file_read_callback(__unused
struct bufferevent
*bev
, void *arg
)
689 struct client_file
*cf
= arg
;
692 struct msg_read_data
*msg
;
695 msg
= xmalloc(sizeof *msg
);
697 bdata
= EVBUFFER_DATA(cf
->event
->input
);
698 bsize
= EVBUFFER_LENGTH(cf
->event
->input
);
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
);
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
);
712 evbuffer_drain(cf
->event
->input
, bsize
);
717 /* Handle a file read open message (client). */
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
,
723 struct msg_read_open
*msg
= imsg
->data
;
724 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
726 struct msg_read_done reply
;
727 struct client_file find
, *cf
;
728 const int flags
= O_NONBLOCK
|O_RDONLY
;
731 if (msglen
< sizeof *msg
)
732 fatalx("bad MSG_READ_OPEN size");
733 if (msglen
== sizeof *msg
)
736 path
= (const char *)(msg
+ 1);
737 log_debug("open read file %d %s", msg
->stream
, path
);
739 find
.stream
= msg
->stream
;
740 if (RB_FIND(client_files
, files
, &find
) != NULL
) {
744 cf
= file_create_with_peer(peer
, files
, msg
->stream
, cb
, cbdata
);
752 cf
->fd
= open(path
, flags
);
753 else if (allow_streams
) {
754 if (msg
->fd
!= STDIN_FILENO
)
757 cf
->fd
= dup(msg
->fd
);
759 close(msg
->fd
); /* can only be used once */
768 cf
->event
= bufferevent_new(cf
->fd
, file_read_callback
, NULL
,
769 file_read_error_callback
, cf
);
770 if (cf
->event
== NULL
)
771 fatalx("out of memory");
772 bufferevent_enable(cf
->event
, EV_READ
);
776 reply
.stream
= msg
->stream
;
778 proc_send(peer
, MSG_READ_DONE
, -1, &reply
, sizeof reply
);
781 /* Handle a read cancel message (client). */
783 file_read_cancel(struct client_files
*files
, struct imsg
*imsg
)
785 struct msg_read_cancel
*msg
= imsg
->data
;
786 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
787 struct client_file find
, *cf
;
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
);
796 file_read_error_callback(NULL
, 0, cf
);
799 /* Handle a write ready message (server). */
801 file_write_ready(struct client_files
*files
, struct imsg
*imsg
)
803 struct msg_write_ready
*msg
= imsg
->data
;
804 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
805 struct client_file find
, *cf
;
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
)
812 if (msg
->error
!= 0) {
813 cf
->error
= msg
->error
;
819 /* Handle read data message (server). */
821 file_read_data(struct client_files
*files
, struct imsg
*imsg
)
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
;
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
)
835 log_debug("file %d read %zu bytes", cf
->stream
, bsize
);
836 if (cf
->error
== 0 && !cf
->closed
) {
837 if (evbuffer_add(cf
->buffer
, bdata
, bsize
) != 0) {
845 /* Handle a read done message (server). */
847 file_read_done(struct client_files
*files
, struct imsg
*imsg
)
849 struct msg_read_done
*msg
= imsg
->data
;
850 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
851 struct client_file find
, *cf
;
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
)
859 log_debug("file %d read done", cf
->stream
);
860 cf
->error
= msg
->error
;