554 lines
15 KiB
Diff
554 lines
15 KiB
Diff
From f09346dd2ceb30d0c7ea03bbd0099967e7e54be0 Mon Sep 17 00:00:00 2001
|
|
From: soscho2143 <mnovikov@mil.ru>
|
|
Date: Sun, 12 Oct 2025 13:52:36 -0400
|
|
Subject: [PATCH 1/2] detect-fash: implement systemd-detect-fash
|
|
|
|
---
|
|
man/systemd-detect-fash.xml | 131 +++++++++
|
|
shell-completion/bash/systemd-detect-fash | 40 +++
|
|
src/detect-fash/detect-fash.c | 312 ++++++++++++++++++++++
|
|
src/detect-fash/meson.build | 9 +
|
|
4 files changed, 492 insertions(+)
|
|
create mode 100644 man/systemd-detect-fash.xml
|
|
create mode 100644 shell-completion/bash/systemd-detect-fash
|
|
create mode 100644 src/detect-fash/detect-fash.c
|
|
create mode 100644 src/detect-fash/meson.build
|
|
|
|
diff --git a/man/systemd-detect-fash.xml b/man/systemd-detect-fash.xml
|
|
new file mode 100644
|
|
index 0000000000000..aaebf4e48650b
|
|
--- /dev/null
|
|
+++ b/man/systemd-detect-fash.xml
|
|
@@ -0,0 +1,131 @@
|
|
+<?xml version='1.0'?> <!--*-nxml-*-->
|
|
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
|
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
|
+
|
|
+<refentry id="systemd-detect-fash"
|
|
+ xmlns:xi="http://www.w3.org/2001/XInclude">
|
|
+
|
|
+ <refentryinfo>
|
|
+ <title>systemd-detect-fash</title>
|
|
+ <productname>systemd</productname>
|
|
+ </refentryinfo>
|
|
+
|
|
+ <refmeta>
|
|
+ <refentrytitle>systemd-detect-fash</refentrytitle>
|
|
+ <manvolnum>1</manvolnum>
|
|
+ </refmeta>
|
|
+
|
|
+ <refnamediv>
|
|
+ <refname>systemd-detect-fash</refname>
|
|
+ <refpurpose>Detect execution in a fascist environment</refpurpose>
|
|
+ </refnamediv>
|
|
+
|
|
+ <refsynopsisdiv>
|
|
+ <cmdsynopsis>
|
|
+ <command>systemd-detect-fash</command>
|
|
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
|
|
+ </cmdsynopsis>
|
|
+ </refsynopsisdiv>
|
|
+
|
|
+ <refsect1>
|
|
+ <title>Description</title>
|
|
+
|
|
+ <para><command>systemd-detect-fash</command> detects execution in
|
|
+ a fascist environment. It identifies the fascist
|
|
+ technology and can distinguish full machine fascism from
|
|
+ installed fashware. <filename>systemd-detect-fash</filename>
|
|
+ exits with a return value of 0 (success) if a fascism
|
|
+ technology is detected, and non-zero (error) otherwise.
|
|
+
|
|
+ <para>When executed without <option>--quiet</option> will print a
|
|
+ short identifier for the detected fascist technology. The
|
|
+ following technologies are currently identified:</para>
|
|
+
|
|
+ <table>
|
|
+ <title>Known fascist technologies</title>
|
|
+ <tgroup cols='3' align='left' colsep='1' rowsep='1'>
|
|
+ <colspec colname="type" />
|
|
+ <colspec colname="id" />
|
|
+ <colspec colname="product" />
|
|
+ <thead>
|
|
+ <row>
|
|
+ <entry>Type</entry>
|
|
+ <entry>ID</entry>
|
|
+ <entry>Product</entry>
|
|
+ </row>
|
|
+ </thead>
|
|
+ <tbody>
|
|
+ <row>
|
|
+ <entry><varname>omarchy</varname></entry>
|
|
+ <entry>Omarchy linux distro. Detected by checking os-release.</entry>
|
|
+ </row>
|
|
+
|
|
+ <row>
|
|
+ <entry><varname>ladybird</varname></entry>
|
|
+ <entry>Ladybird browser. Detected by checking for "ladybird" binary in path.</entry>
|
|
+ </row>
|
|
+
|
|
+ <row>
|
|
+ <entry><varname>hyprland</varname></entry>
|
|
+ <entry>Hyperland window manager. Detected by checking the existence of hyprland config files on disk.</entry>
|
|
+ </row>
|
|
+
|
|
+ <row>
|
|
+ <entry><varname>dhh</varname></entry>
|
|
+ <entry>Checks for DHH's public key on disk.</entry>
|
|
+ </row>
|
|
+ </tbody>
|
|
+ </tgroup>
|
|
+ </table>
|
|
+ </refsect1>
|
|
+
|
|
+ <refsect1>
|
|
+ <title>Options</title>
|
|
+
|
|
+ <para>The following options are understood:</para>
|
|
+
|
|
+ <variablelist>
|
|
+ <varlistentry>
|
|
+ <term><option>-o</option></term>
|
|
+ <term><option>--omarchy</option></term>
|
|
+
|
|
+ <listitem><para>Only detects if os-release is Omarchy.</para></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
+ <varlistentry>
|
|
+ <term><option>-y</option></term>
|
|
+ <term><option>--hyprland</option></term>
|
|
+
|
|
+ <listitem><para>Only detects Hyprland.</para></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
+ <varlistentry>
|
|
+ <term><option>-l</option></term>
|
|
+ <term><option>--ladybird</option></term>
|
|
+
|
|
+ <listitem><para>Only detects Ladybird.</para></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
+ <varlistentry>
|
|
+ <term><option>-d</option></term>
|
|
+ <term><option>--dhh</option></term>
|
|
+
|
|
+ <listitem><para>Only detects DHH.</para></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
+ <varlistentry>
|
|
+ <term><option>-q</option></term>
|
|
+ <term><option>--quiet</option></term>
|
|
+
|
|
+ <listitem><para>Suppress output of the fascist technology identifier.</para></listitem>
|
|
+ </varlistentry>
|
|
+ </refsect1>
|
|
+
|
|
+ <refsect1>
|
|
+ <title>Exit status</title>
|
|
+
|
|
+ <para>If a fascist technology is detected, 0 is returned, a
|
|
+ non-zero code otherwise.</para>
|
|
+ </refsect1>
|
|
+</refentry>
|
|
diff --git a/shell-completion/bash/systemd-detect-fash b/shell-completion/bash/systemd-detect-fash
|
|
new file mode 100644
|
|
index 0000000000000..dc2a7f5f4774a
|
|
--- /dev/null
|
|
+++ b/shell-completion/bash/systemd-detect-fash
|
|
@@ -0,0 +1,40 @@
|
|
+# shellcheck shell=bash
|
|
+# systemd-detect-fash(1) completion -*- shell-script -*-
|
|
+# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
+#
|
|
+# This file is part of systemd.
|
|
+#
|
|
+# systemd is free software; you can redistribute it and/or modify it
|
|
+# under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation; either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# systemd is distributed in the hope that it will be useful, but
|
|
+# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+# General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with systemd; If not, see <https://www.gnu.org/licenses/>.
|
|
+
|
|
+__contains_word() {
|
|
+ local w word=$1; shift
|
|
+ for w in "$@"; do
|
|
+ [[ $w = "$word" ]] && return
|
|
+ done
|
|
+}
|
|
+
|
|
+_systemd_detect_fash() {
|
|
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
|
|
+ local i verb comps
|
|
+
|
|
+ local -A OPTS=(
|
|
+ [STANDALONE]='-h --help --version -q --quiet -o --omarchy -l --ladybird -y --hyprland -d --dhh'
|
|
+ )
|
|
+
|
|
+ _init_completion || return
|
|
+
|
|
+ COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
|
|
+}
|
|
+
|
|
+complete -F _systemd_detect_fash systemd-detect-fash
|
|
diff --git a/src/detect-fash/detect-fash.c b/src/detect-fash/detect-fash.c
|
|
new file mode 100644
|
|
index 0000000000000..311547ec6a619
|
|
--- /dev/null
|
|
+++ b/src/detect-fash/detect-fash.c
|
|
@@ -0,0 +1,312 @@
|
|
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <getopt.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include "alloc-util.h"
|
|
+#include "build.h"
|
|
+#include "log.h"
|
|
+#include "main-func.h"
|
|
+#include "pretty-print.h"
|
|
+#include "string-table.h"
|
|
+
|
|
+static bool arg_quiet = false;
|
|
+static enum {
|
|
+ ANY_FASCISM,
|
|
+ ONLY_LADYBIRD,
|
|
+ ONLY_OMARCHY,
|
|
+ ONLY_HYPRLAND,
|
|
+ ONLY_DHH
|
|
+} arg_mode = ANY_FASCISM;
|
|
+
|
|
+/* detects if os-release is omarchy */
|
|
+static int detect_omarchy(void) {
|
|
+ const char *term = "omarchy";
|
|
+ const int len = 256;
|
|
+
|
|
+ /* if we cannot access os-release we cannot check */
|
|
+ if (access("/etc/os-release", F_OK) != 0)
|
|
+ return -1;
|
|
+
|
|
+ FILE *osfile = fopen("/etc/os-release", "r");
|
|
+ char os[len];
|
|
+ fgets(os, len, osfile);
|
|
+ if (strcasestr(os, term) != NULL)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ detects if the LadyBird browser
|
|
+ has been built on this machine
|
|
+ or if the binary exists in $PATH
|
|
+*/
|
|
+static unsigned detect_ladybird(void) {
|
|
+
|
|
+ /* name of the ladybird binary */
|
|
+ const char* ladybird_bin = "/ladybird";
|
|
+
|
|
+ /* check if build variable is available */
|
|
+ char* LADYBIRD_SOURCE_DIR = getenv("LADYBIRD_SOURCE_DIR");
|
|
+ if (LADYBIRD_SOURCE_DIR != NULL)
|
|
+ return 1;
|
|
+
|
|
+ char* PATH = getenv("PATH");
|
|
+ if (PATH == NULL)
|
|
+ return 0;
|
|
+
|
|
+ /* this value will get mutated so we need to duplicate it */
|
|
+ char* path = strdup(PATH);
|
|
+ /* loop through PATH until we find a file named "ladybird" */
|
|
+ char* path_iter = strtok(path, ":");
|
|
+ char* abs_path = malloc(256);
|
|
+ while (path_iter != NULL) {
|
|
+ strncat(abs_path, path_iter, 128);
|
|
+ strncat(abs_path, ladybird_bin, 128);
|
|
+ /* if we do NOT find the binary at current path, keep going */
|
|
+ if (access(abs_path, F_OK) != 0){
|
|
+ path_iter = strtok(NULL, ":");
|
|
+ abs_path[0] = 0;
|
|
+ continue;
|
|
+ }
|
|
+ free(abs_path);
|
|
+ free(path);
|
|
+ return 1;
|
|
+ }
|
|
+ free(abs_path);
|
|
+ free(path);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* detects if hyprland is installed */
|
|
+static unsigned detect_hyprland(void) {
|
|
+ const char* hyprland_config = "/hypr/hyprland.conf";
|
|
+ const char* XDG_CONFIG_HOME = getenv("XDG_CONFIG_HOME");
|
|
+ const char* HOME = getenv("HOME");
|
|
+ int maxlen = 128;
|
|
+
|
|
+ char *hyprland_abs_path = malloc(maxlen);
|
|
+
|
|
+ if (XDG_CONFIG_HOME != NULL) {
|
|
+ strncat(hyprland_abs_path, XDG_CONFIG_HOME, maxlen - strlen(hyprland_config));
|
|
+ } else if (HOME != NULL) {
|
|
+ strncat(hyprland_abs_path, HOME, maxlen - strlen(hyprland_config));
|
|
+ strcat(hyprland_abs_path, "/.config");
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+ strcat(hyprland_abs_path, hyprland_config);
|
|
+ if (access(hyprland_abs_path, F_OK) == 0){
|
|
+ free(hyprland_abs_path);
|
|
+ return 1;
|
|
+ }
|
|
+ free(hyprland_abs_path);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* detects if this is dhh's computer using his ssh pubkey */
|
|
+static int detect_dhh(void) {
|
|
+ /* fingerprint of dhh's ssh public key */
|
|
+ const char *dhh_fingerprint = "SHA256:YCKX7xo5Hkihy/NVH5ang8Oty9q8Vvqu4sxI7EbDxPg";
|
|
+ /* path to ssh pubkey */
|
|
+ const char *ssh_pubkey = "/.ssh/id_ed25519.pub";
|
|
+ /* command to generate fingerprint */
|
|
+ const char *ssh_fingerpint_cmd = "ssh-keygen -E sha256 -lf ";
|
|
+
|
|
+ /* get the home directory */
|
|
+ char *HOME = getenv("HOME");
|
|
+
|
|
+ if (HOME == NULL)
|
|
+ return -1;
|
|
+ /* check if we have read access to the public key on disk */
|
|
+ char *ssh_pubkey_abs_path = (char *)malloc(strlen(HOME) + strlen(ssh_pubkey) + 1);
|
|
+ ssh_pubkey_abs_path[0] = 0;
|
|
+ strcat(ssh_pubkey_abs_path, HOME);
|
|
+ strcat(ssh_pubkey_abs_path, ssh_pubkey);
|
|
+ if (access(ssh_pubkey_abs_path, F_OK) != 0)
|
|
+ return 0;
|
|
+
|
|
+ /* generate a fingerprint of it */
|
|
+ char *get_fingerprint_cmd = (char *)malloc(strlen(ssh_fingerpint_cmd) + strlen(ssh_pubkey_abs_path) + 1);
|
|
+ get_fingerprint_cmd[0] = 0;
|
|
+ strcat(get_fingerprint_cmd, ssh_fingerpint_cmd);
|
|
+ strcat(get_fingerprint_cmd, ssh_pubkey_abs_path);
|
|
+
|
|
+ char fingerprint[70];
|
|
+ FILE *fingerprint_cmd_output = popen(get_fingerprint_cmd, "r");
|
|
+
|
|
+ if (fingerprint_cmd_output == NULL)
|
|
+ return -1;
|
|
+ fgets(fingerprint, 70, fingerprint_cmd_output);
|
|
+
|
|
+ /* free memory */
|
|
+ pclose(fingerprint_cmd_output);
|
|
+ free(ssh_pubkey_abs_path);
|
|
+ free(get_fingerprint_cmd);
|
|
+
|
|
+ /* comare it to DHH's fingerprint */
|
|
+ if (strstr(fingerprint, dhh_fingerprint) != NULL)
|
|
+ return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int help(void) {
|
|
+ _cleanup_free_ char *link = NULL;
|
|
+ int r;
|
|
+
|
|
+ r = terminal_urlify_man("systemd-detect-fash", "1", &link);
|
|
+ if (r < 0)
|
|
+ return log_oom();
|
|
+
|
|
+ printf("%s [OPTIONS...]\n\n"
|
|
+ "Detect execution in a fascist environment.\n\n"
|
|
+ " -h --help Show this help\n"
|
|
+ " --version Show package version\n"
|
|
+ " -q --quiet Quiet mode\n"
|
|
+ " -o --omarchy Only detect omarchy\n"
|
|
+ " -l --ladybird Only detect ladybird\n"
|
|
+ " -y --hyprland Only detect hyprland\n"
|
|
+ " -d --dhh Only detect dhh\n"
|
|
+ "\nSee the %s for details.\n",
|
|
+ program_invocation_short_name,
|
|
+ link);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int parse_argv(int argc, char *argv[]) {
|
|
+
|
|
+ enum {
|
|
+ ARG_VERSION = 0x100,
|
|
+ ARG_OMARCHY,
|
|
+ ARG_LADYBIRD,
|
|
+ ARG_HYPRLAND,
|
|
+ ARG_DHH
|
|
+ };
|
|
+
|
|
+ static const struct option options[] = {
|
|
+ { "help", no_argument, NULL, 'h' },
|
|
+ { "version", no_argument, NULL, ARG_VERSION },
|
|
+ { "omarchy", no_argument, NULL, 'o' },
|
|
+ { "ladybird", no_argument, NULL, 'l' },
|
|
+ { "hyprland", no_argument, NULL, 'y' },
|
|
+ { "dhh", no_argument, NULL, 'd' },
|
|
+ {}
|
|
+ };
|
|
+
|
|
+ int c;
|
|
+
|
|
+ assert(argc >= 0);
|
|
+ assert(argv);
|
|
+
|
|
+ while ((c = getopt_long(argc, argv, "hqolyd", options, NULL)) >= 0)
|
|
+
|
|
+ switch (c) {
|
|
+
|
|
+ case 'h':
|
|
+ return help();
|
|
+
|
|
+ case ARG_VERSION:
|
|
+ return version();
|
|
+
|
|
+ case 'q':
|
|
+ arg_quiet = true;
|
|
+ break;
|
|
+
|
|
+ case 'l':
|
|
+ arg_mode = ONLY_LADYBIRD;
|
|
+ break;
|
|
+
|
|
+ case 'o':
|
|
+ arg_mode = ONLY_OMARCHY;
|
|
+ break;
|
|
+
|
|
+ case 'y':
|
|
+ arg_mode = ONLY_HYPRLAND;
|
|
+ break;
|
|
+
|
|
+ case 'd':
|
|
+ arg_mode = ONLY_DHH;
|
|
+ break;
|
|
+
|
|
+ case '?':
|
|
+ return -EINVAL;
|
|
+
|
|
+ default:
|
|
+ assert_not_reached();
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int run(int argc, char *argv[]) {
|
|
+ int dhh = 0;
|
|
+ int hyprland = 0;
|
|
+ int ladybird = 0;
|
|
+ int omarchy = 0;
|
|
+ int fascism = 0;
|
|
+ int r;
|
|
+
|
|
+ /* This is mostly intended to be used for scripts which want
|
|
+ * to detect whether we are being run in a fascist
|
|
+ * environment or not */
|
|
+
|
|
+ log_setup();
|
|
+
|
|
+ r = parse_argv(argc, argv);
|
|
+ if (r <= 0)
|
|
+ return r;
|
|
+
|
|
+ switch (arg_mode) {
|
|
+ case ONLY_OMARCHY:
|
|
+ omarchy = detect_omarchy();
|
|
+ fascism = omarchy;
|
|
+ if (omarchy < 0)
|
|
+ return log_error_errno(fascism, "Failed to check for omarchy: %m");
|
|
+ break;
|
|
+
|
|
+ case ONLY_LADYBIRD:
|
|
+ ladybird = detect_ladybird();
|
|
+ fascism = ladybird;
|
|
+ if (ladybird < 0)
|
|
+ return log_error_errno(fascism, "Failed to check for ladybird: %m");
|
|
+ break;
|
|
+
|
|
+ case ONLY_HYPRLAND:
|
|
+ hyprland = detect_hyprland();
|
|
+ fascism = hyprland;
|
|
+ if (hyprland < 0)
|
|
+ return log_error_errno(fascism, "Failed to check for hyprland: %m");
|
|
+ break;
|
|
+
|
|
+ case ONLY_DHH:
|
|
+ dhh = detect_dhh();
|
|
+ fascism = dhh;
|
|
+ if (dhh < 0)
|
|
+ return log_error_errno(fascism, "Failed to check for dhh: %m");
|
|
+ break;
|
|
+
|
|
+ case ANY_FASCISM:
|
|
+ default:
|
|
+ ladybird = detect_ladybird();
|
|
+ omarchy = detect_omarchy();
|
|
+ hyprland = detect_hyprland();
|
|
+ dhh = detect_dhh();
|
|
+ fascism = (ladybird | omarchy | hyprland | dhh);
|
|
+ if (fascism < 0)
|
|
+ return log_error_errno(fascism, "Failed to check for fascism: %m");
|
|
+ }
|
|
+
|
|
+ if (!arg_quiet) {
|
|
+ if (ladybird) puts("ladybird");
|
|
+ if (omarchy) puts("omarchy");
|
|
+ if (dhh) puts("dhh");
|
|
+ if (hyprland) puts("hyprland");
|
|
+ }
|
|
+ return fascism;
|
|
+}
|
|
+
|
|
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
|
|
diff --git a/src/detect-fash/meson.build b/src/detect-fash/meson.build
|
|
new file mode 100644
|
|
index 0000000000000..f4cca34117e7b
|
|
--- /dev/null
|
|
+++ b/src/detect-fash/meson.build
|
|
@@ -0,0 +1,9 @@
|
|
+# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
+
|
|
+executables += [
|
|
+ executable_template + {
|
|
+ 'name' : 'systemd-detect-fash',
|
|
+ 'public' : true,
|
|
+ 'sources' : files('detect-fash.c'),
|
|
+ },
|
|
+]
|
|
|
|
From 825072a331cb6d7464eb4479c4998ab0d020e32f Mon Sep 17 00:00:00 2001
|
|
From: soscho2143 <mnovikov@mil.ru>
|
|
Date: Sun, 12 Oct 2025 14:18:49 -0400
|
|
Subject: [PATCH 2/2] detect-fash: added to meson.build
|
|
|
|
---
|
|
meson.build | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/meson.build b/meson.build
|
|
index c67e7b6c30de4..76c625d22080d 100644
|
|
--- a/meson.build
|
|
+++ b/meson.build
|
|
@@ -2353,6 +2353,7 @@ subdir('src/cryptenroll')
|
|
subdir('src/cryptsetup')
|
|
subdir('src/debug-generator')
|
|
subdir('src/delta')
|
|
+subdir('src/detect-fash')
|
|
subdir('src/detect-virt')
|
|
subdir('src/dissect')
|
|
subdir('src/environment-d-generator')
|