From 184039044a92b83f38b880b0a4a1c5ebc272af9c Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Jun 2017 08:02:20 +0000 Subject: Typo/style; plus man page escaping from jmc. --- screen-write.c | 2 +- tmux.1 | 4 ++-- tty.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/screen-write.c b/screen-write.c index c5af7c07..1f122482 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1390,7 +1390,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } } - /* Update the selection the flag and set the cell. */ + /* Update the selected flag and set the cell. */ selected = screen_check_selection(s, s->cx, s->cy); if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { memcpy(&tmp_gc, gc, sizeof tmp_gc); diff --git a/tmux.1 b/tmux.1 index dfc25ee0..7e9fd5a9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1394,7 +1394,7 @@ The following keys may be used in tree mode: .It Li "t" Ta "Toggle if item is tagged" .It Li "T" Ta "Tag no items" .It Li "C-t" Ta "Tag all items" -.It Li ":" Ta "Run a command for each tagged item" +.It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort order" .It Li "q" Ta "Exit mode" @@ -3736,7 +3736,7 @@ The flag is one of the following symbols appended to the window name: .It Li "*" Ta "Denotes the current window." .It Li "-" Ta "Marks the last window (previously selected)." .It Li "#" Ta "Window is monitored and activity has been detected." -.It Li "!" Ta "A bell has occurred in the window." +.It Li "\&!" Ta "A bell has occurred in the window." .It Li "~" Ta "The window has been silent for the monitor-silence interval." .It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." diff --git a/tty.c b/tty.c index 7759e0ea..3d0e210b 100644 --- a/tty.c +++ b/tty.c @@ -1848,7 +1848,7 @@ tty_check_fg(struct tty *tty, const struct window_pane *wp, */ if (~gc->flags & GRID_FLAG_NOPALETTE) { c = gc->fg; - if (gc->fg < 8 && gc->attr & GRID_ATTR_BRIGHT) + if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) c += 90; if ((c = window_pane_get_palette(wp, c)) != -1) gc->fg = c; -- cgit From adf5628087829bed2eff635760d7cc456dd1e558 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Jun 2017 08:25:57 +0000 Subject: Support SIGUSR2 to stop and start logging for an existing server. Also we currently only have two log levels so just use -v and -vv rather than -v and -vvvv, and clarify the man page entry for -v. --- log.c | 21 +++++++++++++++++---- proc.c | 6 ++++++ server.c | 5 ++++- signal.c | 6 ++++++ tmux.1 | 17 ++++++++++++++++- tmux.h | 2 ++ 6 files changed, 51 insertions(+), 6 deletions(-) diff --git a/log.c b/log.c index 2e953620..c002663e 100644 --- a/log.c +++ b/log.c @@ -62,12 +62,10 @@ log_open(const char *name) if (log_level == 0) return; - - if (log_file != NULL) - fclose(log_file); + log_close(); xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); - log_file = fopen(path, "w"); + log_file = fopen(path, "a"); free(path); if (log_file == NULL) return; @@ -76,6 +74,21 @@ log_open(const char *name) event_set_log_callback(log_event_cb); } +/* Toggle logging. */ +void +log_toggle(const char *name) +{ + if (log_level == 0) { + log_level = 1; + log_open(name); + log_debug("log opened"); + } else { + log_debug("log closed"); + log_level = 0; + log_close(); + } +} + /* Close logging. */ void log_close(void) diff --git a/proc.c b/proc.c index bc27b4ff..1af72fec 100644 --- a/proc.c +++ b/proc.c @@ -265,3 +265,9 @@ proc_kill_peer(struct tmuxpeer *peer) { peer->flags |= PEER_BAD; } + +void +proc_toggle_log(struct tmuxproc *tp) +{ + log_toggle(tp->name); +} diff --git a/server.c b/server.c index e0987fca..4dc739d0 100644 --- a/server.c +++ b/server.c @@ -151,7 +151,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); - if (log_get_level() > 3) + if (log_get_level() > 1) tty_create_log(); if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " "tty ps", NULL) != 0) @@ -365,6 +365,9 @@ server_signal(int sig) } server_add_accept(0); break; + case SIGUSR2: + proc_toggle_log(server_proc); + break; } } diff --git a/signal.c b/signal.c index 79d23af0..49360514 100644 --- a/signal.c +++ b/signal.c @@ -29,6 +29,7 @@ static struct event ev_sigchld; static struct event ev_sigcont; static struct event ev_sigterm; static struct event ev_sigusr1; +static struct event ev_sigusr2; static struct event ev_sigwinch; void @@ -59,6 +60,8 @@ set_signals(void (*handler)(int, short, void *), void *arg) signal_add(&ev_sigterm, NULL); signal_set(&ev_sigusr1, SIGUSR1, handler, arg); signal_add(&ev_sigusr1, NULL); + signal_set(&ev_sigusr2, SIGUSR2, handler, arg); + signal_add(&ev_sigusr2, NULL); signal_set(&ev_sigwinch, SIGWINCH, handler, arg); signal_add(&ev_sigwinch, NULL); } @@ -92,6 +95,8 @@ clear_signals(int after_fork) fatal("sigaction failed"); if (sigaction(SIGUSR1, &sigact, NULL) != 0) fatal("sigaction failed"); + if (sigaction(SIGUSR2, &sigact, NULL) != 0) + fatal("sigaction failed"); if (sigaction(SIGWINCH, &sigact, NULL) != 0) fatal("sigaction failed"); } else { @@ -100,6 +105,7 @@ clear_signals(int after_fork) event_del(&ev_sigcont); event_del(&ev_sigterm); event_del(&ev_sigusr1); + event_del(&ev_sigusr2); event_del(&ev_sigwinch); } } diff --git a/tmux.1 b/tmux.1 index 7e9fd5a9..02259b2b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -199,7 +199,6 @@ characters to the terminal it is running (if not, they are replaced by .Ql _ ) . .It Fl v Request verbose logging. -This option may be specified multiple times for increasing verbosity. Log messages will be saved into .Pa tmux-client-PID.log and @@ -207,6 +206,22 @@ and files in the current directory, where .Em PID is the PID of the server or client process. +.Pp +If +.Fl v +is specified twice, an additional +.Pa tmux-out-PID.log +file is generated with a copy of everything +.Nm +writes to the terminal. +.Pp +The +.Dv SIGUSR2 +signal may be sent to the +.Nm +server process to toggle logging between on (as if +.Fl v +was given) and off. .It Ar command Op Ar flags This specifies one of a set of commands used to control .Nm , diff --git a/tmux.h b/tmux.h index b47a6911..77d5372a 100644 --- a/tmux.h +++ b/tmux.h @@ -1493,6 +1493,7 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, void (*)(struct imsg *, void *), void *); void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); +void proc_toggle_log(struct tmuxproc *); /* cfg.c */ extern int cfg_finished; @@ -2336,6 +2337,7 @@ char *get_proc_name(int, char *); void log_add_level(void); int log_get_level(void); void log_open(const char *); +void log_toggle(const char *); void log_close(void); void printflike(1, 2) log_debug(const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...); -- cgit From 8149bc3fa6e93cb083b165a21baa5ec07dd473dc Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Jun 2017 09:02:36 +0000 Subject: Be more strict about escape sequences that rename windows or set titles: ignore any that not valid UTF-8 outright, and for good measure pass the result through our UTF-8-aware vis(3). --- input.c | 13 +++++++++---- screen.c | 3 ++- tmux.h | 1 + utf8.c | 25 +++++++++++++++++++++++++ window.c | 3 ++- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index 5ba2ec5f..27c0a31a 100644 --- a/input.c +++ b/input.c @@ -1896,8 +1896,10 @@ input_exit_osc(struct input_ctx *ictx) switch (option) { case 0: case 2: - screen_set_title(ictx->ctx.s, p); - server_status_window(ictx->wp->window); + if (utf8_isvalid(p)) { + screen_set_title(ictx->ctx.s, p); + server_status_window(ictx->wp->window); + } break; case 4: input_osc_4(ictx->wp, p); @@ -1909,7 +1911,7 @@ input_exit_osc(struct input_ctx *ictx) input_osc_11(ictx->wp, p); break; case 12: - if (*p != '?') /* ? is colour request */ + if (utf8_isvalid(p) && *p != '?') /* ? is colour request */ screen_set_cursor_colour(ictx->ctx.s, p); break; case 52: @@ -1945,6 +1947,8 @@ input_exit_apc(struct input_ctx *ictx) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); + if (!utf8_isvalid(ictx->input_buf)) + return; screen_set_title(ictx->ctx.s, ictx->input_buf); server_status_window(ictx->wp->window); } @@ -1968,9 +1972,10 @@ input_exit_rename(struct input_ctx *ictx) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); + if (!utf8_isvalid(ictx->input_buf)) + return; window_set_name(ictx->wp->window, ictx->input_buf); options_set_number(ictx->wp->window->options, "automatic-rename", 0); - server_status_window(ictx->wp->window); } diff --git a/screen.c b/screen.c index 087611ae..ebd117a0 100644 --- a/screen.c +++ b/screen.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "tmux.h" @@ -107,7 +108,7 @@ void screen_set_title(struct screen *s, const char *title) { free(s->title); - s->title = xstrdup(title); + utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); } /* Resize screen. */ diff --git a/tmux.h b/tmux.h index 77d5372a..99c42aa4 100644 --- a/tmux.h +++ b/tmux.h @@ -2318,6 +2318,7 @@ enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); enum utf8_state utf8_split(wchar_t, struct utf8_data *); +int utf8_isvalid(const char *); int utf8_strvis(char *, const char *, size_t, int); int utf8_stravis(char **, const char *, int); char *utf8_sanitize(const char *); diff --git a/utf8.c b/utf8.c index 3a315749..099541fe 100644 --- a/utf8.c +++ b/utf8.c @@ -207,6 +207,31 @@ utf8_stravis(char **dst, const char *src, int flag) return (len); } +/* Does this string contain anything that isn't valid UTF-8? */ +int +utf8_isvalid(const char *s) +{ + struct utf8_data ud; + const char *end; + enum utf8_state more; + size_t i; + + end = s + strlen(s); + while (s < end) { + if ((more = utf8_open(&ud, *s)) == UTF8_MORE) { + while (++s < end && more == UTF8_MORE) + more = utf8_append(&ud, *s); + if (more == UTF8_DONE) + continue; + return (0); + } + if (*s < 0x20 || *s > 0x7e) + return (0); + s++; + } + return (1); +} + /* * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free * the returned string. Anything not valid printable ASCII or UTF-8 is diff --git a/window.c b/window.c index 5fe7c8b8..4b59f205 100644 --- a/window.c +++ b/window.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "tmux.h" @@ -408,7 +409,7 @@ void window_set_name(struct window *w, const char *new_name) { free(w->name); - w->name = xstrdup(new_name); + utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); notify_window("window-renamed", w); } -- cgit From 467ece53e63e0520db16c290f2865db95a4aa0f7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Jun 2017 09:02:57 +0000 Subject: Remove unused variable. --- utf8.c | 1 - 1 file changed, 1 deletion(-) diff --git a/utf8.c b/utf8.c index 099541fe..b25ac06b 100644 --- a/utf8.c +++ b/utf8.c @@ -214,7 +214,6 @@ utf8_isvalid(const char *s) struct utf8_data ud; const char *end; enum utf8_state more; - size_t i; end = s + strlen(s); while (s < end) { -- cgit From c5b7faaefc80a04058381b8e61ecc6016d57bde5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Jun 2017 09:22:34 +0000 Subject: Add a timeout to prevent the sequences which wait for a specific terminator (OSC, APC and DCS) waiting forever, which helps to avoid garbage (cat /dev/random) locking up panes completely. This (and the last commit) prompted by a discussion with theo. --- input.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/input.c b/input.c index 27c0a31a..8131c164 100644 --- a/input.c +++ b/input.c @@ -93,6 +93,8 @@ struct input_ctx { const struct input_state *state; + struct event timer; + /* * All input received since we were last in the ground state. Sent to * control clients on connection. @@ -118,6 +120,7 @@ static void input_osc_104(struct window_pane *, const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); static void input_ground(struct input_ctx *); +static void input_enter_dcs(struct input_ctx *); static void input_enter_osc(struct input_ctx *); static void input_exit_osc(struct input_ctx *); static void input_enter_apc(struct input_ctx *); @@ -364,7 +367,7 @@ static const struct input_state input_state_csi_ignore = { /* dcs_enter state definition. */ static const struct input_state input_state_dcs_enter = { "dcs_enter", - input_clear, NULL, + input_enter_dcs, NULL, input_state_dcs_enter_table }; @@ -756,6 +759,30 @@ input_table_compare(const void *key, const void *value) return (strcmp(ictx->interm_buf, entry->interm)); } +/* + * Timer - if this expires then have been waiting for a terminator for too + * long, so reset to ground. + */ +static void +input_timer_callback(__unused int fd, __unused short events, void *arg) +{ + struct input_ctx *ictx = arg; + struct window_pane *wp = ictx->wp; + + log_debug("%s: %%%u %s expired" , __func__, wp->id, ictx->state->name); + input_reset(wp, 0); +} + +/* Start the timer. */ +static void +input_start_timer(struct input_ctx *ictx) +{ + struct timeval tv = { .tv_usec = 100000 }; + + event_del(&ictx->timer); + event_add(&ictx->timer, &tv); +} + /* Reset cell state to default. */ static void input_reset_cell(struct input_ctx *ictx) @@ -782,6 +809,8 @@ input_init(struct window_pane *wp) ictx->since_ground = evbuffer_new(); + evtimer_set(&ictx->timer, input_timer_callback, ictx); + input_reset(wp, 0); } @@ -791,6 +820,8 @@ input_free(struct window_pane *wp) { struct input_ctx *ictx = wp->ictx; + event_del(&ictx->timer); + free(ictx->input_buf); evbuffer_free(ictx->since_ground); @@ -815,14 +846,7 @@ input_reset(struct window_pane *wp, int clear) screen_write_stop(&ictx->ctx); } - *ictx->interm_buf = '\0'; - ictx->interm_len = 0; - - *ictx->param_buf = '\0'; - ictx->param_len = 0; - - *ictx->input_buf = '\0'; - ictx->input_len = 0; + input_clear(ictx); ictx->state = &input_state_ground; ictx->flags = 0; @@ -997,6 +1021,8 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...) static void input_clear(struct input_ctx *ictx) { + event_del(&ictx->timer); + *ictx->interm_buf = '\0'; ictx->interm_len = 0; @@ -1013,6 +1039,7 @@ input_clear(struct input_ctx *ictx) static void input_ground(struct input_ctx *ictx) { + event_del(&ictx->timer); evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground)); if (ictx->input_space > INPUT_BUF_START) { @@ -1842,6 +1869,16 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) } } +/* DCS string started. */ +static void +input_enter_dcs(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + input_clear(ictx); + input_start_timer(ictx); +} + /* DCS terminator (ST) received. */ static int input_dcs_dispatch(struct input_ctx *ictx) @@ -1871,6 +1908,7 @@ input_enter_osc(struct input_ctx *ictx) log_debug("%s", __func__); input_clear(ictx); + input_start_timer(ictx); } /* OSC terminator (ST) received. */ @@ -1937,6 +1975,7 @@ input_enter_apc(struct input_ctx *ictx) log_debug("%s", __func__); input_clear(ictx); + input_start_timer(ictx); } /* APC terminator (ST) received. */ @@ -1960,6 +1999,7 @@ input_enter_rename(struct input_ctx *ictx) log_debug("%s", __func__); input_clear(ictx); + input_start_timer(ictx); } /* Rename terminator (ST) received. */ -- cgit From 2f04108f3a35271ef60b3028699b6363e1714140 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Jun 2017 15:36:33 +0000 Subject: Do not leak command, from David CARLIER. --- mode-tree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode-tree.c b/mode-tree.c index f3155fbf..437f0008 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -685,8 +685,10 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, char *command, *cause; command = cmd_template_replace(template, name, 1); - if (command == NULL || *command == '\0') + if (command == NULL || *command == '\0') { + free(command); return; + } cmdlist = cmd_string_parse(command, NULL, 0, &cause); if (cmdlist == NULL) { -- cgit