From e78319fccd2b8c057deff4b1937b325be1d35140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 17 May 2023 20:51:40 +0200 Subject: [PATCH] utmp: rewrite utmp logging This patch generalizes the utmp support, to not only support libutempter, but also ulog (and in the future, even more interfaces). * Rename config option main.utempter to main.utmp-helper * Add meson option -Dutmp-backend=none|libutempter|ulog|auto * Rename meson option -Ddefault-utempter-path to -Dutmp-default-helper-path * utmp is no longer detected at compile time, but at runtime instead. Meson will configure the following pre-processor macros, based on the selected utmp backend: * UTMP_ADD - argument to pass to utmp helper when adding a record (starting foot) * UTMP_DEL - argument to pass to utmp helper when removing a record (exiting foot) * UTMP_DEL_HAVE_ARGUMENT - if defined, UTMP_DEL expects an extra argument ($WAYLAND_DISPLAY) * UTMP_DEFAULT_HELPER_PATH - path to the default utmp helper binary The documentation has been updated to mention which arguments are passed to the helper binary. Closes #1314 --- CHANGELOG.md | 11 ++++++++ INSTALL.md | 26 ++++++++++--------- config.c | 41 +++++++++++++++++++++-------- config.h | 2 +- doc/foot.ini.5.scd | 21 ++++++++++++--- doc/meson.build | 18 ++++++++++--- foot.ini | 3 ++- meson.build | 63 +++++++++++++++++++++++++++++++-------------- meson_options.txt | 6 +++-- terminal.c | 24 ++++++++++++++--- tests/test-config.c | 2 +- 11 files changed, 159 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9912729..d3b4df58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ ### Added * VT: implemented `XTQMODKEYS` query (`CSI ? Pp m`). +* Meson option `utmp-backend=none|libutempter|ulog|auto`. The default + is `auto`, which will select `libutempter` on Linux, `ulog` on + FreeBSD, and `none` for all others. ### Changed @@ -54,9 +57,17 @@ `CSI R`. The kitty keyboard protocol originally allowed F3 to be encoded as `CSI R`, but this was removed from the specification since `CSI R` conflicts with the _”Cursor Position Report”_. +* `[main].utempter` renamed to `[main].utmp-helper`. The old option + name is still recognized, but will log a deprecation warning. +* Meson option `default-utempter-path` renamed to + `utmp-default-helper-path`. ### Deprecated + +* `[main].utempter` option. + + ### Removed ### Fixed diff --git a/INSTALL.md b/INSTALL.md index 6cc51750..9e2da8ec 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -45,7 +45,8 @@ subprojects. * wayland (_client_ and _cursor_ libraries) * xkbcommon * utf8proc (_optional_, needed for grapheme clustering) -* libutempter (_optional_, needed for utmp logging) +* libutempter (_optional_, needed for utmp logging on Linux) +* ulog (_optional_, needed for utmp logging on FreeBSD) * [fcft](https://codeberg.org/dnkl/fcft) [^1] [^1]: can also be built as subprojects, in which case they are @@ -142,17 +143,18 @@ 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 | -| `-Ddefault-utempter-path` | feature | `auto` | Default path to utempter binary (‘none’ disables default) | libutempter | +| 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 | +| `-Dutmp-backend` | combo | `auto` | Which utmp backend to use (`none`, `libutempter`, `ulog` or `auto`) | libutempter or ulog | +| `-Dutmp-default-helper-path` | string | `auto` | Default path to utmp helper binary. `auto` selects path based on `utmp-backend` | None | Documentation includes the man pages, readme, changelog and license files. diff --git a/config.c b/config.c index 2ede1aa5..c3ab61e2 100644 --- a/config.c +++ b/config.c @@ -1009,13 +1009,29 @@ 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)) + else if (strcmp(key, "utmp-helper") == 0 || strcmp(key, "utempter") == 0) { + if (strcmp(key, "utempter") == 0) { + struct user_notification deprecation = { + .kind = USER_NOTIFICATION_DEPRECATED, + .text = xasprintf( + "%s:%d: \033[1m[main].utempter\033[22m, " + "use \033[1m[main].utmp-helper\033[22m instead", + ctx->path, ctx->lineno), + }; + tll_push_back(conf->notifications, deprecation); + + LOG_WARN( + "%s:%d: [main].utempter is deprecated, " + "use [main].utmp-helper instead", + ctx->path, ctx->lineno); + } + + if (!value_to_str(ctx, &conf->utmp_helper_path)) return false; - if (strcmp(conf->utempter_path, "none") == 0) { - free(conf->utempter_path); - conf->utempter_path = NULL; + if (strcmp(conf->utmp_helper_path, "none") == 0) { + free(conf->utmp_helper_path); + conf->utmp_helper_path = NULL; } return true; @@ -3019,9 +3035,12 @@ 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), +#if defined(UTMP_DEFAULT_HELPER_PATH) + .utmp_helper_path = ((strlen(UTMP_DEFAULT_HELPER_PATH) > 0 && + access(UTMP_DEFAULT_HELPER_PATH, X_OK) == 0) + ? xstrdup(UTMP_DEFAULT_HELPER_PATH) + : NULL), +#endif .notifications = tll_init(), }; @@ -3310,8 +3329,8 @@ 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->utmp_helper_path = + old->utmp_helper_path != NULL ? xstrdup(old->utmp_helper_path) : NULL; conf->notifications.length = 0; conf->notifications.head = conf->notifications.tail = 0; @@ -3379,7 +3398,7 @@ config_free(struct config *conf) tll_remove(conf->env_vars, it); } - free(conf->utempter_path); + free(conf->utmp_helper_path); user_notifications_free(&conf->notifications); } diff --git a/config.h b/config.h index 31dddc64..34517019 100644 --- a/config.h +++ b/config.h @@ -320,7 +320,7 @@ struct config { env_var_list_t env_vars; - char *utempter_path; + char *utmp_helper_path; struct { enum fcft_scaling_filter fcft_filter; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 5ef62045..6a820892 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -343,9 +343,24 @@ empty string to be set, but it must be quoted: *KEY=""*) (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@_. +*utmp-helper* + Path to utmp logging helper binary. + + When starting foot, an utmp record is created by launching the + helper binary with the following arguments: + + ``` + @utmp_add_args@ + ``` + + When foot is closed, the utmp record is removed by launching the + helper binary with the following arguments: + + ``` + @utmp_del_args@ + ``` + + Set to *none* to disable utmp records. Default: _@utmp_helper_path@_. # SECTION: environment diff --git a/doc/meson.build b/doc/meson.build index 86e75952..17f09f39 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -2,16 +2,26 @@ sh = find_program('sh', native: true) scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) -if utempter_path == '' - default_utempter_value = 'not set' +if utmp_backend != 'none' + utmp_add_args = '@0@ $WAYLAND_DISPLAY'.format(utmp_add) + utmp_del_args = (utmp_del_have_argument + ? '@0@ $WAYLAND_DISPLAY'.format(utmp_del) + : '@0@'.format(utmp_del)) + utmp_path = utmp_default_helper_path else - default_utempter_value = utempter_path + utmp_add_args = '' + utmp_del_args = '' + utmp_path = 'none' endif + conf_data = configuration_data( { 'default_terminfo': get_option('default-terminfo'), - 'utempter': default_utempter_value, + 'utmp_backend': utmp_backend, + 'utmp_add_args': utmp_add_args, + 'utmp_del_args': utmp_del_args, + 'utmp_helper_path': utmp_path, } ) diff --git a/foot.ini b/foot.ini index 8266b01b..4b2218a4 100644 --- a/foot.ini +++ b/foot.ini @@ -34,7 +34,8 @@ # word-delimiters=,│`|:"'()[]{}<> # selection-target=primary # workers= -# utempter=/usr/lib/utempter/utempter +# utmp-helper=/usr/lib/utempter/utempter # When utmp backend is ‘libutempter’ (Linux) +# utmp-helper=/usr/libexec/ulog-helper # When utmp backend is ‘ulog’ (FreeBSD) [environment] # name=value diff --git a/meson.build b/meson.build index a6892cc4..c5ef1928 100644 --- a/meson.build +++ b/meson.build @@ -16,30 +16,54 @@ 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() +utmp_backend = get_option('utmp-backend') +if utmp_backend == 'auto' + host_os = host_machine.system() + if host_os == 'linux' + utmp_backend = 'libutempter' + elif host_os == 'freebsd' + utmp_backend = 'ulog' else - utempter_path = '' + utmp_backend = 'none' endif -elif utempter_path == 'none' - utempter_path = '' +endif + +utmp_default_helper_path = get_option('utmp-default-helper-path') + +if utmp_backend == 'none' + utmp_add = '' + utmp_del = '' + utmp_del_have_argument = false + utmp_default_helper_path = '' +elif utmp_backend == 'libutempter' + utmp_add = 'add' + utmp_del = 'del' + utmp_del_have_argument = true + if utmp_default_helper_path == 'auto' + utmp_default_helper_path = join_paths('/usr', get_option('libdir'), 'utempter', 'utempter') + endif +elif utmp_backend == 'ulog' + utmp_add = 'login' + utmp_del = 'logout' + utmp_del_have_argument = false + if utmp_default_helper_path == 'auto' + utmp_default_helper_path = join_paths('/usr', get_option('libexecdir'), 'ulog-helper') + endif +else + error('invalid utmp backend') endif add_project_arguments( ['-D_GNU_SOURCE=200809L', - '-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo')), - '-DFOOT_DEFAULT_UTEMPTER_PATH="@0@"'.format(utempter_path)] + + '-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo'))] + + (utmp_backend != 'none' + ? ['-DUTMP_ADD="@0@"'.format(utmp_add), + '-DUTMP_DEL="@0@"'.format(utmp_del), + '-DUTMP_DEFAULT_HELPER_PATH="@0@"'.format(utmp_default_helper_path)] + : []) + + (utmp_del_have_argument + ? ['-DUTMP_DEL_HAVE_ARGUMENT=1'] + : []) + (is_debug_build ? ['-D_DEBUG'] : [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) + @@ -343,7 +367,8 @@ summary( 'Themes': get_option('themes'), 'IME': get_option('ime'), 'Grapheme clustering': utf8proc.found(), - 'Utempter path': utempter_path, + 'utmp backend': utmp_backend, + 'utmp helper default path': utmp_default_helper_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 c38a8ca8..76121e60 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -22,5 +22,7 @@ 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') +option('utmp-backend', type: 'combo', value: 'auto', choices: ['none', 'libutempter', 'ulog', 'auto'], + description: 'Which utmp logging backend to use. This affects how (with what arguments) the utmp helper binary (see \utmp-default-helper-path\')is called. Default: auto (linux=libutempter, freebsd=ulog, others=none)') +option('utmp-default-helper-path', type: 'string', value: 'auto', + description: 'Default path to the utmp helper binary. Default: auto-detect') diff --git a/terminal.c b/terminal.c index ddc24cb3..dd4c627c 100644 --- a/terminal.c +++ b/terminal.c @@ -207,25 +207,41 @@ fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data) static bool add_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx) { +#if defined(UTMP_ADD) if (ptmx < 0) return true; - if (conf->utempter_path == NULL) + if (conf->utmp_helper_path == NULL) return true; - char *const argv[] = {conf->utempter_path, "add", getenv("WAYLAND_DISPLAY"), NULL}; + char *const argv[] = {conf->utmp_helper_path, UTMP_ADD, getenv("WAYLAND_DISPLAY"), NULL}; return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL); +#else + return true; +#endif } static bool del_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx) { +#if defined(UTMP_DEL) if (ptmx < 0) return true; - if (conf->utempter_path == NULL) + if (conf->utmp_helper_path == NULL) return true; - char *const argv[] = {conf->utempter_path, "del", getenv("WAYLAND_DISPLAY"), NULL}; + char *del_argument = +#if defined(UTMP_DEL_HAVE_ARGUMENT) + getenv("WAYLAND_DISPLAY") +#else + NULL +#endif + ; + + char *const argv[] = {conf->utmp_helper_path, UTMP_DEL, del_argument, NULL}; return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL); +#else + return true; +#endif } #if PTMX_TIMING diff --git a/tests/test-config.c b/tests/test-config.c index 4736a46b..4b7de298 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -458,7 +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_string(&ctx, &parse_section_main, "utempter", &conf.utmp_helper_path); test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);