diff --git a/cmd-list-clients.c b/cmd-list-clients.c index a5b7d147..53a99178 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -31,6 +31,8 @@ #define LIST_CLIENTS_TEMPLATE \ "#{client_name}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}] " \ + "#{?#{!=:#{client_uid},#{uid}}," \ + "[user #{?client_user,#{client_user},#{client_uid},}] ,}" \ "#{?client_flags,(,}#{client_flags}#{?client_flags,),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 2af9cb46..b2665ad9 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1, NULL }, + .args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL }, .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", @@ -162,6 +162,37 @@ out: free(copy); } +static enum cmd_retval +cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + const char *p; + u_int i; + struct cmd_find_state fs; + + p = args_get(args, 'l'); + if (p == NULL) { + if (tc->flags & CLIENT_CLIPBOARDBUFFER) + return (CMD_RETURN_NORMAL); + tc->flags |= CLIENT_CLIPBOARDBUFFER; + } else { + if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0) + return (CMD_RETURN_ERROR); + for (i = 0; i < tc->clipboard_npanes; i++) { + if (tc->clipboard_panes[i] == fs.wp->id) + break; + } + if (i != tc->clipboard_npanes) + return (CMD_RETURN_NORMAL); + tc->clipboard_panes = xreallocarray (tc->clipboard_panes, + tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes); + tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id; + } + tty_clipboard_query(&tc->tty); + return (CMD_RETURN_NORMAL); +} + static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { @@ -224,10 +255,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'l')) { - tty_send_osc52_query(&tc->tty); - return (CMD_RETURN_NORMAL); - } + if (args_has(args, 'l')) + return (cmd_refresh_client_clipboard(self, item)); if (args_has(args, 'F')) /* -F is an alias for -f */ server_client_set_flags(tc, args_get(args, 'F')); diff --git a/format.c b/format.c index 5c6eed36..52eb4642 100644 --- a/format.c +++ b/format.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1387,6 +1388,35 @@ format_cb_client_tty(struct format_tree *ft) return (NULL); } +/* Callback for client_uid. */ +static void * +format_cb_client_uid(struct format_tree *ft) +{ + uid_t uid; + + if (ft->c != NULL) { + uid = proc_get_peer_uid(ft->c->peer); + if (uid != (uid_t)-1) + return (format_printf("%ld", (long)uid)); + } + return (NULL); +} + +/* Callback for client_user. */ +static void * +format_cb_client_user(struct format_tree *ft) +{ + uid_t uid; + struct passwd *pw; + + if (ft->c != NULL) { + uid = proc_get_peer_uid(ft->c->peer); + if (uid != (uid_t)-1 && (pw = getpwuid(uid)) != NULL) + return (xstrdup(pw->pw_name)); + } + return (NULL); +} + /* Callback for client_utf8. */ static void * format_cb_client_utf8(struct format_tree *ft) @@ -2521,6 +2551,24 @@ format_cb_tree_mode_format(__unused struct format_tree *ft) return (xstrdup(window_tree_mode.default_format)); } +/* Callback for uid. */ +static void * +format_cb_uid(__unused struct format_tree *ft) +{ + return (format_printf("%ld", (long)getuid())); +} + +/* Callback for user. */ +static void * +format_cb_user(__unused struct format_tree *ft) +{ + struct passwd *pw; + + if ((pw = getpwuid(getuid())) != NULL) + return (xstrdup(pw->pw_name)); + return NULL; +} + /* Format table type. */ enum format_table_type { FORMAT_TABLE_STRING, @@ -2627,6 +2675,12 @@ static const struct format_table_entry format_table[] = { { "client_tty", FORMAT_TABLE_STRING, format_cb_client_tty }, + { "client_uid", FORMAT_TABLE_STRING, + format_cb_client_uid + }, + { "client_user", FORMAT_TABLE_STRING, + format_cb_client_user + }, { "client_utf8", FORMAT_TABLE_STRING, format_cb_client_utf8 }, @@ -2906,6 +2960,12 @@ static const struct format_table_entry format_table[] = { { "tree_mode_format", FORMAT_TABLE_STRING, format_cb_tree_mode_format }, + { "uid", FORMAT_TABLE_STRING, + format_cb_uid + }, + { "user", FORMAT_TABLE_STRING, + format_cb_user + }, { "version", FORMAT_TABLE_STRING, format_cb_version }, diff --git a/input.c b/input.c index 5adc694d..b1856538 100644 --- a/input.c +++ b/input.c @@ -2682,8 +2682,8 @@ input_osc_52(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *end; - const char *buf; - size_t len; + const char *buf = NULL; + size_t len = 0; u_char *out; int outlen, state; struct screen_write_ctx ctx; @@ -2703,26 +2703,12 @@ input_osc_52(struct input_ctx *ictx, const char *p) log_debug("%s: %s", __func__, end); if (strcmp(end, "?") == 0) { - if ((pb = paste_get_top(NULL)) != NULL) { + if ((pb = paste_get_top(NULL)) != NULL) buf = paste_buffer_data(pb, &len); - outlen = 4 * ((len + 2) / 3) + 1; - out = xmalloc(outlen); - if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { - free(out); - return; - } - } else { - outlen = 0; - out = NULL; - } - bufferevent_write(ictx->event, "\033]52;;", 6); - if (outlen != 0) - bufferevent_write(ictx->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) - bufferevent_write(ictx->event, "\007", 1); + input_reply_clipboard(ictx->event, buf, len, "\007"); else - bufferevent_write(ictx->event, "\033\\", 2); - free(out); + input_reply_clipboard(ictx->event, buf, len, "\033\\"); return; } @@ -2780,3 +2766,26 @@ input_osc_104(struct input_ctx *ictx, const char *p) screen_write_fullredraw(&ictx->ctx); free(copy); } + +void +input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, + const char *end) +{ + char *out = NULL; + size_t outlen = 0; + + if (buf != NULL && len != 0) { + outlen = 4 * ((len + 2) / 3) + 1; + out = xmalloc(outlen); + if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { + free(out); + return; + } + } + + bufferevent_write(bev, "\033]52;;", 6); + if (outlen != 0) + bufferevent_write(bev, out, outlen); + bufferevent_write(bev, end, strlen(end)); + free(out); +} diff --git a/proc.c b/proc.c index 958a9483..a9b1473e 100644 --- a/proc.c +++ b/proc.c @@ -56,6 +56,7 @@ struct tmuxpeer { struct imsgbuf ibuf; struct event event; + uid_t uid; int flags; #define PEER_BAD 0x1 @@ -308,6 +309,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, void (*dispatchcb)(struct imsg *, void *), void *arg) { struct tmuxpeer *peer; + gid_t gid; peer = xcalloc(1, sizeof *peer); peer->parent = tp; @@ -318,6 +320,9 @@ proc_add_peer(struct tmuxproc *tp, int fd, imsg_init(&peer->ibuf, fd); event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + if (getpeereid(fd, &peer->uid, &gid) != 0) + peer->uid = (uid_t)-1; + log_debug("add peer %p: %d (%p)", peer, fd, arg); TAILQ_INSERT_TAIL(&tp->peers, peer, entry); @@ -373,3 +378,9 @@ proc_fork_and_daemon(int *fd) return (pid); } } + +uid_t +proc_get_peer_uid(struct tmuxpeer *peer) +{ + return (peer->uid); +} diff --git a/server-client.c b/server-client.c index 0f043796..3b888d01 100644 --- a/server-client.c +++ b/server-client.c @@ -422,6 +422,7 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); + free(c->clipboard_panes); free(c->term_name); free(c->term_type); diff --git a/tmux.1 b/tmux.1 index 1837e248..475ddfbb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1341,11 +1341,12 @@ and sets an environment variable for the newly created session; it may be specified multiple times. .Tg refresh .It Xo Ic refresh-client -.Op Fl cDlLRSU +.Op Fl cDLRSU .Op Fl A Ar pane:state .Op Fl B Ar name:what:format .Op Fl C Ar size .Op Fl f Ar flags +.Op Fl l Op Ar target-pane .Op Fl t Ar target-client .Op Ar adjustment .Xc @@ -1459,7 +1460,11 @@ sets a comma-separated list of client flags, see .Fl l requests the clipboard from the client using the .Xr xterm 1 -escape sequence and stores it in a new paste buffer. +escape sequence. +If +Ar target-pane +is given, the clipboard is sent (in encoded form), otherwise it is stored in a +new paste buffer. .Pp .Fl L , .Fl R , @@ -5057,6 +5062,8 @@ The following variables are available, where appropriate: .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_uid" Ta "" Ta "UID of client process" +.It Li "client_user" Ta "" Ta "User of client process" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "client_written" Ta "" Ta "Bytes written to client" @@ -5174,6 +5181,8 @@ The following variables are available, where appropriate: .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" +.It Li "uid" Ta "" Ta "Server UID" +.It Li "user" Ta "" Ta "Server user" .It Li "version" Ta "" Ta "Server version" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" @@ -5188,7 +5197,6 @@ The following variables are available, where appropriate: .It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" -.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" @@ -5203,6 +5211,7 @@ The following variables are available, where appropriate: .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" .It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" diff --git a/tmux.h b/tmux.h index 7eccaa55..78283aa2 100644 --- a/tmux.h +++ b/tmux.h @@ -1332,7 +1332,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; struct event start_timer; - struct event query_timer; + struct event clipboard_timer; u_int sx; u_int sy; @@ -1685,50 +1685,50 @@ typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *); typedef void (*overlay_free_cb)(struct client *, void *); typedef void (*overlay_resize_cb)(struct client *, void *); struct client { - const char *name; - struct tmuxpeer *peer; - struct cmdq_list *queue; + const char *name; + struct tmuxpeer *peer; + struct cmdq_list *queue; - struct client_windows windows; + struct client_windows windows; - struct control_state *control_state; - u_int pause_age; + struct control_state *control_state; + u_int pause_age; - pid_t pid; - int fd; - int out_fd; - struct event event; - int retval; + pid_t pid; + int fd; + int out_fd; + struct event event; + int retval; - struct timeval creation_time; - struct timeval activity_time; + struct timeval creation_time; + struct timeval activity_time; - struct environ *environ; + struct environ *environ; struct format_job_tree *jobs; - char *title; - const char *cwd; + char *title; + const char *cwd; - char *term_name; - int term_features; - char *term_type; - char **term_caps; - u_int term_ncaps; + char *term_name; + int term_features; + char *term_type; + char **term_caps; + u_int term_ncaps; - char *ttyname; - struct tty tty; + char *ttyname; + struct tty tty; - size_t written; - size_t discarded; - size_t redraw; + size_t written; + size_t discarded; + size_t redraw; - struct event repeat_timer; + struct event repeat_timer; - struct event click_timer; - u_int click_button; - struct mouse_event click_event; + struct event click_timer; + u_int click_button; + struct mouse_event click_event; - struct status_line status; + struct status_line status; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 @@ -1765,6 +1765,7 @@ struct client { #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL +#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1776,73 +1777,79 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) -#define CLIENT_NODETACHFLAGS \ +#define CLIENT_NODETACHFLAGS \ (CLIENT_DEAD| \ CLIENT_EXIT) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_EXIT) - uint64_t flags; + uint64_t flags; enum { CLIENT_EXIT_RETURN, CLIENT_EXIT_SHUTDOWN, CLIENT_EXIT_DETACH - } exit_type; - enum msgtype exit_msgtype; - char *exit_session; - char *exit_message; + } exit_type; + enum msgtype exit_msgtype; + char *exit_session; + char *exit_message; - struct key_table *keytable; + struct key_table *keytable; - uint64_t redraw_panes; + uint64_t redraw_panes; - int message_ignore_keys; - int message_ignore_styles; - char *message_string; - struct event message_timer; + int message_ignore_keys; + int message_ignore_styles; + char *message_string; + struct event message_timer; - char *prompt_string; - struct utf8_data *prompt_buffer; - char *prompt_last; - size_t prompt_index; - prompt_input_cb prompt_inputcb; - prompt_free_cb prompt_freecb; - void *prompt_data; - u_int prompt_hindex[PROMPT_NTYPES]; - enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; - struct utf8_data *prompt_saved; + char *prompt_string; + struct utf8_data *prompt_buffer; + char *prompt_last; + size_t prompt_index; + prompt_input_cb prompt_inputcb; + prompt_free_cb prompt_freecb; + void *prompt_data; + u_int prompt_hindex[PROMPT_NTYPES]; + enum { + PROMPT_ENTRY, + PROMPT_COMMAND + } prompt_mode; + struct utf8_data *prompt_saved; #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 - int prompt_flags; - enum prompt_type prompt_type; - int prompt_cursor; + int prompt_flags; + enum prompt_type prompt_type; + int prompt_cursor; - struct session *session; - struct session *last_session; + struct session *session; + struct session *last_session; - int references; + int references; - void *pan_window; - u_int pan_ox; - u_int pan_oy; + void *pan_window; + u_int pan_ox; + u_int pan_oy; - overlay_check_cb overlay_check; - overlay_mode_cb overlay_mode; - overlay_draw_cb overlay_draw; - overlay_key_cb overlay_key; - overlay_free_cb overlay_free; - overlay_resize_cb overlay_resize; - void *overlay_data; - struct event overlay_timer; + overlay_check_cb overlay_check; + overlay_mode_cb overlay_mode; + overlay_draw_cb overlay_draw; + overlay_key_cb overlay_key; + overlay_free_cb overlay_free; + overlay_resize_cb overlay_resize; + void *overlay_data; + struct event overlay_timer; - struct client_files files; + struct client_files files; - TAILQ_ENTRY(client) entry; + u_int *clipboard_panes; + u_int clipboard_npanes; + + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -2016,6 +2023,7 @@ void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); pid_t proc_fork_and_daemon(int *); +uid_t proc_get_peer_uid(struct tmuxpeer *); /* cfg.c */ extern int cfg_finished; @@ -2229,7 +2237,7 @@ void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); void tty_cursor(struct tty *, u_int, u_int); -void tty_send_osc52_query(struct tty *); +void tty_clipboard_query(struct tty *); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); @@ -2675,6 +2683,8 @@ void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); void input_parse_screen(struct input_ctx *, struct screen *, screen_write_init_ctx_cb, void *, u_char *, size_t); +void input_reply_clipboard(struct bufferevent *, const char *, size_t, + const char *); /* input-key.c */ void input_key_build(void); diff --git a/tty-keys.c b/tty-keys.c index 30717af3..8538e74b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1154,12 +1154,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, * partial. */ static int -tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) { - size_t end, terminator, needed; - char *copy, *out; - int outlen; + struct client *c = tty->client; + struct window_pane *wp; + size_t end, terminator, needed; + char *copy, *out; + int outlen; + u_int i; *size = 0; @@ -1221,6 +1223,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, if (~tty->flags & TTY_OSC52QUERY) return (0); tty->flags &= ~TTY_OSC52QUERY; + evtimer_del(&tty->clipboard_timer); /* It has to be a string so copy it. */ copy = xmalloc(end + 1); @@ -1237,9 +1240,20 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, } free(copy); - /* Create a new paste buffer. */ + /* Create a new paste buffer and forward to panes. */ log_debug("%s: %.*s", __func__, outlen, out); - paste_add(NULL, out, outlen); + if (c->flags & CLIENT_CLIPBOARDBUFFER) { + paste_add(NULL, out, outlen); + c->flags &= ~CLIENT_CLIPBOARDBUFFER; + } + for (i = 0; i < c->clipboard_npanes; i++) { + wp = window_pane_find_by_id(c->clipboard_panes[i]); + if (wp != NULL) + input_reply_clipboard(wp->event, out, outlen, "\033\\"); + } + free(c->clipboard_panes); + c->clipboard_panes = NULL; + c->clipboard_npanes = 0; return (0); } diff --git a/tty.c b/tty.c index 06b1bfb2..40735ceb 100644 --- a/tty.c +++ b/tty.c @@ -2921,24 +2921,29 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, } static void -tty_query_timer_callback(__unused int fd, __unused short events, void *data) +tty_clipboard_query_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; + struct client *c = tty->client; + + c->flags &= ~CLIENT_CLIPBOARDBUFFER; + free(c->clipboard_panes); + c->clipboard_panes = NULL; + c->clipboard_npanes = 0; tty->flags &= ~TTY_OSC52QUERY; } void -tty_send_osc52_query(struct tty *tty) +tty_clipboard_query(struct tty *tty) { struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) return; tty_putcode_ptr2(tty, TTYC_MS, "", "?"); + tty->flags |= TTY_OSC52QUERY; - - evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); - evtimer_add(&tty->query_timer, &tv); + evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); + evtimer_add(&tty->clipboard_timer, &tv); } -