diff --git a/options-table.c b/options-table.c index a65694973..b41fe2ab6 100644 --- a/options-table.c +++ b/options-table.c @@ -743,6 +743,15 @@ const struct options_table_entry options_table[] = { "'User0', 'User1' and so on." }, +#ifdef HAVE_UTEMPTER + { .name = "utmp", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, + .default_num = 1, + .text = "Whether to maintain utmp records." + }, +#endif + { .name = "variation-selector-always-wide", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, diff --git a/options.c b/options.c index fa10d8a7d..946469ed2 100644 --- a/options.c +++ b/options.c @@ -1406,6 +1406,14 @@ options_push_changes(const char *name) tty_keys_build(&loop->tty); } } + if (strcmp(name, "utmp") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (options_get_number(global_options, name)) + window_pane_utmp_add(wp); + else + window_pane_utmp_remove(wp); + } + } if (strcmp(name, "status") == 0 || strcmp(name, "status-interval") == 0) status_timer_start_all(); diff --git a/server-fn.c b/server-fn.c index e9db5a887..45809ee96 100644 --- a/server-fn.c +++ b/server-fn.c @@ -363,10 +363,7 @@ server_destroy_pane(struct window_pane *wp, int notify) u_int sy = screen_size_y(&wp->base); if (wp->fd != -1) { -#ifdef HAVE_UTEMPTER - utempter_remove_record(wp->fd); - kill(getpid(), SIGCHLD); -#endif + window_pane_utmp_remove(wp); bufferevent_free(wp->event); wp->event = NULL; close(wp->fd); diff --git a/spawn.c b/spawn.c index ab4a196c2..63300e4fb 100644 --- a/spawn.c +++ b/spawn.c @@ -322,6 +322,7 @@ spawn_pane(struct spawn_context *sc, char **cause) sc->wp0->event = NULL; } if (sc->wp0->fd != -1) { + window_pane_utmp_remove(sc->wp0); close(sc->wp0->fd); sc->wp0->fd = -1; } @@ -579,16 +580,8 @@ spawn_pane(struct spawn_context *sc, char **cause) _exit(1); complete: -#ifdef HAVE_UTEMPTER - if (~new_wp->flags & PANE_EMPTY) { - xasprintf(&cp, "tmux(%lu).%%%u", (long)getpid(), new_wp->id); - utempter_add_record(new_wp->fd, cp); - kill(getpid(), SIGCHLD); - free(cp); - } -#endif - new_wp->flags &= ~PANE_EXITED; + window_pane_utmp_add(new_wp); sigprocmask(SIG_SETMASK, &oldset, NULL); window_pane_set_event(new_wp); diff --git a/tmux.1 b/tmux.1 index cf82a209f..6b56822ea 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5262,6 +5262,23 @@ For example: set \-s user\-keys[0] "\ee[5;30012\[ti]" bind User0 resize\-pane \-L 3 .Ed +.It Xo Ic utmp +.Op Ic on | off +.Xc +Maintain utmp records for panes using +.Xr utempter 3 . +The default is on. +Turning this option off asks utempter to remove existing records and prevents +new records from being added. +Turning it on asks utempter to add records for existing panes with running +commands. +Empty panes are not recorded. +Updates to wtmp are performed by +.Xr utempter 3 +according to the system configuration; existing history is not erased. +This option has no effect if +.Nm +was built without utempter support. .It Xo Ic variation\-selector\-always\-wide .Op Cm on | off .Xc diff --git a/tmux.h b/tmux.h index c1cb418ac..14d05f43e 100644 --- a/tmux.h +++ b/tmux.h @@ -1355,6 +1355,7 @@ struct window_pane { #define PANE_CAPTUREALLKEYS 0x100000 #define PANE_FLOATOVERZOOM 0x200000 #define PANE_CLOSEONCANCEL 0x400000 +#define PANE_UTMP 0x800000 bitstr_t *sync_dirty; u_int sync_dirty_size; @@ -3725,6 +3726,8 @@ struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int, u_int, u_int); void window_pane_set_event(struct window_pane *); +void window_pane_utmp_add(struct window_pane *); +void window_pane_utmp_remove(struct window_pane *); void window_pane_wait_finish(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); diff --git a/window.c b/window.c index efe3f01c8..ddc49a2a5 100644 --- a/window.c +++ b/window.c @@ -1579,6 +1579,46 @@ window_pane_scrollbar_redraw_visibility(struct window_pane *wp) server_redraw_window(wp->window); } +#ifdef HAVE_UTEMPTER +void +window_pane_utmp_add(struct window_pane *wp) +{ + char *cp; + + if (wp->fd == -1 || + (wp->flags & (PANE_EMPTY|PANE_EXITED|PANE_UTMP)) || + !options_get_number(global_options, "utmp")) + return; + xasprintf(&cp, "tmux(%lu).%%%u", (long)getpid(), wp->id); + utempter_add_record(wp->fd, cp); + kill(getpid(), SIGCHLD); + wp->flags |= PANE_UTMP; + free(cp); +} +#else +void +window_pane_utmp_add(__unused struct window_pane *wp) +{ +} +#endif + +#ifdef HAVE_UTEMPTER +void +window_pane_utmp_remove(struct window_pane *wp) +{ + if (~wp->flags & PANE_UTMP) + return; + utempter_remove_record(wp->fd); + kill(getpid(), SIGCHLD); + wp->flags &= ~PANE_UTMP; +} +#else +void +window_pane_utmp_remove(__unused struct window_pane *wp) +{ +} +#endif + static void window_pane_destroy(struct window_pane *wp) { @@ -1597,10 +1637,7 @@ window_pane_destroy(struct window_pane *wp) wp->event = NULL; } if (wp->fd != -1) { -#ifdef HAVE_UTEMPTER - utempter_remove_record(wp->fd); - kill(getpid(), SIGCHLD); -#endif + window_pane_utmp_remove(wp); close(wp->fd); wp->fd = -1; }