From: classabbyamp <classabbyamp@users.noreply.github.com>
To: ml@inbox.vuxu.org
Subject: Re: [PR PATCH] [Updated] New package: turnstile-0.1.8
Date: Fri, 22 Mar 2024 21:31:25 +0100 [thread overview]
Message-ID: <20240322203126.04E0C23F74@inbox.vuxu.org> (raw)
In-Reply-To: <gh-mailinglist-notifications-41a7ca26-5023-4802-975b-f1789d68868e-void-packages-44676@inbox.vuxu.org>
[-- Attachment #1: Type: text/plain, Size: 741 bytes --]
There is an updated pull request by classabbyamp against master on the void-packages repository
https://github.com/classabbyamp/void-packages new/turnstile
https://github.com/void-linux/void-packages/pull/44676
New package: turnstile-0.1.8
WIP while i create the runit backend upstream ([mostly done](https://github.com/chimera-linux/turnstile/compare/master...classabbyamp:runit-backend))
#### Testing the changes
- I tested the changes in this PR: **YES**
#### New package
- This new package conforms to the [package requirements](https://github.com/void-linux/void-packages/blob/master/CONTRIBUTING.md#package-requirements): **YES**
A patch file from https://github.com/void-linux/void-packages/pull/44676.patch is attached
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: github-pr-new/turnstile-44676.patch --]
[-- Type: text/x-diff, Size: 38766 bytes --]
From f221363fbeaa3b8311eb5d59e60e34c95c2f455a Mon Sep 17 00:00:00 2001
From: classabbyamp <void@placeviolette.net>
Date: Tue, 11 Jul 2023 22:02:49 -0400
Subject: [PATCH 1/5] polkit: patch to support turnstile
---
srcpkgs/polkit/patches/turnstile.patch | 57 ++++++++++++++++++++++++++
srcpkgs/polkit/template | 2 +-
2 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 srcpkgs/polkit/patches/turnstile.patch
diff --git a/srcpkgs/polkit/patches/turnstile.patch b/srcpkgs/polkit/patches/turnstile.patch
new file mode 100644
index 00000000000000..4f56ed130eefd4
--- /dev/null
+++ b/srcpkgs/polkit/patches/turnstile.patch
@@ -0,0 +1,57 @@
+commit 8d98aa421b92765695af13c033cf7e80375c03fe
+Author: q66 <q66@chimera-linux.org>
+Date: Sun Jul 2 15:44:51 2023 +0200
+
+ ensure turnstile-session processes fall back to display check
+
+ As turnstile session is shared between sessions, let us fall back
+ to the check for whether a graphical session is in place. We need
+ this as our dbus session bus is managed through turnstile. In
+ systemd-using systems there is no problem because user units are
+ not a part of any explicit session (we don't get this luxury
+ because using a session is the only way to make sure our pid is
+ tracked in systemd at all; we need that to be able to resolve
+ PID to UID).
+
+diff --git a/src/polkitbackend/polkitbackendsessionmonitor-systemd.c b/src/polkitbackend/polkitbackendsessionmonitor-systemd.c
+index b00cdbd..9a3f3c3 100644
+--- a/src/polkitbackend/polkitbackendsessionmonitor-systemd.c
++++ b/src/polkitbackend/polkitbackendsessionmonitor-systemd.c
+@@ -346,7 +346,7 @@ polkit_backend_session_monitor_get_session_for_subject (PolkitBackendSessionMoni
+ PolkitUnixProcess *tmp_process = NULL;
+ PolkitUnixProcess *process = NULL;
+ PolkitSubject *session = NULL;
+- char *session_id = NULL;
++ char *session_id = NULL, *service_id = NULL;
+ pid_t pid;
+ #if HAVE_SD_UID_GET_DISPLAY
+ uid_t uid;
+@@ -377,8 +377,26 @@ polkit_backend_session_monitor_get_session_for_subject (PolkitBackendSessionMoni
+
+ if (sd_pid_get_session (pid, &session_id) >= 0)
+ {
+- session = polkit_unix_session_new (session_id);
+- goto out;
++ /* if belonging to turnstile, ignore */
++ if (sd_session_get_service (session_id, &service_id) >= 0)
++ {
++ if (strcmp (service_id, "turnstiled"))
++ {
++ free (service_id);
++ session = polkit_unix_session_new (session_id);
++ goto out;
++ }
++ else
++ {
++ /* turnstile-managed processes are shared */
++ free(service_id);
++ }
++ }
++ else
++ {
++ session = polkit_unix_session_new (session_id);
++ goto out;
++ }
+ }
+
+ #if HAVE_SD_UID_GET_DISPLAY
diff --git a/srcpkgs/polkit/template b/srcpkgs/polkit/template
index 84ab7453bb9b66..0435e1ce5976b8 100644
--- a/srcpkgs/polkit/template
+++ b/srcpkgs/polkit/template
@@ -1,7 +1,7 @@
# Template file for 'polkit'
pkgname=polkit
version=121
-revision=1
+revision=2
build_style=meson
build_helper=gir
configure_args="$(vopt_bool gir introspection) -Dman=true
From ebec18d806f62133a3a8985b36f1cbf6aeb98e0d Mon Sep 17 00:00:00 2001
From: classabbyamp <void@placeviolette.net>
Date: Sun, 2 Jul 2023 22:04:40 -0400
Subject: [PATCH 2/5] 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 2275deba480d3e..72c4638f07d3fe 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 70dfab9fa9965a..38bfa74cd88560 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 <ericonr@disroot.org>"
license="Public Domain"
From 3e2470cc0394e6d9f53b7d349a561d8e0805ddcc Mon Sep 17 00:00:00 2001
From: classabbyamp <void@placeviolette.net>
Date: Tue, 12 Sep 2023 18:54:27 -0400
Subject: [PATCH 3/5] gdm: patch pam files for turnstile
---
srcpkgs/gdm/patches/pam-turnstile-elogind.patch | 17 +++++++++++++++++
srcpkgs/gdm/template | 2 +-
2 files changed, 18 insertions(+), 1 deletion(-)
create mode 100644 srcpkgs/gdm/patches/pam-turnstile-elogind.patch
diff --git a/srcpkgs/gdm/patches/pam-turnstile-elogind.patch b/srcpkgs/gdm/patches/pam-turnstile-elogind.patch
new file mode 100644
index 00000000000000..21df9c826eacb2
--- /dev/null
+++ b/srcpkgs/gdm/patches/pam-turnstile-elogind.patch
@@ -0,0 +1,17 @@
+1. pam_systemd doesn't exist on void, use pam_elogind instead
+ (this may have been the cause of some issues with elogind
+ not starting for GNOME users?)
+2. pam_turnstile allows turnstile to integrate with gdm
+
+See also: https://github.com/chimera-linux/cports/blob/master/main/gdm/patches/pam.patch
+
+--- a/data/pam-arch/gdm-launch-environment.pam
++++ b/data/pam-arch/gdm-launch-environment.pam
+@@ -13,5 +13,6 @@
+ session optional pam_keyinit.so force revoke
+ session required pam_succeed_if.so audit quiet_success user in gdm:gnome-initial-setup
+ session optional pam_permit.so
+--session optional pam_systemd.so
++-session optional pam_turnstile.so
++-session optional pam_elogind.so
+ session required pam_env.so
diff --git a/srcpkgs/gdm/template b/srcpkgs/gdm/template
index c171f236442515..128253f83f885c 100644
--- a/srcpkgs/gdm/template
+++ b/srcpkgs/gdm/template
@@ -1,7 +1,7 @@
# Template file for 'gdm'
pkgname=gdm
version=45.0.1
-revision=1
+revision=2
build_helper="gir"
build_style=meson
configure_args="
From 17b75e1d4a784a9499b73c9b641349df9c4c4cc0 Mon Sep 17 00:00:00 2001
From: classabbyamp <void@placeviolette.net>
Date: Tue, 12 Sep 2023 18:54:33 -0400
Subject: [PATCH 4/5] lightdm: patch pam files for turnstile
---
.../patches/pam-turnstile-elogind.patch | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 srcpkgs/lightdm/patches/pam-turnstile-elogind.patch
diff --git a/srcpkgs/lightdm/patches/pam-turnstile-elogind.patch b/srcpkgs/lightdm/patches/pam-turnstile-elogind.patch
new file mode 100644
index 00000000000000..529531e294f942
--- /dev/null
+++ b/srcpkgs/lightdm/patches/pam-turnstile-elogind.patch
@@ -0,0 +1,32 @@
+1. pam_systemd doesn't exist on void, use pam_elogind instead
+2. pam_turnstile allows turnstile to integrate with lightdm
+
+See also: https://github.com/chimera-linux/cports/blob/master/main/gdm/patches/pam.patch
+
+--- a/data/pam/lightdm
++++ b/data/pam/lightdm
+@@ -17,4 +17,5 @@
+
+ # Setup session
+ session required pam_unix.so
+-session optional pam_systemd.so
++- session optional pam_turnstile.so
++- session optional pam_elogind.so
+--- a/data/pam/lightdm-autologin
++++ b/data/pam/lightdm-autologin
+@@ -19,4 +19,5 @@
+
+ # Setup session
+ session required pam_unix.so
+-session optional pam_systemd.so
++- session optional pam_turnstile.so
++- session optional pam_elogind.so
+--- a/data/pam/lightdm-greeter
++++ b/data/pam/lightdm-greeter
+@@ -14,4 +14,5 @@
+
+ # Setup session
+ session required pam_unix.so
+-session optional pam_systemd.so
++- session optional pam_turnstile.so
++- session optional pam_elogind.so
From d7bee88e71639ac731ac116416c5b93ed5d3187a Mon Sep 17 00:00:00 2001
From: classabbyamp <void@placeviolette.net>
Date: Wed, 28 Jun 2023 05:10:55 -0400
Subject: [PATCH 5/5] New package: turnstile-0.1.8
---
srcpkgs/turnstile/files/README.voidlinux | 50 ++++
srcpkgs/turnstile/files/dbus.run | 11 +
srcpkgs/turnstile/files/turnstiled/run | 4 +
srcpkgs/turnstile/patches/defer-rundir.patch | 121 ++++++++
srcpkgs/turnstile/patches/dummy.patch | 300 +++++++++++++++++++
srcpkgs/turnstile/patches/fix-chdir.patch | 25 ++
srcpkgs/turnstile/patches/runit.patch | 256 ++++++++++++++++
srcpkgs/turnstile/template | 24 ++
8 files changed, 791 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/defer-rundir.patch
create mode 100644 srcpkgs/turnstile/patches/dummy.patch
create mode 100644 srcpkgs/turnstile/patches/fix-chdir.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 00000000000000..32025131de7d62
--- /dev/null
+++ b/srcpkgs/turnstile/files/README.voidlinux
@@ -0,0 +1,50 @@
+# 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.
+
+## Shared Environment for User Services
+
+To give user services access to important environment variables, chpst's
+envdir functionality can be used. See chpst(8) (-e flag) for more info.
+
+To make a service aware of these variables:
+
+ - exec foo
+ + exec chpst -e "$TURNSTILE_ENV_DIR" foo
+
+Inside user services, the convenience variable "$TURNSTILE_ENV_DIR" can be used
+to refer to this directory.
+
+The helper script 'turnstile-update-runit-env' can be used to easily update
+variables in this shared envdir:
+
+ turnstile-update-runit-env DISPLAY XAUTHORITY FOO=bar BAZ=
+
+# D-Bus Session Bus
+
+If you want to manage dbus using a turnstile-managed runit user service:
+
+ mkdir ~/.config/service/dbus
+ ln -s /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 00000000000000..dc7473e45fc52f
--- /dev/null
+++ b/srcpkgs/turnstile/files/dbus.run
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+[ -r ./conf ] && . ./conf
+
+: "${DBUS_SESSION_BUS_ADDRESS:=unix:path=/run/user/$(id -u)/bus}"
+
+if [ -d "$TURNSTILE_ENV_DIR" ]; then
+ echo "$DBUS_SESSION_BUS_ADDRESS" > "$TURNSTILE_ENV_DIR"/DBUS_SESSION_BUS_ADDRESS
+fi
+
+exec chpst -e "$TURNSTILE_ENV_DIR" 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 00000000000000..aa5d624fda19a1
--- /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/defer-rundir.patch b/srcpkgs/turnstile/patches/defer-rundir.patch
new file mode 100644
index 00000000000000..33b94a1489b3dc
--- /dev/null
+++ b/srcpkgs/turnstile/patches/defer-rundir.patch
@@ -0,0 +1,121 @@
+From cae619b4f23c7b1bc44ba8cef59d004a1911a01d Mon Sep 17 00:00:00 2001
+From: q66 <q66@chimera-linux.org>
+Date: Fri, 1 Sep 2023 22:57:46 +0200
+Subject: [PATCH] defer creation of rundir for after pam session is established
+
+---
+ src/exec_utils.cc | 19 ++++++++++++++++++-
+ src/turnstiled.cc | 16 +++++-----------
+ src/turnstiled.hh | 2 +-
+ 3 files changed, 24 insertions(+), 13 deletions(-)
+
+diff --git a/src/exec_utils.cc b/src/exec_utils.cc
+index 49918be..96440ec 100644
+--- a/src/exec_utils.cc
++++ b/src/exec_utils.cc
+@@ -106,6 +106,8 @@ static pam_handle_t *dpam_begin(char const *user, unsigned int gid) {
+ static void sanitize_limits() {
+ struct rlimit l{0, 0};
+
++ print_dbg("srv: sanitize rlimits");
++
+ setrlimit(RLIMIT_NICE, &l);
+ setrlimit(RLIMIT_RTPRIO, &l);
+
+@@ -129,6 +131,8 @@ static bool dpam_open(pam_handle_t *pamh) {
+ /* before opening session, do not rely on just PAM and sanitize a bit */
+ sanitize_limits();
+
++ print_dbg("srv: open pam session");
++
+ auto pst = pam_open_session(pamh, 0);
+ if (pst != PAM_SUCCESS) {
+ fprintf(stderr, "srv: pam_open_session: %s", pam_strerror(pamh, pst));
+@@ -136,6 +140,7 @@ static bool dpam_open(pam_handle_t *pamh) {
+ pam_end(pamh, pst);
+ return false;
+ }
++
+ return true;
+ }
+
+@@ -288,7 +293,7 @@ static void srv_dummy() {
+ exit(0);
+ }
+
+-void srv_child(login &lgn, char const *backend) {
++void srv_child(login &lgn, char const *backend, bool make_rundir) {
+ pam_handle_t *pamh = nullptr;
+ bool is_root = (getuid() == 0);
+ /* create a new session */
+@@ -297,11 +302,23 @@ void srv_child(login &lgn, char const *backend) {
+ }
+ /* begin pam session setup */
+ if (is_root) {
++ print_dbg("srv: establish pam");
+ pamh = dpam_begin(lgn.username.data(), lgn.gid);
+ if (!dpam_open(pamh)) {
+ return;
+ }
+ }
++ /* make rundir if needed, we want to make it as late as possible, ideally
++ * after the PAM session setup is already finalized (so that nothing gets
++ * the idea to nuke it), but before we fork and drop privileges
++ */
++ if (make_rundir) {
++ print_dbg("srv: setup rundir for %u", lgn.uid);
++ if (!rundir_make(lgn.rundir.data(), lgn.uid, lgn.gid)) {
++ return;
++ }
++ }
++ print_dbg("srv: forking for service manager exec");
+ /* handle the parent/child logic here
+ * if we're forking, only child makes it past this func
+ */
+diff --git a/src/turnstiled.cc b/src/turnstiled.cc
+index 71f8372..a886739 100644
+--- a/src/turnstiled.cc
++++ b/src/turnstiled.cc
+@@ -124,16 +124,6 @@ static bool srv_start(login &lgn) {
+ std::snprintf(uidbuf, sizeof(uidbuf), "%u", lgn.uid);
+ /* mark as waiting */
+ lgn.srv_wait = true;
+- /* make rundir if needed, we don't want to create that and login dir
+- * any earlier than here as here we are sure the previous instance has
+- * definitely terminated and stuff like login dirfd is actually clear
+- */
+- if (cdata->manage_rdir) {
+- print_dbg("srv: setup rundir for %u", lgn.uid);
+- if (!rundir_make(lgn.rundir.data(), lgn.uid, lgn.gid)) {
+- return false;
+- }
+- }
+ bool has_backend = !cdata->disable && (
+ (lgn.uid != 0) || cdata->root_session
+ );
+@@ -208,7 +198,11 @@ static bool srv_start(login &lgn) {
+ close(sigpipe[0]);
+ close(sigpipe[1]);
+ /* and run the login */
+- srv_child(lgn, has_backend ? cdata->backend.data() : nullptr);
++ srv_child(
++ lgn,
++ has_backend ? cdata->backend.data() : nullptr,
++ cdata->manage_rdir
++ );
+ exit(1);
+ } else if (pid < 0) {
+ print_err("srv: fork failed (%s)", strerror(errno));
+diff --git a/src/turnstiled.hh b/src/turnstiled.hh
+index d80043c..479ef83 100644
+--- a/src/turnstiled.hh
++++ b/src/turnstiled.hh
+@@ -144,7 +144,7 @@ void cfg_expand_rundir(
+ );
+
+ /* service manager utilities */
+-void srv_child(login &sess, char const *backend);
++void srv_child(login &sess, char const *backend, bool make_rundir);
+ bool srv_boot(login &sess, char const *backend);
+
+ struct cfg_data {
diff --git a/srcpkgs/turnstile/patches/dummy.patch b/srcpkgs/turnstile/patches/dummy.patch
new file mode 100644
index 00000000000000..693cdbe310efd7
--- /dev/null
+++ b/srcpkgs/turnstile/patches/dummy.patch
@@ -0,0 +1,300 @@
+From b7f8fd1c1d6493a83d52347be65a6c6535d5c18f Mon Sep 17 00:00:00 2001
+From: q66 <q66@chimera-linux.org>
+Date: Sat, 2 Sep 2023 14:06:34 +0200
+Subject: [PATCH] write session data in a separate sessions directory
+
+We need this because all of the session data should be publicly
+accessible, while the sessions' individual state directories are
+not.
+
+Also prepare a separate directory for user tracking.
+---
+ src/turnstiled.cc | 52 +++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 37 insertions(+), 15 deletions(-)
+
+diff --git a/src/turnstiled.cc b/src/turnstiled.cc
+index a886739..861c1fe 100644
+--- a/src/turnstiled.cc
++++ b/src/turnstiled.cc
+@@ -58,7 +58,11 @@ static constexpr std::time_t kill_timeout = 60;
+ cfg_data *cdata = nullptr;
+
+ /* the file descriptor for the base directory */
+-static int userv_dirfd = -1;
++static int dirfd_base = -1;
++/* the file descriptor for the users directory */
++static int dirfd_users = -1;
++/* the file descriptor for the sessions directory */
++static int dirfd_sessions = -1;
+
+ login::login() {
+ timer_sev.sigev_notify = SIGEV_SIGNAL;
+@@ -70,7 +74,7 @@ login::login() {
+ void login::remove_sdir() {
+ char buf[32];
+ std::snprintf(buf, sizeof(buf), "%u", this->uid);
+- unlinkat(userv_dirfd, buf, AT_REMOVEDIR);
++ unlinkat(dirfd_base, buf, AT_REMOVEDIR);
+ /* just in case, we know this is a named pipe */
+ unlinkat(this->dirfd, "ready", 0);
+ dir_clear_contents(this->dirfd);
+@@ -131,7 +135,7 @@ static bool srv_start(login &lgn) {
+ if (has_backend) {
+ print_dbg("srv: create login dir for %u", lgn.uid);
+ /* make the directory itself */
+- lgn.dirfd = dir_make_at(userv_dirfd, uidbuf, 0700);
++ lgn.dirfd = dir_make_at(dirfd_base, uidbuf, 0700);
+ if (lgn.dirfd < 0) {
+ print_err(
+ "srv: failed to make login dir for %u (%s)",
+@@ -141,7 +145,7 @@ static bool srv_start(login &lgn) {
+ }
+ /* ensure it's owned by the user */
+ if (fchownat(
+- userv_dirfd, uidbuf, lgn.uid, lgn.gid, AT_SYMLINK_NOFOLLOW
++ dirfd_base, uidbuf, lgn.uid, lgn.gid, AT_SYMLINK_NOFOLLOW
+ ) || fcntl(lgn.dirfd, F_SETFD, FD_CLOEXEC)) {
+ print_err(
+ "srv: login dir setup failed for %u (%s)",
+@@ -194,7 +198,7 @@ static bool srv_start(login &lgn) {
+ sigaction(SIGINT, &sa, nullptr);
+ /* close some descriptors, these can be reused */
+ close(lgn.userpipe);
+- close(userv_dirfd);
++ close(dirfd_base);
+ close(sigpipe[0]);
+ close(sigpipe[1]);
+ /* and run the login */
+@@ -317,11 +321,13 @@ static session *handle_session_new(int fd, unsigned int uid) {
+
+ static bool write_sdata(session const &sess) {
+ char sessname[64], tmpname[64];
+- std::snprintf(tmpname, sizeof(tmpname), "session.%lu.tmp", sess.id);
+- std::snprintf(sessname, sizeof(sessname), "session.%lu", sess.id);
++ std::snprintf(tmpname, sizeof(tmpname), "%lu.tmp", sess.id);
++ std::snprintf(sessname, sizeof(sessname), "%lu", sess.id);
+ auto &lgn = *sess.lgn;
+ int omask = umask(0);
+- int sessfd = openat(lgn.dirfd, tmpname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
++ int sessfd = openat(
++ dirfd_sessions, tmpname, O_CREAT | O_TRUNC | O_WRONLY, 0644
++ );
+ if (sessfd < 0) {
+ print_err("msg: session tmpfile failed (%s)", strerror(errno));
+ umask(omask);
+@@ -365,9 +371,9 @@ static bool write_sdata(session const &sess) {
+ /* done writing */
+ std::fclose(sessf);
+ /* now rename to real file */
+- if (renameat(lgn.dirfd, tmpname, lgn.dirfd, sessname) < 0) {
++ if (renameat(dirfd_sessions, tmpname, dirfd_sessions, sessname) < 0) {
+ print_err("msg: session renameat failed (%s)", strerror(errno));
+- unlinkat(lgn.dirfd, tmpname, 0);
++ unlinkat(dirfd_sessions, tmpname, 0);
+ return false;
+ }
+ return true;
+@@ -375,8 +381,8 @@ static bool write_sdata(session const &sess) {
+
+ static void drop_sdata(session const &sess) {
+ char sessname[64];
+- std::snprintf(sessname, sizeof(sessname), "session.%lu", sess.id);
+- unlinkat(sess.lgn->dirfd, sessname, 0);
++ std::snprintf(sessname, sizeof(sessname), "%lu", sess.id);
++ unlinkat(dirfd_sessions, sessname, 0);
+ }
+
+ static bool sock_block(int fd, short events) {
+@@ -1173,15 +1179,31 @@ int main(int argc, char **argv) {
+ print_err("turnstiled base path does not exist");
+ return 1;
+ }
+- userv_dirfd = dir_make_at(dfd, SOCK_DIR, 0755);
+- if (userv_dirfd < 0) {
++ dirfd_base = dir_make_at(dfd, SOCK_DIR, 0755);
++ if (dirfd_base < 0) {
+ print_err("failed to create base directory (%s)", strerror(errno));
+ return 1;
+ }
++ dirfd_users = dir_make_at(dirfd_base, "users", 0755);
++ if (dirfd_users < 0) {
++ print_err("failed to create users directory (%s)", strerror(errno));
++ return 1;
++ }
++ dirfd_sessions = dir_make_at(dirfd_base, "sessions", 0755);
++ if (dirfd_sessions < 0) {
++ print_err(
++ "failed to create sessions directory (%s)", strerror(errno)
++ );
++ return 1;
++ }
+ close(dfd);
+ }
+ /* ensure it is not accessible by service manager child processes */
+- if (fcntl(userv_dirfd, F_SETFD, FD_CLOEXEC)) {
++ if (
++ fcntl(dirfd_base, F_SETFD, FD_CLOEXEC) ||
++ fcntl(dirfd_users, F_SETFD, FD_CLOEXEC) ||
++ fcntl(dirfd_sessions, F_SETFD, FD_CLOEXEC)
++ ) {
+ print_err("fcntl failed (%s)", strerror(errno));
+ return 1;
+ }
+
+From 27f29a523cb75ba778ca538d319181a60c533eb4 Mon Sep 17 00:00:00 2001
+From: q66 <q66@chimera-linux.org>
+Date: Sun, 3 Sep 2023 16:32:16 +0200
+Subject: [PATCH] treat dummy service backend the same as a regular one
+
+This means less code for handling the dummy backend specifically,
+plus it fixes some bugs (e.g. not being able to write session
+files in a login dir that was not created).
+---
+ src/exec_utils.cc | 16 +++++++--
+ src/turnstiled.cc | 84 +++++++++++++++++++++++------------------------
+ 2 files changed, 55 insertions(+), 45 deletions(-)
+
+diff --git a/src/exec_utils.cc b/src/exec_utils.cc
+index 96440ec..aab57ba 100644
+--- a/src/exec_utils.cc
++++ b/src/exec_utils.cc
+@@ -280,13 +280,25 @@ static void fork_and_wait(
+ }
+
+ /* dummy "service manager" child process with none backend */
+-static void srv_dummy() {
++static void srv_dummy(unsigned int uid) {
+ /* block all signals except the ones we need to terminate */
+ sigset_t mask;
+ sigfillset(&mask);
+ /* kill/stop are ignored, but term is not */
+ sigdelset(&mask, SIGTERM);
+ sigprocmask(SIG_SETMASK, &mask, nullptr);
++ /* mark as ready */
++ char path[4096];
++ std::snprintf(
++ path, sizeof(path), "%s/%s/%u/ready", RUN_PATH, SOCK_DIR, uid
++ );
++ FILE *ready = std::fopen(path, "w");
++ if (!ready) {
++ perror("srv: could not open readiness fifo");
++ exit(1);
++ }
++ std::fprintf(ready, "boop\n");
++ std::fclose(ready);
+ /* this will sleep until a termination signal wakes it */
+ pause();
+ /* in which case just exit */
+@@ -337,7 +349,7 @@ void srv_child(login &lgn, char const *backend, bool make_rundir) {
+ }
+ /* dummy service manager if requested */
+ if (!backend) {
+- srv_dummy();
++ srv_dummy(lgn.uid);
+ return;
+ }
+ /* change directory to home, fall back to / or error */
+diff --git a/src/turnstiled.cc b/src/turnstiled.cc
+index f33705c..f3166e9 100644
+--- a/src/turnstiled.cc
++++ b/src/turnstiled.cc
+@@ -135,51 +135,46 @@ static bool srv_start(login &lgn) {
+ std::snprintf(uidbuf, sizeof(uidbuf), "%u", lgn.uid);
+ /* mark as waiting */
+ lgn.srv_wait = true;
+- bool has_backend = !cdata->disable && (
+- (lgn.uid != 0) || cdata->root_session
+- );
+ /* set up login dir */
+- if (has_backend) {
+- print_dbg("srv: create login dir for %u", lgn.uid);
+- /* make the directory itself */
+- lgn.dirfd = dir_make_at(dirfd_base, uidbuf, 0700);
+- if (lgn.dirfd < 0) {
+- print_err(
+- "srv: failed to make login dir for %u (%s)",
+- lgn.uid, strerror(errno)
+- );
+- return false;
+- }
+- /* ensure it's owned by the user */
+- if (fchownat(
+- dirfd_base, uidbuf, lgn.uid, lgn.gid, AT_SYMLINK_NOFOLLOW
+- ) || fcntl(lgn.dirfd, F_SETFD, FD_CLOEXEC)) {
+- print_err(
+- "srv: login dir setup failed for %u (%s)",
+- lgn.uid, strerror(errno)
+- );
+- lgn.remove_sdir();
+- return false;
+- }
+- print_dbg("srv: create readiness pipe");
++ print_dbg("srv: create login dir for %u", lgn.uid);
++ /* make the directory itself */
++ lgn.dirfd = dir_make_at(dirfd_base, uidbuf, 0700);
++ if (lgn.dirfd < 0) {
++ print_err(
++ "srv: failed to make login dir for %u (%s)",
++ lgn.uid, strerror(errno)
++ );
++ return false;
++ }
++ /* ensure it's owned by the user */
++ if (fchownat(
++ dirfd_base, uidbuf, lgn.uid, lgn.gid, AT_SYMLINK_NOFOLLOW
++ ) || fcntl(lgn.dirfd, F_SETFD, FD_CLOEXEC)) {
++ print_err(
++ "srv: login dir setup failed for %u (%s)",
++ lgn.uid, strerror(errno)
++ );
++ lgn.remove_sdir();
++ return false;
++ }
++ print_dbg("srv: create readiness pipe");
++ unlinkat(lgn.dirfd, "ready", 0);
++ if (mkfifoat(lgn.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(
++ lgn.dirfd, "ready", lgn.uid, lgn.gid, AT_SYMLINK_NOFOLLOW
++ ) || ((lgn.userpipe = openat(
++ lgn.dirfd, "ready", O_NONBLOCK | O_RDONLY
++ )) < 0)) {
++ print_err(
++ "srv: failed to set up ready pipe (%s)", strerror(errno)
++ );
+ unlinkat(lgn.dirfd, "ready", 0);
+- if (mkfifoat(lgn.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(
+- lgn.dirfd, "ready", lgn.uid, lgn.gid, AT_SYMLINK_NOFOLLOW
+- ) || ((lgn.userpipe = openat(
+- lgn.dirfd, "ready", O_NONBLOCK | O_RDONLY
+- )) < 0)) {
+- print_err(
+- "srv: failed to set up ready pipe (%s)", strerror(errno)
+- );
+- unlinkat(lgn.dirfd, "ready", 0);
+- lgn.remove_sdir();
+- return false;
+- }
++ lgn.remove_sdir();
++ return false;
+ }
+ /* set up the timer, issue SIGLARM when it fires */
+ print_dbg("srv: timer set");
+@@ -209,6 +204,9 @@ static bool srv_start(login &lgn) {
+ close(sigpipe[0]);
+ close(sigpipe[1]);
+ /* and run the login */
++ bool has_backend = !cdata->disable && (
++ (lgn.uid != 0) || cdata->root_session
++ );
+ srv_child(
+ lgn,
+ has_backend ? cdata->backend.data() : nullptr,
diff --git a/srcpkgs/turnstile/patches/fix-chdir.patch b/srcpkgs/turnstile/patches/fix-chdir.patch
new file mode 100644
index 00000000000000..3428fa79f77a1e
--- /dev/null
+++ b/srcpkgs/turnstile/patches/fix-chdir.patch
@@ -0,0 +1,25 @@
+From 00fd0b1ad7b5fd262bb83c75cb463ad32b1940c9 Mon Sep 17 00:00:00 2001
+From: q66 <q66@chimera-linux.org>
+Date: Wed, 29 Nov 2023 14:39:16 +0100
+Subject: [PATCH] exec_utils: fix operand for homedir chdir
+
+Using OR would result in the second operand running on success
+of the first so typically all the user services ended up starting
+in / by default.
+---
+ src/exec_utils.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/exec_utils.cc b/src/exec_utils.cc
+index aab57ba..c48f833 100644
+--- a/src/exec_utils.cc
++++ b/src/exec_utils.cc
+@@ -353,7 +353,7 @@ void srv_child(login &lgn, char const *backend, bool make_rundir) {
+ return;
+ }
+ /* change directory to home, fall back to / or error */
+- if ((chdir(lgn.homedir.data()) < 0) || (chdir("/") < 0)) {
++ if ((chdir(lgn.homedir.data()) < 0) && (chdir("/") < 0)) {
+ perror("srv: failed to change directory");
+ return;
+ }
diff --git a/srcpkgs/turnstile/patches/runit.patch b/srcpkgs/turnstile/patches/runit.patch
new file mode 100644
index 00000000000000..b7a8c44e8c1b6f
--- /dev/null
+++ b/srcpkgs/turnstile/patches/runit.patch
@@ -0,0 +1,256 @@
+From 3974343c76392aad6f6998805fc0d2a6fa1eab8d Mon Sep 17 00:00:00 2001
+From: classabbyamp <dev@placeviolette.net>
+Date: Wed, 28 Jun 2023 05:05:25 -0400
+Subject: [PATCH] add runit backend
+
+---
+ backend/meson.build | 25 ++++++++
+ backend/runit | 88 +++++++++++++++++++++++++++
+ backend/runit.conf | 16 +++++
+ backend/turnstile-update-runit-env.in | 31 ++++++++++
+ meson.build | 14 +++--
+ meson_options.txt | 10 +++
+ 6 files changed, 180 insertions(+), 4 deletions(-)
+ create mode 100644 backend/runit
+ create mode 100644 backend/runit.conf
+ create mode 100644 backend/turnstile-update-runit-env.in
+
+diff --git a/backend/meson.build b/backend/meson.build
+index 681e6a0..5a5b200 100644
+--- a/backend/meson.build
++++ b/backend/meson.build
+@@ -13,3 +13,28 @@ 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--'
++ )
++
++ configure_file(
++ input: 'turnstile-update-runit-env.in',
++ output: 'turnstile-update-runit-env',
++ configuration: conf_data,
++ install: true,
++ install_dir: get_option('bindir'),
++ install_mode: 'rwxr-xr-x'
++ )
++endif
+diff --git a/backend/runit b/backend/runit
+new file mode 100644
+index 0000000..a9d7454
+--- /dev/null
++++ b/backend/runit
+@@ -0,0 +1,88 @@
++#!/bin/sh
++#
++# This is the turnstile runit backend. It accepts the action as its first
++# argument, which is either "ready", "run", or "stop". 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
++#
++# Arguments for "stop":
++#
++# pid: the PID of the service manager to stop (gracefully); it should
++# terminate the services it's running and then stop itself
++#
++# Copyright 2023 classabbyamp <dev@placeviolette.net>
++# License: BSD-2-Clause
++
++case "$1" in
++ run) ;;
++ ready)
++ if [ -z "$2" ] || [ ! -d "$2" ]; then
++ echo "runit: invalid readiness service '$2'" >&2
++ exit 69
++ fi
++ exec sv start "$2" >&2
++ ;;
++ stop)
++ # If runsvdir receives a HUP signal, it sends a TERM signal to each
++ # runsv(8) process it is monitoring and then exits with 111.
++ exec kill -s HUP "$2"
++ ;;
++ *)
++ 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"}"
++: "${service_env_dir:="${HOME}/.config/service-env"}"
++
++mkdir -p "${services_dir}/${ready_sv}" > /dev/null 2>&1
++mkdir -p "${service_env_dir}" > /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 TURNSTILE_ENV_DIR="$service_env_dir" \
++ runsvdir -P "$services_dir" \
++ 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'
+diff --git a/backend/runit.conf b/backend/runit.conf
+new file mode 100644
+index 0000000..88a2d04
+--- /dev/null
++++ b/backend/runit.conf
+@@ -0,0 +1,16 @@
++# 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"
++
++# the environment variable directory user service files can read from.
++service_env_dir="${HOME}/.config/service-env"
+diff --git a/backend/turnstile-update-runit-env.in b/backend/turnstile-update-runit-env.in
+new file mode 100644
+index 0000000..9999459
+--- /dev/null
++++ b/backend/turnstile-update-runit-env.in
+@@ -0,0 +1,31 @@
++#!/bin/sh
++# Copyright 2023 classabbyamp <dev@placeviolette.net>
++# License: BSD-2-Clause
++
++usage() {
++ cat <<-EOF
++ turnstile-update-runit-env [VAR] ...
++ Updates values in the shared chpst(8) env dir.
++
++ If VAR is a variable name, the value is taken from the environment.
++ If VAR is VAR=VAL, sets VAR to VAL.
++ EOF
++}
++
++. @CONF_PATH@/backend/runit.conf
++
++if [ $# -eq 0 ] || [ "$1" = "-h" ]; then
++ usage
++ exit 0
++fi
++
++for var; do
++ case "$var" in
++ *=*)
++ eval echo "${var#*=}" > "$service_env_dir/${var%%=*}"
++ ;;
++ *)
++ eval echo '$'"$var" > "$service_env_dir/$var"
++ ;;
++ esac
++done
+diff --git a/meson.build b/meson.build
+index 762aac7..d5467a2 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'))
+@@ -118,10 +119,15 @@ install_data(
+ )
+
+ # decide the default backend
+-if have_dinit
+- default_backend = 'dinit'
+-else
+- default_backend = 'none'
++default_backend = get_option('default_backend')
++if default_backend == ''
++ if have_dinit
++ default_backend = 'dinit'
++ elif have_runit
++ default_backend = 'runit'
++ else
++ default_backend = 'none'
++ endif
+ endif
+
+ uconf_data = configuration_data()
+diff --git a/meson_options.txt b/meson_options.txt
+index 9b03995..4325042 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -3,6 +3,16 @@ 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('default_backend',
++ type: 'string', value: '',
++ description: 'Override the default backend'
++)
++
+ option('rundir',
+ type: 'string', value: '/run',
+ description: 'Where the base directory will be located'
diff --git a/srcpkgs/turnstile/template b/srcpkgs/turnstile/template
new file mode 100644
index 00000000000000..5a435e1ae64a04
--- /dev/null
+++ b/srcpkgs/turnstile/template
@@ -0,0 +1,24 @@
+# Template file for 'turnstile'
+pkgname=turnstile
+version=0.1.8
+revision=1
+build_style=meson
+configure_args="-Ddinit=enabled -Drunit=enabled -Ddefault_backend=runit
+ -Dmanage_rundir=true"
+hostmakedepends="pkg-config scdoc"
+makedepends="pam-devel"
+short_desc="Independent session/login tracker and user service manager"
+maintainer="classabbyamp <void@placeviolette.net>"
+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"
+checksum=7eaab8c80c76ae9a9a711d7dc57ec346b9af09be99b526a5a3129a7fc9bd7a76
+conf_files="/etc/turnstile/turnstiled.conf"
+
+post_install() {
+ vsv turnstiled
+ vsconf "${FILESDIR}/dbus.run"
+ chmod +x "${DESTDIR}/usr/share/examples/turnstile/dbus.run"
+ vdoc "${FILESDIR}/README.voidlinux"
+ vlicense COPYING.md
+}
next prev parent reply other threads:[~2024-03-22 20:31 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-28 9:12 [PR PATCH] New package: turnstile-0.1.4 classabbyamp
2023-06-28 20:45 ` dkwo
2023-06-28 21:36 ` classabbyamp
2023-07-04 8:36 ` [PR PATCH] [Updated] " classabbyamp
2023-07-04 8:40 ` classabbyamp
2023-07-04 8:43 ` classabbyamp
2023-07-04 8:45 ` classabbyamp
2023-07-04 8:59 ` classabbyamp
2023-07-04 9:00 ` classabbyamp
2023-07-04 9:44 ` New package: turnstile-0.1.5 classabbyamp
2023-07-04 9:58 ` classabbyamp
2023-07-09 22:52 ` [PR PATCH] [Updated] " classabbyamp
2023-07-10 14:03 ` junkminerman
2023-07-10 16:37 ` classabbyamp
2023-07-10 16:55 ` classabbyamp
2023-07-10 17:59 ` junkminerman
2023-07-10 18:04 ` classabbyamp
2023-07-10 21:36 ` junkminerman
2023-07-10 21:44 ` classabbyamp
2023-07-11 9:01 ` junkminerman
2023-07-12 3:06 ` [PR PATCH] [Updated] " classabbyamp
2023-07-13 23:29 ` New package: turnstile-0.1.6 junkminerman
2023-07-25 17:27 ` [PR PATCH] [Updated] " classabbyamp
2023-09-08 10:47 ` classabbyamp
2023-09-08 10:56 ` classabbyamp
2023-09-08 11:04 ` [PR PATCH] [Closed]: New package: turnstile-0.1.8 classabbyamp
2023-09-08 11:05 ` classabbyamp
2023-09-08 12:58 ` dkwo
2023-09-08 14:13 ` classabbyamp
2023-09-09 22:21 ` [PR PATCH] [Updated] " classabbyamp
2023-09-09 22:23 ` classabbyamp
2023-09-10 0:02 ` classabbyamp
2023-09-11 8:19 ` dkwo
2023-09-12 22:55 ` [PR PATCH] [Updated] " classabbyamp
2023-09-12 23:09 ` classabbyamp
2023-09-26 17:44 ` dkwo
2023-09-26 18:04 ` dkwo
2023-09-26 18:06 ` classabbyamp
2023-09-26 18:09 ` classabbyamp
2023-09-26 20:15 ` dkwo
2023-09-26 21:15 ` dkwo
2023-09-26 21:16 ` dkwo
2023-09-26 21:25 ` classabbyamp
2023-09-26 21:40 ` dkwo
2023-09-26 21:41 ` dkwo
2023-12-26 1:45 ` github-actions
2023-12-26 17:52 ` [PR PATCH] [Updated] " classabbyamp
2023-12-26 17:54 ` classabbyamp
2023-12-26 19:05 ` dkwo
2023-12-26 19:24 ` classabbyamp
2024-01-16 19:42 ` dkwo
2024-01-16 20:08 ` classabbyamp
2024-03-22 20:31 ` classabbyamp [this message]
2024-03-22 21:02 ` [PR PATCH] [Updated] " classabbyamp
2024-03-22 21:11 ` [PR PATCH] [Merged]: " classabbyamp
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240322203126.04E0C23F74@inbox.vuxu.org \
--to=classabbyamp@users.noreply.github.com \
--cc=ml@inbox.vuxu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).