Nimbin[12]?Linux From Scratch / Packagemanager-LFS-PackageUser-System / stacks/services.stack

Packagemanager-LFS-PackageUser-System git · main

Package-user based package manager for LFS/BLFS

lfs blfs package-manager bash · first commit 2026-08-18 · last commit 2026-10-26 (8 days ago) · synced 8 hours ago · upstream: github.com/nimbin2/Packagemanager-LFS-PackageUser-System

Shell 63.7% Markdown 35.8%
git clone https://git.christianimmanuel.de/linux-from-scratch/Packagemanager-LFS-PackageUser-System.gitwget https://git.christianimmanuel.de/linux-from-scratch/Packagemanager-LFS-PackageUser-System/archive/Packagemanager-LFS-PackageUser-System.tar.gz
stacks/services.stack 13.5 KB · 230 lines raw
# services.stack -- the boot-time services a SysV desktop needs.
# stack-version: 1.14.369
#
# WHAT THIS MACHINE NEVER BUILDS is in avoid.stack, included by every stack
# here: the exclusions are a property of the machine, not of one stack.
# Without it a dependency plan walks into systemd, qt6, cups and texlive.
include avoid

#
# /etc/init.d after an LFS build holds only LFS's own scripts; nothing starts
# dbus, alsa, bluetooth or a cron daemon.  Those scripts live in
# blfs-bootscripts, which is a separate tarball, not a BLFS package -- which
# is why a package's own page says "make install-<service>" and the generated
# script comments it out (1.14.20): the target is in THIS package, not that
# one.
#
#   packagemanager stack services --yes --run
#
# Run it after sway.stack and network.stack: a boot script for a service that
# is not installed is harmless but pointless.

# Everything of your own, under ONE package user (p_mytools): the seatd-user
# wrapper, sway_start, the wifi script, and -- when you uncomment them in
# stacks/mytools/install_mytools -- swas, swov, swbr and browser.  Add a line
# to SOURCES there and re-run; the package user owns the files, pkg.lst
# records them, and `packagemanager remove mytools` removes the lot.
#
# seatd-user replaces a seatd boot service: a setuid program compiled for ONE
# username, so there is no daemon and no `seat` group.
cfg    mytools

# (The kernel is NOT a stack entry: it is `lfs-kernel`, its own tool, so the
#  config can be edited before anything is built.  See /etc/pkgusr/kernel.conf
#  and `lfs-kernel --help`.)

# bash-completion is NOT a BLFS package -- it comes from upstream.  2.18.0 is
# what the remote reports as newest (checked with git ls-remote); `--check-refs`
# will confirm it before any build.
git    bash-completion url=https://github.com/scop/bash-completion ref=2.18.0 \
       have=/usr/share/bash-completion/bash_completion \
       build="autoreconf -i && ./configure --prefix=/usr --sysconfdir=/etc" \
       install="make install"

# coloured ls, the BLFS /etc/bashrc, `su <tab>` over the package users, and
# $LANG.  The Bash Shell Startup Files page is CONFIG, not a package, so no
# package stack ever ran it.
#
# expect= is what this step keeps true, re-checked even when the entry is
# cached (see shadow-pam).  Without it a machine that ran this step before
# the locale was part of it stays at `done` for ever -- and a terminal keeps
# saying `'C' is not a UTF-8 locale`, with nothing to say why.
cfg    shell-env \
       expect='test -f /etc/bashrc && test -f /etc/locale.conf && { test ! -d /etc/profile.d || test -f /etc/profile.d/i18n.sh; }'

# BTRFS, ON A BTRFS MACHINE ONLY.  `btrfs check`, `scrub`, `balance`,
# subvolumes and snapshots all come from btrfs-progs, and without it a btrfs
# root can be mounted and nothing else -- no repair, no snapshot, and
# mkinitramfs/fsck have no helper.  when= asks the machine: a filesystem
# mounted now, or a partition on any disk (so it is also right in a chroot
# whose target is btrfs and in the installer, before the mount).
book   btrfs-progs \
       when="grep -qw btrfs /proc/mounts || lsblk -no FSTYPE 2>/dev/null | grep -qx btrfs || blkid -t TYPE=btrfs >/dev/null 2>&1" \
       expect="command -v btrfs >/dev/null && btrfs --version >/dev/null"

# EXFAT, WHICH ARRIVES IN A POCKET.  mkfs.exfat / fsck.exfat / exfatlabel
# for the cards and sticks that come formatted that way -- no when= test,
# because the medium is not in the machine when the stack runs.
#
# NOT A BLFS PAGE.  The book carries no exFAT package at all (checked
# against the stable index, 1.14.330); what it links to in passing is
# relan/exfat, the FUSE implementation -- a different thing.  This is the
# userspace for the KERNEL driver, which is the modern path and the one
# this machine uses:
#     lfs-kernel enable exfat        # EXFAT_FS=m, then lfs-kernel --run
# relan/exfat (mount.exfat-fuse) is only worth having on a kernel without
# that driver.
#
# The release tarball is an autotools dist, so ./configure is in it; the
# build line falls back to autogen.sh if a future tarball is a bare export.
tar    exfatprogs \
       url=https://github.com/exfatprogs/exfatprogs/releases/download/1.4.2/exfatprogs-1.4.2.tar.xz \
       pkg=exfatprogs-1.4.2.tar.xz \
       prog=mkfs.exfat \
       build="test -x ./configure || ./autogen.sh; ./configure --prefix=/usr --sbindir=/usr/sbin; make" \
       install="make install" \
       expect="command -v mkfs.exfat >/dev/null && command -v fsck.exfat >/dev/null"

# NTFS: the disks Windows formatted.  The kernel's ntfs3 driver mounts them
# (lfs-kernel enable ntfs3); ntfs-3g brings what it does not: mkntfs,
# ntfsfix, ntfsresize, ntfslabel -- which is what gparted calls to create,
# repair and resize an NTFS partition -- and the FUSE mount.ntfs-3g as the
# fallback driver.  The book builds it against its own bundled FUSE
# (--with-fuse=internal), so the only other need is the kernel's:
#     lfs-kernel enable fuse         # FUSE_FS=m, then lfs-kernel --run
book   ntfs-3g \
       expect="command -v mkntfs >/dev/null && command -v ntfsfix >/dev/null"

book   acpid                    # power/lid/button events -- what a laptop needs

# ONE GROUP CARRIES THE PRIVILEGE.  halt/poweroff/reboot must become root, so
# they are root's with a group deciding who may run them, and the kernel's
# power interface is re-grouped on every boot (/sys does not survive one).
# Set POWER_GROUP to override the <collector_prefix>_power default.
# linux-pam, BEFORE the accounts that need it.
#
# `su - u_claude` without a password works through pam_exec.so reading
# /etc/pam.d/su_u_user -- which needs Linux-PAM, and LFS installs neither pam
# nor a pam-aware shadow.  Without it the accounts, groups, shared
# directories and launchers all still work; only PASSWORDLESS su does not.
#
# This entry installs the LIBRARY, which on its own changes nothing: until
# shadow is rebuilt against it, nothing reads /etc/pam.d at all.  That
# rebuild is deliberately NOT here:
#
#     packagemanager update shadow --reinstall --regenerate --run --yes
#
# It is the one step in this whole system that can leave you unable to `su`
# or log in, so it wants a root shell open, the book's Shadow page read, and
# a moment when nothing else is running.  `cfg app-users` prints the same
# thing when it finds no /etc/pam.d.
#
# It was in avoid.stack until 1.14.235 -- as systemd's dependency, which is
# not why it is here now.  With `install`/`update` honouring the avoid list
# (also 1.14.234), asking for it no longer drags systemd in behind it.
book   linux-pam

# ...AND THE REBUILD THAT MAKES IT MEAN ANYTHING, immediately after it.
#
# Linux-PAM above installs a library and changes nothing: until shadow is
# rebuilt against it, nothing reads /etc/pam.d, so `su - u_claude` keeps
# asking for a password however right su_u_user is.  The book's Linux-PAM
# page ends with "You should now reinstall the Shadow package" -- so this
# step does, and then puts back the setuid bits that the rebuild strips
# (the BLFS Shadow page never sets them; the LFS one does).
#
# It is idempotent and checks its own effect: pam linkage with ldd, the
# modes by reading them back.  With no Linux-PAM installed it says so and
# does nothing.
#
# expect= is what this step keeps true, and it is re-checked on every run even
# when the entry is cached: shadow's configure phase or a rebuild can undo it
# later, and a cached "done" then hides it (1.14.275).
cfg    shadow-pam \
       expect='test -u /usr/bin/su && test "$(stat -c %U /usr/bin/su)" = root && { test ! -d /etc/pam.d || { test -f /etc/pam.d/login && test -f /etc/pam.d/passwd && test -f /etc/pam.d/system-auth; }; }'

# THE APPLICATION ACCOUNTS, from a file rather than a command line.
#
# Each line of app-users.conf becomes a `packagemanager user setup` call, and
# this step also does the two things no per-account command can: merge
# /etc/pam.d/su_u_user (one file listing every account that may be entered
# without a password) and write /etc/profile.d/shared-dirs.sh (one file
# exporting SHARED_<NAME> for each link in the hub,
# /home/Shared -- your ~/Shared links there).
#
# Needs a main user: `packagemanager config --main-user <your-login>`.
# Nothing of yours is edited -- no .bashrc, no .bash_profile -- and the PAM
# file is backed up before it is touched.
#
# Re-checked on every run (see shadow-pam): the accounts IT MANAGES (the
# names in app-users.conf -- not every u_*: a hand-made u_test is not its
# business) are not directly in /home, su calls su_u_user, root owns it, and
# it checks the caller, not the target.  And (1.14.312) your ~/Shared is
# the link to the hub, not a directory of links: a machine set up before the
# hub existed re-runs this step once and moves them.
cfg    app-users \
       expect='test -x /usr/bin/xdg-runtime-share && ! awk -F"|" "\$1 !~ /^[[:space:]]*(#|name[[:space:]]|\$)/ { gsub(/[[:space:]]/, \"\", \$1); print \"u_\" \$1 }" /etc/pkgusr/stacks/app-users.conf | while read -r u; do getent passwd "$u" | cut -d: -f6; done | grep -qE "^/home/[^/]+\$" && { test ! -f /etc/pam.d/su || grep -qE "^[[:space:]]*auth[[:space:]]+sufficient[[:space:]]+pam_exec[.]so.*su_u_user" /etc/pam.d/su; } && { test ! -e /etc/pam.d/su_u_user || { test "$(stat -c %U:%a /etc/pam.d/su_u_user)" = root:755 && ! grep -qF -e "=~ \"\$PAM_USER\"" /etc/pam.d/su_u_user; }; } && { m="$(grep "^main_user=" /etc/pkgusr/packagemanager.conf 2>/dev/null | head -n1 | cut -d= -f2)"; h="$(getent passwd "$m" 2>/dev/null | cut -d: -f6)"; [ -z "$h" ] || [ ! -d "$h/Shared" ] || [ -L "$h/Shared" ]; }'

# WHO MAY TURN THE MACHINE OFF.  A package user owns files, not identity:
# halt, poweroff and reboot have to BECOME root, so they stay root's and a
# group decides who may run them -- 4550 root:<collector>_power, with the
# desktop user in that group.  The same step writes the power-perms boot
# script, because /sys/power/state is made fresh at every boot and its group
# has to be re-applied there.
#
# expect= matters more here than almost anywhere else.  Without it this entry
# was cached "done" for ever, and the modes it set are exactly the kind that
# get destroyed silently: a sysvinit rebuild reinstalls halt without the
# setuid bit, and a `chown` -- from anything, including this project's own
# ownership sweep before 1.14.308 -- clears setuid as a side effect the
# kernel never reports.  Then poweroff stops working for the user and nothing
# says why.  Re-checked on every run: the group, the mode and owner of halt,
# and the boot script.
cfg    power \
       expect='g="${POWER_GROUP:-}"; if [ -z "$g" ]; then p="$(grep "^collector_prefix=" /etc/pkgusr/packagemanager.conf 2>/dev/null | head -n1 | cut -d= -f2)"; g="${p%_}_power"; fi; h=/usr/bin/halt; [ -f "$h" ] || h=/sbin/halt; getent group "$g" >/dev/null 2>&1 && test -f /etc/rc.d/init.d/power-perms && test "$(stat -Lc "%a %U:%G" "$h" 2>/dev/null)" = "4550 root:$g"'
book   fcron                    # the cron daemon; make-ca's weekly update needs one
# ...and what its page sets up after the install, as root: /usr/bin/run-parts
# and /etc/cron.{hourly,daily,weekly,monthly}, which the system table calls.
cfg    fcron-periodic \
       expect="test -x /usr/bin/run-parts && test -d /etc/cron.weekly"

# blfs-bootscripts: 20250225 is the FINAL release (BLFS has stopped shipping
# the SysV edition).  Only the scripts for services this system actually has
# are installed -- adding one for something uninstalled just leaves a symlink
# in rc?.d that fails at boot.
#
#   dbus         needed by sway, elogind and most of the desktop
#   alsa         saves/restores the mixer state across reboots
#   bluetooth    bluetoothd, from BlueZ
#   acpid        lid and power button
#   fcron        cron
#   iptables     restores the firewall ruleset at boot
#   random       seeds /dev/urandom from the saved pool
#   service-wpa  the `wpa` helper `ifup` uses for a wireless interface
# install-service-dhcpcd WAS MISSING.  The dhcpcd PACKAGE installs no service
# script; /lib/services/dhcpcd comes from here, and `SERVICE=dhcpcd` (or a
# wpa hand-off to it) silently has nothing to run without it.  Adding a
# package to the network stack was only half the job.
tar    blfs-bootscripts \
       url=https://anduin.linuxfromscratch.org/BLFS/blfs-bootscripts/blfs-bootscripts-20250225.tar.xz \
       pkg=blfs-bootscripts-20250225.tar.xz \
       have=/etc/rc.d/init.d/dbus \
       build="true" \
       install="make install-dbus install-alsa install-bluetooth install-acpid install-fcron install-iptables install-random install-service-wpa install-service-dhcpcd install-sshd"

# ...AND THE SAME LIST AS A MAPPING, so it cannot go stale again.
#
# The install= line above is a list written on one day.  Add a package to a
# stack afterwards and its bootscript is simply not installed: the daemon is
# there, nothing starts it, and nothing says so.  `cfg bootscripts` reads
# bootscripts.conf -- package | proof | targets | creates -- and installs the
# targets whose package IS here and whose script is NOT, then checks the disk
# rather than make's exit status.  It also writes the one thing a bootscript
# cannot supply: /etc/sysconfig/ifconfig.<iface> for a WIRED interface, which
# is what actually makes dhcpcd run at boot (there is no init.d/dhcpcd in
# BLFS at all).  Wireless is left to `check net-wifi`: that file needs an SSID.
#
# expect= is re-checked even when the entry is cached, so a package installed
# later is noticed on the next run.
cfg    bootscripts \
       expect='! command -v dhcpcd >/dev/null 2>&1 || { { test -e /lib/services/dhcpcd || test -e /usr/lib/services/dhcpcd; } && ls /etc/sysconfig/ifconfig.* >/dev/null 2>&1; }'