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
&& (c
== NULL
|| (~c
->flags
& CLIENT_DEAD
)))
156 cf
->cb(c
, cf
->path
, cf
->error
, 1, cf
->buffer
, cf
->data
);
160 /* Add an event to fire the done callback (used by the server). */
162 file_fire_done(struct client_file
*cf
)
164 event_once(-1, EV_TIMEOUT
, file_fire_done_cb
, cf
, NULL
);
167 /* Fire the read callback. */
169 file_fire_read(struct client_file
*cf
)
172 cf
->cb(cf
->c
, cf
->path
, cf
->error
, 0, cf
->buffer
, cf
->data
);
175 /* Can this file be printed to? */
177 file_can_print(struct client
*c
)
181 if (c
->session
!= NULL
&& (~c
->flags
& CLIENT_CONTROL
))
186 /* Print a message to a file. */
188 file_print(struct client
*c
, const char *fmt
, ...)
193 file_vprint(c
, fmt
, ap
);
197 /* Print a message to a file. */
199 file_vprint(struct client
*c
, const char *fmt
, va_list ap
)
201 struct client_file find
, *cf
;
202 struct msg_write_open msg
;
204 if (!file_can_print(c
))
208 if ((cf
= RB_FIND(client_files
, &c
->files
, &find
)) == NULL
) {
209 cf
= file_create_with_client(c
, 1, NULL
, NULL
);
210 cf
->path
= xstrdup("-");
212 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
215 msg
.fd
= STDOUT_FILENO
;
217 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
219 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
224 /* Print a buffer to a file. */
226 file_print_buffer(struct client
*c
, void *data
, size_t size
)
228 struct client_file find
, *cf
;
229 struct msg_write_open msg
;
231 if (!file_can_print(c
))
235 if ((cf
= RB_FIND(client_files
, &c
->files
, &find
)) == NULL
) {
236 cf
= file_create_with_client(c
, 1, NULL
, NULL
);
237 cf
->path
= xstrdup("-");
239 evbuffer_add(cf
->buffer
, data
, size
);
242 msg
.fd
= STDOUT_FILENO
;
244 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
246 evbuffer_add(cf
->buffer
, data
, size
);
251 /* Report an error to a file. */
253 file_error(struct client
*c
, const char *fmt
, ...)
255 struct client_file find
, *cf
;
256 struct msg_write_open msg
;
259 if (!file_can_print(c
))
265 if ((cf
= RB_FIND(client_files
, &c
->files
, &find
)) == NULL
) {
266 cf
= file_create_with_client(c
, 2, NULL
, NULL
);
267 cf
->path
= xstrdup("-");
269 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
272 msg
.fd
= STDERR_FILENO
;
274 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
276 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
283 /* Write data to a file. */
285 file_write(struct client
*c
, const char *path
, int flags
, const void *bdata
,
286 size_t bsize
, client_file_cb cb
, void *cbdata
)
288 struct client_file
*cf
;
289 struct msg_write_open
*msg
;
292 u_int stream
= file_next_stream
++;
296 if (strcmp(path
, "-") == 0) {
297 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
298 cf
->path
= xstrdup("-");
302 (c
->flags
& CLIENT_ATTACHED
) ||
303 (c
->flags
& CLIENT_CONTROL
)) {
310 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
311 cf
->path
= file_get_path(c
, path
);
313 if (c
== NULL
|| c
->flags
& CLIENT_ATTACHED
) {
314 if (flags
& O_APPEND
)
318 f
= fopen(cf
->path
, mode
);
323 if (fwrite(bdata
, 1, bsize
, f
) != bsize
) {
333 evbuffer_add(cf
->buffer
, bdata
, bsize
);
335 msglen
= strlen(cf
->path
) + 1 + sizeof *msg
;
336 if (msglen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
) {
340 msg
= xmalloc(msglen
);
341 msg
->stream
= cf
->stream
;
344 memcpy(msg
+ 1, cf
->path
, msglen
- sizeof *msg
);
345 if (proc_send(cf
->peer
, MSG_WRITE_OPEN
, -1, msg
, msglen
) != 0) {
359 file_read(struct client
*c
, const char *path
, client_file_cb cb
, void *cbdata
)
361 struct client_file
*cf
;
362 struct msg_read_open
*msg
;
365 u_int stream
= file_next_stream
++;
370 if (strcmp(path
, "-") == 0) {
371 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
372 cf
->path
= xstrdup("-");
376 (c
->flags
& CLIENT_ATTACHED
) ||
377 (c
->flags
& CLIENT_CONTROL
)) {
384 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
385 cf
->path
= file_get_path(c
, path
);
387 if (c
== NULL
|| c
->flags
& CLIENT_ATTACHED
) {
388 f
= fopen(cf
->path
, "rb");
394 size
= fread(buffer
, 1, sizeof buffer
, f
);
395 if (evbuffer_add(cf
->buffer
, buffer
, size
) != 0) {
399 if (size
!= sizeof buffer
)
411 msglen
= strlen(cf
->path
) + 1 + sizeof *msg
;
412 if (msglen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
) {
416 msg
= xmalloc(msglen
);
417 msg
->stream
= cf
->stream
;
419 memcpy(msg
+ 1, cf
->path
, msglen
- sizeof *msg
);
420 if (proc_send(cf
->peer
, MSG_READ_OPEN
, -1, msg
, msglen
) != 0) {
432 /* Push event, fired if there is more writing to be done. */
434 file_push_cb(__unused
int fd
, __unused
short events
, void *arg
)
436 struct client_file
*cf
= arg
;
438 if (cf
->c
== NULL
|| ~cf
->c
->flags
& CLIENT_DEAD
)
443 /* Push uwritten data to the client for a file, if it will accept it. */
445 file_push(struct client_file
*cf
)
447 struct msg_write_data
*msg
;
448 size_t msglen
, sent
, left
;
449 struct msg_write_close close
;
451 msg
= xmalloc(sizeof *msg
);
452 left
= EVBUFFER_LENGTH(cf
->buffer
);
455 if (sent
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
)
456 sent
= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
;
458 msglen
= (sizeof *msg
) + sent
;
459 msg
= xrealloc(msg
, msglen
);
460 msg
->stream
= cf
->stream
;
461 memcpy(msg
+ 1, EVBUFFER_DATA(cf
->buffer
), sent
);
462 if (proc_send(cf
->peer
, MSG_WRITE
, -1, msg
, msglen
) != 0)
464 evbuffer_drain(cf
->buffer
, sent
);
466 left
= EVBUFFER_LENGTH(cf
->buffer
);
467 log_debug("file %d sent %zu, left %zu", cf
->stream
, sent
, left
);
471 event_once(-1, EV_TIMEOUT
, file_push_cb
, cf
, NULL
);
472 } else if (cf
->stream
> 2) {
473 close
.stream
= cf
->stream
;
474 proc_send(cf
->peer
, MSG_WRITE_CLOSE
, -1, &close
, sizeof close
);
480 /* Check if any files have data left to write. */
482 file_write_left(struct client_files
*files
)
484 struct client_file
*cf
;
488 RB_FOREACH(cf
, client_files
, files
) {
489 if (cf
->event
== NULL
)
491 left
= EVBUFFER_LENGTH(cf
->event
->output
);
494 log_debug("file %u %zu bytes left", cf
->stream
, left
);
497 return (waiting
!= 0);
500 /* Client file write error callback. */
502 file_write_error_callback(__unused
struct bufferevent
*bev
, __unused
short what
,
505 struct client_file
*cf
= arg
;
507 log_debug("write error file %d", cf
->stream
);
509 bufferevent_free(cf
->event
);
516 cf
->cb(NULL
, NULL
, 0, -1, NULL
, cf
->data
);
519 /* Client file write callback. */
521 file_write_callback(__unused
struct bufferevent
*bev
, void *arg
)
523 struct client_file
*cf
= arg
;
525 log_debug("write check file %d", cf
->stream
);
528 cf
->cb(NULL
, NULL
, 0, -1, NULL
, cf
->data
);
530 if (cf
->closed
&& EVBUFFER_LENGTH(cf
->event
->output
) == 0) {
531 bufferevent_free(cf
->event
);
533 RB_REMOVE(client_files
, cf
->tree
, cf
);
538 /* Handle a file write open message (client). */
540 file_write_open(struct client_files
*files
, struct tmuxpeer
*peer
,
541 struct imsg
*imsg
, int allow_streams
, int close_received
,
542 client_file_cb cb
, void *cbdata
)
544 struct msg_write_open
*msg
= imsg
->data
;
545 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
547 struct msg_write_ready reply
;
548 struct client_file find
, *cf
;
549 const int flags
= O_NONBLOCK
|O_WRONLY
|O_CREAT
;
552 if (msglen
< sizeof *msg
)
553 fatalx("bad MSG_WRITE_OPEN size");
554 if (msglen
== sizeof *msg
)
557 path
= (const char *)(msg
+ 1);
558 log_debug("open write file %d %s", msg
->stream
, path
);
560 find
.stream
= msg
->stream
;
561 if (RB_FIND(client_files
, files
, &find
) != NULL
) {
565 cf
= file_create_with_peer(peer
, files
, msg
->stream
, cb
, cbdata
);
573 cf
->fd
= open(path
, msg
->flags
|flags
, 0644);
574 else if (allow_streams
) {
575 if (msg
->fd
!= STDOUT_FILENO
&& msg
->fd
!= STDERR_FILENO
)
578 cf
->fd
= dup(msg
->fd
);
580 close(msg
->fd
); /* can only be used once */
589 cf
->event
= bufferevent_new(cf
->fd
, NULL
, file_write_callback
,
590 file_write_error_callback
, cf
);
591 if (cf
->event
== NULL
)
592 fatalx("out of memory");
593 bufferevent_enable(cf
->event
, EV_WRITE
);
597 reply
.stream
= msg
->stream
;
599 proc_send(peer
, MSG_WRITE_READY
, -1, &reply
, sizeof reply
);
602 /* Handle a file write data message (client). */
604 file_write_data(struct client_files
*files
, struct imsg
*imsg
)
606 struct msg_write_data
*msg
= imsg
->data
;
607 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
608 struct client_file find
, *cf
;
609 size_t size
= msglen
- sizeof *msg
;
611 if (msglen
< sizeof *msg
)
612 fatalx("bad MSG_WRITE size");
613 find
.stream
= msg
->stream
;
614 if ((cf
= RB_FIND(client_files
, files
, &find
)) == NULL
)
615 fatalx("unknown stream number");
616 log_debug("write %zu to file %d", size
, cf
->stream
);
618 if (cf
->event
!= NULL
)
619 bufferevent_write(cf
->event
, msg
+ 1, size
);
622 /* Handle a file write close message (client). */
624 file_write_close(struct client_files
*files
, struct imsg
*imsg
)
626 struct msg_write_close
*msg
= imsg
->data
;
627 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
628 struct client_file find
, *cf
;
630 if (msglen
!= sizeof *msg
)
631 fatalx("bad MSG_WRITE_CLOSE size");
632 find
.stream
= msg
->stream
;
633 if ((cf
= RB_FIND(client_files
, files
, &find
)) == NULL
)
634 fatalx("unknown stream number");
635 log_debug("close file %d", cf
->stream
);
637 if (cf
->event
== NULL
|| EVBUFFER_LENGTH(cf
->event
->output
) == 0) {
638 if (cf
->event
!= NULL
)
639 bufferevent_free(cf
->event
);
642 RB_REMOVE(client_files
, files
, cf
);
647 /* Client file read error callback. */
649 file_read_error_callback(__unused
struct bufferevent
*bev
, __unused
short what
,
652 struct client_file
*cf
= arg
;
653 struct msg_read_done msg
;
655 log_debug("read error file %d", cf
->stream
);
657 msg
.stream
= cf
->stream
;
659 proc_send(cf
->peer
, MSG_READ_DONE
, -1, &msg
, sizeof msg
);
661 bufferevent_free(cf
->event
);
663 RB_REMOVE(client_files
, cf
->tree
, cf
);
667 /* Client file read callback. */
669 file_read_callback(__unused
struct bufferevent
*bev
, void *arg
)
671 struct client_file
*cf
= arg
;
674 struct msg_read_data
*msg
;
677 msg
= xmalloc(sizeof *msg
);
679 bdata
= EVBUFFER_DATA(cf
->event
->input
);
680 bsize
= EVBUFFER_LENGTH(cf
->event
->input
);
684 if (bsize
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
)
685 bsize
= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
;
686 log_debug("read %zu from file %d", bsize
, cf
->stream
);
688 msglen
= (sizeof *msg
) + bsize
;
689 msg
= xrealloc(msg
, msglen
);
690 msg
->stream
= cf
->stream
;
691 memcpy(msg
+ 1, bdata
, bsize
);
692 proc_send(cf
->peer
, MSG_READ
, -1, msg
, msglen
);
694 evbuffer_drain(cf
->event
->input
, bsize
);
699 /* Handle a file read open message (client). */
701 file_read_open(struct client_files
*files
, struct tmuxpeer
*peer
,
702 struct imsg
*imsg
, int allow_streams
, int close_received
, client_file_cb cb
,
705 struct msg_read_open
*msg
= imsg
->data
;
706 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
708 struct msg_read_done reply
;
709 struct client_file find
, *cf
;
710 const int flags
= O_NONBLOCK
|O_RDONLY
;
713 if (msglen
< sizeof *msg
)
714 fatalx("bad MSG_READ_OPEN size");
715 if (msglen
== sizeof *msg
)
718 path
= (const char *)(msg
+ 1);
719 log_debug("open read file %d %s", msg
->stream
, path
);
721 find
.stream
= msg
->stream
;
722 if (RB_FIND(client_files
, files
, &find
) != NULL
) {
726 cf
= file_create_with_peer(peer
, files
, msg
->stream
, cb
, cbdata
);
734 cf
->fd
= open(path
, flags
);
735 else if (allow_streams
) {
736 if (msg
->fd
!= STDIN_FILENO
)
739 cf
->fd
= dup(msg
->fd
);
741 close(msg
->fd
); /* can only be used once */
750 cf
->event
= bufferevent_new(cf
->fd
, file_read_callback
, NULL
,
751 file_read_error_callback
, cf
);
752 if (cf
->event
== NULL
)
753 fatalx("out of memory");
754 bufferevent_enable(cf
->event
, EV_READ
);
758 reply
.stream
= msg
->stream
;
760 proc_send(peer
, MSG_READ_DONE
, -1, &reply
, sizeof reply
);
763 /* Handle a write ready message (server). */
765 file_write_ready(struct client_files
*files
, struct imsg
*imsg
)
767 struct msg_write_ready
*msg
= imsg
->data
;
768 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
769 struct client_file find
, *cf
;
771 if (msglen
!= sizeof *msg
)
772 fatalx("bad MSG_WRITE_READY size");
773 find
.stream
= msg
->stream
;
774 if ((cf
= RB_FIND(client_files
, files
, &find
)) == NULL
)
776 if (msg
->error
!= 0) {
777 cf
->error
= msg
->error
;
783 /* Handle read data message (server). */
785 file_read_data(struct client_files
*files
, struct imsg
*imsg
)
787 struct msg_read_data
*msg
= imsg
->data
;
788 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
789 struct client_file find
, *cf
;
790 void *bdata
= msg
+ 1;
791 size_t bsize
= msglen
- sizeof *msg
;
793 if (msglen
< sizeof *msg
)
794 fatalx("bad MSG_READ_DATA size");
795 find
.stream
= msg
->stream
;
796 if ((cf
= RB_FIND(client_files
, files
, &find
)) == NULL
)
799 log_debug("file %d read %zu bytes", cf
->stream
, bsize
);
800 if (cf
->error
== 0) {
801 if (evbuffer_add(cf
->buffer
, bdata
, bsize
) != 0) {
809 /* Handle a read done message (server). */
811 file_read_done(struct client_files
*files
, struct imsg
*imsg
)
813 struct msg_read_done
*msg
= imsg
->data
;
814 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
815 struct client_file find
, *cf
;
817 if (msglen
!= sizeof *msg
)
818 fatalx("bad MSG_READ_DONE size");
819 find
.stream
= msg
->stream
;
820 if ((cf
= RB_FIND(client_files
, files
, &find
)) == NULL
)
823 log_debug("file %d read done", cf
->stream
);
824 cf
->error
= msg
->error
;