From aa10b1d2da3ecf9672ae915a49073bd30ee0a785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 23 Sep 2022 20:24:04 +0200 Subject: [PATCH] Add support for creating utmp records MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for creating utmp records using the ‘utempter’ helper binary from the ‘libutempter’ package. * New config option ‘main.utempter’ * New meson command line option, -Ddefault-utempter-path. Defaults to auto-detecting the path. The default value of the new ‘main.utempter’ config option depends on the meson command line option ‘-Ddefault-utempter-path’. If ‘main.utempter’ is *not* set to ‘none’, foot will try to execute the utempter helper binary to create utmp records when a new terminal is instantiated. The record is removed when the terminal instance is destroyed. --- CHANGELOG.md | 2 ++ INSTALL.md | 22 ++++++++++++---------- config.c | 19 +++++++++++++++++++ config.h | 2 ++ doc/foot.ini.5.scd | 4 ++++ doc/meson.build | 7 +++++++ foot.ini | 1 + meson.build | 24 +++++++++++++++++++++++- meson_options.txt | 3 +++ terminal.c | 31 +++++++++++++++++++++++++++++++ tests/test-config.c | 1 + 11 files changed, 105 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd0ac80b..1bc41eef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ ### Added * Support for adjusting the thickness of regular underlines ([#1136][1136]). +* Support (optional) for utmp logging with libutempter. + [1136]: https://codeberg.org/dnkl/foot/issues/1136 diff --git a/INSTALL.md b/INSTALL.md index 18975503..da3a667e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -45,6 +45,7 @@ subprojects. * wayland (_client_ and _cursor_ libraries) * xkbcommon * utf8proc (_optional_, needed for grapheme clustering) +* libutempter (_optional_, needed for utmp logging) * [fcft](https://codeberg.org/dnkl/fcft) [^1] [^1]: can also be built as subprojects, in which case they are @@ -141,16 +142,17 @@ mkdir -p bld/release && cd bld/release Available compile-time options: -| Option | Type | Default | Description | Extra dependencies | -|--------------------------------------|---------|-------------------------|-------------------------------------------------------|--------------------| -| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc | -| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | none | -| `-Dime` | bool | `true` | Enables IME support | None | -| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc | -| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) | -| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none | -| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None | -| `-Dsystemd-units-dir` | string | `${systemduserunitdir}` | Where to install the systemd service files (absolute) | None | +| Option | Type | Default | Description | Extra dependencies | +|--------------------------------------|---------|-------------------------|-----------------------------------------------------------|--------------------| +| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc | +| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | none | +| `-Dime` | bool | `true` | Enables IME support | None | +| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc | +| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) | +| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none | +| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None | +| `-Dsystemd-units-dir` | string | `${systemduserunitdir}` | Where to install the systemd service files (absolute) | None | +| `-Ddefault-utempter-path` | feature | `auto` | Default path to utempter binary (‘none’ disables default) | libutempter | Documentation includes the man pages, readme, changelog and license files. diff --git a/config.c b/config.c index 0ba237a0..e2c007d2 100644 --- a/config.c +++ b/config.c @@ -944,6 +944,18 @@ parse_section_main(struct context *ctx) else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0) return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs); + else if (strcmp(key, "utempter") == 0) { + if (!value_to_str(ctx, &conf->utempter_path)) + return false; + + if (strcmp(conf->utempter_path, "none") == 0) { + free(conf->utempter_path); + conf->utempter_path = NULL; + } + + return true; + } + else { LOG_CONTEXTUAL_ERR("not a valid option: %s", key); return false; @@ -2937,6 +2949,9 @@ config_load(struct config *conf, const char *conf_path, }, .env_vars = tll_init(), + .utempter_path = (strlen(FOOT_DEFAULT_UTEMPTER_PATH) > 0 + ? xstrdup(FOOT_DEFAULT_UTEMPTER_PATH) + : NULL), .notifications = tll_init(), }; @@ -3225,6 +3240,9 @@ config_clone(const struct config *old) tll_push_back(conf->env_vars, copy); } + conf->utempter_path = + old->utempter_path != NULL ? xstrdup(old->utempter_path) : NULL; + conf->notifications.length = 0; conf->notifications.head = conf->notifications.tail = 0; tll_foreach(old->notifications, it) { @@ -3291,6 +3309,7 @@ config_free(struct config *conf) tll_remove(conf->env_vars, it); } + free(conf->utempter_path); user_notifications_free(&conf->notifications); } diff --git a/config.h b/config.h index f98e4d35..d35abbb2 100644 --- a/config.h +++ b/config.h @@ -319,6 +319,8 @@ struct config { env_var_list_t env_vars; + char *utempter_path; + struct { enum fcft_scaling_filter fcft_filter; bool overflowing_glyphs; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 7382a1d2..46a6d33e 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -317,6 +317,10 @@ commented out will usually be installed to */etc/xdg/foot/foot.ini*. (including SMT). Note that this is not always the best value. In some cases, the number of physical _cores_ is better. +*utempter* + Path to utempter helper binary. Set to *none* to disable utmp + records. Default: _@utempter@_. + # SECTION: environment This section is used to define environment variables that will be set diff --git a/doc/meson.build b/doc/meson.build index 75c3be95..86e75952 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -2,9 +2,16 @@ sh = find_program('sh', native: true) scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) +if utempter_path == '' + default_utempter_value = 'not set' +else + default_utempter_value = utempter_path +endif + conf_data = configuration_data( { 'default_terminfo': get_option('default-terminfo'), + 'utempter': default_utempter_value, } ) diff --git a/foot.ini b/foot.ini index 3b8b6f16..62f853fa 100644 --- a/foot.ini +++ b/foot.ini @@ -33,6 +33,7 @@ # word-delimiters=,│`|:"'()[]{}<> # selection-target=primary # workers= +# utempter=/usr/lib/utempter/utempter [environment] # name=value diff --git a/meson.build b/meson.build index 61837459..4d3b6213 100644 --- a/meson.build +++ b/meson.build @@ -16,9 +16,30 @@ if cc.has_function('memfd_create') add_project_arguments('-DMEMFD_CREATE', language: 'c') endif +utempter_path = get_option('default-utempter-path') +if utempter_path == '' + utempter = find_program( + 'utempter', + required: false, + dirs: [join_paths(get_option('prefix'), get_option('libdir'), 'utempter'), + join_paths(get_option('prefix'), get_option('libexecdir'), 'utempter'), + '/usr/lib/utempter', + '/usr/libexec/utempter', + '/lib/utempter'] + ) + if utempter.found() + utempter_path = utempter.full_path() + else + utempter_path = '' + endif +elif utempter_path == 'none' + utempter_path = '' +endif + add_project_arguments( ['-D_GNU_SOURCE=200809L', - '-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo'))] + + '-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo')), + '-DFOOT_DEFAULT_UTEMPTER_PATH="@0@"'.format(utempter_path)] + (is_debug_build ? ['-D_DEBUG'] : [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) + @@ -321,6 +342,7 @@ summary( 'Themes': get_option('themes'), 'IME': get_option('ime'), 'Grapheme clustering': utf8proc.found(), + 'Utempter path': utempter_path, 'Build terminfo': tic.found(), 'Terminfo install location': terminfo_install_location, 'Default TERM': get_option('default-terminfo'), diff --git a/meson_options.txt b/meson_options.txt index 0c660a75..c38a8ca8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -21,3 +21,6 @@ option('custom-terminfo-install-location', type: 'string', value: '', option('systemd-units-dir', type: 'string', value: '', description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}') + +option('default-utempter-path', type: 'string', value: '', + description: 'Default path to utempter helper binary. Default: auto-detect') diff --git a/terminal.c b/terminal.c index 3445b89f..4c9bf3db 100644 --- a/terminal.c +++ b/terminal.c @@ -203,6 +203,30 @@ fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data) return true; } +static bool +add_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx) +{ + if (ptmx < 0) + return true; + if (conf->utempter_path == NULL) + return true; + + char *const argv[] = {conf->utempter_path, "add", NULL}; + return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL); +} + +static bool +del_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx) +{ + if (ptmx < 0) + return true; + if (conf->utempter_path == NULL) + return true; + + char *const argv[] = {conf->utempter_path, "del", NULL}; + return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL); +} + #if PTMX_TIMING static struct timespec last = {0}; #endif @@ -326,6 +350,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) } if (hup) { + del_utmp_record(term->conf, term->reaper, term->ptmx); fdm_del(fdm, fd); term->ptmx = -1; } @@ -1251,6 +1276,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, } term->font_line_height = conf->line_height; + add_utmp_record(conf, reaper, ptmx); + /* Start the slave/client */ if ((term->slave = slave_spawn( term->ptmx, argc, term->cwd, argv, envp, &conf->env_vars, @@ -1514,6 +1541,8 @@ term_shutdown(struct terminal *term) fdm_del(term->fdm, term->blink.fd); fdm_del(term->fdm, term->flash.fd); + del_utmp_record(term->conf, term->reaper, term->ptmx); + if (term->window != NULL && term->window->is_configured) fdm_del(term->fdm, term->ptmx); else @@ -1595,6 +1624,8 @@ term_destroy(struct terminal *term) } } + del_utmp_record(term->conf, term->reaper, term->ptmx); + fdm_del(term->fdm, term->selection.auto_scroll.fd); fdm_del(term->fdm, term->render.app_sync_updates.timer_fd); fdm_del(term->fdm, term->render.title.timer_fd); diff --git a/tests/test-config.c b/tests/test-config.c index 837c5106..ae7969dc 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -458,6 +458,7 @@ test_section_main(void) test_string(&ctx, &parse_section_main, "shell", &conf.shell); test_string(&ctx, &parse_section_main, "term", &conf.term); test_string(&ctx, &parse_section_main, "app-id", &conf.app_id); + test_string(&ctx, &parse_section_main, "utempter", &conf.utempter_path); test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);