Add a utmp option if built with utempter, GitHub issue 5657 from Damian Nowak.

This commit is contained in:
Nicholas Marriott
2026-10-09 13:18:05 +01:00
parent 10c744038f
commit b7fd92b782
7 changed files with 81 additions and 17 deletions

View File

@@ -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,

View File

@@ -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();

View File

@@ -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);

11
spawn.c
View File

@@ -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);

17
tmux.1
View File

@@ -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

3
tmux.h
View File

@@ -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 *);

View File

@@ -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;
}