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>
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.
36 static int file_next_stream
= 3;
38 RB_GENERATE(client_files
, client_file
, entry
, file_cmp
);
40 /* Get path for file, either as given or from working directory. */
42 file_get_path(struct client
*c
, const char *file
)
49 xasprintf(&path
, "%s/%s", server_client_get_cwd(c
, NULL
), file
);
53 /* Tree comparison function. */
55 file_cmp(struct client_file
*cf1
, struct client_file
*cf2
)
57 if (cf1
->stream
< cf2
->stream
)
59 if (cf1
->stream
> cf2
->stream
)
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
71 file_create_with_peer(struct tmuxpeer
*peer
, struct client_files
*files
,
72 int stream
, client_file_cb cb
, void *cbdata
)
74 struct client_file
*cf
;
76 cf
= xcalloc(1, sizeof *cf
);
81 cf
->buffer
= evbuffer_new();
82 if (cf
->buffer
== NULL
)
83 fatalx("out of memory");
90 RB_INSERT(client_files
, files
, cf
);
95 /* Create a file object in the server, communicating with the given client. */
97 file_create_with_client(struct client
*c
, int stream
, client_file_cb cb
,
100 struct client_file
*cf
;
102 if (c
!= NULL
&& (c
->flags
& CLIENT_ATTACHED
))
105 cf
= xcalloc(1, sizeof *cf
);
110 cf
->buffer
= evbuffer_new();
111 if (cf
->buffer
== NULL
)
112 fatalx("out of memory");
118 cf
->peer
= cf
->c
->peer
;
119 cf
->tree
= &cf
->c
->files
;
120 RB_INSERT(client_files
, &cf
->c
->files
, cf
);
129 file_free(struct client_file
*cf
)
131 if (--cf
->references
!= 0)
134 evbuffer_free(cf
->buffer
);
137 if (cf
->tree
!= NULL
)
138 RB_REMOVE(client_files
, cf
->tree
, cf
);
140 server_client_unref(cf
->c
);
145 /* Event to fire the done callback. */
147 file_fire_done_cb(__unused
int fd
, __unused
short events
, void *arg
)
149 struct client_file
*cf
= arg
;
150 struct client
*c
= cf
->c
;
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
);
158 /* Add an event to fire the done callback (used by the server). */
160 file_fire_done(struct client_file
*cf
)
162 event_once(-1, EV_TIMEOUT
, file_fire_done_cb
, cf
, NULL
);
165 /* Fire the read callback. */
167 file_fire_read(struct client_file
*cf
)
170 cf
->cb(cf
->c
, cf
->path
, cf
->error
, 0, cf
->buffer
, cf
->data
);
173 /* Can this file be printed to? */
175 file_can_print(struct client
*c
)
178 (c
->flags
& CLIENT_ATTACHED
) ||
179 (c
->flags
& CLIENT_CONTROL
))
184 /* Print a message to a file. */
186 file_print(struct client
*c
, const char *fmt
, ...)
191 file_vprint(c
, fmt
, ap
);
195 /* Print a message to a file. */
197 file_vprint(struct client
*c
, const char *fmt
, va_list ap
)
199 struct client_file find
, *cf
;
200 struct msg_write_open msg
;
202 if (!file_can_print(c
))
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("-");
210 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
213 msg
.fd
= STDOUT_FILENO
;
215 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
217 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
222 /* Print a buffer to a file. */
224 file_print_buffer(struct client
*c
, void *data
, size_t size
)
226 struct client_file find
, *cf
;
227 struct msg_write_open msg
;
229 if (!file_can_print(c
))
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("-");
237 evbuffer_add(cf
->buffer
, data
, size
);
240 msg
.fd
= STDOUT_FILENO
;
242 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
244 evbuffer_add(cf
->buffer
, data
, size
);
249 /* Report an error to a file. */
251 file_error(struct client
*c
, const char *fmt
, ...)
253 struct client_file find
, *cf
;
254 struct msg_write_open msg
;
257 if (!file_can_print(c
))
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("-");
267 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
270 msg
.fd
= STDERR_FILENO
;
272 proc_send(c
->peer
, MSG_WRITE_OPEN
, -1, &msg
, sizeof msg
);
274 evbuffer_add_vprintf(cf
->buffer
, fmt
, ap
);
281 /* Write data to a file. */
283 file_write(struct client
*c
, const char *path
, int flags
, const void *bdata
,
284 size_t bsize
, client_file_cb cb
, void *cbdata
)
286 struct client_file
*cf
;
287 struct msg_write_open
*msg
;
290 u_int stream
= file_next_stream
++;
294 if (strcmp(path
, "-") == 0) {
295 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
296 cf
->path
= xstrdup("-");
300 (c
->flags
& CLIENT_ATTACHED
) ||
301 (c
->flags
& CLIENT_CONTROL
)) {
308 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
309 cf
->path
= file_get_path(c
, path
);
311 if (c
== NULL
|| c
->flags
& CLIENT_ATTACHED
) {
312 if (flags
& O_APPEND
)
316 f
= fopen(cf
->path
, mode
);
321 if (fwrite(bdata
, 1, bsize
, f
) != bsize
) {
331 evbuffer_add(cf
->buffer
, bdata
, bsize
);
333 msglen
= strlen(cf
->path
) + 1 + sizeof *msg
;
334 if (msglen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
) {
338 msg
= xmalloc(msglen
);
339 msg
->stream
= cf
->stream
;
342 memcpy(msg
+ 1, cf
->path
, msglen
- sizeof *msg
);
343 if (proc_send(cf
->peer
, MSG_WRITE_OPEN
, -1, msg
, msglen
) != 0) {
357 file_read(struct client
*c
, const char *path
, client_file_cb cb
, void *cbdata
)
359 struct client_file
*cf
;
360 struct msg_read_open
*msg
;
363 u_int stream
= file_next_stream
++;
368 if (strcmp(path
, "-") == 0) {
369 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
370 cf
->path
= xstrdup("-");
374 (c
->flags
& CLIENT_ATTACHED
) ||
375 (c
->flags
& CLIENT_CONTROL
)) {
382 cf
= file_create_with_client(c
, stream
, cb
, cbdata
);
383 cf
->path
= file_get_path(c
, path
);
385 if (c
== NULL
|| c
->flags
& CLIENT_ATTACHED
) {
386 f
= fopen(cf
->path
, "rb");
392 size
= fread(buffer
, 1, sizeof buffer
, f
);
393 if (evbuffer_add(cf
->buffer
, buffer
, size
) != 0) {
397 if (size
!= sizeof buffer
)
409 msglen
= strlen(cf
->path
) + 1 + sizeof *msg
;
410 if (msglen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
) {
414 msg
= xmalloc(msglen
);
415 msg
->stream
= cf
->stream
;
417 memcpy(msg
+ 1, cf
->path
, msglen
- sizeof *msg
);
418 if (proc_send(cf
->peer
, MSG_READ_OPEN
, -1, msg
, msglen
) != 0) {
431 /* Cancel a file read. */
433 file_cancel(struct client_file
*cf
)
435 struct msg_read_cancel msg
;
437 log_debug("read cancel file %d", cf
->stream
);
443 msg
.stream
= cf
->stream
;
444 proc_send(cf
->peer
, MSG_READ_CANCEL
, -1, &msg
, sizeof msg
);
447 /* Push event, fired if there is more writing to be done. */
449 file_push_cb(__unused
int fd
, __unused
short events
, void *arg
)
451 struct client_file
*cf
= arg
;
453 if (cf
->c
== NULL
|| ~cf
->c
->flags
& CLIENT_DEAD
)
458 /* Push uwritten data to the client for a file, if it will accept it. */
460 file_push(struct client_file
*cf
)
462 struct msg_write_data
*msg
;
463 size_t msglen
, sent
, left
;
464 struct msg_write_close close
;
466 msg
= xmalloc(sizeof *msg
);
467 left
= EVBUFFER_LENGTH(cf
->buffer
);
470 if (sent
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
)
471 sent
= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
- sizeof *msg
;
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)
479 evbuffer_drain(cf
->buffer
, sent
);
481 left
= EVBUFFER_LENGTH(cf
->buffer
);
482 log_debug("file %d sent %zu, left %zu", cf
->stream
, sent
, left
);
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
);
495 /* Check if any files have data left to write. */
497 file_write_left(struct client_files
*files
)
499 struct client_file
*cf
;
503 RB_FOREACH(cf
, client_files
, files
) {
504 if (cf
->event
== NULL
)
506 left
= EVBUFFER_LENGTH(cf
->event
->output
);
509 log_debug("file %u %zu bytes left", cf
->stream
, left
);
512 return (waiting
!= 0);
515 /* Client file write error callback. */
517 file_write_error_callback(__unused
struct bufferevent
*bev
, __unused
short what
,
520 struct client_file
*cf
= arg
;
522 log_debug("write error file %d", cf
->stream
);
524 bufferevent_free(cf
->event
);
531 cf
->cb(NULL
, NULL
, 0, -1, NULL
, cf
->data
);
534 /* Client file write callback. */
536 file_write_callback(__unused
struct bufferevent
*bev
, void *arg
)
538 struct client_file
*cf
= arg
;
540 log_debug("write check file %d", cf
->stream
);
543 cf
->cb(NULL
, NULL
, 0, -1, NULL
, cf
->data
);
545 if (cf
->closed
&& EVBUFFER_LENGTH(cf
->event
->output
) == 0) {
546 bufferevent_free(cf
->event
);
548 RB_REMOVE(client_files
, cf
->tree
, cf
);
553 /* Handle a file write open message (client). */
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
)
559 struct msg_write_open
*msg
= imsg
->data
;
560 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
562 struct msg_write_ready reply
;
563 struct client_file find
, *cf
;
564 const int flags
= O_NONBLOCK
|O_WRONLY
|O_CREAT
;
567 if (msglen
< sizeof *msg
)
568 fatalx("bad MSG_WRITE_OPEN size");
569 if (msglen
== sizeof *msg
)
572 path
= (const char *)(msg
+ 1);
573 log_debug("open write file %d %s", msg
->stream
, path
);
575 find
.stream
= msg
->stream
;
576 if (RB_FIND(client_files
, files
, &find
) != NULL
) {
580 cf
= file_create_with_peer(peer
, files
, msg
->stream
, cb
, cbdata
);
588 cf
->fd
= open(path
, msg
->flags
|flags
, 0644);
589 else if (allow_streams
) {
590 if (msg
->fd
!= STDOUT_FILENO
&& msg
->fd
!= STDERR_FILENO
)
593 cf
->fd
= dup(msg
->fd
);
595 close(msg
->fd
); /* can only be used once */
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
);
612 reply
.stream
= msg
->stream
;
614 proc_send(peer
, MSG_WRITE_READY
, -1, &reply
, sizeof reply
);
617 /* Handle a file write data message (client). */
619 file_write_data(struct client_files
*files
, struct imsg
*imsg
)
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
;
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
);
633 if (cf
->event
!= NULL
)
634 bufferevent_write(cf
->event
, msg
+ 1, size
);
637 /* Handle a file write close message (client). */
639 file_write_close(struct client_files
*files
, struct imsg
*imsg
)
641 struct msg_write_close
*msg
= imsg
->data
;
642 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
643 struct client_file find
, *cf
;
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
);
652 if (cf
->event
== NULL
|| EVBUFFER_LENGTH(cf
->event
->output
) == 0) {
653 if (cf
->event
!= NULL
)
654 bufferevent_free(cf
->event
);
657 RB_REMOVE(client_files
, files
, cf
);
662 /* Client file read error callback. */
664 file_read_error_callback(__unused
struct bufferevent
*bev
, __unused
short what
,
667 struct client_file
*cf
= arg
;
668 struct msg_read_done msg
;
670 log_debug("read error file %d", cf
->stream
);
672 msg
.stream
= cf
->stream
;
674 proc_send(cf
->peer
, MSG_READ_DONE
, -1, &msg
, sizeof msg
);
676 bufferevent_free(cf
->event
);
678 RB_REMOVE(client_files
, cf
->tree
, cf
);
682 /* Client file read callback. */
684 file_read_callback(__unused
struct bufferevent
*bev
, void *arg
)
686 struct client_file
*cf
= arg
;
689 struct msg_read_data
*msg
;
692 msg
= xmalloc(sizeof *msg
);
694 bdata
= EVBUFFER_DATA(cf
->event
->input
);
695 bsize
= EVBUFFER_LENGTH(cf
->event
->input
);
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
);
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
);
709 evbuffer_drain(cf
->event
->input
, bsize
);
714 /* Handle a file read open message (client). */
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
,
720 struct msg_read_open
*msg
= imsg
->data
;
721 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
723 struct msg_read_done reply
;
724 struct client_file find
, *cf
;
725 const int flags
= O_NONBLOCK
|O_RDONLY
;
728 if (msglen
< sizeof *msg
)
729 fatalx("bad MSG_READ_OPEN size");
730 if (msglen
== sizeof *msg
)
733 path
= (const char *)(msg
+ 1);
734 log_debug("open read file %d %s", msg
->stream
, path
);
736 find
.stream
= msg
->stream
;
737 if (RB_FIND(client_files
, files
, &find
) != NULL
) {
741 cf
= file_create_with_peer(peer
, files
, msg
->stream
, cb
, cbdata
);
749 cf
->fd
= open(path
, flags
);
750 else if (allow_streams
) {
751 if (msg
->fd
!= STDIN_FILENO
)
754 cf
->fd
= dup(msg
->fd
);
756 close(msg
->fd
); /* can only be used once */
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
);
773 reply
.stream
= msg
->stream
;
775 proc_send(peer
, MSG_READ_DONE
, -1, &reply
, sizeof reply
);
778 /* Handle a read cancel message (client). */
780 file_read_cancel(struct client_files
*files
, struct imsg
*imsg
)
782 struct msg_read_cancel
*msg
= imsg
->data
;
783 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
784 struct client_file find
, *cf
;
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
);
793 file_read_error_callback(NULL
, 0, cf
);
796 /* Handle a write ready message (server). */
798 file_write_ready(struct client_files
*files
, struct imsg
*imsg
)
800 struct msg_write_ready
*msg
= imsg
->data
;
801 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
802 struct client_file find
, *cf
;
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
)
809 if (msg
->error
!= 0) {
810 cf
->error
= msg
->error
;
816 /* Handle read data message (server). */
818 file_read_data(struct client_files
*files
, struct imsg
*imsg
)
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
;
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
)
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) {
842 /* Handle a read done message (server). */
844 file_read_done(struct client_files
*files
, struct imsg
*imsg
)
846 struct msg_read_done
*msg
= imsg
->data
;
847 size_t msglen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
848 struct client_file find
, *cf
;
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
)
856 log_debug("file %d read done", cf
->stream
);
857 cf
->error
= msg
->error
;