iso: enable bakery user units globally for later accounts
Bins live in /usr/local, so a later useradd no longer gets ~/.local/bin copies. systemctl --global enable the bakery --user units (bake writes /etc/systemd/user/*.wants/, and post-install + live-setup run the same enable) so first login starts breadd, breadbox-sync, breadclipd, breadcrumbs, and breadmill. Stock useradd -m copies skel (Hyprland + bakery state). Rollback is still grub-btrfs.
This commit is contained in:
parent
34043086b9
commit
70d4dd424b
12 changed files with 336 additions and 21 deletions
43
README.md
43
README.md
|
|
@ -129,10 +129,11 @@ machine's bakery-installed bread binaries + breadhelp content from the
|
||||||
builder's `~/.local` into the image at `/usr/local` (bins, share/data,
|
builder's `~/.local` into the image at `/usr/local` (bins, share/data,
|
||||||
desktop files, licenses) and `/usr/lib/systemd/user` (units). Per-user
|
desktop files, licenses) and `/usr/lib/systemd/user` (units). Per-user
|
||||||
bakery state (`installed.json` + index cache) is seeded in `/etc/skel`.
|
bakery state (`installed.json` + index cache) is seeded in `/etc/skel`.
|
||||||
BOS opts in via `/etc/bakery/config.toml` (`prefix = "/usr/local"`);
|
User units are `systemctl --global enable`'d so a later `useradd -m`
|
||||||
default bakery without that file is still `~/.local`. Snapper `@`
|
starts them on first login. BOS opts in via `/etc/bakery/config.toml`
|
||||||
snapshots include `/usr/local`; recovery is still grub-btrfs, not
|
(`prefix = "/usr/local"`); default bakery without that file is still
|
||||||
`snapper rollback`.
|
`~/.local`. Snapper `@` snapshots include `/usr/local`; recovery is
|
||||||
|
still grub-btrfs, not `snapper rollback`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./build-local.sh # release-quality (xz squashfs)
|
sudo ./build-local.sh # release-quality (xz squashfs)
|
||||||
|
|
@ -197,8 +198,38 @@ Hyprland session in QEMU. The disk lives on NVMe (not the tmpfs `/tmp`) to
|
||||||
avoid memory pressure.
|
avoid memory pressure.
|
||||||
|
|
||||||
Post-install, `scripts/smoke-test.sh` (run as the installed user) checks
|
Post-install, `scripts/smoke-test.sh` (run as the installed user) checks
|
||||||
subvolumes, services, bakery bins on PATH, and breadhelp content under
|
subvolumes, services, bakery bins on PATH, breadhelp content under
|
||||||
`/usr/local/share/breadhelp/content`.
|
`/usr/local/share/breadhelp/content`, and that bakery user units are
|
||||||
|
`--global` enabled (or the preset / wants files exist).
|
||||||
|
|
||||||
|
## Second account
|
||||||
|
|
||||||
|
Bakery desktop apps live in `/usr/local` — shared, already on PATH. A later
|
||||||
|
account does **not** get a private copy of those binaries.
|
||||||
|
|
||||||
|
`/etc/default/useradd` keeps `SKEL=/etc/skel`. Stock `useradd -m` is enough:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo useradd -m alice
|
||||||
|
sudo passwd alice
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Apps**: `/usr/local/bin` (and `/usr/local/share`) — already there.
|
||||||
|
- **Session files**: `useradd -m` copies `/etc/skel` (Hyprland, bread
|
||||||
|
config, bakery `installed.json` + index cache) so first login has a
|
||||||
|
session. Skel does not contain bakery binaries.
|
||||||
|
- **Daemons**: `breadd`, `breadbox-sync`, `breadclipd`, `breadcrumbs`,
|
||||||
|
`breadmill`, … are `systemctl --global enable`'d at install (and on
|
||||||
|
the live image). Creating a user starts them on first login.
|
||||||
|
- **Login**: greetd/breadgreet lists any local user with a login shell
|
||||||
|
(`SHELL=/usr/bin/zsh` is the useradd default).
|
||||||
|
|
||||||
|
`breadclipd` is WantedBy=`graphical-session.target`. BOS does not activate
|
||||||
|
that target (no uwsm), so Hyprland still `systemctl --user start`s it after
|
||||||
|
the compositor is up. `--global enable` still records it for every account.
|
||||||
|
|
||||||
|
Rollback is still the GRUB snapshots submenu (grub-btrfs), not
|
||||||
|
`snapper rollback`. `/usr/local` rides the `@` snapshot.
|
||||||
|
|
||||||
## bos-settings
|
## bos-settings
|
||||||
|
|
||||||
|
|
|
||||||
126
build-local.sh
126
build-local.sh
|
|
@ -249,26 +249,87 @@ PY
|
||||||
# bakery package's service (breadbox-sync, breadmill, breadclipd, ...) was
|
# bakery package's service (breadbox-sync, breadmill, breadclipd, ...) was
|
||||||
# silently left out, so those daemons never start on a fresh install/live
|
# silently left out, so those daemons never start on a fresh install/live
|
||||||
# boot until the user re-runs `bakery install` (which needs network).
|
# boot until the user re-runs `bakery install` (which needs network).
|
||||||
# Source of truth is the *filtered* installed.json we just wrote: only
|
# Units come from installed.json + the bakery index + local unit files
|
||||||
# lockfile packages. Units go to /usr/lib/systemd/user with ExecStart
|
# whose ExecStart is a lockfile binary (installed.json has omitted
|
||||||
# rewritten to /usr/local/bin (not %h/.local/bin). Recreate whichever
|
# breadcrumbs.service before). Units go to /usr/lib/systemd/user with
|
||||||
|
# ExecStart rewritten to /usr/local/bin. Recreate whichever
|
||||||
# *.target.wants enable symlink bakery created locally (or that skel
|
# *.target.wants enable symlink bakery created locally (or that skel
|
||||||
# already ships). Hand-committed skel units (breadd.service carries a
|
# already ships), and write /etc/systemd/user/*.wants/ (--global).
|
||||||
|
# Hand-committed skel units (breadd.service carries a
|
||||||
# RuntimeDirectoryPreserve=yes fix not yet upstreamed) are the source
|
# RuntimeDirectoryPreserve=yes fix not yet upstreamed) are the source
|
||||||
# for that unit and also get their ExecStart rewritten in skel.
|
# for that unit and also get their ExecStart rewritten in skel.
|
||||||
echo "=== baking bakery service units into /usr/lib/systemd/user ==="
|
echo "=== baking bakery service units into /usr/lib/systemd/user ==="
|
||||||
SYSTEMD_USER_DIR="$LAPTOP_HOME/.config/systemd/user"
|
SYSTEMD_USER_DIR="$LAPTOP_HOME/.config/systemd/user"
|
||||||
SKEL_SYSTEMD="$SKEL/.config/systemd/user"
|
SKEL_SYSTEMD="$SKEL/.config/systemd/user"
|
||||||
install -d -m 0755 "$IMAGE_UNITS"
|
install -d -m 0755 "$IMAGE_UNITS"
|
||||||
mapfile -t SERVICE_UNITS < <(python3 - "$SKEL/.local/state/bakery/installed.json" <<'PY'
|
# installed.json on the builder can omit a service even when the index and
|
||||||
import json, sys
|
# the local unit file exist (breadcrumbs has done this). Merge all three
|
||||||
with open(sys.argv[1]) as f:
|
# so every lockfile daemon is baked and can be --global enabled.
|
||||||
d = json.load(f)
|
mapfile -t SERVICE_UNITS < <(python3 - \
|
||||||
for pkg in d.get("packages", d).values():
|
"$SKEL/.local/state/bakery/installed.json" \
|
||||||
for s in pkg.get("services", []):
|
"$BAKERY_CACHE/index.json" \
|
||||||
print(s["unit"] if isinstance(s, dict) else s)
|
"$SYSTEMD_USER_DIR" \
|
||||||
|
"${BREAD_BINS[@]}" <<'PY'
|
||||||
|
import json, os, sys
|
||||||
|
|
||||||
|
installed_path, index_path, user_dir, *bins = sys.argv[1:]
|
||||||
|
wanted = set(bins)
|
||||||
|
units = set()
|
||||||
|
|
||||||
|
def add_svc(svc):
|
||||||
|
name = svc["unit"] if isinstance(svc, dict) else svc
|
||||||
|
if not name or str(name).startswith(("breadcast", "breadarr")):
|
||||||
|
return
|
||||||
|
units.add(str(name))
|
||||||
|
|
||||||
|
if os.path.isfile(installed_path):
|
||||||
|
with open(installed_path) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
for pkg in data.get("packages", data).values():
|
||||||
|
if isinstance(pkg, dict):
|
||||||
|
for svc in pkg.get("services") or []:
|
||||||
|
add_svc(svc)
|
||||||
|
|
||||||
|
if os.path.isfile(index_path):
|
||||||
|
with open(index_path) as f:
|
||||||
|
idx = json.load(f)
|
||||||
|
for name, pkg in (idx.get("packages") or {}).items():
|
||||||
|
if not isinstance(pkg, dict):
|
||||||
|
continue
|
||||||
|
pbins = []
|
||||||
|
for b in pkg.get("binaries") or []:
|
||||||
|
n = b["name"] if isinstance(b, dict) else b
|
||||||
|
pbins.append(str(n).removesuffix("-x86_64"))
|
||||||
|
if name in wanted or any(b in wanted for b in pbins):
|
||||||
|
for svc in pkg.get("services") or []:
|
||||||
|
add_svc(svc)
|
||||||
|
|
||||||
|
if os.path.isdir(user_dir):
|
||||||
|
for fn in os.listdir(user_dir):
|
||||||
|
if not fn.endswith(".service"):
|
||||||
|
continue
|
||||||
|
path = os.path.join(user_dir, fn)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
text = open(path).read()
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
for line in text.splitlines():
|
||||||
|
if line.lstrip().startswith("ExecStart="):
|
||||||
|
argv0 = line.split("=", 1)[1].split()
|
||||||
|
if argv0 and os.path.basename(argv0[0]) in wanted:
|
||||||
|
add_svc(fn)
|
||||||
|
break
|
||||||
|
|
||||||
|
for unit in sorted(units):
|
||||||
|
print(unit)
|
||||||
PY
|
PY
|
||||||
)
|
)
|
||||||
|
if [[ ! " ${SERVICE_UNITS[*]} " =~ " breadd.service " ]]; then
|
||||||
|
echo "ERROR: breadd.service not in the bakery unit list — refusing to bake" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
rewrite_exec_start() {
|
rewrite_exec_start() {
|
||||||
local src="$1" dest="$2"
|
local src="$1" dest="$2"
|
||||||
python3 - "$src" "$dest" <<'PY'
|
python3 - "$src" "$dest" <<'PY'
|
||||||
|
|
@ -301,7 +362,7 @@ for unit in "${SERVICE_UNITS[@]}"; do
|
||||||
else
|
else
|
||||||
src="$SYSTEMD_USER_DIR/$unit"
|
src="$SYSTEMD_USER_DIR/$unit"
|
||||||
if [[ ! -f "$src" ]]; then
|
if [[ ! -f "$src" ]]; then
|
||||||
echo "ERROR: $unit listed in bakery installed.json but not found at $src" >&2
|
echo "ERROR: $unit listed as a bakery service but not found at $src" >&2
|
||||||
echo "Refusing to bake an image whose daemons will never start." >&2
|
echo "Refusing to bake an image whose daemons will never start." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -320,9 +381,50 @@ for unit in "${SERVICE_UNITS[@]}"; do
|
||||||
ln -sf "../$unit" "$IMAGE_UNITS/$target_name/$unit"
|
ln -sf "../$unit" "$IMAGE_UNITS/$target_name/$unit"
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
# systemctl --global enable equivalent: /etc/systemd/user/<WantedBy>.wants/
|
||||||
|
# so the live image and a later useradd inherit the unit without a per-home
|
||||||
|
# enable. Vendor wants above are extra; this is what --global writes.
|
||||||
|
python3 - "$IMAGE_UNITS/$unit" "$AIROOTFS/etc/systemd/user" "$unit" <<'PY'
|
||||||
|
import os, sys
|
||||||
|
unit_path, etc_user, unit = sys.argv[1:]
|
||||||
|
in_install = False
|
||||||
|
targets = []
|
||||||
|
for line in open(unit_path):
|
||||||
|
s = line.strip()
|
||||||
|
if s.startswith("[") and s.endswith("]"):
|
||||||
|
in_install = s == "[Install]"
|
||||||
|
continue
|
||||||
|
if in_install and s.startswith("WantedBy="):
|
||||||
|
targets.extend(t for t in s.split("=", 1)[1].split() if t)
|
||||||
|
for target in targets:
|
||||||
|
wants = os.path.join(etc_user, f"{target}.wants")
|
||||||
|
os.makedirs(wants, exist_ok=True)
|
||||||
|
dest = os.path.join(wants, unit)
|
||||||
|
if os.path.lexists(dest):
|
||||||
|
os.remove(dest)
|
||||||
|
os.symlink(f"/usr/lib/systemd/user/{unit}", dest)
|
||||||
|
print(f" global enable {unit} -> {dest}")
|
||||||
|
PY
|
||||||
echo " baked $unit -> $IMAGE_UNITS/$unit"
|
echo " baked $unit -> $IMAGE_UNITS/$unit"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Document the baked set. The committed preset is the fallback; the staged
|
||||||
|
# copy lists whatever this bake actually shipped.
|
||||||
|
preset_dest="$AIROOTFS/usr/lib/systemd/user-preset/90-bos-bakery.preset"
|
||||||
|
install -d -m 0755 "$(dirname "$preset_dest")"
|
||||||
|
{
|
||||||
|
echo "# Bakery systemd --user units baked into this image."
|
||||||
|
echo "# Applied by systemctl --global enable (post-install + live setup)"
|
||||||
|
echo "# so a later useradd starts them on first login."
|
||||||
|
echo "# breadclipd is also started from hyprland.lua: WantedBy="
|
||||||
|
echo "# graphical-session.target is not reached on BOS (no uwsm)."
|
||||||
|
for unit in "${SERVICE_UNITS[@]}"; do
|
||||||
|
[[ -n "$unit" ]] || continue
|
||||||
|
printf 'enable %s\n' "$unit"
|
||||||
|
done
|
||||||
|
} >"$preset_dest"
|
||||||
|
echo " wrote $preset_dest"
|
||||||
|
|
||||||
# mkarchiso resets every airootfs file to 0644, so executables must be declared
|
# mkarchiso resets every airootfs file to 0644, so executables must be declared
|
||||||
# in profiledef.sh's file_permissions array or they ship non-executable and the
|
# in profiledef.sh's file_permissions array or they ship non-executable and the
|
||||||
# exec-once launches fail with "permission denied". Inject a 0755 entry for each
|
# exec-once launches fail with "permission denied". Inject a 0755 entry for each
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,15 @@ fi
|
||||||
# /usr/lib/systemd/user (system prefix). Per-user bakery state (installed.json
|
# /usr/lib/systemd/user (system prefix). Per-user bakery state (installed.json
|
||||||
# + index cache) is seeded from /etc/skel/.local and copied into the user's
|
# + index cache) is seeded from /etc/skel/.local and copied into the user's
|
||||||
# home below, so the install works fully offline with no DNS for bakery.
|
# home below, so the install works fully offline with no DNS for bakery.
|
||||||
|
#
|
||||||
|
# systemd --user units in /usr/lib/systemd/user are not enabled for new
|
||||||
|
# accounts unless enabled --global (or the user enables them). Do that here
|
||||||
|
# so a later `useradd -m` starts breadd / breadbox-sync / breadclipd /
|
||||||
|
# breadcrumbs / breadmill on first login. Safe if the helper is missing.
|
||||||
|
if [[ -x /usr/local/bin/bos-enable-bakery-user-units ]]; then
|
||||||
|
/usr/local/bin/bos-enable-bakery-user-units \
|
||||||
|
|| echo "WARN: enabling bakery user units globally failed"
|
||||||
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Deploy dotfiles + the bakery bread ecosystem into the user's home (Calamares
|
# Deploy dotfiles + the bakery bread ecosystem into the user's home (Calamares
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,8 @@ GROUP=users
|
||||||
HOME=/home
|
HOME=/home
|
||||||
INACTIVE=-1
|
INACTIVE=-1
|
||||||
EXPIRE=
|
EXPIRE=
|
||||||
|
# useradd -m copies Hyprland + bakery per-user state from here. Bakery
|
||||||
|
# binaries live in /usr/local/bin (not skel). User units are enabled
|
||||||
|
# --global so a second account starts them on first login.
|
||||||
SKEL=/etc/skel
|
SKEL=/etc/skel
|
||||||
CREATE_MAIL_SPOOL=no
|
CREATE_MAIL_SPOOL=no
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,8 @@ hl.on("hyprland.start", function()
|
||||||
-- pywal only runs for real once the user picks a wallpaper themselves.
|
-- pywal only runs for real once the user picks a wallpaper themselves.
|
||||||
[[bash -c 'until awww img /usr/share/backgrounds/bos/bread-background.png 2>/dev/null; do sleep 0.3; done']],
|
[[bash -c 'until awww img /usr/share/backgrounds/bos/bread-background.png 2>/dev/null; do sleep 0.3; done']],
|
||||||
-- breadd runs as a systemd user service (/usr/lib/systemd/user/breadd.service,
|
-- breadd runs as a systemd user service (/usr/lib/systemd/user/breadd.service,
|
||||||
-- plus a skel copy). It autostarts at login but before Hyprland exists, so
|
-- enabled --global so every account starts it). It autostarts at login
|
||||||
|
-- but before Hyprland exists, so
|
||||||
-- push the compositor's Wayland env into the user manager and restart breadd
|
-- push the compositor's Wayland env into the user manager and restart breadd
|
||||||
-- to pick it up — that's how it gets HYPRLAND_INSTANCE_SIGNATURE to talk to Hyprland.
|
-- to pick it up — that's how it gets HYPRLAND_INSTANCE_SIGNATURE to talk to Hyprland.
|
||||||
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
|
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/usr/lib/systemd/user/breadd.service
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Bakery systemd --user units. `systemctl --global enable` (post-install and
|
||||||
|
# live setup) applies these so a later `useradd -m` starts them on first login.
|
||||||
|
# Bake rewrites this list from the units actually copied into the image.
|
||||||
|
#
|
||||||
|
# breadclipd is WantedBy=graphical-session.target. BOS does not activate that
|
||||||
|
# target (no uwsm); Hyprland still `systemctl --user start`s it after the
|
||||||
|
# compositor is up. --global enable still records it for every account.
|
||||||
|
enable breadd.service
|
||||||
|
enable breadbox-sync.service
|
||||||
|
enable breadclipd.service
|
||||||
|
enable breadcrumbs.service
|
||||||
|
enable breadmill.service
|
||||||
90
iso/airootfs/usr/local/bin/bos-enable-bakery-user-units
Executable file
90
iso/airootfs/usr/local/bin/bos-enable-bakery-user-units
Executable file
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Enable bakery systemd --user units for every account (current and future).
|
||||||
|
#
|
||||||
|
# `systemctl --global enable` writes /etc/systemd/user/<target>.wants/ so a
|
||||||
|
# later `useradd -m` does not need per-home enablement. Bins live in
|
||||||
|
# /usr/local; only per-user state comes from skel.
|
||||||
|
#
|
||||||
|
# Safe on the live image and in the Calamares post-install chroot.
|
||||||
|
# Idempotent. Does not start units (no user session required).
|
||||||
|
#
|
||||||
|
# breadclipd is WantedBy=graphical-session.target. BOS does not activate
|
||||||
|
# that target (no uwsm), so Hyprland still `systemctl --user start`s it.
|
||||||
|
# --global enable still records it for every account / bos-settings.
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
UNITS_DIR=/usr/lib/systemd/user
|
||||||
|
PRESET=/usr/lib/systemd/user-preset/90-bos-bakery.preset
|
||||||
|
|
||||||
|
is_blocked() {
|
||||||
|
case "$1" in
|
||||||
|
breadcast*|breadarr*) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
is_bakery_unit() {
|
||||||
|
local unit="$1" path="$UNITS_DIR/$unit"
|
||||||
|
[[ -f "$path" ]] || return 1
|
||||||
|
is_blocked "$unit" && return 1
|
||||||
|
grep -qE '^ExecStart=/usr/local/bin/' "$path"
|
||||||
|
}
|
||||||
|
|
||||||
|
list_from_preset() {
|
||||||
|
[[ -f "$PRESET" ]] || return 0
|
||||||
|
awk '/^enable[[:space:]]/ { print $2 }' "$PRESET"
|
||||||
|
}
|
||||||
|
|
||||||
|
list_from_units_dir() {
|
||||||
|
[[ -d "$UNITS_DIR" ]] || return 0
|
||||||
|
local path unit
|
||||||
|
for path in "$UNITS_DIR"/*.service; do
|
||||||
|
[[ -f "$path" ]] || continue
|
||||||
|
unit="$(basename "$path")"
|
||||||
|
is_bakery_unit "$unit" && printf '%s\n' "$unit"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
list_from_installed_json() {
|
||||||
|
local json=/etc/skel/.local/state/bakery/installed.json
|
||||||
|
[[ -f "$json" ]] || return 0
|
||||||
|
command -v python3 >/dev/null 2>&1 || return 0
|
||||||
|
python3 - "$json" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
with open(sys.argv[1]) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
for pkg in data.get("packages", data).values():
|
||||||
|
if not isinstance(pkg, dict):
|
||||||
|
continue
|
||||||
|
for svc in pkg.get("services") or []:
|
||||||
|
name = svc["unit"] if isinstance(svc, dict) else svc
|
||||||
|
if name and not str(name).startswith(("breadcast", "breadarr")):
|
||||||
|
print(name)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
mapfile -t units < <(
|
||||||
|
{ list_from_preset; list_from_units_dir; list_from_installed_json; } \
|
||||||
|
| sed '/^$/d' | sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ ${#units[@]} -eq 0 ]]; then
|
||||||
|
echo "WARN: no bakery user units found to enable globally"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v systemctl >/dev/null 2>&1; then
|
||||||
|
echo "WARN: systemctl missing — cannot --global enable bakery user units"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for unit in "${units[@]}"; do
|
||||||
|
[[ -f "$UNITS_DIR/$unit" ]] || continue
|
||||||
|
is_blocked "$unit" && continue
|
||||||
|
if ! grep -q '^\[Install\]' "$UNITS_DIR/$unit"; then
|
||||||
|
echo "WARN: $unit has no [Install] section — skip --global enable"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
systemctl --global enable "$unit" \
|
||||||
|
|| echo "WARN: systemctl --global enable $unit failed"
|
||||||
|
done
|
||||||
|
|
@ -7,9 +7,17 @@
|
||||||
# bos-launch-calamares). Runs once at boot, before the tty1 autologin getty.
|
# bos-launch-calamares). Runs once at boot, before the tty1 autologin getty.
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Bakery user units live in /usr/lib/systemd/user. --global enable writes
|
||||||
|
# /etc/systemd/user/*.wants/ so liveuser (and any later account) starts
|
||||||
|
# them on first login. Idempotent; bins are already in /usr/local.
|
||||||
|
if [[ -x /usr/local/bin/bos-enable-bakery-user-units ]]; then
|
||||||
|
/usr/local/bin/bos-enable-bakery-user-units \
|
||||||
|
|| echo "WARN: enabling bakery user units globally failed"
|
||||||
|
fi
|
||||||
|
|
||||||
# useradd -m copies /etc/skel, so the live user gets the real BOS desktop
|
# useradd -m copies /etc/skel, so the live user gets the real BOS desktop
|
||||||
# (breadd + breadbar + breadbox + keybinds) — proper live-media functionality,
|
# (hypr + bread config + bakery state) — proper live-media functionality,
|
||||||
# not an installer kiosk.
|
# not an installer kiosk. Binaries are /usr/local, not skel.
|
||||||
if ! id liveuser &>/dev/null; then
|
if ! id liveuser &>/dev/null; then
|
||||||
useradd -m -s /usr/bin/zsh liveuser
|
useradd -m -s /usr/bin/zsh liveuser
|
||||||
for g in wheel video input audio storage power; do
|
for g in wheel video input audio storage power; do
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,5 @@ file_permissions=(
|
||||||
["/usr/local/bin/bos-update"]="0:0:755"
|
["/usr/local/bin/bos-update"]="0:0:755"
|
||||||
["/usr/local/bin/bos-rescue"]="0:0:755"
|
["/usr/local/bin/bos-rescue"]="0:0:755"
|
||||||
["/usr/local/bin/bos-first-boot"]="0:0:755"
|
["/usr/local/bin/bos-first-boot"]="0:0:755"
|
||||||
|
["/usr/local/bin/bos-enable-bakery-user-units"]="0:0:755"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ if [[ -n "$AIROOTFS" || -n "$SKEL" ]]; then
|
||||||
bad "skel still has bakery bin $b (belongs in /usr/local/bin)"
|
bad "skel still has bakery bin $b (belongs in /usr/local/bin)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
check_file "$SKEL/.config/hypr/hyprland.lua" "skel hyprland.lua"
|
||||||
fi
|
fi
|
||||||
image_units_json=""
|
image_units_json=""
|
||||||
if [[ -n "$SKEL" && -f "$SKEL/.local/state/bakery/installed.json" ]]; then
|
if [[ -n "$SKEL" && -f "$SKEL/.local/state/bakery/installed.json" ]]; then
|
||||||
|
|
@ -183,6 +184,34 @@ PY
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
check_file "$AIROOTFS/usr/lib/systemd/user-preset/90-bos-bakery.preset" \
|
||||||
|
"bakery user preset"
|
||||||
|
if [[ -L "$AIROOTFS/etc/systemd/user/default.target.wants/breadd.service" ]] \
|
||||||
|
|| [[ -f "$AIROOTFS/etc/systemd/user/default.target.wants/breadd.service" ]]; then
|
||||||
|
ok "breadd.service globally enabled (etc wants)"
|
||||||
|
else
|
||||||
|
bad "breadd.service missing from /etc/systemd/user/default.target.wants"
|
||||||
|
fi
|
||||||
|
# After bake the image has /usr/local/bin/breadd and every preset unit.
|
||||||
|
# The committed airootfs only has the preset + breadd wants.
|
||||||
|
if [[ -f "$AIROOTFS/usr/lib/systemd/user-preset/90-bos-bakery.preset" ]] \
|
||||||
|
&& [[ -x "$AIROOTFS/usr/local/bin/breadd" ]]; then
|
||||||
|
while read -r verb unit; do
|
||||||
|
[[ "$verb" == enable && -n "$unit" ]] || continue
|
||||||
|
check_file "$AIROOTFS/usr/lib/systemd/user/$unit" "preset unit $unit"
|
||||||
|
if [[ -L "$AIROOTFS/etc/systemd/user/default.target.wants/$unit" ]] \
|
||||||
|
|| [[ -L "$AIROOTFS/etc/systemd/user/graphical-session.target.wants/$unit" ]]; then
|
||||||
|
ok "$unit globally enabled (etc wants)"
|
||||||
|
else
|
||||||
|
bad "$unit missing from /etc/systemd/user/*.target.wants"
|
||||||
|
fi
|
||||||
|
done < "$AIROOTFS/usr/lib/systemd/user-preset/90-bos-bakery.preset"
|
||||||
|
fi
|
||||||
|
if [[ -x "$AIROOTFS/usr/local/bin/bos-enable-bakery-user-units" ]]; then
|
||||||
|
ok "bos-enable-bakery-user-units executable"
|
||||||
|
else
|
||||||
|
bad "bos-enable-bakery-user-units missing or not executable"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,34 @@ check "bos-netcheck present" "command -v bos-netcheck"
|
||||||
check "bos-rescue present" "command -v bos-rescue"
|
check "bos-rescue present" "command -v bos-rescue"
|
||||||
check "bos-first-boot present" "command -v bos-first-boot"
|
check "bos-first-boot present" "command -v bos-first-boot"
|
||||||
|
|
||||||
|
echo "== bakery user units (global enable) =="
|
||||||
|
# A later useradd does not enable --user units unless they were enabled
|
||||||
|
# --global (or the user enables them). post-install + live-setup + bake
|
||||||
|
# write /etc/systemd/user/<target>.wants/ and a preset listing the set.
|
||||||
|
check "bakery user preset present" \
|
||||||
|
"[ -f /usr/lib/systemd/user-preset/90-bos-bakery.preset ]"
|
||||||
|
check "bos-enable-bakery-user-units present" \
|
||||||
|
"command -v bos-enable-bakery-user-units"
|
||||||
|
check "breadd.service globally enabled" \
|
||||||
|
"systemctl --global is-enabled breadd.service || [ -L /etc/systemd/user/default.target.wants/breadd.service ]"
|
||||||
|
if [[ -f /usr/lib/systemd/user-preset/90-bos-bakery.preset ]]; then
|
||||||
|
while read -r verb unit; do
|
||||||
|
[[ "$verb" == enable && -n "$unit" ]] || continue
|
||||||
|
[[ -f /usr/lib/systemd/user/$unit ]] || continue
|
||||||
|
check "$unit globally enabled" \
|
||||||
|
"systemctl --global is-enabled $unit || [ -L /etc/systemd/user/default.target.wants/$unit ] || [ -L /etc/systemd/user/graphical-session.target.wants/$unit ]"
|
||||||
|
done < /usr/lib/systemd/user-preset/90-bos-bakery.preset
|
||||||
|
fi
|
||||||
|
check "skel hyprland.lua present" "[ -f /etc/skel/.config/hypr/hyprland.lua ]"
|
||||||
|
check "skel bakery installed.json present" \
|
||||||
|
"[ -f /etc/skel/.local/state/bakery/installed.json ]"
|
||||||
|
check "skel bakery index cache present" \
|
||||||
|
"[ -f /etc/skel/.cache/bakery/index.json ]"
|
||||||
|
check "skel has no bakery binaries" \
|
||||||
|
"! [ -e /etc/skel/.local/bin/bakery ] && ! [ -e /etc/skel/.local/bin/breadd ]"
|
||||||
|
check "useradd SKEL is /etc/skel" \
|
||||||
|
"grep -q '^SKEL=/etc/skel' /etc/default/useradd"
|
||||||
|
|
||||||
echo "== default dotfiles =="
|
echo "== default dotfiles =="
|
||||||
check "hyprland.lua present" "[ -f \"\$HOME/.config/hypr/hyprland.lua\" ]"
|
check "hyprland.lua present" "[ -f \"\$HOME/.config/hypr/hyprland.lua\" ]"
|
||||||
check "binds.json present" "[ -f \"\$HOME/.config/hypr/binds.json\" ]"
|
check "binds.json present" "[ -f \"\$HOME/.config/hypr/binds.json\" ]"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue