From 904de399f3ac5fcaa9ea7f5256b22a102b028a52 Mon Sep 17 00:00:00 2001 From: classabbyamp Date: Sun, 2 Jul 2023 22:04:40 -0400 Subject: [PATCH 1/2] pam-base: add turnstile pam --- srcpkgs/pam-base/files/system-login | 1 + srcpkgs/pam-base/template | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/srcpkgs/pam-base/files/system-login b/srcpkgs/pam-base/files/system-login index 2275deba480d..72c4638f07d3 100644 --- a/srcpkgs/pam-base/files/system-login +++ b/srcpkgs/pam-base/files/system-login @@ -14,6 +14,7 @@ session optional pam_loginuid.so session include system-auth session optional pam_motd.so motd=/etc/motd session optional pam_mail.so dir=/var/mail standard quiet +-session optional pam_turnstile.so -session optional pam_elogind.so -session optional pam_dumb_runtime_dir.so session required pam_env.so diff --git a/srcpkgs/pam-base/template b/srcpkgs/pam-base/template index 70dfab9fa996..38bfa74cd885 100644 --- a/srcpkgs/pam-base/template +++ b/srcpkgs/pam-base/template @@ -1,7 +1,7 @@ # Template file for 'pam-base' pkgname=pam-base version=0.4 -revision=2 +revision=3 short_desc="PAM base configuration files" maintainer="Érico Nogueira " license="Public Domain" From 572da3b6a2a95f83d13eedd477037620e6f7bc99 Mon Sep 17 00:00:00 2001 From: classabbyamp Date: Wed, 28 Jun 2023 05:10:55 -0400 Subject: [PATCH 2/2] New package: turnstile-0.1.5 --- srcpkgs/turnstile/files/README.voidlinux | 32 +++ srcpkgs/turnstile/files/dbus.run | 5 + srcpkgs/turnstile/files/turnstiled/run | 4 + srcpkgs/turnstile/patches/pipe.patch | 256 +++++++++++++++++++++++ srcpkgs/turnstile/patches/runit.patch | 184 ++++++++++++++++ srcpkgs/turnstile/template | 24 +++ 6 files changed, 505 insertions(+) create mode 100644 srcpkgs/turnstile/files/README.voidlinux create mode 100755 srcpkgs/turnstile/files/dbus.run create mode 100644 srcpkgs/turnstile/files/turnstiled/run create mode 100644 srcpkgs/turnstile/patches/pipe.patch create mode 100644 srcpkgs/turnstile/patches/runit.patch create mode 100644 srcpkgs/turnstile/template diff --git a/srcpkgs/turnstile/files/README.voidlinux b/srcpkgs/turnstile/files/README.voidlinux new file mode 100644 index 000000000000..46214113067e --- /dev/null +++ b/srcpkgs/turnstile/files/README.voidlinux @@ -0,0 +1,32 @@ +# User Services + +User services can be placed in ~/.config/service/. + +To ensure that a subset of services are started before login can proceed, +these services can be listed in ~/.config/service/turnstile-ready/conf, for +example: + + core_services="dbus foo" + +The turnstile-ready service is created by turnstile on first login. + +# D-Bus Session Bus + +If you want to manage dbus using a turnstile-managed runit user service: + + mkdir ~/.config/service/dbus + cp /usr/share/examples/turnstile/dbus.run ~/.config/service/dbus/run + +# Elogind Replacement + +Turnstile is not (nor ever will be, according to the developer) a complete +replacement for elogind, but it can replace several parts, including +XDG_RUNTIME_DIR management. + +If using turnstile with elogind: +- disable rundir management in /etc/turnstile/turnstiled.conf + (manage_rundir = no) + +If using turnstile without elogind: +- install and enable seatd for seat management +- install and enable acpid for lid switch/button handling diff --git a/srcpkgs/turnstile/files/dbus.run b/srcpkgs/turnstile/files/dbus.run new file mode 100755 index 000000000000..08d45d41be45 --- /dev/null +++ b/srcpkgs/turnstile/files/dbus.run @@ -0,0 +1,5 @@ +#!/bin/sh + +[ -r ./conf ] && . ./conf + +exec dbus-daemon --session --nofork --nopidfile --address="$DBUS_SESSION_BUS_ADDRESS" $OPTS diff --git a/srcpkgs/turnstile/files/turnstiled/run b/srcpkgs/turnstile/files/turnstiled/run new file mode 100644 index 000000000000..aa5d624fda19 --- /dev/null +++ b/srcpkgs/turnstile/files/turnstiled/run @@ -0,0 +1,4 @@ +#!/bin/sh + +exec 2>&1 +exec turnstiled diff --git a/srcpkgs/turnstile/patches/pipe.patch b/srcpkgs/turnstile/patches/pipe.patch new file mode 100644 index 000000000000..7ef856ad24bb --- /dev/null +++ b/srcpkgs/turnstile/patches/pipe.patch @@ -0,0 +1,256 @@ +From 795cb7359d542aa470e119e79ea7f74339d294ab Mon Sep 17 00:00:00 2001 +From: q66 +Date: Tue, 4 Jul 2023 01:15:26 +0200 +Subject: [PATCH] use named pipes for signaling readiness instead of fds + +This is more versatile as it allows any process to open the +readiness pipe, which is important for service managers that +cannot directly report their readiness with a file descriptor +(as those would then have the pipe fd opened forever which would +result in readiness not being reported). +--- + backend/dinit | 10 ++++----- + src/exec_utils.cc | 26 +++++++---------------- + src/turnstiled.cc | 53 ++++++++++++++++++++++++++++++----------------- + src/turnstiled.hh | 2 +- + 4 files changed, 47 insertions(+), 44 deletions(-) + +diff --git a/backend/dinit b/backend/dinit +index 792c2dc..8eeeeaf 100644 +--- a/backend/dinit ++++ b/backend/dinit +@@ -15,8 +15,8 @@ + # + # Arguments for "run": + # +-# ready_fd: file descriptor that should be poked with a string; this will +-# be passed to the "ready" script of the sequence as its sole ++# ready_p: path to named pipe (fifo) that should be poked with a string; this ++# will be passed to the "ready" script of the sequence as its sole + # argument (here this is a control socket path) + # srvdir: an internal directory that can be used by the service manager + # for any purpose (usually to keep track of its state) +@@ -47,11 +47,11 @@ case "$1" in + ;; + esac + +-DINIT_READY_FD="$2" ++DINIT_READY_PIPE="$2" + DINIT_DIR="$3" + DINIT_CONF="$4/dinit.conf" + +-if [ -z "$DINIT_READY_FD" -o -z "$DINIT_DIR" -o ! -d "$DINIT_DIR" ]; then ++if [ ! -p "$DINIT_READY_PIPE" -o ! -d "$DINIT_DIR" ]; then + echo "dinit: invalid input argument(s)" >&2 + exit 69 + fi +@@ -106,4 +106,4 @@ type = internal + waits-for.d = ${system_boot_dir} + EOF + +-exec dinit --user --ready-fd "$DINIT_READY_FD" --services-dir "$DINIT_DIR" "$@" ++exec dinit --user --ready-fd 3 --services-dir "$DINIT_DIR" "$@" 3>"$DINIT_READY_PIPE" +diff --git a/src/exec_utils.cc b/src/exec_utils.cc +index 06da738..c923d65 100644 +--- a/src/exec_utils.cc ++++ b/src/exec_utils.cc +@@ -150,7 +150,7 @@ static void sig_handler(int sign) { + write(sigpipe[1], &sign, sizeof(sign)); + } + +-static void fork_and_wait(pam_handle_t *pamh, int dpipe) { ++static void fork_and_wait(pam_handle_t *pamh) { + int pst, status; + struct pollfd pfd; + struct sigaction sa{}; +@@ -186,8 +186,6 @@ static void fork_and_wait(pam_handle_t *pamh, int dpipe) { + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); +- /* make sure we don't block this pipe */ +- close(dpipe); + /* our own little event loop */ + for (;;) { + auto pret = poll(&pfd, 1, -1); +@@ -240,13 +238,7 @@ fail: + } + + /* dummy "service manager" child process with none backend */ +-static void srv_dummy(int pipew) { +- /* we're always ready, the dummy process just sleeps forever */ +- if (write(pipew, "poke", 5) != 5) { +- perror("dummy: failed to poke the pipe"); +- return; +- } +- close(pipew); ++static void srv_dummy() { + /* block all signals except the ones we need to terminate */ + sigset_t mask; + sigfillset(&mask); +@@ -259,7 +251,7 @@ static void srv_dummy(int pipew) { + exit(0); + } + +-void srv_child(session &sess, char const *backend, int dpipe, bool dummy) { ++void srv_child(session &sess, char const *backend, bool dummy) { + pam_handle_t *pamh = nullptr; + bool is_root = (getuid() == 0); + /* create a new session */ +@@ -276,10 +268,10 @@ void srv_child(session &sess, char const *backend, int dpipe, bool dummy) { + /* handle the parent/child logic here + * if we're forking, only child makes it past this func + */ +- fork_and_wait(pamh, dpipe); ++ fork_and_wait(pamh); + /* dummy service manager if requested */ + if (dummy) { +- srv_dummy(dpipe); ++ srv_dummy(); + return; + } + /* drop privs */ +@@ -329,12 +321,8 @@ void srv_child(session &sess, char const *backend, int dpipe, bool dummy) { + add_str(LIBEXEC_PATH, "/", backend); + /* arg1: action */ + add_str("run"); +- /* arg1: ready_fd */ +- { +- char pipestr[32]; +- std::snprintf(pipestr, sizeof(pipestr), "%d", dpipe); +- add_str(pipestr); +- } ++ /* arg1: ready pipe */ ++ add_str(RUN_PATH, "/", SOCK_DIR, "/", sess.uids, "/ready"); + /* arg2: srvdir */ + add_str(RUN_PATH, "/", SOCK_DIR, "/", sess.uids, "/", tdirn); + /* arg3: confdir */ +diff --git a/src/turnstiled.cc b/src/turnstiled.cc +index d2ad545..3feb5ad 100644 +--- a/src/turnstiled.cc ++++ b/src/turnstiled.cc +@@ -116,7 +116,6 @@ static int sigpipe[2] = {-1, -1}; + + /* start the service manager instance for a session */ + static bool srv_start(session &sess) { +- int dpipe[2]; + /* mark as waiting */ + sess.srv_wait = true; + /* make rundir if needed, we don't want to create that and session dir +@@ -152,11 +151,25 @@ static bool srv_start(session &sess) { + sess.remove_sdir(); + return false; + } +- } +- /* here we'll receive the initial readiness string from the backend */ +- if (pipe2(dpipe, O_NONBLOCK) < 0) { +- print_err("srv: pipe2 failed (%s)", strerror(errno)); +- return false; ++ print_dbg("srv: create readiness pipe"); ++ unlinkat(sess.dirfd, "ready", 0); ++ if (mkfifoat(sess.dirfd, "ready", 0700) < 0) { ++ print_err("srv: failed to make ready pipe (%s)", strerror(errno)); ++ return false; ++ } ++ /* ensure it's owned by user too, and open in nonblocking mode */ ++ if (fchownat( ++ sess.dirfd, "ready", sess.uid, sess.gid, AT_SYMLINK_NOFOLLOW ++ ) || ((sess.userpipe = openat( ++ sess.dirfd, "ready", O_NONBLOCK | O_RDONLY ++ )) < 0)) { ++ print_err( ++ "srv: failed to set up ready pipe (%s)", strerror(errno) ++ ); ++ unlinkat(sess.dirfd, "ready", 0); ++ sess.remove_sdir(); ++ return false; ++ } + } + /* set up the timer, issue SIGLARM when it fires */ + print_dbg("srv: timer set"); +@@ -181,22 +194,25 @@ static bool srv_start(session &sess) { + sigaction(SIGTERM, &sa, nullptr); + sigaction(SIGINT, &sa, nullptr); + /* close some descriptors, these can be reused */ ++ close(sess.userpipe); + close(userv_dirfd); +- close(dpipe[0]); + close(sigpipe[0]); + close(sigpipe[1]); + /* and run the session */ +- srv_child(sess, cdata->backend.data(), dpipe[1], cdata->disable); ++ srv_child(sess, cdata->backend.data(), cdata->disable); + exit(1); + } else if (pid < 0) { + print_err("srv: fork failed (%s)", strerror(errno)); + return false; + } + /* close the write end on our side */ +- close(dpipe[1]); + sess.srv_pending = false; + sess.srv_pid = pid; +- sess.userpipe = dpipe[0]; ++ if (sess.userpipe < 0) { ++ /* disabled */ ++ return srv_boot(sess, nullptr); ++ } ++ /* otherwise queue the pipe */ + sess.pipe_queued = true; + return true; + } +@@ -728,6 +744,7 @@ static bool fd_handle_pipe(std::size_t i) { + /* this should never happen */ + return false; + } ++ bool done = false; + if (fds[i].revents & POLLIN) { + /* read the string from the pipe */ + for (;;) { +@@ -735,10 +752,15 @@ static bool fd_handle_pipe(std::size_t i) { + if (read(fds[i].fd, &c, 1) != 1) { + break; + } ++ if (c == '\0') { ++ /* done receiving */ ++ done = true; ++ break; ++ } + sess->srvstr.push_back(c); + } + } +- if (fds[i].revents & POLLHUP) { ++ if (done || (fds[i].revents & POLLHUP)) { + /* kill the pipe, we don't need it anymore */ + close(sess->userpipe); + sess->userpipe = -1; +@@ -747,15 +769,8 @@ static bool fd_handle_pipe(std::size_t i) { + fds[i].fd = -1; + fds[i].revents = 0; + --npipes; +- /* but error early if needed */ +- if (sess->srvstr.empty()) { +- print_err("read failed (%s)", strerror(errno)); +- return true; +- } + /* wait for the boot service to come up */ +- if (!srv_boot( +- *sess, cdata->disable ? nullptr : cdata->backend.data() +- )) { ++ if (!srv_boot(*sess, cdata->backend.data())) { + /* this is an unrecoverable condition */ + return false; + } +diff --git a/src/turnstiled.hh b/src/turnstiled.hh +index f2739c4..58e4edb 100644 +--- a/src/turnstiled.hh ++++ b/src/turnstiled.hh +@@ -86,7 +86,7 @@ bool cfg_expand_rundir( + ); + + /* service manager utilities */ +-void srv_child(session &sess, char const *backend, int p, bool d); ++void srv_child(session &sess, char const *backend, bool d); + bool srv_boot(session &sess, char const *backend); + + struct cfg_data { +-- +2.41.0 diff --git a/srcpkgs/turnstile/patches/runit.patch b/srcpkgs/turnstile/patches/runit.patch new file mode 100644 index 000000000000..8c120f382206 --- /dev/null +++ b/srcpkgs/turnstile/patches/runit.patch @@ -0,0 +1,184 @@ +From 22933f404cee49974f51c0873c897d0b565c5620 Mon Sep 17 00:00:00 2001 +From: classabbyamp +Date: Wed, 28 Jun 2023 05:05:25 -0400 +Subject: [PATCH] add runit backend + +--- + backend/meson.build | 16 +++++++++ + backend/runit | 80 +++++++++++++++++++++++++++++++++++++++++++++ + backend/runit.conf | 13 ++++++++ + meson.build | 3 ++ + meson_options.txt | 5 +++ + 5 files changed, 117 insertions(+) + create mode 100644 backend/runit + create mode 100644 backend/runit.conf + +diff --git a/backend/meson.build b/backend/meson.build +index 681e6a0..e4c63f1 100644 +--- a/backend/meson.build ++++ b/backend/meson.build +@@ -13,3 +13,19 @@ if have_dinit + install_mode: 'rw-r--r--' + ) + endif ++ ++# runit backend ++ ++if have_runit ++ install_data( ++ 'runit', ++ install_dir: join_paths(get_option('libexecdir'), 'turnstile'), ++ install_mode: 'rwxr-xr-x' ++ ) ++ ++ install_data( ++ 'runit.conf', ++ install_dir: join_paths(get_option('sysconfdir'), 'turnstile/backend'), ++ install_mode: 'rw-r--r--' ++ ) ++endif +diff --git a/backend/runit b/backend/runit +new file mode 100644 +index 0000000..e8370d6 +--- /dev/null ++++ b/backend/runit +@@ -0,0 +1,80 @@ ++#!/bin/sh ++# ++# This is the turnstile runit backend. It accepts the action as its first ++# argument, which is either "ready" or "run". In case of "run", it's invoked ++# directly through /bin/sh as if it was a login shell, and therefore it has ++# acccess to shell profile, and the shebang is functionally useless but ++# should be preserved as a convention. For "ready", it's a regular shell. ++# ++# Arguments for "ready": ++# ++# ready_sv: path to the readiness service ++# ++# Arguments for "run": ++# ++# ready_p: readiness pipe (fifo). has the path to the ready service written to it. ++# srvdir: unused ++# confdir: the path where turnstile's configuration data resides, used ++# to source the configuration file ++# ++# Copyright 2023 classabbyamp ++# License: BSD-2-Clause ++ ++case "$1" in ++ run) ;; ++ ready) ++ set -x ++ echo "runit: ready_sv: $2" >&2 ++ if [ -z "$2" ] || [ ! -d "$2" ]; then ++ echo "runit: invalid readiness service '$2'" >&2 ++ exit 69 ++ fi ++ sv start "$2" >&2 ++ set +x ++ exit 0 ++ ;; ++ *) ++ exit 32 ++ ;; ++esac ++ ++RUNIT_READY_PIPE="$2" ++RUNIT_CONF="$4/runit.conf" ++ ++if [ ! -p "$RUNIT_READY_PIPE" ]; then ++ echo "runit: invalid input argument(s)" >&2 ++ exit 69 ++fi ++ ++if [ -z "$HOME" ] || [ ! -d "$HOME" ]; then ++ echo "runit: invalid home directory" >&2 ++ exit 70 ++fi ++ ++shift $# ++ ++# be strict ++set -e ++ ++# source the conf ++[ -r "$RUNIT_CONF" ] && . "$RUNIT_CONF" ++ ++# set some defaults in case the conf cannot be read or is mangled ++: "${ready_sv:="turnstile-ready"}" ++: "${services_dir:="${HOME}/.config/service"}" ++ ++mkdir -p "${services_dir}/${ready_sv}" > /dev/null 2>&1 ++ ++# this must succeed ++cat << EOF > "${services_dir}/${ready_sv}/run" ++#!/bin/sh ++[ -r ./conf ] && . ./conf ++[ -n "\$core_services" ] && SVDIR=".." sv start \$core_services ++[ -p "$RUNIT_READY_PIPE" ] && printf "${services_dir}/${ready_sv}" > "$RUNIT_READY_PIPE" ++exec pause ++EOF ++chmod +x "${services_dir}/${ready_sv}/run" ++ ++exec env DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" \ ++ runsvdir -P "$services_dir" \ ++ 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................' +diff --git a/backend/runit.conf b/backend/runit.conf +new file mode 100644 +index 0000000..7b258c9 +--- /dev/null ++++ b/backend/runit.conf +@@ -0,0 +1,13 @@ ++# This is the configuration file for turnstile's runit backend. ++# ++# It follows the POSIX shell syntax (being sourced into a script). ++# The complete launch environment available to dinit can be used. ++# ++# It is a low-level configuration file. In most cases, it should ++# not be modified by the user. ++ ++# the name of the service that turnstile will check for login readiness ++ready_sv="turnstile-ready" ++ ++# the directory user service files are read from. ++services_dir="${HOME}/.config/service" +diff --git a/meson.build b/meson.build +index 823ff2a..de82e21 100644 +--- a/meson.build ++++ b/meson.build +@@ -23,6 +23,7 @@ scdoc_dep = dependency( + ) + + have_dinit = get_option('dinit').enabled() ++have_runit = get_option('runit').enabled() + + conf_data = configuration_data() + conf_data.set_quoted('RUN_PATH', get_option('rundir')) +@@ -103,6 +104,8 @@ install_data( + # decide the default backend + if have_dinit + default_backend = 'dinit' ++elif have_runit ++ default_backend = 'runit' + else + default_backend = 'none' + endif +diff --git a/meson_options.txt b/meson_options.txt +index 9b03995..85ea1ce 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -3,6 +3,11 @@ option('dinit', + description: 'Whether to install Dinit-related backend and data' + ) + ++option('runit', ++ type: 'feature', value: 'disabled', ++ description: 'Whether to install runit-related backend and data' ++) ++ + option('rundir', + type: 'string', value: '/run', + description: 'Where the base directory will be located' +-- +2.41.0 + diff --git a/srcpkgs/turnstile/template b/srcpkgs/turnstile/template new file mode 100644 index 000000000000..470b56b7c206 --- /dev/null +++ b/srcpkgs/turnstile/template @@ -0,0 +1,24 @@ +# Template file for 'turnstile' +pkgname=turnstile +version=0.1.5+1 +revision=1 +build_style=meson +configure_args="-Ddinit=disabled -Drunit=enabled -Dmanage_rundir=true" +hostmakedepends="pkg-config scdoc" +makedepends="pam-devel" +short_desc="Independent session/login tracker and user service manager" +maintainer="classabbyamp " +license="BSD-2-Clause" +homepage="https://github.com/chimera-linux/turnstile" +# distfiles="https://github.com/chimera-linux/turnstile/archive/refs/tags/v${version}.tar.gz" +_commit="b5f3b18419efa088697310fb67584ccc6b5f8142" +distfiles="https://github.com/chimera-linux/turnstile/archive/${_commit}.tar.gz" +checksum=1a9d9a1391015a11f31c1b1865fb452c4f05a1d476f22f93547b6c46b846bc1f +conf_files="/etc/turnstile/turnstiled.conf" + +post_install() { + vsv turnstiled + vsconf "${FILESDIR}/dbus.run" + vdoc "${FILESDIR}/README.voidlinux" + vlicense COPYING.md +}