git.christianimmanuel.de / Linux From Scratch / Packagemanager-LFS-PackageUser-System

Packagemanager-LFS-PackageUser-System git · main

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
d612779Fixed cfg steps And stuffChristian Immanuel · 7 days ago
dOLD/
dskel-u_xdg/
-.bash_profile1.4 KB
-blfs81.1 KB
-collector-groups.export817 B
-HANDOFF.md55.8 KB
-last_build_step.sh11.5 KB
-lfs302.8 KB
-lfs-completion.bash5 KB
-lfs-helper287.7 KB
-lfs-sanity.sh12.5 KB
-Makefile3.6 KB
-packagemanager246 KB
-packagemanager_install32.7 KB
-README.md7.6 KB
-test_lfs_crosschain.sh417.4 KB
-test_packagemanager.sh10.5 KB

README.md

lfs-pkgusr

Build and maintain an LFS/BLFS system where **every package is owned by its own user**.

100% vibecode, but tested. Prompted into existence rather than hand
written. Every fix carries a regression test. It builds my own system —
read it before you point it at yours.

What it does

Each package gets an unprivileged user that owns the files it installs.

  • You always know which package installed which file.
  • Removing a package is removing that user's files.
  • A broken build cannot overwrite another package's files.

Directories shared by several packages are handled by collector groups (<prefix>_<owner>), so a package may install into another's directory without owning it.

Works offline from a downloaded copy of the books.


The four tools

ToolRunsDoes
lfshostDrives the whole build, chapters 1-10
lfs-helperinside the chrootBuilds each package as its user (bash, no Python needed)
packagemanagerbuilt systemInstalls and updates packages
blfsbuilt systemReads the BLFS book, generates install scripts

Install

make install                        # to /usr
make install PREFIX=/usr/local
make install DESTDIR=/mnt/lfs       # into a tree, not the running system
make uninstall

/usr is the default: the chroot invokes these by name, and packagemanager runs packagemanager_install off $PATH.

Installs the four tools, packagemanager_install, and the completion script.

Requires: Python 3.9+, bash, coreutils, tar. Book parsing needs beautifulsoup4 and requests.


Build a system

lfs build-system session      # partition, mount point, prefix, locale
lfs run                       # everything up to the chroot, then enter it
lfs-helper build-all          # inside: build chapters 7-9

lfs run continues from wherever it stopped. Run it again after a reboot, a failure, or a cancel.

CommandDoes
lfs build-system nextWhat is done, what comes next
lfs build-system listEvery step and its state
lfs build-system snapshot save <name> --runSave the build
lfs build-system snapshot restore <name> --runPut it back
lfs build-system restart --runDelete it all and start over

Use the built system

A fresh system has no download tool, and lfs and blfs need requests and beautifulsoup4 to read the books. Bootstrap both, offline, from what get-sources already downloaded:

packagemanager setup            # what it would do
packagemanager setup --run

Each module is installed as its own package user. Run it again after fixing a failure; it skips what is already there.

packagemanager install <pkg> --recursive --run
packagemanager update <pkg> --run
packagemanager which-package /usr/bin/foo
blfs order <pkg>                     # build order, dependencies first
blfs sources <pkg>                   # download URLs

Application users

Separate from package users. An account a program runs as, so a browser cannot read your files:

packagemanager user create firefox --shared --share-dir --launcher
FlagDoes
--sharedJoins the group on your XDG runtime dir, and links /etc/pkgusr/skel-u_xdg/.bash_profile — display, session bus, audio
--share-dirA directory both accounts can write, linked into your home
--launcherA wrapper in ~/bin that runs the program as that account

The session group is read from the runtime directory, not assumed. Set it up once for your own account; every shared user then joins whatever is there.


When ownership starts

Book 7.6 creates /etc/passwd. Before that no name resolves, so ownership has no meaning and nothing tries to set it.

init-ownership is a step in the build, right after init-files (book 7.6). It creates the install group, sets the install directories, gives package users to everything already built, and adopts it. From there on every install is owned by the package that made it.

lfs-helper establish-ownership      # by hand, or to check whether it happened

Repair

Everything is dry-run by default. --run applies.

CommandFixes
lfs-helper check-toolchainCan the compiler build anything?
lfs-helper fix-ownership --runGive each package its files
lfs-helper fix-orphans --runFiles whose owner does not exist
lfs-helper which-package <path>Which package installed this
lfs-helper manifestsWhat each package installed
lfs-helper sort-users --runSort passwd and group by id

Names and places

Three kinds of account share one passwd file, so each carries a prefix. Without one a package called man or news collides with a real account.

One account, one prefix — its own.

ThingLooks likeLives in
package userp_gcc/usr/src/pkgusr/p_gcc
config-step usercfg_bootscripts/usr/src/cfg/cfg_bootscripts
application useru_firefox/usr/src/u_firefox

There is no tmp_ and no init_. A temporary step is the same package chapter 8 rebuilds, so it is built as that package's user. An init step owns no files and gets no account.

Prefixes and locations are set once, in lfs build-system session.

Sources, build trees and state are three different things:

DirectoryJob
$LFS/sourcesdownloaded tarballs, never written by a build
<package home>/srcwhere that package unpacks and builds, owned by its user
$LFS/buildscratch for steps with no account. Deleted whole

Everything the build knows about the tree lives in /usr/src/lfs-pkgusr, beside the accounts it describes:

scripts/one shell script per build step
manifests/what each package installed
logs/one log per step, plus verify.log
progress/what has been built, and how far
groups/collector groups
config/settings you may edit

Conventions

RangeUsed for
9998lfs build user
9999install group
10000+Package users, in build order
90000+Collector groups

Install directories are group-writable during the build and become sticky (o+t) at the end, so packages can no longer overwrite each other.

The account roots are not install directories. Only root creates a home there, so they stay root:root 0755 — group-writable with no sticky bit would let any package user delete another package's home.


Checking your version

The tools print a fingerprint of their own contents:

lfs --version              # name, version, build id
lfs-helper --version       # inside the chroot

Same build id means same code. The chroot copy refreshes automatically when you enter it.


Tests

bash test_lfs_crosschain.sh ./lfs

Each test encodes a bug that actually happened, with a comment explaining what broke.

Run it from a directory holding all five scripts — about 100 tests skip without them.


Caveats

  • The kernel and the bootloader are yours. Nothing here configures, builds
  • or installs a kernel, and nothing is written to any ESP, boot sector or partition table. The machine keeps booting exactly as it does now. The build ends by saying so and printing what to run.

  • A rEFInd entry can be added beside your existing bootloader if you ask for it
  • (lfs config bootloader refind). It only ever adds to a mounted ESP.

  • lfs-helper and packagemanager_install duplicate some logic. Unifying them
  • is worthwhile but not done.

  • Tested on x86_64 only.

Licence

GPLv2-or-later.