Compare commits

..
34 Commits
Author SHA1 Message Date
Joachim Wiberg d7fd5bc902 .github: ensure regression tests do not run in parallel
At least the sysvpart.sh regression test cannot run in parallel yet with
other tests (probably runparts.sh), so we must ensure the tests never
run in parallel, in particular at release.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 16:34:16 +01:00
Joachim Wiberg 3f98220d5d test: simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 15:54:06 +01:00
Joachim Wiberg 0ca509a42e test: debug sysroot setup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 15:47:11 +01:00
Joachim Wiberg d17144d16f .github: extract test results dir at release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 15:46:43 +01:00
Joachim Wiberg a608c5a5c9 .github: always upload test results, regardless
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 15:46:10 +01:00
Joachim Wiberg 9b44b99480 .github: remember to ldconfig
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 15:40:06 +01:00
Joachim Wiberg ad225156f1 Update ChangeLog and bump version for release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 11:09:16 +01:00
Joachim Wiberg eb92a915d3 initctl: drop logically dead code, found by Coverity Scan
The defines already check for plain mode.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 10:46:48 +01:00
Joachim Wiberg 69a4f2c115 Drop logically dead code, found by Coverity Scan
Checks for uid and gid introduced in d017661

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 10:42:41 +01:00
Joachim Wiberg 9ce95fe8c0 .github: fix logic in weekly workflow
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 09:51:02 +01:00
Joachim Wiberg 06889cc014 .github: verify system can find .so libs in /usr/local
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 09:48:33 +01:00
Joachim Wiberg 329f11b4da test: add barebones /etc/{passwd,group} and an ld.so.conf
Finit now requires being able to query at least for the root user and
group before starting any services.

Also, add support for using libraries installed in /usr/local

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-01-01 09:45:06 +01:00
Joachim Wiberg ce40e2b9d2 Rename tty services early from "init" -> "getty"
Finit has support for "Please press Enter to activate this console."
which means there's no getty yet running.  However, when profiling
systems with Finit, and embedded systems in general, a common metric
is the time from power-on to getty has started.

This commit makes sure to rename the process so that BusyBox pidof is
capable of detecting that "getty" has started.  This is mostly for the
bootchart2 project's bootchartd, the native BusyBox bootchartd does not
have this issue.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-31 23:34:04 +01:00
Joachim Wiberg d0176612c9 Default to user/group root for services and check for errors
This is a refactor of getuser() and getgroup() so that they always
return a valid user, and group, for all normal use-cases.  When an
error occurs we now handle it properly in service_fork() so as to
not attempt to start services with an invalid user/group setting
as root.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-31 23:29:28 +01:00
Joachim Wiberg 21753e26dd Set critical env PATH + SHELL early
When running Finit under boothcartd (bootchart2 project) the PATH is
lost due to a bug.  This was a wakeup, so set critical variables in
main() early, before calling fs_init().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-31 23:29:28 +01:00
Joachim Wiberg 0dc2513b32 Follow-up to 702a606, too long string to hide cursor
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-31 23:29:27 +01:00
Joachim Wiberg d16bfa789b Follow-up to 7c8ab79, missing semicolon
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 15:22:09 +01:00
Joachim Wiberg 0b27778c96 .github: install missing glightbox plugin for mkdocs
This is for automatic image zoom.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 15:21:06 +01:00
Joachim Wiberg 3e25297e3c doc: major overhaul of distro recs, simplify & clarify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 14:12:13 +01:00
Joachim Wiberg 6560968c8b doc: update feature list
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 13:54:22 +01:00
Joachim Wiberg c9a3c5c885 doc: reorg. of menu a bit
Try out navigation.tabs, if we don't like it we can revert.

Reorg for stricter sections, what information actually belongs where?
E.g., introduction is now in a Getting Started section.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 13:52:13 +01:00
Joachim Wiberg a2d45e8f5e doc: add missing link to setrlimit(2)
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 13:06:56 +01:00
Joachim Wiberg f6c69eb108 doc: follow-up to be2a0ec, clearify capabilities with @root
Also, add links to relevant man pages.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 13:06:07 +01:00
Joachim Wiberg f0032ab6b7 Throttle failing services, e.g., tty, on error exit code
Some 'respawn' type services, like gettys, may hog the CPU in error
states if the service immediately exits.  E.g., due to missing dev.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 12:32:01 +01:00
Joachim Wiberg 53c5d0e55a Always reset ownership and permissions on TTY device nodes
During /bin/login phase the TTY device node is chowned and chmodded to
the authenticated user.  It will remain in this state until the next
call to getty.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 12:12:18 +01:00
Joachim Wiberg 7c8ab7915f Fix #458: follow-up to be2a0ec, capabilities breaks root access
A user reports inability to re-start getty on the console after logging
out from a serial console.  After some digging it was found that the tty
was owned by the last user logged in and 600.  Even thougn getty runs as
root, it did not have permission to re-open the device node.

Turns out there was a minor bug in the new capability code that cleared
all capabilities from the root user.  A surprising amount of programs
worked just fine, but restarting getty gave it away.

The fix is to only call cap_setuid() when capabilities are set for the
service, otherwise we just fall back to setuid().

Also, refactor service_register() wrt. capabilities a bit so that we can
give users an early warning if the configuration is invalid, by adding a
parse_caps() helper function that calls cap_iab_from_text() to verify.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-27 00:49:48 +01:00
Joachim Wiberg 12f7855a78 Update ChangeLog for v4.15 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-26 13:41:18 +01:00
Joachim Wiberg bcc3360b53 Update site_url for generated documentation
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-26 13:30:30 +01:00
Joachim Wiberg d3408d14a8 Simplify and update links
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-26 13:28:43 +01:00
Joachim Wiberg 804060444a .github: fix deploy issue
- We already have the SHA
 - Use 'git add -A' to handle deleted files too

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-26 13:16:45 +01:00
Joachim Wiberg de544a5b36 .github: deploy docs to project's pages
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-26 12:47:25 +01:00
Joachim Wiberg 0d48d1df49 Merge pull request #461 from aanderse/master
Add support for supplementary groups
2025-12-26 10:28:51 +01:00
Aaron Andersen b46592e818 Add support for supplementary groups
Implement supplementary group support for services, allowing them to
access resources owned by multiple groups. Uses the @user:group,sup1,sup2
syntax to explicitly specify supplementary groups, in addition to now
reading group membership from /etc/group.
2025-12-24 09:54:37 -05:00
Joachim Wiberg 22bc218c84 Fix #462: /dev/pts mounted with wrong mode
Before this fix:

    admin@infix:~$ sudo ls -la /dev/pts/
    total 0
    drwxr-xr-x    2 root     root             0 Dec 24 08:16 .
    drwxr-xr-x   13 root     root         13340 Dec 24 08:16 ..
    cr--------    1 root     tty       136,   0 Dec 24 08:18 0
    crw-rw-rw-    1 root     root        5,   2 Dec 24 08:16 ptmx
    admin@infix:~$ mount | grep devpts
    devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=400,ptmxmode=666)

After:

    admin@infix-00-00-00:~$ sudo ls -l /dev/pts/
    total 0
    crw--w----    1 root     tty       136,   0 Dec 24 08:21 0
    crw-rw-rw-    1 root     root        5,   2 Dec 24 08:20 ptmx
    admin@infix-00-00-00:~$ mount |grep pts
    devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-24 09:23:30 +01:00
29 changed files with 677 additions and 173 deletions
+7 -6
View File
@@ -40,23 +40,23 @@ jobs:
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.6.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v4
- name: Static Finit
run: |
./autogen.sh
./configure --prefix= --enable-static
make -j3 V=1
make -j9 V=1
- name: Regular Finit
run: |
./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \
--enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \
--with-keventd --with-libsystemd \
CFLAGS="-fsanitize=address -ggdb"
make -j3 clean
make -j3 V=1
make -j9 clean
make -j9 V=1
- name: Install to /tmp
run: |
DESTDIR=/tmp make install-strip
@@ -78,8 +78,9 @@ jobs:
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Run Unit Tests
run: |
make check || (cat test/test-suite.log; false)
make -j1 check || (cat test/test-suite.log; false)
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: finit-test-${{ matrix.compiler }}
+2 -2
View File
@@ -60,8 +60,8 @@ jobs:
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.6.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- name: Configure
run: |
+47 -15
View File
@@ -19,12 +19,6 @@ on:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
@@ -46,23 +40,61 @@ jobs:
pipx inject mkdocs mkdocs-material
pipx inject mkdocs pymdown-extensions
pipx inject mkdocs mkdocs-callouts
pipx inject mkdocs mkdocs-glightbox
- name: Build documentation
run: mkdocs build
run: mkdocs build --clean
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
- name: Upload site artifact
uses: actions/upload-artifact@v4
with:
name: site
path: site/
deploy:
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
- name: Download site artifact
uses: actions/download-artifact@v4
with:
name: site
path: site/
- name: Checkout pages repo
uses: actions/checkout@v4
with:
repository: finit-project/finit-project.github.io
path: pages
ssh-key: ${{ secrets.DOCS_DEPLOY_KEY }}
persist-credentials: true
- name: Setup SSH for push
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
- name: Sync site to pages repo
run: |
rsync -a --delete --exclude .git site/ pages/
- name: Commit and push
run: |
cd pages/
if [ -z "$(git status --porcelain)" ]; then
exit 0
fi
SRC_REPO="${GITHUB_REPOSITORY}"
SRC_REF="${GITHUB_SHA::7}"
SRC_URL="https://github.com/${SRC_REPO}/commit/${SRC_REF}"
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add -A
git commit -m "docs: update from ${SRC_REPO}@${SRC_REF}" \
-m "For details, see ${SRC_URL}"
git push
+15 -5
View File
@@ -13,8 +13,6 @@ jobs:
name: Build and upload release tarball
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
env:
MAKEFLAGS: -j3
steps:
- uses: actions/checkout@v4
- name: Installing dependencies ...
@@ -25,8 +23,9 @@ jobs:
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.6.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- name: Setting release variables ...
id: build
run: |
@@ -54,10 +53,21 @@ jobs:
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Build release ...
run: |
make release || (cat test/test-suite.log; false)
# Get version for finding distcheck build directory
./configure --version | head -1
ver=$(./configure --version | head -1 | awk '{print $NF}')
dir=finit-$ver/_build/sub
make -j1 release || (cat $dir/test/test-suite.log; false)
ls -lF ../
mkdir -p artifacts/
mv ../*.tar.* artifacts/
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: finit-release-test-logs
path: finit-*/_build/sub/test/*.log
- name: Extract ChangeLog entry ...
run: |
awk '/-----*/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \
+11 -4
View File
@@ -14,14 +14,14 @@ jobs:
schedule:
runs-on: ubuntu-latest
outputs:
run: ${{ steps.check.outputs.run }}
run: ${{ steps.schedule.outputs.run }}
steps:
- uses: actions/checkout@v4
- name: Check for any commits since last week
id: schedule
run: |
commits="$(git log --format=%H --since='1 week ago')"
if [ -n "$commits" ]; then
if [ -z "$commits" ]; then
echo "run=true" >> $GITHUB_OUTPUT
else
echo "run=false" >> $GITHUB_OUTPUT
@@ -39,8 +39,9 @@ jobs:
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
tar xf libuev-2.4.1.tar.xz
tar xf libite-2.6.2.tar.gz
(cd libuev-2.4.1 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libite-2.6.2 && ./configure --prefix=/usr && make && sudo make install-strip)
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
sudo ldconfig
- uses: actions/checkout@v4
- name: Base run
run: |
@@ -58,3 +59,9 @@ jobs:
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
dir=finit-$ver/_build/sub
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: finit-weekly-test-logs
path: finit-*/_build/sub/test/*.log
+12 -12
View File
@@ -1,17 +1,16 @@
[![License Badge][]][License] [![Release Badge][]][Release] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
<img align="right" src="doc/img/finit3.png" alt="Finit: Fast Init">
Finit is an alternative to [SysV init](https://en.wikipedia.org/wiki/Init)
and [systemd](https://www.freedesktop.org/wiki/Software/systemd/), focused
on small and embedded Linux systems, although fully usable on server and
[desktop systems][finix].
Finit is a fast, simple alternative to [SysV init](https://en.wikipedia.org/wiki/Init)
and [systemd](https://systemd.io), designed for small and embedded Linux systems.
It can also run on desktop and server systems, like [finix][].
> Reverse engineered from the [EeePC fastinit][]
> "gaps filled with frog DNA …"
> — [Claudio Matsuoka][]
For detailed information, explore our extensive documentation
:books: **<http://finit-project.github.io/finit/>**
:books: **<http://finit-project.github.io>**
<div align="center">
<img src="doc/img/alpine-screenshot2.png" alt="Alpine screenshot"><br/>
@@ -26,14 +25,14 @@ tutorials:
* :milky_way: [Void Linux](contrib/void/)
> [!NOTE]
> Support for various Linux distributions does not mean Finit installs
> easily on all architectures. Bundled install scripts are examples for
> standard installations, tested on amd64 (x86_64) systems. Custom
> setups, e.g., for embedded systems, can be found in the following
> [Buildroot][] based examples: [myLinux][], [Infix][], or the plain
> [br2-finit-demo](https://github.com/finit-project/br2-finit-demo).
> Finit can run on various Linux distributions, but the bundled install
> scripts are examples only. They have been tested on amd64 (x86_64) systems
> with standard configurations.
>
> For embedded systems, see these [Buildroot][]-based examples:
> [myLinux][], [Infix][], or [br2-finit-demo][].
[finix]: https://github.com/aanderse/finix
[finix]: https://github.com/finix-community/finix
[Buildroot]: https://buildroot.org
[Infix]: https://kernelkit.github.io
[myLinux]: https://github.com/troglobit/myLinux/
@@ -48,3 +47,4 @@ tutorials:
[GitHub Status]: https://github.com/finit-project/finit/actions/workflows/build.yml/badge.svg
[Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
[br2-finit-demo]: https://github.com/finit-project/br2-finit-demo
+1 -1
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.15-rc1], [https://github.com/finit-project/finit/issues],
AC_INIT([Finit], [4.15], [https://github.com/finit-project/finit/issues],
[finit], [https://http://finit-project.github.io/])
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
+30 -2
View File
@@ -3,9 +3,11 @@ Change Log
All relevant changes are documented in this file.
[4.15][UNRELEASED]
[4.15][] - 2026-01-01
---------------------
The Finit project now has a new home page: <https://finit-project.github.io>
### Changes
- Change default reboot to SOC reset from WDT reset, issue #460
- Bump required versions of libite (v2.6.2) and libuev (v2.4.1)
@@ -24,17 +26,43 @@ All relevant changes are documented in this file.
descriptive names like `/system/udevd/` instead of filename derived names
like `/system/10-hotplug/`. Syntax supports combining with other options:
`cgroup.system,name:foo,delegate,cpu.max:10000`
- Add support for Linux capabilities, allowing services to run with minimal
required privileges instead of running as root. Uses the modern IAB API
(Inheritable, Ambient, Bounding) from libcap. By Aaron Andersen
- Add support for supplementary groups for services. Uses the syntax
`@user:group,sup1,sup2` to explicitly specify supplementary groups, in
addition to reading group membership from `/etc/group`. By Aaron Andersen
- Add CPU throttled information to `initctl` output, showing when a service
has been throttled due to reaching its CPU quota
- Add memory field to `initctl --json` status output for cgroup-enabled
services
- Default to user and group `root` (UID 0) for services
- Always check that service user and group resolve to valid UID/GID pairs,
otherwise log error and refuse to start service
- Hide cursor at boot and shutdown for cleaner visual experience
- Rename `tty` services early from `init` -> `getty` even though no getty
process has been started yet. This to aid in boot timing detection using
tools like `bootchartd` (BusyBox version or bootchart2 project) when Finit
only shows *"Please press Enter to activate this console."*
- Services with `respawn` option (including TTYs) now delay retry on non-zero
exit codes instead of immediate respawn. Prevents busy-loop when service
fails repeatedly, which on some systems may have a crippling effect
### Fixes
- Fix #453: RTC plugin fixes by Ming Liu, Atlas Copco
- Fix #455: ANSI escape sequences logged by tools using glib logging,
caused by use of PTY instead of pipe for stdout/stderr redirection
- Fix #462: `/dev/pts` mounted with wrong mode (400 instead of 620), causing
permission issues with pseudo-terminals
- getty: fix terminal scrollback issues after login on console TTY. Mouse
scroll wheel and Shift+PgUp/PgDn sometimes would not work properly
- Fix flickering and artifacts in `initctl top`
- Fix file descriptor leak in `initctl top`
- Fix incorrect "max" limit reporting in `initctl [top|cgroup|--json]` output
for child cgroups. Resolves effective limits by walking the cgroup hierarchy
- Fix issue with running Finit under `bootchartd` (bootchart2 project), always
set `PATH` and `SHELL` envs early, before attempting to mount `/proc` et al
- Always reset ownership and permissions on TTY device nodes before launching getty
- Ensure mount/unmount skips `noauto` entries
@@ -1885,7 +1913,7 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.14...HEAD
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.15...HEAD
[4.15]: https://github.com/finit-project/finit/compare/4.14...4.15
[4.14]: https://github.com/finit-project/finit/compare/4.13...4.14
[4.13]: https://github.com/troglobit/finit/compare/4.12...4.13
+20 -10
View File
@@ -115,9 +115,10 @@ Common capabilities include (see `man 7 capabilities` for the complete list):
- Only grant what the service actually needs
- Don't grant `cap_sys_admin` unless absolutely necessary
2. **Always specify a user**
- Always use `@user` to drop to a non-root user
- Capabilities work best when combined with user separation
2. **Specify a user (preferably non-root)**
- The `@user` directive is **required** for `caps:` to take effect
- For ambient capabilities (`^`), use a non-root user (not `@root`)
- Example: `@www-data`, `@nginx`, `@tcpdump`
3. **Use ambient capabilities (`^`)**
- The `^` prefix ensures capabilities survive exec()
@@ -162,15 +163,24 @@ ps -o user,pid,cmd -p $(pidof nginx)
## Limitations
- Capabilities are only applied when both `@user` and `caps:` are specified
- The service must drop to a non-root user for capabilities to be effective
- The `caps:` directive requires `@user` to be specified for it to take effect
- Without `@user`, the service runs as root with full capabilities and
the `caps:` configuration is silently ignored
- You can use `@root` with `caps:`, but see below about ambient capabilities
- For ambient capabilities (`^`, recommended), the user **must be non-root**
- Using `@root` with `caps:^...` will not work effectively, as ambient
capabilities are only added to the effective set when euid ≠ 0
- Use inheritable (`%`) or bounding (`!`) capabilities with `@root` if needed
- Services without `caps:` use standard privilege dropping:
- Services with `@user` (non-root) have no special capabilities
- Services without `@user` run as root with full capabilities
- Some very old binaries may not work correctly with ambient capabilities
- File system capabilities are not managed by Finit (use `setcap` for that)
## See Also
- `man 7 capabilities` - Linux capabilities overview
- `man 3 cap_iab` - IAB capability API documentation
- `man 8 setcap` - Set file capabilities
- `man 8 getcap` - Query file capabilities
- `man 1 capsh` - Capability shell wrapper
- [capabilities(7)](https://man7.org/linux/man-pages/man7/capabilities.7.html) - Linux capabilities overview
- [cap_iab(3)](https://man7.org/linux/man-pages/man3/cap_iab.3.html) - IAB capability API documentation
- [setcap(8)](https://man7.org/linux/man-pages/man8/setcap.8.html) - Set file capabilities
- [getcap(8)](https://man7.org/linux/man-pages/man8/getcap.8.html) - Query file capabilities
- [capsh(1)](https://man7.org/linux/man-pages/man1/capsh.1.html) - Capability shell wrapper
+5 -4
View File
@@ -127,11 +127,12 @@ Resource Limits
Set the hard or soft limit for a resource, or both if that argument is
omitted. `RESOURCE` is the lower-case `RLIMIT_` string constants from
`setrlimit(2)`, without prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
LIMIT is an integer that depends on the resource being modified, see
the man page, or the kernel `/proc/PID/limits` file, for details.
Finit versions before v3.1 used `infinity` for `unlimited`, which is
still supported, albeit deprecated.
[setrlimit(2)](https://man7.org/linux/man-pages/man2/setrlimit.2.html),
or the kernel `/proc/PID/limits` file, for details. Finit versions
before v3.1 used `infinity` for `unlimited`, which is still supported,
albeit deprecated.
# No process is allowed more than 8MB of address space
rlimit hard as 8388608
+16 -1
View File
@@ -116,10 +116,25 @@ Non-privileged Services
Every `run`, `task`, or `service` can also list the privileges the
`/path/to/cmd` should be executed with. Prefix the command with
`@USR[:GRP]`, group is optional, like this:
`@USR[:GRP[,SUPP,...]]`, where group and supplementary groups are
optional, like this:
run [2345] @joe:users logger "Hello world"
Finit reads the user's supplementary group membership from `/etc/group`
automatically. Any groups the user belongs to will be inherited by
the service.
To specify additional supplementary groups beyond those in `/etc/group`,
append them after the primary group, separated by commas:
service @caddy:caddy,ssl-cert /usr/bin/caddy run
This runs the `caddy` service as user `caddy`, with primary group
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
plus the additional `ssl-cert` group. This is useful when a service
needs access to resources owned by groups not listed in `/etc/group`.
For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add `:ID` somewhere between the `run`, `task`,
`service` keyword and the command, like this:
+105 -46
View File
@@ -1,80 +1,139 @@
Distro Recommendations
======================
By default Finit uses the following directories for configuration files:
Finit supports two directory layouts for managing service configurations:
1. **Simple layout** - all configuration files directly in `/etc/finit.d/`
2. **Distro layout** - separate `available/` and `enabled/` directories
The distro layout is recommended for distributions that need clear
separation between installed and enabled services, and want to use the
`initctl enable/disable` commands for service management.
## Simple Layout
Finit supports using a flat directory structure:
```
/etc/
|-- finit.d/ -- Regular (enabled) services
|-- finit.d/ -- All enabled services
| |-- lighttpd.conf
| `- *.conf
|-- finit.conf -- Bootstrap tasks and services
:
```
To enable a service one simply drops a small configuration file in the
`/etc/finit.d/` directory. This practice works with systems that
keep disabled services elsewhere, or generates them as needed from some
other tool.
In this layout, you enable a service by placing its `.conf` file in
`/etc/finit.d/` and disable it by removing the file. This works well for
embedded systems or custom setups where services are managed by other tools
or generated dynamically.
Distributions, however, may want a clearer separation of enabled and
available (installed but not enabled) services. They may even want to
customize the directories used, for brand labeling or uniformity.
> [!NOTE] No tooling support
> The `initctl enable/disable` commands **do not work** with this layout.
> You must manually manage files in `/etc/finit.d/`.
To that end Finit allows for a sub-directory `/etc/finit.d/available/`
where installed but disabled services can reside. Adding a symlink to a
configuration in this sub-directory enables the service, but it will not
be started.
## Distro Layout
To change the default configuration directory and configuration file
names the Finit `configure` script offers the following two options at
build time:
Distributions typically want clearer separation between available (installed)
and enabled services. Finit supports this through the `available/` and
`enabled/` subdirectories:
```
/etc/
|-- finit.d/
| |-- available/ -- Installed but disabled services
| | |-- httpd.conf
| | |-- ntpd.conf
| | `-- sshd.conf
| |-- enabled/ -- Enabled services (symlinks)
| | `- sshd.conf -- Symlink to ../available/sshd.conf
|-- finit.conf -- Bootstrap tasks and services
:
```
In this layout:
- **available/** contains all installed service configurations
- **enabled/** contains symlinks to configurations that should start at boot
- Services are enabled/disabled by creating/removing symlinks
> [!IMPORTANT] Recommended
> This is the recommended layout. In fact, the `initctl enable/disable`
> commands **require** this layout with both `available/` and `enabled/`
> directories and will not work without it.
## Managing Services with initctl
When using the distro layout, the `initctl` tool provides convenient commands
for managing service configurations:
```
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf by creating symlink in enabled/
disable <CONF> Disable .conf by removing symlink from enabled/
reload Reload *.conf in /etc/finit.d/ (activate changes)
```
Example usage:
```bash
# Enable sshd (creates enabled/sshd.conf -> ../available/sshd.conf)
initctl enable sshd
# Disable httpd (removes enabled/httpd.conf symlink)
initctl disable httpd
# Apply changes (reload configuration and start/stop services)
initctl reload
```
The `.conf` suffix is optional - `initctl` adds it automatically if missing.
> [!NOTE] Remember `initctl reload`
> Changes made with `enable`/`disable` take effect only after running
> `initctl reload`, unless Finit was built with `--enable-auto-reload`
> which automatically detects and applies configuration changes.
### Service Overrides
The `initctl` tool only operates on symlinks in the `enabled/` directory.
If you place a regular (non-symlink) `.conf` file directly in the parent
directory (e.g., `/etc/finit.d/`), `initctl` will ignore it. This allows
you to create system-level overrides that cannot be accidentally disabled
by package management tools.
## Customizing Directory Paths
Distributions can customize the directory names and locations at build time
using the Finit `configure` script:
```sh
./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf
```
> [!IMPORTANT]
> Remember `--prefix` et al as well, the default is likely *not* what
> you want. See the [build docs][1] for details.
This changes the default `/etc/finit.d/` to `/etc/init.d/` and moves the
bootstrap configuration file accordingly.
The resulting directory structure is depicted below. Please notice how
`/etc/finit.conf` now resides in the same sub-directory as a non-symlink
`/etc/init.d/init.conf`:
> [!IMPORTANT] Remember to set `--prefix`
> Remember to set `--prefix` and related options appropriately. The default
> prefix is `/usr/local`, which is likely not what you want for a system
> init. See the [build documentation][1] for details.
Example with custom paths:
```
/etc/
|-- init.d/
| |-- available/ -- Regular (disabled) services
| |-- available/ -- Installed services
| | |-- httpd.conf
| | |-- ntpd.conf
| | `-- sshd.conf
| |-- sshd.conf -- Symlink to available/sshd.conf
| |-- enabled/ -- Enabled services (symlinks)
| | `- sshd.conf -- Symlink to ../available/sshd.conf
| `- init.conf -- Bootstrap tasks and services
:
```
To facilitate the task of managing configurations, be it a service,
task, run, or other stanza, the `initctl` tool has a a few built-in
commands:
```
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
reload Reload *.conf in /etc/finit.d/ (activates changes)
```
To enable a service like `sshd.conf`, above, use
initctl enable sshd
The `.conf` suffix is not needed, `initctl` adds it implicitly if it is
missing. The `disable` command works in a similar fashion.
Note, however, that `initctl` only operates on symlinks and it always
requires the `available/` sub-directory. Any non-symlink in the parent
directory, here `/etc/init.d/`, will be considered a system override by
`initctl`.
Notice how both the service directory and bootstrap configuration file now
use the custom `/etc/init.d/` path specified at build time.
[1]: build.md#configure
+81
View File
@@ -1,6 +1,10 @@
Finit Features
==============
This page highlights some of Finit's key features with examples and usage
scenarios. For complete documentation, configuration syntax, and advanced
options, see the [Configuration](config/index.md) section.
**Process Supervision**
Start, monitor and restart services should they fail.
@@ -162,6 +166,28 @@ See the [Linux Capabilities](config/capabilities.md) section for detailed
information, examples, and security best practices.
**Supplementary Groups**
Finit supports supplementary groups for services, allowing them to access
resources owned by multiple groups without running as root. This complements
capabilities for fine-grained privilege control.
```conf
service @caddy:caddy,ssl-cert /usr/bin/caddy run
```
In this example, the Caddy web server runs as user `caddy` with primary group
`caddy`, but also has access to resources owned by the `ssl-cert` group (such
as TLS certificates).
Finit automatically reads the user's supplementary group membership from
`/etc/group`. Additional groups can be specified explicitly using the syntax
`@user:group,sup1,sup2,...`.
See the [Non-privileged Services](config/services.md#non-privileged-services)
section for more information.
**Cgroups**
Finit supports cgroups v2 and comes with the following default groups in
@@ -207,4 +233,59 @@ commands for details.
> disabled early at boot.
**Service Management**
Finit includes the `initctl` tool for managing services and system state at
runtime. Key capabilities include:
- **Enable/Disable services**: Manage which services start at boot by moving
configuration files between `/etc/finit.d/available` and
`/etc/finit.d/enabled`
- **Start/Stop/Restart**: Control individual services without requiring a
full system reboot
- **Status monitoring**: View service state, PID, uptime, and resource usage
- **Condition management**: Set and clear user-defined conditions to control
service dependencies
- **Cgroup monitoring**: Real-time process and resource monitoring with
`initctl top`, similar to the traditional `top` command but cgroup-aware
Example commands:
```bash
initctl enable myservice # Enable service for next boot
initctl start myservice # Start service now
initctl status # Show all services
initctl top # Interactive resource monitor
initctl cond set usr/custom # Set custom condition
```
See the [Commands & Status](initctl.md) section for complete documentation.
**Rescue Mode**
Finit provides a built-in rescue mode for system recovery and maintenance.
When booting with the `rescue` kernel parameter, the system enters a
protected maintenance shell.
If the bundled `sulogin` program is available (from Finit, util-linux, or
BusyBox), you'll be prompted for the root password before accessing the
maintenance shell. This provides secure access for system recovery.
If `sulogin` is not available, Finit falls back to reading
`/lib/finit/rescue.conf` and boots the system in a limited maintenance mode.
```
# Kernel command line
linux /vmlinuz root=/dev/sda1 rescue
```
In rescue mode, `initctl` will not work. After fixing the problem, use
`reboot -f` to force reboot.
Rescue mode can be disabled at build time with `configure --without-rescue`.
See the [Rescue Mode](config/rescue.md) section for more information.
[5]: https://en.wikipedia.org/wiki/Runlevel
+19 -14
View File
@@ -1,7 +1,7 @@
Introduction
============
![Alpine screenshot](img/alpine-screenshot2.png){ align=right }
![Alpine screenshot](img/alpine-screenshot2.png){ align=right width=40% }
> Reverse engineered from the [EeePC fastinit][]
> "gaps filled with frog DNA …"
@@ -17,26 +17,30 @@ PID file monitoring, or [conditions](conditions.md).
Features
--------
* [Runlevels][5], defined per service
* One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
* Runparts and `/etc/rc.local` support
* [Runlevels](config/runlevels.md), defined per service
* [One-shot tasks](config/task-and-run.md), [services](config/services.md) (daemons), or [SysV init][4] start/stop scripts
* [Runparts](config/runparts.md) and `/etc/rc.local` support
* Process supervision similar to [systemd][]
* Sourcing environment files
* Conditions for network/process/custom dependencies
* Readiness notification; PID files (native) for synchronizing system
* Fine-grained privilege control:
- [Linux capabilities](config/capabilities.md) for minimal required privileges
- [Supplementary groups](config/services.md#non-privileged-services) for multi-group resource access
* [Sourcing environment files](config/service-env.md)
* [Conditions](conditions.md) for network/process/custom dependencies
* [Readiness notification](config/service-sync.md); PID files (native) for synchronizing system
startup, support for systemd [sd_notify()][], or [s6 style][] too
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
* Pre/Post script actions
* [Pre/Post script actions](config/service-wrappers.md)
* Rudimentary [templating support](config/templating.md)
* Tooling to enable/disable services
* Built-in getty
* [Tooling](initctl.md) to enable/disable services
* [Automatic reload](features.md#automatic-reload) of modified configuration files (optional)
* [Built-in getty](config/tty.md)
* Built-in watchdog, with support for hand-over to [watchdogd][]
* Built-in support for Debian/BusyBox [`/etc/network/interfaces`][3]
* Cgroups v2, both configuration and monitoring in `initctl top`
* Plugin support for customization
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
* [Cgroups v2](config/cgroups.md), both configuration and monitoring in [`initctl top`](initctl.md)
* [Plugin support](plugins.md) for customization
* Proper [rescue mode](config/rescue.md) with bundled `sulogin` for protected maintenance shell
* Integration with [watchdogd][] for full system supervision
* Logging to kernel ring buffer before `syslogd` has started, see the
* [Logging](config/logging.md) to kernel ring buffer before `syslogd` has started, see the
recommended [sysklogd][] project for complete logging integration
and how to log to the kernel ring buffer from scripts using `logger`
@@ -64,6 +68,7 @@ and proposed extensions.
[4]: https://en.wikipedia.org/wiki/Init
[5]: https://en.wikipedia.org/wiki/Runlevel
[6]: https://github.com/finit-project/finit
[dinit]: https://davmac.org/projects/dinit/
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html
+28 -21
View File
@@ -1,6 +1,6 @@
site_name: Documentation
site_name: ""
site_description: Fast Init for Linux Systems
site_url: http://finit-project.github.io/finit/
site_url: http://finit-project.github.io
repo_url: https://github.com/finit-project/finit
repo_name: Finit Project
copyright: Copyright &copy; 2008-2025 Joachim Wiberg
@@ -11,6 +11,7 @@ extra_css:
exclude_docs: |
TODO.md
ChangeLog.md
theme:
logo: img/logo.png
@@ -22,7 +23,7 @@ theme:
- navigation.instant
- navigation.instant.progress
- navigation.tracking
# - navigation.tabs
- navigation.tabs
# - navigation.sections
# - navigation.expand
# - navigation.top
@@ -49,20 +50,16 @@ theme:
name: Switch to light mode
nav:
- Introduction: index.md
- Example: example.md
- Features: features.md
- Commands & Status: initctl.md
- Rebooting & Halting: commands.md
- Runlevels: runlevels.md
- Runparts & /etc/rc.local: runparts.md
- Getting Started:
- Introduction: index.md
- Features: features.md
- Quick Example: example.md
- Building: build.md
- Configuration:
- Overview: config/index.md
- Files & Layout: config/files.md
- Conditions: conditions.md
- Cgroups: config/cgroups.md
- Capabilities: config/capabilities.md
- Environment Variables: config/env.md
- Runlevels: config/runlevels.md
- One-shot Tasks: config/task-and-run.md
- Services:
- Syntax: config/services.md
@@ -71,24 +68,29 @@ nav:
- Synchronization: config/service-sync.md
- Wrapper Scripts: config/service-wrappers.md
- Run-parts Scripts: config/runparts.md
- Runlevels: config/runlevels.md
- TTY & Console: config/tty.md
- Templating: config/templating.md
- Environment Variables: config/env.md
- Logging: config/logging.md
- Cgroups: config/cgroups.md
- Capabilities: config/capabilities.md
- Templating: config/templating.md
- SysV Compatibility: config/sysv.md
- Rescue Mode: config/rescue.md
- Limitations: config/limitations.md
- Usage:
- Building: build.md
- Command Line: cmdline.md
- Plugins: plugins.md
- Services: service.md
- Commands & Status: initctl.md
- Rebooting & Halting: commands.md
- Command Line Options: cmdline.md
- Managing Services: service.md
- Signals: signals.md
- Reference:
- Distributions: distro.md
- keventd: keventd.md
- Runlevels Overview: runlevels.md
- Runparts & rc.local: runparts.md
- Plugins: plugins.md
- Watchdog: watchdog.md
- keventd: keventd.md
- Service State Machine: state-machine.md
- Distributions: distro.md
- Requirements: requirements.md
markdown_extensions:
@@ -109,6 +111,11 @@ markdown_extensions:
plugins:
- search
- callouts
- glightbox
extra:
homepage: https://finit-project.github.io/
social:
- icon: fontawesome/brands/github
link: https://github.com/finit-project/finit
name: Finit on GitHub
+4 -9
View File
@@ -37,7 +37,7 @@
#define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m"
#define CLREOL plain ? "" : "\e[K" /* Clear to end of line */
#define CLREOL plain ? "" : "\e[K" /* Clear to end of line */
#define NONE " "
#define PIPE plain ? "| " : "│ "
@@ -630,14 +630,9 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
strlcat(row, ++i == num ? END : FORK, rlen);
snprintf(proc, sizeof(proc), " %d %s", pid, cmdline);
if (plain) {
strlcat(row, proc, rlen);
} else {
strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen);
strlcat(row, CRST, sizeof(row));
}
strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen);
strlcat(row, CRST, sizeof(row));
printf("\r%s%s\n", row, CLREOL);
pos++;
+6
View File
@@ -286,6 +286,12 @@ static void prepare_tty(char *tty, speed_t speed, const char *procname, struct r
char name[80];
int fd, dummy;
/* Set proper protections and ownership */
if (chown(tty, 0, 0))
logit(LOG_ERR, "failed chowning 0:0 %s: %s", tty, strerror(errno));
if (chmod(tty, 0620))
logit(LOG_ERR, "failed chmoding 620 %s: %s", tty, strerror(errno));
fd = open(tty, O_RDWR);
if (fd < 0) {
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
+17 -5
View File
@@ -39,6 +39,7 @@
#include <sys/sysmacros.h>
#endif
#include <sys/prctl.h>
#include <paths.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
@@ -60,8 +61,9 @@
#include "util.h"
#include "utmp-api.h"
uev_ctx_t *ctx = NULL; /* Main loop context */
svc_t *wdog = NULL; /* No watchdog by default */
uev_ctx_t *ctx; /* Main loop context */
svc_t *wdog; /* No watchdog by default */
char *arg0; /* Saved for setprocnm() */
/*
@@ -371,7 +373,6 @@ static void fs_finalize(void)
/* Modern systems use /dev/pts */
if (!fismnt("/dev/pts")) {
char opts[32];
int mode;
int gid;
gid = getgroup("tty");
@@ -379,8 +380,7 @@ static void fs_finalize(void)
gid = 0;
/* 0600 is default on Debian, use 0620 to get mesg y by default */
mode = 0620;
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
snprintf(opts, sizeof(opts), "gid=%d,mode=0620,ptmxmode=0666", gid);
makedir("/dev/pts", 0755);
fs_mount("devpts", "/dev/pts", "devpts", flags, opts);
@@ -610,6 +610,9 @@ int main(int argc, char *argv[])
{
uev_ctx_t loop;
/* Save argv for setprocnm() in child processes */
arg0 = argv[0];
/* user calling telinit or init */
if (getpid() != 1) {
if (geteuid() == 0)
@@ -624,6 +627,15 @@ int main(int argc, char *argv[])
return EX_NOPERM;
}
/*
* Sanity check critical environment variables. We need PATH
* and SHELL for early commands like mount in fs_init().
*/
if (!checkenv("PATH"))
setenv("PATH", _PATH_STDPATH, 1);
if (!checkenv("SHELL"))
setenv("SHELL", _PATH_BSHELL, 1);
/*
* Need /dev, /proc, and /sys for console=, remount and cgroups
*/
+24
View File
@@ -32,6 +32,7 @@
#include <stdarg.h>
#include <sysexits.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
#else
@@ -527,6 +528,29 @@ int in_container(void)
return 0;
}
/**
* setprocnm - Set process name in argv[0] for ps/pidof
* @name: New process name to set
*
* Changes the process name by modifying argv[0]. This is particularly
* useful for compatibility with BusyBox's pidof which reads cmdline
* from /proc/pid/cmdline rather than /proc/pid/comm.
*/
void setprocnm(const char *name)
{
extern char *arg0;
size_t len;
if (!arg0 || !name)
return;
prctl(PR_SET_NAME, name, 0, 0, 0);
len = strlen(arg0);
memset(arg0, 0, len);
strlcpy(arg0, name, len + 1);
}
/**
* Local Variables:
* indent-tabs-mode: t
+12
View File
@@ -72,6 +72,7 @@ int print_result (int fail);
void set_hostname (char **hostname);
void networking (int updown);
int in_container (void);
void setprocnm (const char *name);
int complete (char *cmd, int pid);
int run (char *cmd, char *log);
@@ -82,6 +83,17 @@ pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimi
pid_t run_bg (char *cmd, char *args[]);
int run_parts (char *dir, char *cmd, const char *env[], int progress, int sysv);
static inline int checkenv(const char *env)
{
const char *val = getenv(env);
if (!val || !val[0])
return 0;
return 1;
}
static inline int dprint(int fd, const char *s, size_t len)
{
size_t loop = 3;
+137 -11
View File
@@ -25,6 +25,7 @@
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */
#include <grp.h> /* setgroups() */
#include <sched.h> /* sched_yield() */
#include <string.h>
#include <sys/reboot.h>
@@ -495,14 +496,21 @@ static void compose_cmdline(svc_t *svc, char *buf, size_t len)
static void set_uid(uid_t uid, svc_t *svc)
{
#ifdef HAVE_LIBCAP
if (cap_setuid(uid)) {
err(1, "%s: failed cap_setuid(%d)", svc_ident(svc, NULL, 0), uid);
return;
}
/* After dropping privileges, set the specific capabilities we need */
/*
* Only use cap_setuid() if capabilities are configured,
* otherwise it would (by design) drop all capabilities,
* breaking root services
*/
if (svc->capabilities[0]) {
cap_iab_t cap_iab = cap_iab_from_text(svc->capabilities);
cap_iab_t cap_iab;
if (cap_setuid(uid)) {
err(1, "%s: failed cap_setuid(%d)", svc_ident(svc, NULL, 0), uid);
return;
}
/* After dropping privileges, set the specific capabilities we need */
cap_iab = cap_iab_from_text(svc->capabilities);
if (!cap_iab) {
err(1, "%s: failed parsing capabilities '%s'",
svc_ident(svc, NULL, 0), svc->capabilities);
@@ -515,11 +523,10 @@ static void set_uid(uid_t uid, svc_t *svc)
svc_ident(svc, NULL, 0));
}
cap_free(cap_iab);
}
#else
} else
#endif
if (setuid(uid))
err(1, "%s: failed setuid(%d)", svc_ident(svc, NULL, 0), uid);
#endif
}
static pid_t service_fork(svc_t *svc)
@@ -549,7 +556,21 @@ static pid_t service_fork(svc_t *svc)
#else
int uid = getuser(svc->username, &home);
int gid = getgroup(svc->group);
/* Check for invalid user/group - fail rather than silent fallback */
if (uid < 0) {
err(1, "%s: user '%s' not found, cannot start service",
svc_ident(svc, NULL, 0), svc->username);
return -1;
}
if (gid < 0) {
err(1, "%s: group '%s' not found, cannot start service",
svc_ident(svc, NULL, 0), svc->group);
return -1;
}
#endif
if (svc_is_tty(svc))
setprocnm("getty");
sched_yield();
@@ -560,6 +581,42 @@ static pid_t service_fork(svc_t *svc)
svc_ident(svc, NULL, 0), rlim2str(i));
}
#ifndef ENABLE_STATIC
/* Set supplementary groups from /etc/group and config */
{
gid_t supgids[NGROUPS_MAX];
int ngroups = NGROUPS_MAX;
int i, j, n = 0;
/* Get user's supplementary groups from /etc/group */
if (getgrouplist(svc->username, gid, supgids, &ngroups) >= 0)
n = ngroups;
/* Add explicitly configured supplementary groups */
for (i = 0; i < svc->num_supgroups && n < NGROUPS_MAX; i++) {
int g = getgroup(svc->supgroups[i]);
int found = 0;
if (g < 0) {
warn("%s: unknown supplementary group '%s'",
svc_ident(svc, NULL, 0), svc->supgroups[i]);
continue;
}
/* Skip if already in list from /etc/group */
for (j = 0; j < n; j++) {
if (supgids[j] == (gid_t)g) {
found = 1;
break;
}
}
if (!found)
supgids[n++] = g;
}
if (n > 0 && setgroups(n, supgids))
err(1, "%s: failed setgroups()", svc_ident(svc, NULL, 0));
}
#endif
/* Set desired user+group */
if (gid >= 0) {
if (setgid(gid))
@@ -1384,6 +1441,26 @@ static void parse_env(svc_t *svc, char *env)
strlcpy(svc->env, env, sizeof(svc->env));
}
static void parse_caps(svc_t *svc, char *caps)
{
#ifdef HAVE_LIBCAP
cap_iab_t cap_iab;
cap_iab = cap_iab_from_text(caps);
if (!cap_iab) {
err(1, "%s: failed parsing capabilities '%s'", svc_ident(svc, NULL, 0), caps);
memset(svc->capabilities, 0, sizeof(svc->capabilities));
return;
}
cap_free(cap_iab);
strlcpy(svc->capabilities, caps, sizeof(svc->capabilities));
#else
(void)svc;
(void)caps;
#endif
}
/*
* the @cgroup argument can be, e.g., .system,mem.max:1234 or just the
* default group with some cfg, e.g., :mem.max:1234 as a side effect,
@@ -1957,7 +2034,29 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *ptr = strchr(username, ':');
if (ptr) {
char *sup;
*ptr++ = 0;
/* Check for supplementary groups: group,sup1,sup2,... */
sup = strchr(ptr, ',');
if (sup) {
*sup++ = 0;
svc->num_supgroups = 0;
while (sup) {
char *next = strchr(sup, ',');
if (next)
*next++ = 0;
if (svc->num_supgroups >= MAX_NUM_SUPGROUPS) {
warn("%s: too many supplementary groups, max %d",
svc->cmd, MAX_NUM_SUPGROUPS);
break;
}
strlcpy(svc->supgroups[svc->num_supgroups], sup,
sizeof(svc->supgroups[0]));
svc->num_supgroups++;
sup = next;
}
}
strlcpy(svc->group, ptr, sizeof(svc->group));
}
strlcpy(svc->username, username, sizeof(svc->username));
@@ -2052,7 +2151,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else
memset(svc->env, 0, sizeof(svc->env));
if (caps)
strlcpy(svc->capabilities, caps, sizeof(svc->capabilities));
parse_caps(svc, caps);
else
memset(svc->capabilities, 0, sizeof(svc->capabilities));
if (file)
@@ -2529,6 +2628,7 @@ static void service_cleanup_script(svc_t *svc)
static void service_retry(svc_t *svc)
{
char *restart_cnt = (char *)&svc->restart_cnt;
int rc = WEXITSTATUS(svc->status);
int timeout;
service_timeout_cancel(svc);
@@ -2537,6 +2637,18 @@ static void service_retry(svc_t *svc)
return;
if (svc->respawn) {
/*
* Non-zero exit indicates an error that may not be resolved
* by immediate retry. Add delay to prevent busy-loop and to
* rate-limit retries, e.g. when TTY device doesn't exist.
*/
if (WIFEXITED(svc->status) && rc != 0) {
dbg("%s exited with error %d, delaying respawn ...",
svc_ident(svc, NULL, 0), rc);
service_timeout_after(svc, svc->restart_tmo, service_retry);
return;
}
dbg("%s crashed/exited, respawning ...", svc_ident(svc, NULL, 0));
svc_unblock(svc);
service_step(svc);
@@ -2899,6 +3011,8 @@ restart:
if (!svc->pid) {
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
int rc = WEXITSTATUS(svc->status);
svc_restarting(svc); /* BLOCK_RESTARTING */
svc_set_state(svc, SVC_HALTED_STATE);
@@ -2913,6 +3027,18 @@ restart:
goto done;
}
/*
* Non-zero exit for respawn services (TTYs): delay retry
* to prevent busy-loop and rate-limit retries
*/
if (WIFEXITED(svc->status) &&
rc != 0) {
dbg("%s exited with error %d, delaying respawn ...",
svc_ident(svc, NULL, 0), rc);
service_timeout_after(svc, svc->restart_tmo, service_retry);
goto done;
}
dbg("respawning %s", svc_ident(svc, NULL, 0));
svc_unblock(svc);
break;
+1 -1
View File
@@ -362,7 +362,7 @@ restart:
/* Restore terse mode and run hooks before shutdown */
if (runlevel == 0 || runlevel == 6) {
/* Hide cursor, we're going down ... */
dprint(STDOUT_FILENO, "\033[?25l", 14);
dprint(STDOUT_FILENO, "\033[?25l", 6);
api_exit();
log_exit();
+3
View File
@@ -101,6 +101,7 @@ typedef enum {
#define MAX_STR_LEN 64
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
#define MAX_USER_LEN 16
#define MAX_NUM_SUPGROUPS 4
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC_ARGS 64
@@ -191,6 +192,8 @@ typedef struct svc {
/* Identity */
char username[MAX_USER_LEN];
char group[MAX_USER_LEN];
char supgroups[MAX_NUM_SUPGROUPS][MAX_USER_LEN];
int num_supgroups;
char capabilities[MAX_CMD_LEN];
/* Command, arguments and service description */
+38 -4
View File
@@ -180,16 +180,34 @@ char *str(char *fmt, ...)
return buf;
}
/*
* Return UID for the given user, with fallback to 'root' if user is unset.
* Fails with -1 only if user (incl. fallback) does not exist.
*/
int getuser(const char *username, char **home)
{
#ifdef ENABLE_STATIC
if (!username || !username[0])
username = "root";
if (home)
*home = "/"; /* XXX: Fixme */
return fgetint("/etc/passwd", "x:\n", username);
#else
struct passwd *usr;
const struct passwd *usr;
if (!username || (usr = getpwnam(username)) == NULL)
if (!username || !username[0]) {
usr = getpwuid(0);
if (!usr)
return -1;
if (home)
*home = usr->pw_dir;
return usr->pw_uid;
}
usr = getpwnam(username);
if (!usr)
return -1;
if (home)
@@ -198,14 +216,30 @@ int getuser(const char *username, char **home)
#endif
}
/*
* Return GID for the given group, with fallback to 'root' if group is unset.
* Fails with -1 only if user (incl. fallback) does not exist.
*/
int getgroup(const char *group)
{
#ifdef ENABLE_STATIC
if (!group || !group[0])
group = "root";
return fgetint("/etc/group", "x:\n", group);
#else
struct group *grp;
const struct group *grp;
if ((grp = getgrnam(group)) == NULL)
if (!group || !group[0]) {
grp = getgrgid(0);
if (!grp)
return -1;
return grp->gr_gid;
}
grp = getgrnam(group);
if (!grp)
return -1;
return grp->gr_gid;
+1
View File
@@ -10,6 +10,7 @@ EXTRA_DIST = skel/bin/busybox-x86_64.sha256 skel/sbin/service.sh skel/etc/env
skel/etc/rcS.d/S01abc.sh skel/etc/rcS.d/S02def.sh \
skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \
skel/etc/inittab skel/etc/hostname skel/etc/fstab \
skel/etc/passwd skel/etc/group skel/etc/ld.so.conf \
skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \
skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \
skel/etc/finit.d/enabled/.empty \
+18
View File
@@ -2,6 +2,14 @@
set -eu
echo "=== Finit Test Sysroot Setup ==="
echo "Date: $(date)"
echo "SYSROOT: $SYSROOT"
echo "top_builddir: $top_builddir"
echo "srcdir: $srcdir"
echo "================================"
echo
# shellcheck disable=SC2154
make -C "$top_builddir" DESTDIR="$SYSROOT" install
@@ -21,3 +29,13 @@ done
for conf in 10-hotplug.conf; do
find "$SYSROOT" -name $conf -delete
done
# Update dynamic linker cache for /usr/local/lib libraries
echo "Running ldconfig in sysroot: $SYSROOT"
echo "Contents of $SYSROOT/etc/ld.so.conf:"
cat "$SYSROOT/etc/ld.so.conf" || echo "Warning: ld.so.conf not found"
echo "Libraries in $SYSROOT/usr/local/lib:"
ls -la "$SYSROOT/usr/local/lib/" 2>/dev/null || echo "Warning: /usr/local/lib not found in sysroot"
ldconfig -v -r "$SYSROOT" || echo "Warning: ldconfig failed with exit code $?"
echo "Verifying ldconfig cache was created:"
ls -la "$SYSROOT/etc/ld.so.cache" || echo "Warning: ld.so.cache not created"
+9
View File
@@ -0,0 +1,9 @@
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
dialout:x:20:
nogroup:x:65534:
+3
View File
@@ -0,0 +1,3 @@
/usr/local/lib
/usr/lib
/lib
+5
View File
@@ -0,0 +1,5 @@
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin