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| d612779 | Fixed cfg steps And stuff | Christian Immanuel · 7 days ago |
| d | OLD/ | |
| d | skel-u_xdg/ | |
| - | .bash_profile | 1.4 KB |
| - | blfs | 81.1 KB |
| - | collector-groups.export | 817 B |
| - | HANDOFF.md | 55.8 KB |
| - | last_build_step.sh | 11.5 KB |
| - | lfs | 302.8 KB |
| - | lfs-completion.bash | 5 KB |
| - | lfs-helper | 287.7 KB |
| - | lfs-sanity.sh | 12.5 KB |
| - | Makefile | 3.6 KB |
| - | packagemanager | 246 KB |
| - | packagemanager_install | 32.7 KB |
| - | README.md | 7.6 KB |
| - | test_lfs_crosschain.sh | 417.4 KB |
| - | test_packagemanager.sh | 10.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
| Tool | Runs | Does |
|---|---|---|
lfs | host | Drives the whole build, chapters 1-10 |
lfs-helper | inside the chroot | Builds each package as its user (bash, no Python needed) |
packagemanager | built system | Installs and updates packages |
blfs | built system | Reads 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-9lfs run continues from wherever it stopped. Run it again after a reboot, a
failure, or a cancel.
| Command | Does |
|---|---|
lfs build-system next | What is done, what comes next |
lfs build-system list | Every step and its state |
lfs build-system snapshot save <name> --run | Save the build |
lfs build-system snapshot restore <name> --run | Put it back |
lfs build-system restart --run | Delete 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 --runEach 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 URLsApplication 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| Flag | Does |
|---|---|
--shared | Joins the group on your XDG runtime dir, and links /etc/pkgusr/skel-u_xdg/.bash_profile — display, session bus, audio |
--share-dir | A directory both accounts can write, linked into your home |
--launcher | A 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 happenedRepair
Everything is dry-run by default. --run applies.
| Command | Fixes |
|---|---|
lfs-helper check-toolchain | Can the compiler build anything? |
lfs-helper fix-ownership --run | Give each package its files |
lfs-helper fix-orphans --run | Files whose owner does not exist |
lfs-helper which-package <path> | Which package installed this |
lfs-helper manifests | What each package installed |
lfs-helper sort-users --run | Sort 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.
| Thing | Looks like | Lives in |
|---|---|---|
| package user | p_gcc | /usr/src/pkgusr/p_gcc |
| config-step user | cfg_bootscripts | /usr/src/cfg/cfg_bootscripts |
| application user | u_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:
| Directory | Job |
|---|---|
$LFS/sources | downloaded tarballs, never written by a build |
<package home>/src | where that package unpacks and builds, owned by its user |
$LFS/build | scratch 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
| Range | Used for |
|---|---|
| 9998 | lfs build user |
| 9999 | install 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 chrootSame build id means same code. The chroot copy refreshes automatically when you enter it.
Tests
bash test_lfs_crosschain.sh ./lfsEach 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
- A rEFInd entry can be added beside your existing bootloader if you ask for it
lfs-helperandpackagemanager_installduplicate some logic. Unifying them- Tested on x86_64 only.
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.
(lfs config bootloader refind). It only ever adds to a mounted ESP.
is worthwhile but not done.
Licence
GPLv2-or-later.