mirror of
https://github.com/troglobit/finit.git
synced 2026-10-13 10:14:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
197c5767ea | ||
|
|
22072e477c | ||
|
|
bda2f94e8a | ||
|
|
abb4561516 | ||
|
|
309cc7f741 |
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
finit is a small project, as such we have no possibility to support older versions.
|
finit is a small project, as such we have no possibility to support older versions.
|
||||||
The only supported version is the latest released on GitHub:
|
The only supported version is the latest released on GitHub:
|
||||||
|
|
||||||
<https://github.com/finit-project/finit/releases>
|
<https://github.com/troglobit/finit/releases>
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|||||||
@@ -34,15 +34,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install pkg-config tree jq libcap-dev
|
sudo apt-get -y install pkg-config tree jq libuev-dev libite-dev
|
||||||
sudo apt-get -y install pkg-config
|
|
||||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
|
||||||
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 && make && sudo make install-strip)
|
|
||||||
(cd libite-2.6.2 && ./configure && make && sudo make install-strip)
|
|
||||||
sudo ldconfig
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Static Finit
|
- name: Static Finit
|
||||||
run: |
|
run: |
|
||||||
@@ -80,7 +72,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
make check || (cat test/test-suite.log; false)
|
make check || (cat test/test-suite.log; false)
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: finit-test-${{ matrix.compiler }}
|
name: finit-test-${{ matrix.compiler }}
|
||||||
|
|||||||
@@ -55,13 +55,13 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install pkg-config libcap-dev
|
sudo apt-get -y install pkg-config
|
||||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
wget https://github.com/troglobit/libuev/releases/download/v2.4.0/libuev-2.4.0.tar.xz
|
||||||
wget https://github.com/troglobit/libite/releases/download/v2.6.2/libite-2.6.2.tar.gz
|
wget https://github.com/troglobit/libite/releases/download/v2.5.2/libite-2.5.2.tar.gz
|
||||||
tar xf libuev-2.4.1.tar.xz
|
tar xf libuev-2.4.0.tar.xz
|
||||||
tar xf libite-2.6.2.tar.gz
|
tar xf libite-2.5.2.tar.gz
|
||||||
(cd libuev-2.4.1 && ./configure && make && sudo make install-strip)
|
(cd libuev-2.4.0 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||||
(cd libite-2.6.2 && ./configure && make && sudo make install-strip)
|
(cd libite-2.5.2 && ./configure --prefix=/usr && make && sudo make install-strip)
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+15
-47
@@ -19,6 +19,12 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: "pages"
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -40,61 +46,23 @@ jobs:
|
|||||||
pipx inject mkdocs mkdocs-material
|
pipx inject mkdocs mkdocs-material
|
||||||
pipx inject mkdocs pymdown-extensions
|
pipx inject mkdocs pymdown-extensions
|
||||||
pipx inject mkdocs mkdocs-callouts
|
pipx inject mkdocs mkdocs-callouts
|
||||||
pipx inject mkdocs mkdocs-glightbox
|
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: mkdocs build --clean
|
run: mkdocs build
|
||||||
|
|
||||||
- name: Upload site artifact
|
- name: Upload Pages artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: site
|
|
||||||
path: site/
|
path: site/
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Download site artifact
|
- name: Deploy to GitHub Pages
|
||||||
uses: actions/download-artifact@v4
|
id: deployment
|
||||||
with:
|
uses: actions/deploy-pages@v4
|
||||||
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
|
|
||||||
|
|||||||
@@ -20,14 +20,7 @@ jobs:
|
|||||||
- name: Installing dependencies ...
|
- name: Installing dependencies ...
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install pkg-config jq libcap-dev
|
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
|
||||||
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 && make && sudo make install-strip)
|
|
||||||
(cd libite-2.6.2 && ./configure && make && sudo make install-strip)
|
|
||||||
sudo ldconfig
|
|
||||||
- name: Setting release variables ...
|
- name: Setting release variables ...
|
||||||
id: build
|
id: build
|
||||||
run: |
|
run: |
|
||||||
@@ -55,21 +48,10 @@ jobs:
|
|||||||
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
|
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
|
||||||
- name: Build release ...
|
- name: Build release ...
|
||||||
run: |
|
run: |
|
||||||
# Get version for finding distcheck build directory
|
make release || (cat test/test-suite.log; false)
|
||||||
./configure --version | head -1
|
|
||||||
ver=$(./configure --version | head -1 | awk '{print $NF}')
|
|
||||||
dir=finit-$ver/_build/sub
|
|
||||||
|
|
||||||
make release || (cat $dir/test/test-suite.log; false)
|
|
||||||
ls -lF ../
|
ls -lF ../
|
||||||
mkdir -p artifacts/
|
mkdir -p artifacts/
|
||||||
mv ../*.tar.* 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 ...
|
- name: Extract ChangeLog entry ...
|
||||||
run: |
|
run: |
|
||||||
awk '/-----*/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \
|
awk '/-----*/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ jobs:
|
|||||||
schedule:
|
schedule:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
run: ${{ steps.schedule.outputs.run }}
|
run: ${{ steps.check.outputs.run }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Check for any commits since last week
|
- name: Check for any commits since last week
|
||||||
id: schedule
|
id: schedule
|
||||||
run: |
|
run: |
|
||||||
commits="$(git log --format=%H --since='1 week ago')"
|
commits="$(git log --format=%H --since='1 week ago')"
|
||||||
if [ -z "$commits" ]; then
|
if [ -n "$commits" ]; then
|
||||||
echo "run=true" >> $GITHUB_OUTPUT
|
echo "run=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "run=false" >> $GITHUB_OUTPUT
|
echo "run=false" >> $GITHUB_OUTPUT
|
||||||
@@ -34,14 +34,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install pkg-config jq libcap-dev
|
sudo apt-get -y install pkg-config jq libuev-dev libite-dev
|
||||||
wget https://github.com/troglobit/libuev/releases/download/v2.4.1/libuev-2.4.1.tar.xz
|
|
||||||
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 && make && sudo make install-strip)
|
|
||||||
(cd libite-2.6.2 && ./configure && make && sudo make install-strip)
|
|
||||||
sudo ldconfig
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Base run
|
- name: Base run
|
||||||
run: |
|
run: |
|
||||||
@@ -59,9 +52,3 @@ jobs:
|
|||||||
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
|
ver=$(/tmp/sbin/initctl -V | awk '/Finit.*/ {print $2;}')
|
||||||
dir=finit-$ver/_build/sub
|
dir=finit-$ver/_build/sub
|
||||||
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
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
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
[![License Badge][]][License] [![Release Badge][]][Release] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
|
[![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status][]][Coverity Scan]
|
||||||
<img align="right" src="doc/img/finit3.png" alt="Finit: Fast Init">
|
<img align="right" src="doc/img/finit3.png" alt="Finit: Fast Init">
|
||||||
|
|
||||||
Finit is a fast, simple alternative to [SysV init](https://en.wikipedia.org/wiki/Init)
|
Finit is an alternative to [SysV init](https://en.wikipedia.org/wiki/Init)
|
||||||
and [systemd](https://systemd.io), designed for small and embedded Linux systems.
|
and [systemd](https://www.freedesktop.org/wiki/Software/systemd/), focused
|
||||||
It can also run on desktop and server systems, like [finix][].
|
on small and embedded Linux systems, although fully usable on server and
|
||||||
|
[desktop systems][finix].
|
||||||
|
|
||||||
> Reverse engineered from the [EeePC fastinit][]
|
> Reverse engineered from the [EeePC fastinit][]
|
||||||
> "gaps filled with frog DNA …"
|
> "gaps filled with frog DNA …"
|
||||||
> — [Claudio Matsuoka][]
|
> — [Claudio Matsuoka][]
|
||||||
|
|
||||||
For detailed information, explore our extensive documentation
|
For detailed information, explore our extensive documentation
|
||||||
:books: **<http://finit-project.github.io>**
|
:books: **<https://troglobit.github.io/finit/>**
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="doc/img/alpine-screenshot2.png" alt="Alpine screenshot"><br/>
|
<img src="doc/img/alpine-screenshot2.png" alt="Alpine screenshot"><br/>
|
||||||
@@ -19,32 +20,28 @@ For detailed information, explore our extensive documentation
|
|||||||
For working examples, see the :rocket: [contrib/](contrib/) section or these
|
For working examples, see the :rocket: [contrib/](contrib/) section or these
|
||||||
tutorials:
|
tutorials:
|
||||||
|
|
||||||
* :hammer_and_wrench: [Buildroot embedded Linux][br2demo],
|
* :hammer_and_wrench: [Buildroot embedded Linux](https://troglobit.com/post/2022-12-26-buildroot-demo-of-fastinit-finit/),
|
||||||
* :package: [Debian GNU/Linux](contrib/debian/),
|
* :package: [Debian GNU/Linux](contrib/debian/),
|
||||||
* :mountain: [Alpine Linux](contrib/alpine/), and
|
* :mountain: [Alpine Linux](contrib/alpine/), and
|
||||||
* :milky_way: [Void Linux](contrib/void/)
|
* :milky_way: [Void Linux](contrib/void/)
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Finit can run on various Linux distributions, but the bundled install
|
> Support for various Linux distributions does not mean Finit installs
|
||||||
> scripts are examples only. They have been tested on amd64 (x86_64) systems
|
> easily on all architectures. Bundled install scripts are examples for
|
||||||
> with standard configurations.
|
> standard installations, tested on amd64 (x86_64) systems. Custom
|
||||||
>
|
> setups, e.g., for embedded systems, can be found in the following
|
||||||
> For embedded systems, see these [Buildroot][]-based examples:
|
> [Buildroot][] based examples: [myLinux][], [Infix][], or the plain
|
||||||
> [myLinux][], [Infix][], or [br2-finit-demo][].
|
> [br2-finit-demo](https://github.com/troglobit/br2-finit-demo).
|
||||||
|
|
||||||
[finix]: https://github.com/finix-community/finix
|
[finix]: https://github.com/aanderse/finix
|
||||||
[Buildroot]: https://buildroot.org
|
[Buildroot]: https://buildroot.org
|
||||||
[Infix]: https://kernelkit.github.io
|
[Infix]: https://kernelkit.github.io
|
||||||
[myLinux]: https://github.com/troglobit/myLinux/
|
[myLinux]: https://github.com/troglobit/myLinux/
|
||||||
[br2demo]: https://troglobit.com/post/2022-12-26-buildroot-demo-of-fastinit-finit/
|
|
||||||
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
[EeePC fastinit]: https://web.archive.org/web/20071208212450/http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||||
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
[Claudio Matsuoka]: https://github.com/cmatsuoka
|
||||||
[License]: https://en.wikipedia.org/wiki/MIT_License
|
[License]: https://en.wikipedia.org/wiki/MIT_License
|
||||||
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
|
||||||
[Release]: https://github.com/finit-project/finit/releases
|
[GitHub]: https://github.com/troglobit/finit/actions/workflows/build.yml/
|
||||||
[Release Badge]: https://img.shields.io/github/v/release/finit-project/finit
|
[GitHub Status]: https://github.com/troglobit/finit/actions/workflows/build.yml/badge.svg
|
||||||
[GitHub]: https://github.com/finit-project/finit/actions/workflows/build.yml/
|
|
||||||
[GitHub Status]: https://github.com/finit-project/finit/actions/workflows/build.yml/badge.svg
|
|
||||||
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
[Coverity Scan]: https://scan.coverity.com/projects/3545
|
||||||
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
|
||||||
[br2-finit-demo]: https://github.com/finit-project/br2-finit-demo
|
|
||||||
|
|||||||
+4
-19
@@ -1,5 +1,5 @@
|
|||||||
AC_INIT([Finit], [4.15], [https://github.com/finit-project/finit/issues],
|
AC_INIT([Finit], [4.14], [https://github.com/troglobit/finit/issues],
|
||||||
[finit], [https://http://finit-project.github.io/])
|
[finit], [https://troglobit.com/projects/finit/])
|
||||||
AC_CONFIG_AUX_DIR(aux)
|
AC_CONFIG_AUX_DIR(aux)
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
|
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
@@ -48,8 +48,8 @@ AC_TYPE_UINT32_T
|
|||||||
PKG_PROG_PKG_CONFIG
|
PKG_PROG_PKG_CONFIG
|
||||||
|
|
||||||
# Check for required libraries
|
# Check for required libraries
|
||||||
PKG_CHECK_MODULES([uev], [libuev >= 2.4.1])
|
PKG_CHECK_MODULES([uev], [libuev >= 2.2.0])
|
||||||
PKG_CHECK_MODULES([lite], [libite >= 2.6.1])
|
PKG_CHECK_MODULES([lite], [libite >= 2.2.0])
|
||||||
|
|
||||||
# Check for configured Finit features
|
# Check for configured Finit features
|
||||||
AC_ARG_ENABLE(auto_reload,
|
AC_ARG_ENABLE(auto_reload,
|
||||||
@@ -72,10 +72,6 @@ AC_ARG_ENABLE(cgroup,
|
|||||||
AS_HELP_STRING([--disable-cgroup], [Disable cgroup v2 support, default: autodetect from /sys/fs/cgroup]),,[
|
AS_HELP_STRING([--disable-cgroup], [Disable cgroup v2 support, default: autodetect from /sys/fs/cgroup]),,[
|
||||||
enable_cgroup=yes])
|
enable_cgroup=yes])
|
||||||
|
|
||||||
AC_ARG_ENABLE(libcap,
|
|
||||||
AS_HELP_STRING([--disable-libcap], [Disable Linux capabilities support]),,[
|
|
||||||
enable_libcap=yes])
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(redirect,
|
AC_ARG_ENABLE(redirect,
|
||||||
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
AS_HELP_STRING([--disable-redirect], [Disable redirection of service output to /dev/null]),,[
|
||||||
enable_redirect=yes])
|
enable_redirect=yes])
|
||||||
@@ -208,16 +204,6 @@ AS_IF([test "x$enable_kernel_logging" = "xyes"], [
|
|||||||
AS_IF([test "x$enable_cgroup" = "xyes"], [
|
AS_IF([test "x$enable_cgroup" = "xyes"], [
|
||||||
AC_DEFINE(CGROUP2_ENABLED, 1, [Autodetect cgroup v2 support from /sys/fs/cgroup])])
|
AC_DEFINE(CGROUP2_ENABLED, 1, [Autodetect cgroup v2 support from /sys/fs/cgroup])])
|
||||||
|
|
||||||
AS_IF([test "x$enable_libcap" = "xyes"], [
|
|
||||||
AC_CHECK_LIB([cap], [cap_from_text], [
|
|
||||||
AC_DEFINE(HAVE_LIBCAP, 1, [Have libcap for Linux capabilities support])
|
|
||||||
LIBS="$LIBS -lcap"
|
|
||||||
], [
|
|
||||||
AC_MSG_WARN([libcap not found, capabilities support disabled])
|
|
||||||
enable_libcap=no
|
|
||||||
])
|
|
||||||
])
|
|
||||||
|
|
||||||
AS_IF([test "x$enable_fastboot" = "xyes"], [
|
AS_IF([test "x$enable_fastboot" = "xyes"], [
|
||||||
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
|
AC_DEFINE(FAST_BOOT, 1, [Skip fsck check on filesystems listed in /etc/fstab])])
|
||||||
|
|
||||||
@@ -433,7 +419,6 @@ Optional features:
|
|||||||
Built-in logrotate....: $enable_logrotate
|
Built-in logrotate....: $enable_logrotate
|
||||||
Replacement libsystemd: $with_libsystemd
|
Replacement libsystemd: $with_libsystemd
|
||||||
Use cgroup v2.........: $enable_cgroup
|
Use cgroup v2.........: $enable_cgroup
|
||||||
Use libcap............: $enable_libcap
|
|
||||||
Parse kernel cmdline..: $enable_kernel_cmdline
|
Parse kernel cmdline..: $enable_kernel_cmdline
|
||||||
Keep kernel logging...: $enable_kernel_logging
|
Keep kernel logging...: $enable_kernel_logging
|
||||||
Skip fsck check.......: $enable_fastboot
|
Skip fsck check.......: $enable_fastboot
|
||||||
|
|||||||
+1
-64
@@ -3,67 +3,6 @@ Change Log
|
|||||||
|
|
||||||
All relevant changes are documented in this file.
|
All relevant changes are documented in this file.
|
||||||
|
|
||||||
[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)
|
|
||||||
- Allow longer service identifiers (`:id`), up to 64 characters
|
|
||||||
- Allow skipping bootstrap wait (180 sec) with Ctrl-C
|
|
||||||
- Unlock `initctl` commands start/stop/restart/reload service during bootstrap,
|
|
||||||
shutdown, and reboot. Also, allow `initctl reload` in bootstrap. This makes
|
|
||||||
it possible to start manual:yes type services at botostrap, for example, which
|
|
||||||
has been a common feature request
|
|
||||||
- Add support for `clone3()` syscall with atomic cgroup assignment, from Linux
|
|
||||||
kernel 5.3+. Eliminates race conditions where forked processes (e.g., logger
|
|
||||||
processes) ended up in wrong cgroups. Falls back to `fork()` on older kernels
|
|
||||||
- Add support for cgroup delegation, useful for, e.g., container runtimes
|
|
||||||
creating sub-groups for the container instance's processes
|
|
||||||
- Add cgroup leaf name override: `cgroup.system,name:udevd` allows using
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
[4.14][] - 2025-08-29
|
[4.14][] - 2025-08-29
|
||||||
@@ -1913,9 +1852,7 @@ Major bug fix release.
|
|||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
[UNRELEASED]: https://github.com/finit-project/finit/compare/4.15...HEAD
|
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.12...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
|
[4.13]: https://github.com/troglobit/finit/compare/4.12...4.13
|
||||||
[4.12]: https://github.com/troglobit/finit/compare/4.11...4.12
|
[4.12]: https://github.com/troglobit/finit/compare/4.11...4.12
|
||||||
[4.11]: https://github.com/troglobit/finit/compare/4.10...4.11
|
[4.11]: https://github.com/troglobit/finit/compare/4.10...4.11
|
||||||
|
|||||||
+2
-1
@@ -186,6 +186,7 @@ it only for debugging start up issues when Finit crashes.
|
|||||||
> None of these options should be enabled on production systems since
|
> None of these options should be enabled on production systems since
|
||||||
> they can potentially give a user root access.
|
> they can potentially give a user root access.
|
||||||
|
|
||||||
[1]: https://github.com/finit-project/finit/releases/download/4.3/finit-4.3.tar.gz
|
|
||||||
|
[1]: ftp://troglobit.com/finit/finit-4.3.tar.gz
|
||||||
[libuEv]: https://github.com/troglobit/libuev
|
[libuEv]: https://github.com/troglobit/libuev
|
||||||
[libite]: https://github.com/troglobit/libite
|
[libite]: https://github.com/troglobit/libite
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
Linux Capabilities
|
|
||||||
==================
|
|
||||||
|
|
||||||
Finit supports Linux capabilities, allowing services to run with minimal
|
|
||||||
required privileges instead of running as root. This significantly improves
|
|
||||||
system security by following the principle of least privilege.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Linux capabilities divide the traditional root privileges into distinct units
|
|
||||||
that can be independently granted to processes. For example, a web server only
|
|
||||||
needs the capability to bind to privileged ports (< 1024), not full root access.
|
|
||||||
|
|
||||||
Finit uses the modern IAB (Inheritable, Ambient, Bounding) API from libcap,
|
|
||||||
which is the same approach used by other modern service managers like dinit.
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
Capabilities are specified using the `caps:` directive in service configuration:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
service [2345] name:nginx \
|
|
||||||
@www-data:www-data \
|
|
||||||
caps:^cap_net_bind_service \
|
|
||||||
/usr/sbin/nginx -g 'daemon off;' \
|
|
||||||
-- Web server
|
|
||||||
```
|
|
||||||
|
|
||||||
This example allows nginx to bind to privileged ports (like 80 and 443) while
|
|
||||||
running as the unprivileged `www-data` user.
|
|
||||||
|
|
||||||
## IAB Format
|
|
||||||
|
|
||||||
The capability string uses the IAB (Inheritable, Ambient, Bounding) format
|
|
||||||
with the following prefixes:
|
|
||||||
|
|
||||||
- `^` **Ambient** (and Inheritable) - **Recommended for most use cases**
|
|
||||||
- Capabilities survive across `exec()` calls
|
|
||||||
- Automatically raised to effective after exec
|
|
||||||
- Example: `^cap_net_bind_service`
|
|
||||||
|
|
||||||
- `%` **Inheritable** only
|
|
||||||
- Requires the executed binary to have matching file capabilities
|
|
||||||
- Less common, more complex setup
|
|
||||||
- Example: `%cap_net_admin`
|
|
||||||
|
|
||||||
- `!` **Bounding** - Block capability from bounding set
|
|
||||||
- Prevents the service from ever acquiring this capability
|
|
||||||
- Useful for security hardening
|
|
||||||
- Example: `!cap_sys_admin`
|
|
||||||
|
|
||||||
Multiple capabilities can be specified as a comma-separated list:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
caps:^cap_net_raw,^cap_net_admin,^cap_net_bind_service
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Use Cases
|
|
||||||
|
|
||||||
### Web Server (Privileged Ports)
|
|
||||||
|
|
||||||
Allow a web server to bind to ports 80 and 443 without running as root:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
service [2345] name:webserver \
|
|
||||||
@www-data:www-data \
|
|
||||||
caps:^cap_net_bind_service \
|
|
||||||
/usr/sbin/nginx -g 'daemon off;'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Network Monitoring (Raw Sockets)
|
|
||||||
|
|
||||||
Allow packet capture without root privileges:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
service [2345] name:tcpdump \
|
|
||||||
@tcpdump \
|
|
||||||
caps:^cap_net_raw,^cap_net_admin \
|
|
||||||
/usr/sbin/tcpdump -i eth0 -w /var/log/capture.pcap
|
|
||||||
```
|
|
||||||
|
|
||||||
### NTP Daemon (System Time)
|
|
||||||
|
|
||||||
Allow time synchronization without full root:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
service [2345] name:ntpd \
|
|
||||||
@ntp \
|
|
||||||
caps:^cap_sys_time,^cap_sys_nice \
|
|
||||||
/usr/sbin/ntpd -n
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Capabilities
|
|
||||||
|
|
||||||
Common capabilities include (see `man 7 capabilities` for the complete list):
|
|
||||||
|
|
||||||
- `cap_chown` - Make arbitrary changes to file UIDs and GIDs
|
|
||||||
- `cap_dac_override` - Bypass file read, write, and execute permission checks
|
|
||||||
- `cap_dac_read_search` - Bypass file read permission checks
|
|
||||||
- `cap_fowner` - Bypass permission checks on operations that normally require filesystem UID
|
|
||||||
- `cap_kill` - Bypass permission checks for sending signals
|
|
||||||
- `cap_net_admin` - Perform various network-related operations
|
|
||||||
- `cap_net_bind_service` - Bind to privileged ports (< 1024)
|
|
||||||
- `cap_net_raw` - Use RAW and PACKET sockets
|
|
||||||
- `cap_setgid` - Make arbitrary manipulations of process GIDs
|
|
||||||
- `cap_setuid` - Make arbitrary manipulations of process UIDs
|
|
||||||
- `cap_sys_admin` - Perform system administration operations (very powerful!)
|
|
||||||
- `cap_sys_module` - Load and unload kernel modules
|
|
||||||
- `cap_sys_nice` - Raise process nice value and change scheduling
|
|
||||||
- `cap_sys_time` - Set system clock
|
|
||||||
|
|
||||||
## Security Best Practices
|
|
||||||
|
|
||||||
1. **Use the minimum required capabilities**
|
|
||||||
- Only grant what the service actually needs
|
|
||||||
- Don't grant `cap_sys_admin` unless absolutely necessary
|
|
||||||
|
|
||||||
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()
|
|
||||||
- Simpler than setting file capabilities on binaries
|
|
||||||
|
|
||||||
4. **Block dangerous capabilities**
|
|
||||||
- Use `!` to explicitly block capabilities you don't want
|
|
||||||
- Example: `!cap_sys_admin,!cap_sys_module`
|
|
||||||
|
|
||||||
5. **Test with `getpcaps`**
|
|
||||||
- After starting a service, verify its capabilities:
|
|
||||||
```bash
|
|
||||||
getpcaps $(pidof nginx)
|
|
||||||
```
|
|
||||||
- Should show only the capabilities you granted
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
After configuring a service with capabilities, verify it works correctly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Start the service
|
|
||||||
initctl start webserver
|
|
||||||
|
|
||||||
# Check the process capabilities
|
|
||||||
getpcaps $(pidof nginx)
|
|
||||||
|
|
||||||
# Should show something like:
|
|
||||||
# 12345: cap_net_bind_service=eip
|
|
||||||
|
|
||||||
# Verify the user
|
|
||||||
ps -o user,pid,cmd -p $(pidof nginx)
|
|
||||||
|
|
||||||
# Should show the service running as the specified user
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Linux kernel 4.3+ (for ambient capabilities support)
|
|
||||||
- libcap library installed
|
|
||||||
- Finit built with `--enable-libcap`
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- [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
|
|
||||||
+7
-111
@@ -49,121 +49,17 @@ or
|
|||||||
|
|
||||||
service [...] <...> cgroup.maint /path/to/foo args -- description
|
service [...] <...> cgroup.maint /path/to/foo args -- description
|
||||||
|
|
||||||
The latter form also allows per-stanza limits. Two syntaxes are supported:
|
The latter form also allows per-stanza limits on the form:
|
||||||
|
|
||||||
**New comma-separated syntax (recommended):**
|
|
||||||
|
|
||||||
service [...] <...> cgroup.maint,cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
|
||||||
|
|
||||||
**Old colon-separated syntax (legacy):**
|
|
||||||
|
|
||||||
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
service [...] <...> cgroup.maint:cpu.max:10000,mem.max:655360 /path/to/foo args -- description
|
||||||
|
|
||||||
Both syntaxes work identically. The new comma-separated syntax is recommended
|
Notice the comma separation and the `mem.` exception to the rule: every
|
||||||
as it's more consistent with other option parsing in Finit.
|
cgroup setting maps directly to cgroup v2 syntax. I.e., `cpu.max` maps
|
||||||
|
to the file `/sys/fs/cgroup/maint/foo/cpu.max`. There is no filtering,
|
||||||
Note the `mem.` exception to the rule: every cgroup setting maps directly to
|
except for expanding the shorthand `mem.` to `memory.`, if the file is
|
||||||
cgroup v2 syntax. I.e., `cpu.max` maps to the file `/sys/fs/cgroup/maint/foo/cpu.max`.
|
not available, either the cgroup controller is not available in your
|
||||||
There is no filtering, except for expanding the shorthand `mem.` to `memory.`.
|
Linux kernel, or the name is misspelled.
|
||||||
If the file is not available, either the cgroup controller is not available
|
|
||||||
in your Linux kernel, or the name is misspelled.
|
|
||||||
|
|
||||||
### Overriding Cgroup Leaf Names
|
|
||||||
|
|
||||||
By default, the cgroup leaf directory name is derived from the service
|
|
||||||
configuration filename (without the `.conf` extension). For example, a
|
|
||||||
service defined in `system/10-hotplug.conf` would create a cgroup at
|
|
||||||
`/sys/fs/cgroup/system/10-hotplug/` by default.
|
|
||||||
|
|
||||||
To use a more descriptive name (recommended for clarity), you can specify
|
|
||||||
`name:` in the cgroup directive:
|
|
||||||
|
|
||||||
service [...] <...> cgroup.system,name:udevd /lib/systemd/systemd-udevd -- Device event daemon
|
|
||||||
|
|
||||||
This creates the cgroup at `/sys/fs/cgroup/system/udevd/` instead.
|
|
||||||
|
|
||||||
The syntax supports combining the name override with other options:
|
|
||||||
|
|
||||||
service [...] <...> cgroup.system,name:udevd,cpu.max:10000 /lib/systemd/systemd-udevd -- Device event daemon
|
|
||||||
|
|
||||||
Or with delegation:
|
|
||||||
|
|
||||||
service [2345] user:podman group:podman \
|
|
||||||
cgroup.containers,name:podman,delegate,mem.max:4G \
|
|
||||||
/usr/bin/podman system service -- Podman API
|
|
||||||
|
|
||||||
A daemon using `SCHED_RR` currently need to run outside the default cgroups.
|
A daemon using `SCHED_RR` currently need to run outside the default cgroups.
|
||||||
|
|
||||||
service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
|
service [...] <...> cgroup.root /path/to/daemon arg -- Real-Time process
|
||||||
|
|
||||||
Cgroup Delegation
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
For services that need to create their own child cgroups (container runtimes
|
|
||||||
like Docker, Podman, systemd-nspawn, LXC), use the `delegate` option:
|
|
||||||
|
|
||||||
service [2345] user:dockerd group:dockerd \
|
|
||||||
cgroup.system,delegate /usr/bin/dockerd -- Docker daemon
|
|
||||||
|
|
||||||
Or with the old colon syntax:
|
|
||||||
|
|
||||||
service [2345] user:dockerd group:dockerd \
|
|
||||||
cgroup.system:delegate /usr/bin/dockerd -- Docker daemon
|
|
||||||
|
|
||||||
This allows the container runtime to:
|
|
||||||
|
|
||||||
- Create child cgroups for containers
|
|
||||||
- Manage controller settings for containers
|
|
||||||
- Move processes between cgroups
|
|
||||||
|
|
||||||
When delegation is enabled, Finit:
|
|
||||||
|
|
||||||
1. Creates the service cgroup as a **domain group** (not a leaf)
|
|
||||||
2. Enables all available controllers in `cgroup.subtree_control`
|
|
||||||
3. Changes ownership of delegation files to the service user
|
|
||||||
4. Moves the service process to the cgroup root
|
|
||||||
5. Lets the container runtime manage its own subdirectories
|
|
||||||
|
|
||||||
**Requirements:**
|
|
||||||
|
|
||||||
- The service should specify `user:` and `group:` for proper ownership
|
|
||||||
- Controllers are delegated from the parent cgroup
|
|
||||||
|
|
||||||
**Example with additional config (new syntax):**
|
|
||||||
|
|
||||||
service [2345] user:podman group:podman \
|
|
||||||
cgroup.containers,delegate,mem.max:4G \
|
|
||||||
/usr/bin/podman system service -- Podman API
|
|
||||||
|
|
||||||
**Or with old syntax:**
|
|
||||||
|
|
||||||
service [2345] user:podman group:podman \
|
|
||||||
cgroup.containers:delegate,mem.max:4G \
|
|
||||||
/usr/bin/podman system service -- Podman API
|
|
||||||
|
|
||||||
Both examples delegate the cgroup while also setting a 4GB memory limit.
|
|
||||||
|
|
||||||
**Cgroup structure with delegation:**
|
|
||||||
|
|
||||||
Initially, the service process runs directly in the cgroup root:
|
|
||||||
|
|
||||||
/sys/fs/cgroup/system/container@web/
|
|
||||||
├── cgroup.procs (service PID - owned by service user)
|
|
||||||
├── cgroup.subtree_control (+cpu +memory +io - owned by service user)
|
|
||||||
└── (container children will be created here)
|
|
||||||
|
|
||||||
Once the container runtime creates child cgroups (e.g., `libpod-*/`), cgroups v2
|
|
||||||
enforces the "no internal processes" rule. When Finit detects this (`EBUSY` error),
|
|
||||||
it automatically creates an `supervisor/` subdirectory and moves service-related
|
|
||||||
processes there:
|
|
||||||
|
|
||||||
/sys/fs/cgroup/system/container@web/
|
|
||||||
├── cgroup.procs (empty)
|
|
||||||
├── cgroup.subtree_control (+cpu +memory +io)
|
|
||||||
├── supervisor/ (service processes)
|
|
||||||
│ └── cgroup.procs (conmon PIDs, etc.)
|
|
||||||
└── libpod-$HASH/ (container processes)
|
|
||||||
└── cgroup.procs (container PIDs)
|
|
||||||
|
|
||||||
This happens automatically - no configuration needed. Without delegation, the
|
|
||||||
cgroup would be a leaf and the container runtime could not create child cgroups.
|
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ the following layout is recommended:
|
|||||||
`- ...
|
`- ...
|
||||||
|
|
||||||
Configuration files in `/etc` are provided by the user, or projects like
|
Configuration files in `/etc` are provided by the user, or projects like
|
||||||
[finit-skel](https://github.com/finit-project/finit-skel) and extended by
|
[finit-skel](https://github.com/troglobit/finit-skel) and extended by
|
||||||
the user.
|
the user.
|
||||||
|
|
||||||
The files in `/lib/finit/system/*.conf` are system critical services and
|
The files in `/lib/finit/system/*.conf` are system critical services and
|
||||||
|
|||||||
@@ -127,12 +127,11 @@ Resource Limits
|
|||||||
Set the hard or soft limit for a resource, or both if that argument is
|
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
|
omitted. `RESOURCE` is the lower-case `RLIMIT_` string constants from
|
||||||
`setrlimit(2)`, without prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
`setrlimit(2)`, without prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||||
|
|
||||||
LIMIT is an integer that depends on the resource being modified, see
|
LIMIT is an integer that depends on the resource being modified, see
|
||||||
[setrlimit(2)](https://man7.org/linux/man-pages/man2/setrlimit.2.html),
|
the man page, or the kernel `/proc/PID/limits` file, for details.
|
||||||
or the kernel `/proc/PID/limits` file, for details. Finit versions
|
Finit versions before v3.1 used `infinity` for `unlimited`, which is
|
||||||
before v3.1 used `infinity` for `unlimited`, which is still supported,
|
still supported, albeit deprecated.
|
||||||
albeit deprecated.
|
|
||||||
|
|
||||||
# No process is allowed more than 8MB of address space
|
# No process is allowed more than 8MB of address space
|
||||||
rlimit hard as 8388608
|
rlimit hard as 8388608
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ started at any time by running `initctl start <service>`.
|
|||||||
|
|
||||||
Other run/task/service options are:
|
Other run/task/service options are:
|
||||||
|
|
||||||
* `caps:...` -- see the [Linux Capabilities](capabilities.md) section
|
|
||||||
* `cgroups:...` -- see the [Cgroups](cgroups.md) section
|
* `cgroups:...` -- see the [Cgroups](cgroups.md) section
|
||||||
* `env:[-]/path/to/env` -- see the [Service Environment](service-env.md) section
|
* `env:[-]/path/to/env` -- see the [Service Environment](service-env.md) section
|
||||||
* `log:...` -- see [Redirecting Output](logging.md#redirecting-output)
|
* `log:...` -- see [Redirecting Output](logging.md#redirecting-output)
|
||||||
|
|||||||
+1
-16
@@ -116,25 +116,10 @@ Non-privileged Services
|
|||||||
|
|
||||||
Every `run`, `task`, or `service` can also list the privileges the
|
Every `run`, `task`, or `service` can also list the privileges the
|
||||||
`/path/to/cmd` should be executed with. Prefix the command with
|
`/path/to/cmd` should be executed with. Prefix the command with
|
||||||
`@USR[:GRP[,SUPP,...]]`, where group and supplementary groups are
|
`@USR[:GRP]`, group is optional, like this:
|
||||||
optional, like this:
|
|
||||||
|
|
||||||
run [2345] @joe:users logger "Hello world"
|
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
|
For multiple instances of the same command, e.g. a DHCP client or
|
||||||
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
multiple web servers, add `:ID` somewhere between the `run`, `task`,
|
||||||
`service` keyword and the command, like this:
|
`service` keyword and the command, like this:
|
||||||
|
|||||||
+46
-105
@@ -1,139 +1,80 @@
|
|||||||
Distro Recommendations
|
Distro Recommendations
|
||||||
======================
|
======================
|
||||||
|
|
||||||
Finit supports two directory layouts for managing service configurations:
|
By default Finit uses the following directories for configuration files:
|
||||||
|
|
||||||
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/
|
/etc/
|
||||||
|-- finit.d/ -- All enabled services
|
|-- finit.d/ -- Regular (enabled) services
|
||||||
| |-- lighttpd.conf
|
| |-- lighttpd.conf
|
||||||
| `- *.conf
|
| `- *.conf
|
||||||
|-- finit.conf -- Bootstrap tasks and services
|
|-- finit.conf -- Bootstrap tasks and services
|
||||||
:
|
:
|
||||||
```
|
```
|
||||||
|
|
||||||
In this layout, you enable a service by placing its `.conf` file in
|
To enable a service one simply drops a small configuration file in the
|
||||||
`/etc/finit.d/` and disable it by removing the file. This works well for
|
`/etc/finit.d/` directory. This practice works with systems that
|
||||||
embedded systems or custom setups where services are managed by other tools
|
keep disabled services elsewhere, or generates them as needed from some
|
||||||
or generated dynamically.
|
other tool.
|
||||||
|
|
||||||
> [!NOTE] No tooling support
|
Distributions, however, may want a clearer separation of enabled and
|
||||||
> The `initctl enable/disable` commands **do not work** with this layout.
|
available (installed but not enabled) services. They may even want to
|
||||||
> You must manually manage files in `/etc/finit.d/`.
|
customize the directories used, for brand labeling or uniformity.
|
||||||
|
|
||||||
## Distro Layout
|
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.
|
||||||
|
|
||||||
Distributions typically want clearer separation between available (installed)
|
To change the default configuration directory and configuration file
|
||||||
and enabled services. Finit supports this through the `available/` and
|
names the Finit `configure` script offers the following two options at
|
||||||
`enabled/` subdirectories:
|
build time:
|
||||||
|
|
||||||
```
|
|
||||||
/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
|
```sh
|
||||||
./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf
|
./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
This changes the default `/etc/finit.d/` to `/etc/init.d/` and moves the
|
> [!IMPORTANT]
|
||||||
bootstrap configuration file accordingly.
|
> Remember `--prefix` et al as well, the default is likely *not* what
|
||||||
|
> you want. See the [build docs][1] for details.
|
||||||
|
|
||||||
> [!IMPORTANT] Remember to set `--prefix`
|
The resulting directory structure is depicted below. Please notice how
|
||||||
> Remember to set `--prefix` and related options appropriately. The default
|
`/etc/finit.conf` now resides in the same sub-directory as a non-symlink
|
||||||
> prefix is `/usr/local`, which is likely not what you want for a system
|
`/etc/init.d/init.conf`:
|
||||||
> init. See the [build documentation][1] for details.
|
|
||||||
|
|
||||||
Example with custom paths:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
/etc/
|
/etc/
|
||||||
|-- init.d/
|
|-- init.d/
|
||||||
| |-- available/ -- Installed services
|
| |-- available/ -- Regular (disabled) services
|
||||||
| | |-- httpd.conf
|
| | |-- httpd.conf
|
||||||
| | |-- ntpd.conf
|
| | |-- ntpd.conf
|
||||||
| | `-- sshd.conf
|
| | `-- sshd.conf
|
||||||
| |-- enabled/ -- Enabled services (symlinks)
|
| |-- sshd.conf -- Symlink to available/sshd.conf
|
||||||
| | `- sshd.conf -- Symlink to ../available/sshd.conf
|
|
||||||
| `- init.conf -- Bootstrap tasks and services
|
| `- init.conf -- Bootstrap tasks and services
|
||||||
:
|
:
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice how both the service directory and bootstrap configuration file now
|
To facilitate the task of managing configurations, be it a service,
|
||||||
use the custom `/etc/init.d/` path specified at build time.
|
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`.
|
||||||
|
|
||||||
[1]: build.md#configure
|
[1]: build.md#configure
|
||||||
|
|||||||
+1
-1
@@ -134,4 +134,4 @@ and as soon as it comes back up again `ntpd` is restarted automatically.
|
|||||||
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
|
[finit.conf(5)]: https://man.troglobit.com/man5/finit.conf.5.html
|
||||||
[infix]: https://kernelkit.github.io
|
[infix]: https://kernelkit.github.io
|
||||||
[myLinux]: https://github.com/troglobit/myLinux/
|
[myLinux]: https://github.com/troglobit/myLinux/
|
||||||
[contrib]: https://github.com/finit-project/finit/tree/master/contrib
|
[contrib]: https://github.com/troglobit/finit/tree/master/contrib
|
||||||
|
|||||||
-113
@@ -1,10 +1,6 @@
|
|||||||
Finit Features
|
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**
|
**Process Supervision**
|
||||||
|
|
||||||
Start, monitor and restart services should they fail.
|
Start, monitor and restart services should they fail.
|
||||||
@@ -134,60 +130,6 @@ unnecessary overhead, which can be removed at build-time using:
|
|||||||
configure --enable-auto-reload
|
configure --enable-auto-reload
|
||||||
|
|
||||||
|
|
||||||
**Linux Capabilities**
|
|
||||||
|
|
||||||
Finit supports Linux capabilities, allowing services to run with minimal
|
|
||||||
required privileges instead of running as root. This improves security by
|
|
||||||
following the principle of least privilege.
|
|
||||||
|
|
||||||
```conf
|
|
||||||
service [2345] name:nginx \
|
|
||||||
www-data:www-data \
|
|
||||||
caps:^cap_net_bind_service \
|
|
||||||
/usr/sbin/nginx -g 'daemon off;'
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example, nginx runs as the unprivileged `www-data` user but retains
|
|
||||||
the ability to bind to privileged ports (80, 443) through the
|
|
||||||
`cap_net_bind_service` capability.
|
|
||||||
|
|
||||||
The `caps:` directive uses the IAB (Inheritable, Ambient, Bounding) format:
|
|
||||||
- `^` = Ambient (recommended) - capabilities survive exec()
|
|
||||||
- `%` = Inheritable only - requires file capabilities
|
|
||||||
- `!` = Bounding - block from acquiring capability
|
|
||||||
|
|
||||||
Multiple capabilities can be specified as comma-separated:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
caps:^cap_net_raw,^cap_net_admin,!cap_sys_admin
|
|
||||||
```
|
|
||||||
|
|
||||||
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**
|
**Cgroups**
|
||||||
|
|
||||||
Finit supports cgroups v2 and comes with the following default groups in
|
Finit supports cgroups v2 and comes with the following default groups in
|
||||||
@@ -233,59 +175,4 @@ commands for details.
|
|||||||
> disabled early at boot.
|
> 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
|
[5]: https://en.wikipedia.org/wiki/Runlevel
|
||||||
|
|||||||
+15
-20
@@ -1,7 +1,7 @@
|
|||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
{ align=right width=40% }
|
{ align=right }
|
||||||
|
|
||||||
> Reverse engineered from the [EeePC fastinit][]
|
> Reverse engineered from the [EeePC fastinit][]
|
||||||
> "gaps filled with frog DNA …"
|
> "gaps filled with frog DNA …"
|
||||||
@@ -17,30 +17,26 @@ PID file monitoring, or [conditions](conditions.md).
|
|||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* [Runlevels](config/runlevels.md), defined per service
|
* [Runlevels][5], defined per service
|
||||||
* [One-shot tasks](config/task-and-run.md), [services](config/services.md) (daemons), or [SysV init][4] start/stop scripts
|
* One-shot tasks, services (daemons), or [SysV init][4] start/stop scripts
|
||||||
* [Runparts](config/runparts.md) and `/etc/rc.local` support
|
* Runparts and `/etc/rc.local` support
|
||||||
* Process supervision similar to [systemd][]
|
* Process supervision similar to [systemd][]
|
||||||
* Fine-grained privilege control:
|
* Sourcing environment files
|
||||||
- [Linux capabilities](config/capabilities.md) for minimal required privileges
|
* Conditions for network/process/custom dependencies
|
||||||
- [Supplementary groups](config/services.md#non-privileged-services) for multi-group resource access
|
* Readiness notification; PID files (native) for synchronizing system
|
||||||
* [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
|
startup, support for systemd [sd_notify()][], or [s6 style][] too
|
||||||
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
|
* Limited support for [tmpfiles.d(5)][] (no aging, attributes, or subvolumes)
|
||||||
* [Pre/Post script actions](config/service-wrappers.md)
|
* Pre/Post script actions
|
||||||
* Rudimentary [templating support](config/templating.md)
|
* Rudimentary [templating support](config/templating.md)
|
||||||
* [Tooling](initctl.md) to enable/disable services
|
* Tooling to enable/disable services
|
||||||
* [Automatic reload](features.md#automatic-reload) of modified configuration files (optional)
|
* Built-in getty
|
||||||
* [Built-in getty](config/tty.md)
|
|
||||||
* Built-in watchdog, with support for hand-over to [watchdogd][]
|
* Built-in watchdog, with support for hand-over to [watchdogd][]
|
||||||
* Built-in support for Debian/BusyBox [`/etc/network/interfaces`][3]
|
* Built-in support for Debian/BusyBox [`/etc/network/interfaces`][3]
|
||||||
* [Cgroups v2](config/cgroups.md), both configuration and monitoring in [`initctl top`](initctl.md)
|
* Cgroups v2, both configuration and monitoring in `initctl top`
|
||||||
* [Plugin support](plugins.md) for customization
|
* Plugin support for customization
|
||||||
* Proper [rescue mode](config/rescue.md) with bundled `sulogin` for protected maintenance shell
|
* Proper rescue mode with bundled `sulogin` for protected maintenance shell
|
||||||
* Integration with [watchdogd][] for full system supervision
|
* Integration with [watchdogd][] for full system supervision
|
||||||
* [Logging](config/logging.md) to kernel ring buffer before `syslogd` has started, see the
|
* Logging to kernel ring buffer before `syslogd` has started, see the
|
||||||
recommended [sysklogd][] project for complete logging integration
|
recommended [sysklogd][] project for complete logging integration
|
||||||
and how to log to the kernel ring buffer from scripts using `logger`
|
and how to log to the kernel ring buffer from scripts using `logger`
|
||||||
|
|
||||||
@@ -67,8 +63,7 @@ and proposed extensions.
|
|||||||
[3]: https://manpages.debian.org/unstable/ifupdown2/interfaces.5.en.html
|
[3]: https://manpages.debian.org/unstable/ifupdown2/interfaces.5.en.html
|
||||||
[4]: https://en.wikipedia.org/wiki/Init
|
[4]: https://en.wikipedia.org/wiki/Init
|
||||||
[5]: https://en.wikipedia.org/wiki/Runlevel
|
[5]: https://en.wikipedia.org/wiki/Runlevel
|
||||||
[6]: https://github.com/finit-project/finit
|
[6]: https://github.com/troglobit/finit
|
||||||
[dinit]: https://davmac.org/projects/dinit/
|
|
||||||
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
[systemd]: https://www.freedesktop.org/wiki/Software/systemd/
|
||||||
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
[sd_notify()]: https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||||
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html
|
[s6 style]: https://skarnet.org/software/s6/notifywhenup.html
|
||||||
|
|||||||
+25
-32
@@ -1,8 +1,8 @@
|
|||||||
site_name: ""
|
site_name: Documentation
|
||||||
site_description: Fast Init for Linux Systems
|
site_description: Fast Init for Linux Systems
|
||||||
site_url: http://finit-project.github.io
|
site_url: https://troglobit.github.io/finit/
|
||||||
repo_url: https://github.com/finit-project/finit
|
repo_url: https://github.com/troglobit/finit
|
||||||
repo_name: Finit Project
|
repo_name: troglobit/finit
|
||||||
copyright: Copyright © 2008-2025 Joachim Wiberg
|
copyright: Copyright © 2008-2025 Joachim Wiberg
|
||||||
docs_dir: doc/
|
docs_dir: doc/
|
||||||
edit_uri: edit/master/doc/
|
edit_uri: edit/master/doc/
|
||||||
@@ -11,7 +11,6 @@ extra_css:
|
|||||||
|
|
||||||
exclude_docs: |
|
exclude_docs: |
|
||||||
TODO.md
|
TODO.md
|
||||||
ChangeLog.md
|
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
logo: img/logo.png
|
logo: img/logo.png
|
||||||
@@ -23,7 +22,7 @@ theme:
|
|||||||
- navigation.instant
|
- navigation.instant
|
||||||
- navigation.instant.progress
|
- navigation.instant.progress
|
||||||
- navigation.tracking
|
- navigation.tracking
|
||||||
- navigation.tabs
|
# - navigation.tabs
|
||||||
# - navigation.sections
|
# - navigation.sections
|
||||||
# - navigation.expand
|
# - navigation.expand
|
||||||
# - navigation.top
|
# - navigation.top
|
||||||
@@ -50,16 +49,19 @@ theme:
|
|||||||
name: Switch to light mode
|
name: Switch to light mode
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Getting Started:
|
- Introduction: index.md
|
||||||
- Introduction: index.md
|
- Example: example.md
|
||||||
- Features: features.md
|
- Features: features.md
|
||||||
- Quick Example: example.md
|
- Commands & Status: initctl.md
|
||||||
- Building: build.md
|
- Rebooting & Halting: commands.md
|
||||||
|
- Runlevels: runlevels.md
|
||||||
|
- Runparts & /etc/rc.local: runparts.md
|
||||||
- Configuration:
|
- Configuration:
|
||||||
- Overview: config/index.md
|
- Overview: config/index.md
|
||||||
- Files & Layout: config/files.md
|
- Files & Layout: config/files.md
|
||||||
- Conditions: conditions.md
|
- Conditions: conditions.md
|
||||||
- Runlevels: config/runlevels.md
|
- Cgroups: config/cgroups.md
|
||||||
|
- Environment Variables: config/env.md
|
||||||
- One-shot Tasks: config/task-and-run.md
|
- One-shot Tasks: config/task-and-run.md
|
||||||
- Services:
|
- Services:
|
||||||
- Syntax: config/services.md
|
- Syntax: config/services.md
|
||||||
@@ -68,29 +70,24 @@ nav:
|
|||||||
- Synchronization: config/service-sync.md
|
- Synchronization: config/service-sync.md
|
||||||
- Wrapper Scripts: config/service-wrappers.md
|
- Wrapper Scripts: config/service-wrappers.md
|
||||||
- Run-parts Scripts: config/runparts.md
|
- Run-parts Scripts: config/runparts.md
|
||||||
|
- Runlevels: config/runlevels.md
|
||||||
- TTY & Console: config/tty.md
|
- TTY & Console: config/tty.md
|
||||||
- Environment Variables: config/env.md
|
|
||||||
- Logging: config/logging.md
|
|
||||||
- Cgroups: config/cgroups.md
|
|
||||||
- Capabilities: config/capabilities.md
|
|
||||||
- Templating: config/templating.md
|
- Templating: config/templating.md
|
||||||
|
- Logging: config/logging.md
|
||||||
- SysV Compatibility: config/sysv.md
|
- SysV Compatibility: config/sysv.md
|
||||||
- Rescue Mode: config/rescue.md
|
- Rescue Mode: config/rescue.md
|
||||||
- Limitations: config/limitations.md
|
- Limitations: config/limitations.md
|
||||||
- Usage:
|
- Usage:
|
||||||
- Commands & Status: initctl.md
|
- Building: build.md
|
||||||
- Rebooting & Halting: commands.md
|
- Command Line: cmdline.md
|
||||||
- Command Line Options: cmdline.md
|
- Plugins: plugins.md
|
||||||
- Managing Services: service.md
|
- Services: service.md
|
||||||
- Signals: signals.md
|
- Signals: signals.md
|
||||||
- Reference:
|
- Reference:
|
||||||
- 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
|
- Distributions: distro.md
|
||||||
|
- keventd: keventd.md
|
||||||
|
- Watchdog: watchdog.md
|
||||||
|
- Service State Machine: state-machine.md
|
||||||
- Requirements: requirements.md
|
- Requirements: requirements.md
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
@@ -111,11 +108,7 @@ markdown_extensions:
|
|||||||
plugins:
|
plugins:
|
||||||
- search
|
- search
|
||||||
- callouts
|
- callouts
|
||||||
- glightbox
|
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
homepage: https://finit-project.github.io/
|
homepage: https://troglobit.com/projects/finit/
|
||||||
social:
|
|
||||||
- icon: fontawesome/brands/github
|
|
||||||
link: https://github.com/finit-project/finit
|
|
||||||
name: Finit on GitHub
|
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
|||||||
|
|
||||||
/* Complement log in service.c for non-forking services */
|
/* Complement log in service.c for non-forking services */
|
||||||
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
|
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
|
||||||
cgroup_move_svc(svc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -170,9 +170,9 @@ static void file_save(void *arg)
|
|||||||
static void file_restore(void *arg)
|
static void file_restore(void *arg)
|
||||||
{
|
{
|
||||||
struct tm tm = { 0 };
|
struct tm tm = { 0 };
|
||||||
char msg[120];
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
char msg[120];
|
||||||
|
|
||||||
if (!rtc_file) {
|
if (!rtc_file) {
|
||||||
snprintf(msg, sizeof(msg), "Resetting system clock to kernel default, %s.", rtc_timestamp);
|
snprintf(msg, sizeof(msg), "Resetting system clock to kernel default, %s.", rtc_timestamp);
|
||||||
@@ -188,11 +188,12 @@ static void file_restore(void *arg)
|
|||||||
|
|
||||||
if (fgets(buf, sizeof(buf), fp)) {
|
if (fgets(buf, sizeof(buf), fp)) {
|
||||||
chomp(buf);
|
chomp(buf);
|
||||||
|
strptime(buf, RTC_FMT, &tm);
|
||||||
if (!strptime(buf, RTC_FMT, &tm))
|
if (!strptime(buf, RTC_FMT, &tm))
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
} else
|
}
|
||||||
rc = 1;
|
rc = 1;
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ logit_LDADD = $(lite_LIBS)
|
|||||||
|
|
||||||
finit_SOURCES = api.c cgroup.c cgroup.h \
|
finit_SOURCES = api.c cgroup.c cgroup.h \
|
||||||
client.c client.h \
|
client.c client.h \
|
||||||
clone3.c clone3.h \
|
|
||||||
cond.c cond-w.c cond.h \
|
cond.c cond-w.c cond.h \
|
||||||
conf.c conf.h \
|
conf.c conf.h \
|
||||||
devmon.c devmon.h \
|
devmon.c devmon.h \
|
||||||
|
|||||||
+19
-387
@@ -21,7 +21,6 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -62,160 +61,6 @@ static uev_t cgw;
|
|||||||
static int avail;
|
static int avail;
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up inotify watch on cgroup for automatic cleanup when empty.
|
|
||||||
* Should be called after cgroup_prepare() but before process starts.
|
|
||||||
*/
|
|
||||||
int cgroup_watch(const char *group, const char *name)
|
|
||||||
{
|
|
||||||
char path[256];
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!avail)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Special case: root cgroup has no separate events file to watch */
|
|
||||||
if (!strcmp(group, "root"))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s/cgroup.events", group, name);
|
|
||||||
rc = iwatch_add(&iw_cgroup, path, 0);
|
|
||||||
if (rc < 0) {
|
|
||||||
warn("Failed setting up inotify watch on %s", path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg("Watching %s for automatic cleanup", path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Derive cgroup name from service (mirrors group_name() in service.c)
|
|
||||||
* For use by plugins that need to determine the cgroup path.
|
|
||||||
*
|
|
||||||
* MUST return the same value that group_name() in service.c returns!
|
|
||||||
*/
|
|
||||||
char *cgroup_svc_name(svc_t *svc, char *buf, size_t len)
|
|
||||||
{
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
/* Use explicit cgroup leaf name if specified */
|
|
||||||
if (svc->cgroup.leafname[0]) {
|
|
||||||
strlcpy(buf, svc->cgroup.leafname, len);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!svc->file[0])
|
|
||||||
return svc_ident(svc, buf, len);
|
|
||||||
|
|
||||||
ptr = strrchr(svc->file, '/');
|
|
||||||
if (ptr)
|
|
||||||
ptr++;
|
|
||||||
else
|
|
||||||
ptr = svc->file;
|
|
||||||
|
|
||||||
strlcpy(buf, ptr, len);
|
|
||||||
|
|
||||||
/* Strip .conf extension - this gives us the cgroup name */
|
|
||||||
ptr = strstr(buf, ".conf");
|
|
||||||
if (ptr)
|
|
||||||
*ptr = 0;
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper function to move a PID to a service's cgroup.
|
|
||||||
* @group: Top-level cgroup (e.g., "system", "user", "maint")
|
|
||||||
* @name: Service-specific cgroup name (e.g., "dockerd", "nginx")
|
|
||||||
* @pid: Process ID to move
|
|
||||||
* @delegate: If 1, handle potential EBUSY by using supervisor/ subdirectory
|
|
||||||
*
|
|
||||||
* Useful for forking services where child PIDs need to be moved after they appear.
|
|
||||||
*
|
|
||||||
* For delegated cgroups, handles the cgroups v2 "no internal processes" rule:
|
|
||||||
* if the parent has child cgroups, tries to reuse process-named subdirectory or
|
|
||||||
* falls back to supervisor/ subdirectory.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -1 on failure
|
|
||||||
*/
|
|
||||||
int cgroup_move_pid(const char *group, const char *name, int pid, int delegate)
|
|
||||||
{
|
|
||||||
int rc = 0, reuse = 0;
|
|
||||||
char path[512];
|
|
||||||
|
|
||||||
if (!avail)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Sanity checks */
|
|
||||||
if (!group || !name || pid <= 0) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disallow path traversal */
|
|
||||||
if (strstr(group, "..") || strstr(name, "..") ||
|
|
||||||
strchr(group, '/') || strchr(name, '/')) {
|
|
||||||
errno = EINVAL;
|
|
||||||
warn("Invalid cgroup path components: group=%s name=%s", group, name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s/%s", group, name);
|
|
||||||
if (!fisdir(path)) {
|
|
||||||
warn("Cgroup %s does not exist", path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rc = fnwrite(str("%d", pid), "%s/cgroup.procs", path))) {
|
|
||||||
/*
|
|
||||||
* EBUSY means the cgroup has children (cgroups v2 "no internal processes" rule).
|
|
||||||
* For delegated cgroups, fall back to subdirectory.
|
|
||||||
*/
|
|
||||||
if (errno == EBUSY && delegate) {
|
|
||||||
char comm[64];
|
|
||||||
|
|
||||||
/* Check if subdirectory matching process name, e.g., conmon, exists */
|
|
||||||
readsnf(comm, sizeof(comm), "/proc/%d/comm", pid);
|
|
||||||
strlcat(path, "/", sizeof(path));
|
|
||||||
strlcat(path, comm, sizeof(path));
|
|
||||||
if (fisdir(path))
|
|
||||||
reuse = 1;
|
|
||||||
else
|
|
||||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s/%s/supervisor", group, name);
|
|
||||||
|
|
||||||
rc = fnwrite(str("%d", pid), "%s/cgroup.procs", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
warn("Failed moving pid %d to cgroup %s", pid, path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reuse) {
|
|
||||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s/%s/supervisor", group, name);
|
|
||||||
rmdir(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cgroup_move_svc(svc_t *svc)
|
|
||||||
{
|
|
||||||
const char *group = "system";
|
|
||||||
char name[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
if (!avail || !svc)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (svc->cgroup.name[0])
|
|
||||||
group = svc->cgroup.name;
|
|
||||||
|
|
||||||
return cgroup_move_pid(group, cgroup_svc_name(svc, name, sizeof(name)),
|
|
||||||
svc->pid, svc->cgroup.delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cgset(const char *path, char *ctrl, char *prop)
|
static void cgset(const char *path, char *ctrl, char *prop)
|
||||||
{
|
{
|
||||||
char *val;
|
char *val;
|
||||||
@@ -303,99 +148,40 @@ static void group_init(char *path, int leaf, const char *cfg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cgroup_create(const char *group, const char *name, const char *cfg,
|
static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg)
|
||||||
int delegate, const char *username, const char *grpname,
|
|
||||||
char *pathbuf, size_t pathlen)
|
|
||||||
{
|
{
|
||||||
char path[256];
|
char path[256];
|
||||||
|
|
||||||
|
dbg("group %s, name %s, pid %d, cfg %s", group, name, pid, cfg ?: "NIL");
|
||||||
|
if (pid < 0 || pid == 1) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create and initialize new group */
|
||||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
||||||
|
group_init(path, 1, cfg);
|
||||||
|
|
||||||
if (delegate) {
|
/* move process to new group */
|
||||||
char initpath[PATH_MAX];
|
if (fnwrite(str("%d", pid), "%s/cgroup.procs", path))
|
||||||
|
err(1, "Failed moving pid %d to group %s", pid, path);
|
||||||
|
|
||||||
/* For delegation, create as domain group (not leaf) */
|
strlcat(path, "/cgroup.events", sizeof(path));
|
||||||
group_init(path, 0, cfg);
|
|
||||||
|
|
||||||
/* Enable controllers for delegation */
|
return iwatch_add(&iw_cgroup, path, 0);
|
||||||
if (fnwrite(controllers, "%s/cgroup.subtree_control", path))
|
|
||||||
warn("Failed enabling controllers in %s for delegation", path);
|
|
||||||
|
|
||||||
/* Change ownership of delegation files */
|
|
||||||
if (username && username[0] && grpname && grpname[0]) {
|
|
||||||
uid_t uid = getuser(username, NULL);
|
|
||||||
gid_t gid = getgroup(grpname);
|
|
||||||
|
|
||||||
if (uid != (uid_t)-1 && gid != (gid_t)-1) {
|
|
||||||
char filepath[PATH_MAX];
|
|
||||||
char *files[] = {
|
|
||||||
"cgroup.procs",
|
|
||||||
"cgroup.subtree_control",
|
|
||||||
"cgroup.threads",
|
|
||||||
"cgroup.type",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; files[i]; i++) {
|
|
||||||
snprintf(filepath, sizeof(filepath), "%s/%s", path, files[i]);
|
|
||||||
if (chown(filepath, uid, gid))
|
|
||||||
warn("Failed chown %s to %d:%d", filepath, uid, gid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(initpath, sizeof(initpath), "%s/supervisor", path);
|
|
||||||
group_init(initpath, 1, NULL);
|
|
||||||
strlcpy(path, initpath, sizeof(path));
|
|
||||||
} else {
|
|
||||||
/* Normal leaf cgroup */
|
|
||||||
group_init(path, 1, cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fisdir(path)) {
|
|
||||||
warn("Cgroup directory %s doesn't exist after creation", path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the path to caller if they provided a buffer */
|
|
||||||
if (pathbuf && pathlen > 0)
|
|
||||||
strlcpy(pathbuf, path, pathlen);
|
|
||||||
|
|
||||||
return cgroup_watch(group, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cgroup_leaf_init(const char *group, const char *name, int pid, const char *cfg,
|
int cgroup_user(char *name, int pid)
|
||||||
int delegate, const char *username, const char *grpname)
|
|
||||||
{
|
|
||||||
dbg("group %s, name %s, pid %d, cfg %s, delegate %d", group, name, pid, cfg ?: "NIL", delegate);
|
|
||||||
if (cgroup_create(group, name, cfg, delegate, username, grpname, NULL, 0))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
dbg("Assigning PID %d to cgroup %s/%s", pid, group, name);
|
|
||||||
|
|
||||||
/* Special case: "root" cgroup means the actual cgroup root */
|
|
||||||
if (!strcmp(group, "root"))
|
|
||||||
return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs");
|
|
||||||
|
|
||||||
if (cgroup_move_pid(group, name, pid, delegate))
|
|
||||||
err(1, "Failed moving pid %d to group %s/%s", pid, group, name);
|
|
||||||
|
|
||||||
/* Set up inotify watch for cgroup cleanup */
|
|
||||||
return cgroup_watch(group, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int cgroup_user(const char *name, int pid)
|
|
||||||
{
|
{
|
||||||
if (!avail)
|
if (!avail)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return cgroup_leaf_init("user", name, pid, NULL, 0, NULL, NULL);
|
return cgroup_leaf_init("user", name, pid, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cgroup_service(const char *name, int pid, struct cgroup *cg, char *username, char *grpname)
|
int cgroup_service(char *name, int pid, struct cgroup *cg)
|
||||||
{
|
{
|
||||||
char *group = "system";
|
char *group = "system";
|
||||||
int delegate = 0;
|
|
||||||
|
|
||||||
if (!avail)
|
if (!avail)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -412,64 +198,9 @@ int cgroup_service(const char *name, int pid, struct cgroup *cg, char *username,
|
|||||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name);
|
||||||
if (fisdir(path))
|
if (fisdir(path))
|
||||||
group = cg->name;
|
group = cg->name;
|
||||||
|
|
||||||
delegate = cg->delegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cgroup_leaf_init(group, name, pid, cg ? cg->cfg : NULL, delegate, username, grpname);
|
return cgroup_leaf_init(group, name, pid, cg ? cg->cfg : NULL);
|
||||||
}
|
|
||||||
|
|
||||||
/* Create cgroup for the requested type of service, return fd to cgroup for clone3() */
|
|
||||||
int cgroup_prepare(svc_t *svc, const char *name)
|
|
||||||
{
|
|
||||||
const char *group;
|
|
||||||
const char *cfg = NULL;
|
|
||||||
const char *username = NULL;
|
|
||||||
const char *grpname = NULL;
|
|
||||||
int delegate = 0;
|
|
||||||
char path[256];
|
|
||||||
int fd = -1;
|
|
||||||
|
|
||||||
if (!avail)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!svc) {
|
|
||||||
/* Helper process (e.g., networking) */
|
|
||||||
group = "system";
|
|
||||||
} else if (svc_is_tty(svc)) {
|
|
||||||
/* TTY/getty services go in user cgroup */
|
|
||||||
group = "user";
|
|
||||||
} else if (svc->cgroup.name[0] && !strcmp(svc->cgroup.name, "root")) {
|
|
||||||
/* Special case: SCHED_RR processes go in root cgroup */
|
|
||||||
fd = open("/sys/fs/cgroup", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
|
||||||
if (fd < 0)
|
|
||||||
warn("Failed opening root cgroup");
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
} else {
|
|
||||||
/* Regular service */
|
|
||||||
group = svc->cgroup.name[0] ? svc->cgroup.name : "system";
|
|
||||||
cfg = svc->cgroup.cfg;
|
|
||||||
delegate = svc->cgroup.delegate;
|
|
||||||
username = svc->username;
|
|
||||||
grpname = svc->group;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the cgroup and get the path back */
|
|
||||||
if (cgroup_create(group, name, cfg, delegate, username, grpname, path, sizeof(path)))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Open and return fd for clone3() */
|
|
||||||
fd = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
|
||||||
if (fd < 0)
|
|
||||||
warn("Failed opening cgroup %s", path);
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void append_ctrl(char *ctrl)
|
static void append_ctrl(char *ctrl)
|
||||||
@@ -674,79 +405,6 @@ int cgroup_add(char *name, char *cfg, int is_protected)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Walk a cgroup directory and remove all empty subdirectories recursively.
|
|
||||||
*/
|
|
||||||
static void cgroup_prune_recursive(const char *path)
|
|
||||||
{
|
|
||||||
struct dirent *d;
|
|
||||||
DIR *dir;
|
|
||||||
|
|
||||||
dir = opendir(path);
|
|
||||||
if (!dir)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while ((d = readdir(dir)) != NULL) {
|
|
||||||
char subpath[PATH_MAX];
|
|
||||||
|
|
||||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
|
||||||
continue;
|
|
||||||
if (d->d_type != DT_DIR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(subpath, sizeof(subpath), "%s/%s", path, d->d_name);
|
|
||||||
cgroup_prune_recursive(subpath);
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to remove this directory. If it still has children or processes,
|
|
||||||
* rmdir() will fail with ENOTEMPTY/EBUSY - that's fine.
|
|
||||||
*/
|
|
||||||
if (rmdir(path) && errno != ENOTEMPTY && errno != EBUSY)
|
|
||||||
warn("Failed to prune %s", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Housekeeping: prune all empty cgroup subdirectories.
|
|
||||||
* Called after runlevel transitions to clean up finished run/task cgroups.
|
|
||||||
*/
|
|
||||||
void cgroup_prune(void)
|
|
||||||
{
|
|
||||||
struct dirent *entry;
|
|
||||||
struct cg *cg;
|
|
||||||
char path[256];
|
|
||||||
DIR *dir;
|
|
||||||
|
|
||||||
if (!avail)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dbg("Pruning empty cgroup directories...");
|
|
||||||
|
|
||||||
/* Walk through each top-level cgroup (init, system, user) */
|
|
||||||
TAILQ_FOREACH(cg, &cgroups, link) {
|
|
||||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name);
|
|
||||||
|
|
||||||
dir = opendir(path);
|
|
||||||
if (!dir)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Check each subdirectory */
|
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
|
||||||
char subpath[512];
|
|
||||||
|
|
||||||
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
|
|
||||||
continue;
|
|
||||||
if (entry->d_type != DT_DIR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(subpath, sizeof(subpath), "%s/%s", path, entry->d_name);
|
|
||||||
cgroup_prune_recursive(subpath);
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove inactive top-level cgroup
|
* Remove inactive top-level cgroup
|
||||||
*/
|
*/
|
||||||
@@ -770,8 +428,7 @@ int cgroup_del(char *dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rmdir(dir) && errno != ENOENT) {
|
if (rmdir(dir) && errno != ENOENT) {
|
||||||
if (errno != EBUSY)
|
dbg("Failed removing %s: %s", dir, strerror(errno));
|
||||||
warn("Failed rmdir(%s): %s", dir, strerror(errno));
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,31 +442,6 @@ int cgroup_del(char *dir)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Delete cgroup for a service (convenience wrapper)
|
|
||||||
*/
|
|
||||||
int cgroup_del_svc(svc_t *svc, const char *name)
|
|
||||||
{
|
|
||||||
const char *group;
|
|
||||||
char path[256];
|
|
||||||
|
|
||||||
if (!avail)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Determine group from service */
|
|
||||||
if (svc_is_tty(svc)) {
|
|
||||||
group = "user";
|
|
||||||
} else if (svc->cgroup.name[0] && !strcmp(svc->cgroup.name, "root")) {
|
|
||||||
/* Root cgroup - nothing to delete */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
group = svc->cgroup.name[0] ? svc->cgroup.name : "system";
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
|
|
||||||
return cgroup_del(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
|
/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */
|
||||||
void cgroup_config(void)
|
void cgroup_config(void)
|
||||||
{
|
{
|
||||||
|
|||||||
+8
-23
@@ -26,36 +26,21 @@
|
|||||||
|
|
||||||
#include <uev/uev.h>
|
#include <uev/uev.h>
|
||||||
|
|
||||||
/* Forward declaration */
|
|
||||||
typedef struct svc svc_t;
|
|
||||||
|
|
||||||
struct cgroup {
|
struct cgroup {
|
||||||
char name[16];
|
char name[16];
|
||||||
char cfg[128];
|
char cfg[128];
|
||||||
char leafname[128];
|
|
||||||
char delegate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void cgroup_mark_all(void);
|
void cgroup_mark_all(void);
|
||||||
void cgroup_cleanup (void);
|
void cgroup_cleanup (void);
|
||||||
|
|
||||||
int cgroup_add (char *name, char *cfg, int is_protected);
|
int cgroup_add (char *name, char *cfg, int is_protected);
|
||||||
int cgroup_del (char *dir);
|
int cgroup_del (char *dir);
|
||||||
int cgroup_del_svc (svc_t *svc, const char *name);
|
void cgroup_config (void);
|
||||||
void cgroup_config (void);
|
|
||||||
|
|
||||||
void cgroup_init (uev_ctx_t *ctx);
|
void cgroup_init (uev_ctx_t *ctx);
|
||||||
|
|
||||||
int cgroup_user (const char *name, int pid);
|
int cgroup_user (char *name, int pid);
|
||||||
int cgroup_service (const char *name, int pid, struct cgroup *cg, char *username, char *group);
|
int cgroup_service (char *name, int pid, struct cgroup *cg);
|
||||||
|
|
||||||
char *cgroup_svc_name(svc_t *svc, char *buf, size_t len);
|
|
||||||
int cgroup_prepare (svc_t *svc, const char *name);
|
|
||||||
int cgroup_watch (const char *group, const char *name);
|
|
||||||
|
|
||||||
int cgroup_move_pid(const char *group, const char *name, int pid, int delegate);
|
|
||||||
int cgroup_move_svc(svc_t *svc);
|
|
||||||
|
|
||||||
void cgroup_prune (void);
|
|
||||||
|
|
||||||
#endif /* FINIT_CGROUP_H_ */
|
#endif /* FINIT_CGROUP_H_ */
|
||||||
|
|||||||
+65
-243
@@ -29,15 +29,19 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <search.h>
|
#include <search.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#ifdef _LIBITE_LITE
|
||||||
|
# include <libite/lite.h>
|
||||||
|
#else
|
||||||
|
# include <lite/lite.h>
|
||||||
|
#endif
|
||||||
#include <sys/sysinfo.h> /* sysinfo() */
|
#include <sys/sysinfo.h> /* sysinfo() */
|
||||||
|
|
||||||
#include "cgutil.h"
|
#include "cgutil.h"
|
||||||
#include "initctl.h"
|
#include "initctl.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#define CDIM plain ? "" : "\e[2m"
|
#define CDIM plain ? "" : "\e[2m"
|
||||||
#define CRST plain ? "" : "\e[0m"
|
#define CRST plain ? "" : "\e[0m"
|
||||||
#define CLREOL plain ? "" : "\e[K" /* Clear to end of line */
|
|
||||||
|
|
||||||
#define NONE " "
|
#define NONE " "
|
||||||
#define PIPE plain ? "| " : "│ "
|
#define PIPE plain ? "| " : "│ "
|
||||||
@@ -98,7 +102,6 @@ char *pid_cmdline(int pid)
|
|||||||
buf[i] = ' ';
|
buf[i] = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +144,7 @@ char *pid_cgroup(int pid)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *cgroup_val(char *path, char *file, char *buf, size_t len)
|
static char *cgroup_val(char *path, char *file, char *buf, size_t len)
|
||||||
{
|
{
|
||||||
char *val = NULL;
|
char *val = NULL;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@@ -249,42 +252,6 @@ uint64_t cgroup_memory(char *group)
|
|||||||
return cgroup_uint64(path, "memory.current");
|
return cgroup_uint64(path, "memory.current");
|
||||||
}
|
}
|
||||||
|
|
||||||
int cgroup_throttle(char *group, uint64_t *throttled_usec, uint64_t *nr_throttled)
|
|
||||||
{
|
|
||||||
char path[256];
|
|
||||||
char buf[256];
|
|
||||||
FILE *fp;
|
|
||||||
int found = 0;
|
|
||||||
|
|
||||||
paste(path, sizeof(path), FINIT_CGPATH, group);
|
|
||||||
|
|
||||||
fp = fopenf("r", "%s/cpu.stat", path);
|
|
||||||
if (!fp)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*throttled_usec = 0;
|
|
||||||
*nr_throttled = 0;
|
|
||||||
|
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
|
||||||
chomp(buf);
|
|
||||||
|
|
||||||
if (!strncmp(buf, "throttled_usec", 14)) {
|
|
||||||
*throttled_usec = strtoull(&buf[15], NULL, 10);
|
|
||||||
found++;
|
|
||||||
} else if (!strncmp(buf, "nr_throttled", 12)) {
|
|
||||||
*nr_throttled = strtoull(&buf[13], NULL, 10);
|
|
||||||
found++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found == 2)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static float cgroup_cpuload(struct cg *cg)
|
static float cgroup_cpuload(struct cg *cg)
|
||||||
{
|
{
|
||||||
char fn[256];
|
char fn[256];
|
||||||
@@ -377,148 +344,18 @@ struct cg *cg_stats(char *path)
|
|||||||
return cg;
|
return cg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* query config */
|
||||||
* Compare two memory limit values, returning the more restrictive one.
|
|
||||||
* For memory.max: smaller value wins (except "max" means unlimited)
|
|
||||||
* For memory.min: larger value wins (more protection)
|
|
||||||
*/
|
|
||||||
static uint64_t cmp_mem_limit(const char *a, const char *b, int is_max)
|
|
||||||
{
|
|
||||||
uint64_t val_a, val_b;
|
|
||||||
|
|
||||||
if (!a || !a[0])
|
|
||||||
return b && b[0] ? strtoull(b, NULL, 10) : 0;
|
|
||||||
if (!b || !b[0])
|
|
||||||
return strtoull(a, NULL, 10);
|
|
||||||
|
|
||||||
/* Handle "max" (unlimited) */
|
|
||||||
if (!strcmp(a, "max"))
|
|
||||||
return !strcmp(b, "max") ? UINT64_MAX : strtoull(b, NULL, 10);
|
|
||||||
if (!strcmp(b, "max"))
|
|
||||||
return strtoull(a, NULL, 10);
|
|
||||||
|
|
||||||
val_a = strtoull(a, NULL, 10);
|
|
||||||
val_b = strtoull(b, NULL, 10);
|
|
||||||
|
|
||||||
if (is_max)
|
|
||||||
return val_a < val_b ? val_a : val_b; /* min for max limit */
|
|
||||||
else
|
|
||||||
return val_a > val_b ? val_a : val_b; /* max for min limit */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare two cpu.max values (format: "quota period")
|
|
||||||
* Returns the more restrictive quota (smallest quota/period ratio)
|
|
||||||
* Stores result in dst, up to len bytes
|
|
||||||
*/
|
|
||||||
static void cmp_cpu_max(const char *a, const char *b, char *dst, size_t len)
|
|
||||||
{
|
|
||||||
char a_copy[32], b_copy[32];
|
|
||||||
char *a_quota, *a_period, *b_quota, *b_period;
|
|
||||||
uint64_t qa, pa, qb, pb;
|
|
||||||
double ratio_a, ratio_b;
|
|
||||||
|
|
||||||
if (!a || !a[0]) {
|
|
||||||
if (b && b[0])
|
|
||||||
strlcpy(dst, b, len);
|
|
||||||
else
|
|
||||||
dst[0] = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!b || !b[0]) {
|
|
||||||
strlcpy(dst, a, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse a */
|
|
||||||
strlcpy(a_copy, a, sizeof(a_copy));
|
|
||||||
a_quota = strtok(a_copy, " ");
|
|
||||||
a_period = strtok(NULL, " ");
|
|
||||||
|
|
||||||
/* Parse b */
|
|
||||||
strlcpy(b_copy, b, sizeof(b_copy));
|
|
||||||
b_quota = strtok(b_copy, " ");
|
|
||||||
b_period = strtok(NULL, " ");
|
|
||||||
|
|
||||||
/* Handle "max" quota (unlimited) */
|
|
||||||
if (a_quota && !strcmp(a_quota, "max")) {
|
|
||||||
strlcpy(dst, b, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (b_quota && !strcmp(b_quota, "max")) {
|
|
||||||
strlcpy(dst, a, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare ratios (quota/period) - smaller ratio is more restrictive */
|
|
||||||
if (!a_quota || !a_period || !b_quota || !b_period) {
|
|
||||||
strlcpy(dst, a, len); /* fallback */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qa = strtoull(a_quota, NULL, 10);
|
|
||||||
pa = strtoull(a_period, NULL, 10);
|
|
||||||
qb = strtoull(b_quota, NULL, 10);
|
|
||||||
pb = strtoull(b_period, NULL, 10);
|
|
||||||
|
|
||||||
if (pa == 0 || pb == 0) {
|
|
||||||
strlcpy(dst, a, len); /* fallback */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ratio_a = (double)qa / (double)pa;
|
|
||||||
ratio_b = (double)qb / (double)pb;
|
|
||||||
|
|
||||||
strlcpy(dst, ratio_a <= ratio_b ? a : b, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* query config with hierarchical limit resolution */
|
|
||||||
struct cg *cg_conf(char *path)
|
struct cg *cg_conf(char *path)
|
||||||
{
|
{
|
||||||
static struct cg cg;
|
static struct cg cg;
|
||||||
char parent[512];
|
|
||||||
char tmp[32];
|
|
||||||
uint64_t mem_min_val, mem_max_val;
|
|
||||||
|
|
||||||
/* Read initial values from the leaf cgroup */
|
|
||||||
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
|
cgroup_val(path, "memory.min", cg.cg_mem.min, sizeof(cg.cg_mem.min));
|
||||||
cgroup_val(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
|
cgroup_memval(path, "memory.max", cg.cg_mem.max, sizeof(cg.cg_mem.max));
|
||||||
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
|
cgroup_val(path, "cpu.weight", cg.cg_cpu.weight, sizeof(cg.cg_cpu.weight));
|
||||||
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
cgroup_val(path, "cpu.max", cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
||||||
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
|
cgroup_val(path, "cpuset.cpus.effective", cg.cg_cpu.set, sizeof(cg.cg_cpu.set));
|
||||||
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
|
cg.cg_vmsize = cgroup_uint64(path, "memory.current");
|
||||||
|
|
||||||
/* Walk up the hierarchy to find most restrictive limits */
|
|
||||||
strlcpy(parent, path, sizeof(parent));
|
|
||||||
while (strcmp(parent, FINIT_CGPATH) != 0) {
|
|
||||||
char *slash = strrchr(parent, '/');
|
|
||||||
if (!slash || slash == parent)
|
|
||||||
break;
|
|
||||||
*slash = '\0'; /* Move up one level */
|
|
||||||
|
|
||||||
/* Compare and update memory.min (take maximum) */
|
|
||||||
if (cgroup_val(parent, "memory.min", tmp, sizeof(tmp))) {
|
|
||||||
mem_min_val = cmp_mem_limit(cg.cg_mem.min, tmp, 0);
|
|
||||||
if (mem_min_val == UINT64_MAX)
|
|
||||||
strlcpy(cg.cg_mem.min, "max", sizeof(cg.cg_mem.min));
|
|
||||||
else
|
|
||||||
snprintf(cg.cg_mem.min, sizeof(cg.cg_mem.min), "%lu", mem_min_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare and update memory.max (take minimum) */
|
|
||||||
if (cgroup_val(parent, "memory.max", tmp, sizeof(tmp))) {
|
|
||||||
mem_max_val = cmp_mem_limit(cg.cg_mem.max, tmp, 1);
|
|
||||||
if (mem_max_val == UINT64_MAX)
|
|
||||||
strlcpy(cg.cg_mem.max, "max", sizeof(cg.cg_mem.max));
|
|
||||||
else
|
|
||||||
snprintf(cg.cg_mem.max, sizeof(cg.cg_mem.max), "%lu", mem_max_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compare and update cpu.max (take most restrictive) */
|
|
||||||
if (cgroup_val(parent, "cpu.max", tmp, sizeof(tmp)))
|
|
||||||
cmp_cpu_max(cg.cg_cpu.max, tmp, cg.cg_cpu.max, sizeof(cg.cg_cpu.max));
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cg;
|
return &cg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,29 +378,32 @@ static int cgroup_filter(const struct dirent *entry)
|
|||||||
int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
||||||
{
|
{
|
||||||
struct dirent **namelist = NULL;
|
struct dirent **namelist = NULL;
|
||||||
char s[32], r[32], l[32], row[1024];
|
char s[32], r[32], l[32];
|
||||||
|
char row[ttcols + 9]; /* + control codes */
|
||||||
size_t rlen = sizeof(row) - 1;
|
size_t rlen = sizeof(row) - 1;
|
||||||
|
size_t rplen = rlen - 9;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct cg *cg;
|
struct cg *cg;
|
||||||
char buf[512];
|
char buf[512];
|
||||||
|
int rc = 0;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int i, n;
|
int i, n;
|
||||||
int num;
|
int num;
|
||||||
|
|
||||||
if (pos >= ttrows - 1)
|
if (pos >= ttrows)
|
||||||
return pos;
|
return 0;
|
||||||
|
|
||||||
if (-1 == lstat(path, &st))
|
if (-1 == lstat(path, &st))
|
||||||
return pos;
|
return 1;
|
||||||
|
|
||||||
if ((st.st_mode & S_IFMT) != S_IFDIR) {
|
if ((st.st_mode & S_IFMT) != S_IFDIR) {
|
||||||
errno = ENOTDIR;
|
errno = ENOTDIR;
|
||||||
return pos;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = fopenf("r", "%s/cgroup.procs", path);
|
fp = fopenf("r", "%s/cgroup.procs", path);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return pos;
|
return -1;
|
||||||
num = 0;
|
num = 0;
|
||||||
while (fgets(buf, sizeof(buf), fp))
|
while (fgets(buf, sizeof(buf), fp))
|
||||||
num++;
|
num++;
|
||||||
@@ -573,7 +413,7 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 1:
|
case 1:
|
||||||
cg = cg_stats(path);
|
cg = cg_stats(path);
|
||||||
snprintf(row, rlen, " %6.6s %6.6s %6.6s %5.1f %5.1f %s",
|
snprintf(row, rplen, "\r %6.6s %6.6s %6.6s %5.1f %5.1f %s",
|
||||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
memsz(cg->cg_rss, r, sizeof(r)),
|
memsz(cg->cg_rss, r, sizeof(r)),
|
||||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||||
@@ -581,20 +421,18 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
cg = cg_conf(path);
|
cg = cg_conf(path);
|
||||||
snprintf(row, rlen, "%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
|
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6s [%-6.6s%6.6s] %s",
|
||||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
|
cg->cg_mem.min, cg->cg_mem.max, cg->cg_cpu.set,
|
||||||
cg->cg_cpu.weight, cg->cg_cpu.max, path);
|
cg->cg_cpu.weight, cg->cg_cpu.max, path);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
strlcpy(row, path, rlen);
|
strlcpy(row, "\r", rplen);
|
||||||
|
strlcat(row, path, rplen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\r%s%s\n", row, CLREOL);
|
puts(row);
|
||||||
pos++;
|
|
||||||
if (pos >= ttrows - 1)
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num > 0) {
|
if (num > 0) {
|
||||||
@@ -612,40 +450,53 @@ int cgroup_tree(char *path, char *pfx, int mode, int pos)
|
|||||||
/* skip kernel threads for now (no cmdline) */
|
/* skip kernel threads for now (no cmdline) */
|
||||||
cmdline = pid_cmdline(pid);
|
cmdline = pid_cmdline(pid);
|
||||||
if (cmdline) {
|
if (cmdline) {
|
||||||
char proc[512]; /* Build full command, truncate row later */
|
char proc[ttcols];
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 1:
|
case 1:
|
||||||
snprintf(row, rlen, "%37s", " ");
|
snprintf(row, rplen, "\r%37s", " ");
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
snprintf(row, rlen, " --.-- [ ] [ ] ");
|
snprintf(row, rplen, "\r --.-- [ ] [ ] ");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
row[0] = '\0';
|
strlcpy(row, "\r", rplen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcat(row, pfx, rlen);
|
strlcat(row, pfx, rplen);
|
||||||
strlcat(row, ++i == num ? END : FORK, rlen);
|
strlcat(row, ++i == num ? END : FORK, rlen);
|
||||||
|
|
||||||
snprintf(proc, sizeof(proc), " %d %s", pid, cmdline);
|
snprintf(proc, sizeof(proc), " %d %s", pid, cmdline);
|
||||||
strlcat(row, CDIM, rlen);
|
|
||||||
strlcat(row, proc, rlen);
|
|
||||||
strlcat(row, CRST, sizeof(row));
|
|
||||||
|
|
||||||
printf("\r%s%s\n", row, CLREOL);
|
if (plain) {
|
||||||
pos++;
|
strlcat(row, proc, rplen);
|
||||||
|
} else {
|
||||||
|
int len;
|
||||||
|
|
||||||
|
strlcat(row, CDIM, rlen);
|
||||||
|
strlcat(row, proc, rlen);
|
||||||
|
|
||||||
|
len = strlen(row) + strlen(CRST);
|
||||||
|
if (len > (int)rlen)
|
||||||
|
row[rlen - strlen(CRST)] = 0;
|
||||||
|
|
||||||
|
strlcat(row, CRST, sizeof(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(row);
|
||||||
|
|
||||||
free(cmdline);
|
free(cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
if (pos >= ttrows - 1)
|
if (mode == 1) {
|
||||||
|
pos += i;
|
||||||
|
if (pos >= ttrows)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
n = scandir(path, &namelist, cgroup_filter, alphasort);
|
||||||
@@ -654,15 +505,12 @@ out:
|
|||||||
char *nm = namelist[i]->d_name;
|
char *nm = namelist[i]->d_name;
|
||||||
char prefix[80];
|
char prefix[80];
|
||||||
|
|
||||||
if (pos >= ttrows - 1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 1:
|
case 1:
|
||||||
cg = cg_stats(buf);
|
cg = cg_stats(buf);
|
||||||
snprintf(row, rlen,
|
snprintf(row, rplen,
|
||||||
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
"\r %6.6s %6.6s %6.6s %5.1f %5.1f ",
|
||||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
memsz(cg->cg_rss, r, sizeof(r)),
|
memsz(cg->cg_rss, r, sizeof(r)),
|
||||||
memsz(cg->cg_vmlib, l, sizeof(l)),
|
memsz(cg->cg_vmlib, l, sizeof(l)),
|
||||||
@@ -670,33 +518,32 @@ out:
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
cg = cg_conf(buf);
|
cg = cg_conf(buf);
|
||||||
snprintf(row, rlen, "%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
|
snprintf(row, rplen, "\r%6.6s [%-6.6s%6.6s] %6.6s [%-6.6s%6.6s] ",
|
||||||
memsz(cg->cg_vmsize, s, sizeof(s)),
|
memsz(cg->cg_vmsize, s, sizeof(s)),
|
||||||
cg->cg_mem.min, cg->cg_mem.max,
|
cg->cg_mem.min, cg->cg_mem.max,
|
||||||
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
|
cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
row[0] = '\0';
|
strlcpy(row, "\r", rplen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcat(row, pfx, rlen);
|
strlcat(row, pfx, rplen);
|
||||||
if (i + 1 == n) {
|
if (i + 1 == n) {
|
||||||
strlcat(row, END, rlen);
|
strlcat(row, END, rplen);
|
||||||
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
snprintf(prefix, sizeof(prefix), "%s ", pfx);
|
||||||
} else {
|
} else {
|
||||||
strlcat(row, FORK, rlen);
|
strlcat(row, FORK, rplen);
|
||||||
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
|
||||||
}
|
}
|
||||||
strlcat(row, " ", rlen);
|
strlcat(row, " ", rplen);
|
||||||
|
|
||||||
strlcat(row, nm, rlen);
|
strlcat(row, nm, rplen);
|
||||||
strlcat(row, "/ ", rlen);
|
strlcat(row, "/ ", rplen);
|
||||||
|
|
||||||
printf("\r%s%s\n", row, CLREOL);
|
puts(row);
|
||||||
pos++;
|
|
||||||
|
|
||||||
pos = cgroup_tree(buf, prefix, mode, pos);
|
rc += cgroup_tree(buf, prefix, mode, pos + i);
|
||||||
|
|
||||||
free(namelist[i]);
|
free(namelist[i]);
|
||||||
}
|
}
|
||||||
@@ -704,7 +551,7 @@ out:
|
|||||||
free(namelist);
|
free(namelist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pos;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int show_cgps(char *arg)
|
int show_cgps(char *arg)
|
||||||
@@ -723,39 +570,17 @@ int show_cgps(char *arg)
|
|||||||
|
|
||||||
static void cgtop(uev_t *w, void *arg, int events)
|
static void cgtop(uev_t *w, void *arg, int events)
|
||||||
{
|
{
|
||||||
static int first = 1;
|
|
||||||
int lines;
|
|
||||||
|
|
||||||
(void)w;
|
(void)w;
|
||||||
(void)events;
|
(void)events;
|
||||||
|
|
||||||
/* Re-probe screen size in case terminal was resized */
|
fputs("\e[2J\e[1;1H", stdout);
|
||||||
ttinit(1);
|
if (heading)
|
||||||
|
|
||||||
/* Clear screen on first run to remove any artifacts */
|
|
||||||
if (first) {
|
|
||||||
fputs("\e[?7l\e[2J\e[H", stdout); /* Disable line wrap, clear, home */
|
|
||||||
first = 0;
|
|
||||||
} else {
|
|
||||||
/* Move cursor to home instead of clearing screen to avoid flicker */
|
|
||||||
fputs("\e[H", stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = 0;
|
|
||||||
if (heading) {
|
|
||||||
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
|
||||||
lines = 1;
|
cgroup_tree(arg, NULL, 1, 0);
|
||||||
}
|
|
||||||
cgroup_tree(arg, NULL, 1, lines);
|
|
||||||
|
|
||||||
/* Clear from cursor to end of screen to remove leftover lines */
|
|
||||||
fputs("\e[J", stdout);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup(void)
|
static void cleanup(void)
|
||||||
{
|
{
|
||||||
fputs("\e[?7h", stdout); /* Re-enable line wrap */
|
|
||||||
ttcooked();
|
ttcooked();
|
||||||
showcursor();
|
showcursor();
|
||||||
puts("");
|
puts("");
|
||||||
@@ -806,9 +631,6 @@ int show_cgtop(char *arg)
|
|||||||
if (!hcreate(ttrows + 25))
|
if (!hcreate(ttrows + 25))
|
||||||
ERR(70, "failed creating hash table");
|
ERR(70, "failed creating hash table");
|
||||||
|
|
||||||
/* Ensure we have correct terminal size before starting */
|
|
||||||
ttinit(1);
|
|
||||||
|
|
||||||
sysinfo(&si);
|
sysinfo(&si);
|
||||||
total_ram = si.totalram * si.mem_unit;
|
total_ram = si.totalram * si.mem_unit;
|
||||||
|
|
||||||
|
|||||||
@@ -57,9 +57,7 @@ int cgroup_avail(void);
|
|||||||
char *pid_cmdline (int pid);
|
char *pid_cmdline (int pid);
|
||||||
char *pid_cgroup (int pid);
|
char *pid_cgroup (int pid);
|
||||||
|
|
||||||
char *cgroup_val(char *path, char *file, char *buf, size_t len);
|
|
||||||
uint64_t cgroup_memory(char *group);
|
uint64_t cgroup_memory(char *group);
|
||||||
int cgroup_throttle(char *group, uint64_t *throttled_usec, uint64_t *nr_throttled);
|
|
||||||
|
|
||||||
struct cg *cg_stats(char *path);
|
struct cg *cg_stats(char *path);
|
||||||
struct cg *cg_conf (char *path);
|
struct cg *cg_conf (char *path);
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
/* Portable clone3() wrapper with fallback to fork()
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Joachim Wiberg <troglobit@gmail.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sched.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "clone3.h"
|
|
||||||
#include "finit.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
/* Clone3 flags from linux/sched.h */
|
|
||||||
#ifndef CLONE_INTO_CGROUP
|
|
||||||
#define CLONE_INTO_CGROUP 0x200000000ULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Linux kernel struct clone_args for clone3() syscall */
|
|
||||||
struct clone3_args {
|
|
||||||
uint64_t flags; /* Flags bit mask */
|
|
||||||
uint64_t pidfd; /* File descriptor for PID */
|
|
||||||
uint64_t child_tid; /* Child TID */
|
|
||||||
uint64_t parent_tid; /* Parent TID */
|
|
||||||
uint64_t exit_signal; /* Signal to deliver on exit */
|
|
||||||
uint64_t stack; /* Stack address */
|
|
||||||
uint64_t stack_size; /* Stack size */
|
|
||||||
uint64_t tls; /* TLS descriptor */
|
|
||||||
uint64_t set_tid; /* Set PID */
|
|
||||||
uint64_t set_tid_size; /* Number of set_tid entries */
|
|
||||||
uint64_t cgroup; /* Cgroup file descriptor */
|
|
||||||
} __attribute__((aligned(8)));
|
|
||||||
|
|
||||||
#ifdef __NR_clone3
|
|
||||||
static int use_clone3 = 1;
|
|
||||||
#else
|
|
||||||
static int use_clone3 = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int has_clone3(void)
|
|
||||||
{
|
|
||||||
return use_clone3 == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t call_clone3(uint64_t flags, int cgroup_fd)
|
|
||||||
{
|
|
||||||
struct clone3_args cl = {
|
|
||||||
.flags = flags,
|
|
||||||
.exit_signal = SIGCHLD,
|
|
||||||
};
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
if (!use_clone3)
|
|
||||||
goto fallback;
|
|
||||||
|
|
||||||
if (cgroup_fd >= 0) {
|
|
||||||
cl.flags |= CLONE_INTO_CGROUP;
|
|
||||||
cl.cgroup = cgroup_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = syscall(__NR_clone3, &cl, sizeof(cl));
|
|
||||||
if (pid != -1)
|
|
||||||
return pid;
|
|
||||||
|
|
||||||
logit(LOG_WARNING, "clone3() failed, falling back to fork(): %s", strerror(errno));
|
|
||||||
use_clone3 = 0;
|
|
||||||
fallback:
|
|
||||||
return fork();
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/* Portable clone3() wrapper with fallback to fork()
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Joachim Wiberg <troglobit@gmail.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FINIT_CLONE3_H_
|
|
||||||
#define FINIT_CLONE3_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
pid_t call_clone3 (uint64_t flags, int cgroup_fd);
|
|
||||||
int has_clone3 (void);
|
|
||||||
|
|
||||||
#endif /* FINIT_CLONE3_H_ */
|
|
||||||
+2
-35
@@ -106,9 +106,7 @@ char *runparts = NULL;
|
|||||||
int runparts_progress;
|
int runparts_progress;
|
||||||
int runparts_sysv;
|
int runparts_sysv;
|
||||||
|
|
||||||
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
|
char cgroup_current[16]; /* cgroup.NAME sets current cgroup for a set of services */
|
||||||
char cgroup_settings_current[128]; /* cgroup.system,cpu.weight:500 - cgroup settings */
|
|
||||||
int cgroup_delegate_current; /* cgroup.system,delegate - delegation flag */
|
|
||||||
|
|
||||||
struct conf_change {
|
struct conf_change {
|
||||||
TAILQ_ENTRY(conf_change) link;
|
TAILQ_ENTRY(conf_change) link;
|
||||||
@@ -1107,38 +1105,7 @@ static int parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
|||||||
|
|
||||||
/* Set current cgroup for the following services/run/tasks */
|
/* Set current cgroup for the following services/run/tasks */
|
||||||
if (MATCH_CMD(line, "cgroup.", x)) {
|
if (MATCH_CMD(line, "cgroup.", x)) {
|
||||||
char *group, *token, *saveptr;
|
strlcpy(cgroup_current, x, sizeof(cgroup_current));
|
||||||
char settings[128] = {0};
|
|
||||||
|
|
||||||
/* Reset cgroup state */
|
|
||||||
cgroup_settings_current[0] = '\0';
|
|
||||||
cgroup_delegate_current = 0;
|
|
||||||
|
|
||||||
/* First token is the group name (system/user/init) */
|
|
||||||
group = strtok_r(x, ",", &saveptr);
|
|
||||||
if (group)
|
|
||||||
strlcpy(cgroup_current, group, sizeof(cgroup_current));
|
|
||||||
else
|
|
||||||
strlcpy(cgroup_current, x, sizeof(cgroup_current));
|
|
||||||
|
|
||||||
/* Parse any remaining comma-separated options */
|
|
||||||
while ((token = strtok_r(NULL, ",", &saveptr)) != NULL) {
|
|
||||||
if (strncmp(token, "name:", 5) == 0) {
|
|
||||||
/* Cgroup leaf name override - only valid in per-service directives */
|
|
||||||
warnx("cgroup %s: name: option not valid in global directive!", group);
|
|
||||||
} else if (strcmp(token, "delegate") == 0) {
|
|
||||||
cgroup_delegate_current = 1;
|
|
||||||
} else {
|
|
||||||
/* Other settings (cpu.weight:500, memory.max:1G, etc.) */
|
|
||||||
if (settings[0])
|
|
||||||
strlcat(settings, ",", sizeof(settings));
|
|
||||||
strlcat(settings, token, sizeof(settings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings[0])
|
|
||||||
strlcpy(cgroup_settings_current, settings, sizeof(cgroup_settings_current));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ extern int logfile_count_max;
|
|||||||
|
|
||||||
extern struct rlimit global_rlimit[];
|
extern struct rlimit global_rlimit[];
|
||||||
extern char cgroup_current[];
|
extern char cgroup_current[];
|
||||||
extern char cgroup_settings_current[];
|
|
||||||
extern int cgroup_delegate_current;
|
|
||||||
|
|
||||||
int str2rlim(char *str);
|
int str2rlim(char *str);
|
||||||
char *rlim2str(int rlim);
|
char *rlim2str(int rlim);
|
||||||
|
|||||||
@@ -286,12 +286,6 @@ static void prepare_tty(char *tty, speed_t speed, const char *procname, struct r
|
|||||||
char name[80];
|
char name[80];
|
||||||
int fd, dummy;
|
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);
|
fd = open(tty, O_RDWR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
|
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
|
||||||
|
|||||||
+5
-17
@@ -39,7 +39,6 @@
|
|||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <paths.h>
|
|
||||||
#ifdef _LIBITE_LITE
|
#ifdef _LIBITE_LITE
|
||||||
# include <libite/lite.h>
|
# include <libite/lite.h>
|
||||||
#else
|
#else
|
||||||
@@ -61,9 +60,8 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
uev_ctx_t *ctx; /* Main loop context */
|
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||||
svc_t *wdog; /* No watchdog by default */
|
svc_t *wdog = NULL; /* No watchdog by default */
|
||||||
char *arg0; /* Saved for setprocnm() */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -373,6 +371,7 @@ static void fs_finalize(void)
|
|||||||
/* Modern systems use /dev/pts */
|
/* Modern systems use /dev/pts */
|
||||||
if (!fismnt("/dev/pts")) {
|
if (!fismnt("/dev/pts")) {
|
||||||
char opts[32];
|
char opts[32];
|
||||||
|
int mode;
|
||||||
int gid;
|
int gid;
|
||||||
|
|
||||||
gid = getgroup("tty");
|
gid = getgroup("tty");
|
||||||
@@ -380,7 +379,8 @@ static void fs_finalize(void)
|
|||||||
gid = 0;
|
gid = 0;
|
||||||
|
|
||||||
/* 0600 is default on Debian, use 0620 to get mesg y by default */
|
/* 0600 is default on Debian, use 0620 to get mesg y by default */
|
||||||
snprintf(opts, sizeof(opts), "gid=%d,mode=0620,ptmxmode=0666", gid);
|
mode = 0620;
|
||||||
|
snprintf(opts, sizeof(opts), "gid=%d,mode=%d,ptmxmode=0666", gid, mode);
|
||||||
|
|
||||||
makedir("/dev/pts", 0755);
|
makedir("/dev/pts", 0755);
|
||||||
fs_mount("devpts", "/dev/pts", "devpts", flags, opts);
|
fs_mount("devpts", "/dev/pts", "devpts", flags, opts);
|
||||||
@@ -610,9 +610,6 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
uev_ctx_t loop;
|
uev_ctx_t loop;
|
||||||
|
|
||||||
/* Save argv for setprocnm() in child processes */
|
|
||||||
arg0 = argv[0];
|
|
||||||
|
|
||||||
/* user calling telinit or init */
|
/* user calling telinit or init */
|
||||||
if (getpid() != 1) {
|
if (getpid() != 1) {
|
||||||
if (geteuid() == 0)
|
if (geteuid() == 0)
|
||||||
@@ -627,15 +624,6 @@ int main(int argc, char *argv[])
|
|||||||
return EX_NOPERM;
|
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
|
* Need /dev, /proc, and /sys for console=, remount and cgroups
|
||||||
*/
|
*/
|
||||||
|
|||||||
+1
-28
@@ -32,7 +32,6 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/prctl.h>
|
|
||||||
#ifdef _LIBITE_LITE
|
#ifdef _LIBITE_LITE
|
||||||
# include <libite/lite.h>
|
# include <libite/lite.h>
|
||||||
#else
|
#else
|
||||||
@@ -90,9 +89,6 @@ void console_init(void)
|
|||||||
/* Reset attributes, background and foreground color */
|
/* Reset attributes, background and foreground color */
|
||||||
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
|
dprint(STDOUT_FILENO, "\033[49m\033[39m\e[2J", 14);
|
||||||
|
|
||||||
/* Hide cursor while we boot up. */
|
|
||||||
dprint(STDOUT_FILENO, "\033[?25l", 6);
|
|
||||||
|
|
||||||
log_init();
|
log_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +463,7 @@ void networking(int updown)
|
|||||||
_exit(EX_OSERR);
|
_exit(EX_OSERR);
|
||||||
_exit(WEXITSTATUS(rc));
|
_exit(WEXITSTATUS(rc));
|
||||||
}
|
}
|
||||||
cgroup_service("network", pid, NULL, NULL, NULL);
|
cgroup_service("network", pid, NULL);
|
||||||
print(pid > 0 ? 0 : 1, "%s network interfaces ...",
|
print(pid > 0 ? 0 : 1, "%s network interfaces ...",
|
||||||
updown ? "Bringing up" : "Taking down");
|
updown ? "Bringing up" : "Taking down");
|
||||||
|
|
||||||
@@ -528,29 +524,6 @@ int in_container(void)
|
|||||||
return 0;
|
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:
|
* Local Variables:
|
||||||
* indent-tabs-mode: t
|
* indent-tabs-mode: t
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ int print_result (int fail);
|
|||||||
void set_hostname (char **hostname);
|
void set_hostname (char **hostname);
|
||||||
void networking (int updown);
|
void networking (int updown);
|
||||||
int in_container (void);
|
int in_container (void);
|
||||||
void setprocnm (const char *name);
|
|
||||||
|
|
||||||
int complete (char *cmd, int pid);
|
int complete (char *cmd, int pid);
|
||||||
int run (char *cmd, char *log);
|
int run (char *cmd, char *log);
|
||||||
@@ -83,17 +82,6 @@ pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimi
|
|||||||
pid_t run_bg (char *cmd, char *args[]);
|
pid_t run_bg (char *cmd, char *args[]);
|
||||||
int run_parts (char *dir, char *cmd, const char *env[], int progress, int sysv);
|
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)
|
static inline int dprint(int fd, const char *s, size_t len)
|
||||||
{
|
{
|
||||||
size_t loop = 3;
|
size_t loop = 3;
|
||||||
|
|||||||
+8
-122
@@ -33,6 +33,11 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <utmp.h>
|
#include <utmp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#ifdef _LIBITE_LITE
|
||||||
|
# include <libite/lite.h>
|
||||||
|
#else
|
||||||
|
# include <lite/lite.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "initctl.h"
|
#include "initctl.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
@@ -40,6 +45,7 @@
|
|||||||
#include "serv.h"
|
#include "serv.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "cgutil.h"
|
#include "cgutil.h"
|
||||||
|
#include "util.h"
|
||||||
#include "utmp-api.h"
|
#include "utmp-api.h"
|
||||||
|
|
||||||
struct cmd {
|
struct cmd {
|
||||||
@@ -118,7 +124,7 @@ void print_header(const char *fmt, ...)
|
|||||||
fputc('=', stdout);
|
fputc('=', stdout);
|
||||||
fputs("\n", stdout);
|
fputs("\n", stdout);
|
||||||
} else {
|
} else {
|
||||||
char buf[ttcols - 9];
|
char buf[ttcols];
|
||||||
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
printheader(stdout, buf, 0);
|
printheader(stdout, buf, 0);
|
||||||
@@ -1009,7 +1015,6 @@ static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev)
|
|||||||
indent, svc_typestr(svc),
|
indent, svc_typestr(svc),
|
||||||
indent, svc->forking ? "true" : "false",
|
indent, svc->forking ? "true" : "false",
|
||||||
indent, svc_status(svc));
|
indent, svc_status(svc));
|
||||||
|
|
||||||
if (svc->state != SVC_RUNNING_STATE) {
|
if (svc->state != SVC_RUNNING_STATE) {
|
||||||
int rc, sig;
|
int rc, sig;
|
||||||
|
|
||||||
@@ -1025,134 +1030,24 @@ static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev)
|
|||||||
"%s \"exit\": { \"%s\": %d },\n",
|
"%s \"exit\": { \"%s\": %d },\n",
|
||||||
indent, "signal", sig);
|
indent, "signal", sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
"%s \"origin\": \"%s\",\n"
|
"%s \"origin\": \"%s\",\n"
|
||||||
"%s \"command\": \"%s\",\n",
|
"%s \"command\": \"%s\",\n",
|
||||||
indent, svc->file[0] ? svc->file : "built-in",
|
indent, svc->file[0] ? svc->file : "built-in",
|
||||||
indent, svc_command(svc, buf, sizeof(buf), 0));
|
indent, svc_command(svc, buf, sizeof(buf), 0));
|
||||||
|
|
||||||
svc_environ(svc, buf, sizeof(buf), 0);
|
svc_environ(svc, buf, sizeof(buf), 0);
|
||||||
if (buf[0])
|
if (buf[0])
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
"%s \"environment\": \"%s\",\n", indent, buf);
|
"%s \"environment\": \"%s\",\n", indent, buf);
|
||||||
|
|
||||||
svc_cond(svc, buf, sizeof(buf), 0);
|
svc_cond(svc, buf, sizeof(buf), 0);
|
||||||
if (buf[0])
|
if (buf[0])
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
"%s \"condition\": %s,\n", indent, buf);
|
"%s \"condition\": %s,\n", indent, buf);
|
||||||
|
|
||||||
if (svc->manual)
|
if (svc->manual)
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
"%s \"starts\": %d,\n", indent, svc->once);
|
"%s \"starts\": %d,\n", indent, svc->once);
|
||||||
|
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
"%s \"restarts\": %d,\n", indent, svc->restart_tot); /* XXX: add restart_cnt and restart_max */
|
"%s \"restarts\": %d,\n", indent, svc->restart_tot); /* XXX: add restart_cnt and restart_max */
|
||||||
|
|
||||||
/* Add memory and CPU information if cgroup support is available */
|
|
||||||
if (cgrp && svc->pid > 1) {
|
|
||||||
char *group = pid_cgroup(svc->pid);
|
|
||||||
|
|
||||||
if (group) {
|
|
||||||
uint64_t throttled_usec = 0;
|
|
||||||
uint64_t nr_throttled = 0;
|
|
||||||
char path[256];
|
|
||||||
struct cg *cg;
|
|
||||||
|
|
||||||
fprintf(fp, "%s \"cgroup\": \"%s\",\n", indent, group);
|
|
||||||
fprintf(fp, "%s \"memory\": %lu,\n", indent, cgroup_memory(group));
|
|
||||||
|
|
||||||
if (cgroup_throttle(group, &throttled_usec, &nr_throttled) == 0) {
|
|
||||||
fprintf(fp, "%s \"cpu\": {\n", indent);
|
|
||||||
fprintf(fp, "%s \"throttled_usec\": %lu,\n", indent, throttled_usec);
|
|
||||||
fprintf(fp, "%s \"nr_throttled\": %lu\n", indent, nr_throttled);
|
|
||||||
fprintf(fp, "%s },\n", indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add limits structure using existing cg_conf() */
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group);
|
|
||||||
cg = cg_conf(path);
|
|
||||||
|
|
||||||
if (cg && (cg->cg_mem.min[0] || cg->cg_mem.max[0] ||
|
|
||||||
cg->cg_cpu.weight[0] || cg->cg_cpu.max[0])) {
|
|
||||||
int has_memory = cg->cg_mem.min[0] || cg->cg_mem.max[0];
|
|
||||||
int has_cpu = cg->cg_cpu.weight[0] || cg->cg_cpu.max[0];
|
|
||||||
|
|
||||||
fprintf(fp, "%s \"limits\": {\n", indent);
|
|
||||||
|
|
||||||
if (has_memory) {
|
|
||||||
fprintf(fp, "%s \"memory\": {\n", indent);
|
|
||||||
if (cg->cg_mem.min[0]) {
|
|
||||||
if (!strcmp(cg->cg_mem.min, "max"))
|
|
||||||
fprintf(fp, "%s \"min\": \"%s\"", indent, cg->cg_mem.min);
|
|
||||||
else
|
|
||||||
fprintf(fp, "%s \"min\": %s", indent, cg->cg_mem.min);
|
|
||||||
if (cg->cg_mem.max[0])
|
|
||||||
fprintf(fp, ",\n");
|
|
||||||
else
|
|
||||||
fprintf(fp, "\n");
|
|
||||||
}
|
|
||||||
if (cg->cg_mem.max[0]) {
|
|
||||||
if (!strcmp(cg->cg_mem.max, "max"))
|
|
||||||
fprintf(fp, "%s \"max\": \"%s\"\n", indent, cg->cg_mem.max);
|
|
||||||
else
|
|
||||||
fprintf(fp, "%s \"max\": %s\n", indent, cg->cg_mem.max);
|
|
||||||
}
|
|
||||||
fprintf(fp, "%s }", indent);
|
|
||||||
if (has_cpu)
|
|
||||||
fprintf(fp, ",\n");
|
|
||||||
else
|
|
||||||
fprintf(fp, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_cpu) {
|
|
||||||
char cpu_burst[32];
|
|
||||||
char *quota, *period;
|
|
||||||
char max_copy[32];
|
|
||||||
|
|
||||||
fprintf(fp, "%s \"cpu\": {\n", indent);
|
|
||||||
|
|
||||||
if (cg->cg_cpu.weight[0])
|
|
||||||
fprintf(fp, "%s \"weight\": %s,\n", indent, cg->cg_cpu.weight);
|
|
||||||
|
|
||||||
if (cg->cg_cpu.max[0]) {
|
|
||||||
/* Split cpu.max into quota and period */
|
|
||||||
strlcpy(max_copy, cg->cg_cpu.max, sizeof(max_copy));
|
|
||||||
quota = strtok(max_copy, " ");
|
|
||||||
period = strtok(NULL, " ");
|
|
||||||
|
|
||||||
fprintf(fp, "%s \"max\": {\n", indent);
|
|
||||||
if (quota) {
|
|
||||||
if (!strcmp(quota, "max"))
|
|
||||||
fprintf(fp, "%s \"quota\": \"%s\"", indent, quota);
|
|
||||||
else
|
|
||||||
fprintf(fp, "%s \"quota\": %s", indent, quota);
|
|
||||||
if (period)
|
|
||||||
fprintf(fp, ",\n");
|
|
||||||
else
|
|
||||||
fprintf(fp, "\n");
|
|
||||||
}
|
|
||||||
if (period)
|
|
||||||
fprintf(fp, "%s \"period\": %s\n", indent, period);
|
|
||||||
fprintf(fp, "%s }", indent);
|
|
||||||
|
|
||||||
/* Check for cpu.max.burst (kernel >= 5.14) */
|
|
||||||
if (cgroup_val(path, "cpu.max.burst", cpu_burst, sizeof(cpu_burst)))
|
|
||||||
fprintf(fp, ",\n%s \"max_burst\": %s\n", indent, cpu_burst);
|
|
||||||
else
|
|
||||||
fprintf(fp, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(fp, "%s }\n", indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(fp, "%s },\n", indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(fp,
|
fprintf(fp,
|
||||||
"%s \"pidfile\": \"%s\",\n"
|
"%s \"pidfile\": \"%s\",\n"
|
||||||
"%s \"pid\": %d,\n"
|
"%s \"pid\": %d,\n"
|
||||||
@@ -1239,11 +1134,8 @@ static int show_status(char *arg)
|
|||||||
printf(" Starts : %d\n", svc->once);
|
printf(" Starts : %d\n", svc->once);
|
||||||
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
|
printf(" Restarts : %d (%d/%d)\n", svc->restart_tot, svc->restart_cnt, svc->restart_max);
|
||||||
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||||
|
|
||||||
if (cgrp && svc->pid > 1) {
|
if (cgrp && svc->pid > 1) {
|
||||||
const struct cg *cg;
|
const struct cg *cg;
|
||||||
uint64_t throttled_usec = 0;
|
|
||||||
uint64_t nr_throttled = 0;
|
|
||||||
char path[256];
|
char path[256];
|
||||||
char *group;
|
char *group;
|
||||||
|
|
||||||
@@ -1255,12 +1147,6 @@ static int show_status(char *arg)
|
|||||||
cg = cg_conf(path);
|
cg = cg_conf(path);
|
||||||
|
|
||||||
printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
|
printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm)));
|
||||||
|
|
||||||
if (cgroup_throttle(group, &throttled_usec, &nr_throttled) == 0) {
|
|
||||||
printf("CPU Throttle : %lu usec (%lu times)\n",
|
|
||||||
throttled_usec, nr_throttled);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n",
|
printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n",
|
||||||
group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max,
|
group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max,
|
||||||
cg->cg_mem.min, cg->cg_mem.max);
|
cg->cg_mem.min, cg->cg_mem.max);
|
||||||
@@ -1724,7 +1610,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (interactive)
|
if (interactive)
|
||||||
ttinit(0);
|
ttinit();
|
||||||
|
|
||||||
return cmd_parse(argc - optind, &argv[optind], command);
|
return cmd_parse(argc - optind, &argv[optind], command);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ static void run_env(const char *env[])
|
|||||||
int run_parts(char *dir, char *cmd, const char *env[], int progress, int sysv)
|
int run_parts(char *dir, char *cmd, const char *env[], int progress, int sysv)
|
||||||
{
|
{
|
||||||
size_t cmdlen = cmd ? strlen(cmd) : strlen("start");
|
size_t cmdlen = cmd ? strlen(cmd) : strlen("start");
|
||||||
struct dirent **d = NULL;
|
struct dirent **d;
|
||||||
int i, num;
|
int i, num;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
|||||||
+55
-247
@@ -25,7 +25,6 @@
|
|||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
#include <ctype.h> /* isblank() */
|
#include <ctype.h> /* isblank() */
|
||||||
#include <grp.h> /* setgroups() */
|
|
||||||
#include <sched.h> /* sched_yield() */
|
#include <sched.h> /* sched_yield() */
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/reboot.h>
|
#include <sys/reboot.h>
|
||||||
@@ -40,12 +39,8 @@
|
|||||||
# include <lite/lite.h>
|
# include <lite/lite.h>
|
||||||
#endif
|
#endif
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
#ifdef HAVE_LIBCAP
|
|
||||||
# include <sys/capability.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
#include "clone3.h"
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
@@ -278,6 +273,7 @@ static int lredirect(svc_t *svc)
|
|||||||
pid_t svc_pid = getpid();
|
pid_t svc_pid = getpid();
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
int fd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use a pipe to connect to logger. This ensures isatty()
|
* Use a pipe to connect to logger. This ensures isatty()
|
||||||
@@ -290,6 +286,7 @@ static int lredirect(svc_t *svc)
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
fd = pipefd[1]; /* Write end for service */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First time, check if we have sysklogd logger tool.
|
* First time, check if we have sysklogd logger tool.
|
||||||
@@ -479,6 +476,30 @@ static int is_norespawn(void)
|
|||||||
fexist("/tmp/norespawn");
|
fexist("/tmp/norespawn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* used for process group name, derived from originating filename,
|
||||||
|
* so to group multiple services, place them in the same .conf
|
||||||
|
*/
|
||||||
|
static char *group_name(svc_t *svc, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (!svc->file[0])
|
||||||
|
return svc_ident(svc, buf, len);
|
||||||
|
|
||||||
|
ptr = strrchr(svc->file, '/');
|
||||||
|
if (ptr)
|
||||||
|
ptr++;
|
||||||
|
else
|
||||||
|
ptr = svc->file;
|
||||||
|
|
||||||
|
strlcpy(buf, ptr, len);
|
||||||
|
ptr = strstr(buf, ".conf");
|
||||||
|
if (ptr)
|
||||||
|
*ptr = 0;
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
static void compose_cmdline(svc_t *svc, char *buf, size_t len)
|
static void compose_cmdline(svc_t *svc, char *buf, size_t len)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -493,61 +514,11 @@ 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
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cap_iab_set_proc(cap_iab) != 0) {
|
|
||||||
cap_free(cap_iab);
|
|
||||||
err(1, "%s: failed setting capabilities",
|
|
||||||
svc_ident(svc, NULL, 0));
|
|
||||||
}
|
|
||||||
cap_free(cap_iab);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
if (setuid(uid))
|
|
||||||
err(1, "%s: failed setuid(%d)", svc_ident(svc, NULL, 0), uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static pid_t service_fork(svc_t *svc)
|
static pid_t service_fork(svc_t *svc)
|
||||||
{
|
{
|
||||||
const char *cgnm;
|
|
||||||
char grnam[128];
|
|
||||||
int cgfd = -1;
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
cgnm = cgroup_svc_name(svc, grnam, sizeof(grnam));
|
pid = fork();
|
||||||
cgfd = cgroup_prepare(svc, cgnm);
|
|
||||||
|
|
||||||
pid = call_clone3(0, cgfd);
|
|
||||||
if (cgfd >= 0)
|
|
||||||
close(cgfd);
|
|
||||||
|
|
||||||
if (pid < 0) {
|
|
||||||
cgroup_del_svc(svc, cgnm);
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
char *home = NULL;
|
char *home = NULL;
|
||||||
#ifdef ENABLE_STATIC
|
#ifdef ENABLE_STATIC
|
||||||
@@ -556,21 +527,7 @@ static pid_t service_fork(svc_t *svc)
|
|||||||
#else
|
#else
|
||||||
int uid = getuser(svc->username, &home);
|
int uid = getuser(svc->username, &home);
|
||||||
int gid = getgroup(svc->group);
|
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
|
#endif
|
||||||
if (svc_is_tty(svc))
|
|
||||||
setprocnm("getty");
|
|
||||||
|
|
||||||
sched_yield();
|
sched_yield();
|
||||||
|
|
||||||
@@ -581,42 +538,6 @@ static pid_t service_fork(svc_t *svc)
|
|||||||
svc_ident(svc, NULL, 0), rlim2str(i));
|
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 */
|
/* Set desired user+group */
|
||||||
if (gid >= 0) {
|
if (gid >= 0) {
|
||||||
if (setgid(gid))
|
if (setgid(gid))
|
||||||
@@ -624,7 +545,8 @@ static pid_t service_fork(svc_t *svc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uid >= 0) {
|
if (uid >= 0) {
|
||||||
set_uid(uid, svc);
|
if (setuid(uid))
|
||||||
|
err(1, "%s: failed setuid(%d)", svc_ident(svc, NULL, 0), uid);
|
||||||
|
|
||||||
/* Set default path for regular users */
|
/* Set default path for regular users */
|
||||||
if (uid > 0)
|
if (uid > 0)
|
||||||
@@ -642,11 +564,13 @@ static pid_t service_fork(svc_t *svc)
|
|||||||
source_env(svc);
|
source_env(svc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid > 0 && !has_clone3()) {
|
if (pid > 1) {
|
||||||
|
char grnam[80];
|
||||||
|
|
||||||
if (svc_is_tty(svc))
|
if (svc_is_tty(svc))
|
||||||
cgroup_user("getty", pid);
|
cgroup_user("getty", pid);
|
||||||
else
|
else
|
||||||
cgroup_service(cgnm, pid, &svc->cgroup, svc->username, svc->group);
|
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid, &svc->cgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
@@ -804,7 +728,7 @@ static int service_start(svc_t *svc)
|
|||||||
}
|
}
|
||||||
} else { /* pid == 0 */
|
} else { /* pid == 0 */
|
||||||
char str[strlen(NOTIFY_PATH) + 32];
|
char str[strlen(NOTIFY_PATH) + 32];
|
||||||
char *args[MAX_NUM_SVC_ARGS + 3];
|
char *args[MAX_NUM_SVC_ARGS + 1];
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (!svc_is_tty(svc))
|
if (!svc_is_tty(svc))
|
||||||
@@ -1203,12 +1127,12 @@ int service_stop(svc_t *svc)
|
|||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char *args[MAX_NUM_SVC_ARGS + 3];
|
char *args[MAX_NUM_SVC_ARGS + 1];
|
||||||
size_t i = 0, j;
|
size_t i = 0, j;
|
||||||
|
|
||||||
args[i++] = svc->cmd;
|
args[i++] = svc->cmd;
|
||||||
/* this handles, e.g., bridge-stop br0 stop */
|
/* this handles, e.g., bridge-stop br0 stop */
|
||||||
for (j = 0; j < MAX_NUM_SVC_ARGS; j++) {
|
for (j = 0; j < MAX_NUM_SVC_ARGS - 2; j++) {
|
||||||
if (!strlen(svc->args[j]))
|
if (!strlen(svc->args[j]))
|
||||||
break;
|
break;
|
||||||
args[i++] = svc->args[j];
|
args[i++] = svc->args[j];
|
||||||
@@ -1441,89 +1365,37 @@ static void parse_env(svc_t *svc, char *env)
|
|||||||
strlcpy(svc->env, env, sizeof(svc->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
|
* 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,
|
* default group with some cfg, e.g., :mem.max:1234 as a side effect,
|
||||||
* cgroupinit also work, selecting group init.
|
* cgroupinit also work, selecting group init.
|
||||||
*/
|
*/
|
||||||
static void parse_cgroup(svc_t *svc, char *cgroup)
|
static void parse_cgroup(svc_t *svc, char *cgroup)
|
||||||
{
|
{
|
||||||
|
char *ptr = NULL;
|
||||||
|
|
||||||
if (!cgroup)
|
if (!cgroup)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Per-service cgroup directive completely overrides global settings */
|
|
||||||
svc->cgroup.delegate = 0;
|
|
||||||
svc->cgroup.leafname[0] = 0;
|
|
||||||
svc->cgroup.cfg[0] = 0;
|
|
||||||
|
|
||||||
/* Detect syntax: old colon-based vs new comma-separated */
|
|
||||||
if (cgroup[0] == '.') {
|
if (cgroup[0] == '.') {
|
||||||
char *token, *ptr, *group;
|
ptr = strchr(cgroup, ':');
|
||||||
char settings[128] = {0};
|
if (ptr)
|
||||||
char cgcopy[256];
|
*ptr++ = 0;
|
||||||
|
group:
|
||||||
/* New comma-separated syntax: cgroup.system,name:udevd,delegate,cpu.max:10000 */
|
strlcpy(svc->cgroup.name, &cgroup[1], sizeof(svc->cgroup.name));
|
||||||
strlcpy(cgcopy, cgroup, sizeof(cgcopy));
|
if (!ptr)
|
||||||
group = strtok_r(cgcopy, ",", &ptr);
|
|
||||||
if (group && group[0] == '.')
|
|
||||||
strlcpy(svc->cgroup.name, &group[1], sizeof(svc->cgroup.name));
|
|
||||||
|
|
||||||
/* Parse remaining comma-separated options */
|
|
||||||
while ((token = strtok_r(NULL, ",", &ptr)) != NULL) {
|
|
||||||
if (strncmp(token, "name:", 5) == 0) {
|
|
||||||
/* Cgroup leaf name override */
|
|
||||||
strlcpy(svc->cgroup.leafname, token + 5, sizeof(svc->cgroup.leafname));
|
|
||||||
} else if (strcmp(token, "delegate") == 0) {
|
|
||||||
svc->cgroup.delegate = 1;
|
|
||||||
} else {
|
|
||||||
/* Other settings (cpu.weight:500, memory.max:1G, etc.) */
|
|
||||||
if (settings[0])
|
|
||||||
strlcat(settings, ",", sizeof(settings));
|
|
||||||
strlcat(settings, token, sizeof(settings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings[0])
|
|
||||||
strlcpy(svc->cgroup.cfg, settings, sizeof(svc->cgroup.cfg));
|
|
||||||
|
|
||||||
dbg("%s: cgroup name:%s leafname:%s cfg:%s delegate:%s", svc_ident(svc, NULL, 0),
|
|
||||||
svc->cgroup.name, svc->cgroup.leafname, svc->cgroup.cfg,
|
|
||||||
svc->cgroup.delegate ? "yes" : "no");
|
|
||||||
} else if (cgroup[0] == ':') {
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
/* Old syntax: cgroup:settings (keeps current group) */
|
|
||||||
ptr = &cgroup[1];
|
|
||||||
if (strlen(ptr) >= sizeof(svc->cgroup.cfg)) {
|
|
||||||
errx(1, "%s: cgroup settings too long (>%zu chars)", svc_ident(svc, NULL, 0),
|
|
||||||
sizeof(svc->cgroup.cfg));
|
|
||||||
return;
|
return;
|
||||||
}
|
} else if (cgroup[0] == ':') {
|
||||||
strlcpy(svc->cgroup.cfg, ptr, sizeof(svc->cgroup.cfg));
|
ptr = &cgroup[1];
|
||||||
} else {
|
} else
|
||||||
/* Just a group name without dot or colon */
|
goto group;
|
||||||
strlcpy(svc->cgroup.name, cgroup, sizeof(svc->cgroup.name));
|
|
||||||
|
if (strlen(ptr) >= sizeof(svc->cgroup)) {
|
||||||
|
errx(1, "%s: cgroup settings too long (>%zu chars)", svc_ident(svc, NULL, 0), sizeof(svc->cgroup));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strlcpy(svc->cgroup.cfg, ptr, sizeof(svc->cgroup.cfg));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_sighalt(svc_t *svc, char *arg)
|
static void parse_sighalt(svc_t *svc, char *arg)
|
||||||
@@ -1787,7 +1659,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
char *ready_script = NULL, *conflict = NULL;
|
char *ready_script = NULL, *conflict = NULL;
|
||||||
char *reload_script = NULL, *stop_script = NULL;
|
char *reload_script = NULL, *stop_script = NULL;
|
||||||
char *cleanup_script = NULL;
|
char *cleanup_script = NULL;
|
||||||
char *caps = NULL;
|
|
||||||
char ident[MAX_IDENT_LEN];
|
char ident[MAX_IDENT_LEN];
|
||||||
char *ifstmt = NULL;
|
char *ifstmt = NULL;
|
||||||
char *notify = NULL;
|
char *notify = NULL;
|
||||||
@@ -1903,8 +1774,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
stop_script = arg;
|
stop_script = arg;
|
||||||
else if (MATCH_CMD(cmd, "env:", arg))
|
else if (MATCH_CMD(cmd, "env:", arg))
|
||||||
env = arg;
|
env = arg;
|
||||||
else if (MATCH_CMD(cmd, "caps:", arg))
|
|
||||||
caps = arg;
|
|
||||||
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
|
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
|
||||||
else if (MATCH_CMD(cmd, "cgroup", arg))
|
else if (MATCH_CMD(cmd, "cgroup", arg))
|
||||||
cgroup = arg;
|
cgroup = arg;
|
||||||
@@ -2034,29 +1903,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
char *ptr = strchr(username, ':');
|
char *ptr = strchr(username, ':');
|
||||||
|
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
char *sup;
|
|
||||||
|
|
||||||
*ptr++ = 0;
|
*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->group, ptr, sizeof(svc->group));
|
||||||
}
|
}
|
||||||
strlcpy(svc->username, username, sizeof(svc->username));
|
strlcpy(svc->username, username, sizeof(svc->username));
|
||||||
@@ -2150,10 +1997,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
parse_env(svc, env);
|
parse_env(svc, env);
|
||||||
else
|
else
|
||||||
memset(svc->env, 0, sizeof(svc->env));
|
memset(svc->env, 0, sizeof(svc->env));
|
||||||
if (caps)
|
|
||||||
parse_caps(svc, caps);
|
|
||||||
else
|
|
||||||
memset(svc->capabilities, 0, sizeof(svc->capabilities));
|
|
||||||
if (file)
|
if (file)
|
||||||
strlcpy(svc->file, file, sizeof(svc->file));
|
strlcpy(svc->file, file, sizeof(svc->file));
|
||||||
else
|
else
|
||||||
@@ -2205,14 +2048,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
|
|
||||||
/* Seed with currently active group, may be empty */
|
/* Seed with currently active group, may be empty */
|
||||||
strlcpy(svc->cgroup.name, cgroup_current, sizeof(svc->cgroup.name));
|
strlcpy(svc->cgroup.name, cgroup_current, sizeof(svc->cgroup.name));
|
||||||
|
|
||||||
/* Apply cgroup settings if specified */
|
|
||||||
if (cgroup_settings_current[0])
|
|
||||||
strlcpy(svc->cgroup.cfg, cgroup_settings_current, sizeof(svc->cgroup.cfg));
|
|
||||||
|
|
||||||
/* Apply delegation flag */
|
|
||||||
svc->cgroup.delegate = cgroup_delegate_current;
|
|
||||||
|
|
||||||
if (cgroup)
|
if (cgroup)
|
||||||
parse_cgroup(svc, cgroup);
|
parse_cgroup(svc, cgroup);
|
||||||
|
|
||||||
@@ -2628,7 +2463,6 @@ static void service_cleanup_script(svc_t *svc)
|
|||||||
static void service_retry(svc_t *svc)
|
static void service_retry(svc_t *svc)
|
||||||
{
|
{
|
||||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||||
int rc = WEXITSTATUS(svc->status);
|
|
||||||
int timeout;
|
int timeout;
|
||||||
|
|
||||||
service_timeout_cancel(svc);
|
service_timeout_cancel(svc);
|
||||||
@@ -2637,18 +2471,6 @@ static void service_retry(svc_t *svc)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (svc->respawn) {
|
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));
|
dbg("%s crashed/exited, respawning ...", svc_ident(svc, NULL, 0));
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
@@ -3011,8 +2833,6 @@ restart:
|
|||||||
|
|
||||||
if (!svc->pid) {
|
if (!svc->pid) {
|
||||||
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
|
if (svc_is_daemon(svc) || svc_is_tty(svc)) {
|
||||||
int rc = WEXITSTATUS(svc->status);
|
|
||||||
|
|
||||||
svc_restarting(svc); /* BLOCK_RESTARTING */
|
svc_restarting(svc); /* BLOCK_RESTARTING */
|
||||||
svc_set_state(svc, SVC_HALTED_STATE);
|
svc_set_state(svc, SVC_HALTED_STATE);
|
||||||
|
|
||||||
@@ -3027,18 +2847,6 @@ restart:
|
|||||||
goto done;
|
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));
|
dbg("respawning %s", svc_ident(svc, NULL, 0));
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "cgroup.h"
|
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@@ -192,10 +191,6 @@ static void sm_check_bootstrap(void *work)
|
|||||||
dbg("Clean up all bootstrap-only tasks/services ...");
|
dbg("Clean up all bootstrap-only tasks/services ...");
|
||||||
svc_prune_bootstrap();
|
svc_prune_bootstrap();
|
||||||
|
|
||||||
/* Clean up empty cgroups from bootstrap tasks (e.g., udevadm, coldplug) */
|
|
||||||
dbg("Pruning empty cgroups from bootstrap ...");
|
|
||||||
cgroup_prune();
|
|
||||||
|
|
||||||
/* All services/tasks/etc. in configure runlevel have started */
|
/* All services/tasks/etc. in configure runlevel have started */
|
||||||
dbg("Running svc up hooks ...");
|
dbg("Running svc up hooks ...");
|
||||||
plugin_run_hooks(HOOK_SVC_UP);
|
plugin_run_hooks(HOOK_SVC_UP);
|
||||||
@@ -361,9 +356,6 @@ restart:
|
|||||||
|
|
||||||
/* Restore terse mode and run hooks before shutdown */
|
/* Restore terse mode and run hooks before shutdown */
|
||||||
if (runlevel == 0 || runlevel == 6) {
|
if (runlevel == 0 || runlevel == 6) {
|
||||||
/* Hide cursor, we're going down ... */
|
|
||||||
dprint(STDOUT_FILENO, "\033[?25l", 6);
|
|
||||||
|
|
||||||
api_exit();
|
api_exit();
|
||||||
log_exit();
|
log_exit();
|
||||||
plugin_run_hooks(HOOK_SHUTDOWN);
|
plugin_run_hooks(HOOK_SHUTDOWN);
|
||||||
@@ -410,10 +402,6 @@ restart:
|
|||||||
dbg("All services have been stopped, calling runlevel change hooks ...");
|
dbg("All services have been stopped, calling runlevel change hooks ...");
|
||||||
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
||||||
|
|
||||||
/* Clean up empty cgroups from previous runlevel */
|
|
||||||
dbg("Pruning empty cgroups from previous runlevel ...");
|
|
||||||
cgroup_prune();
|
|
||||||
|
|
||||||
dbg("Starting services new to this runlevel ...");
|
dbg("Starting services new to this runlevel ...");
|
||||||
sm.in_reload = 0;
|
sm.in_reload = 0;
|
||||||
service_step_all(SVC_TYPE_ANY);
|
service_step_all(SVC_TYPE_ANY);
|
||||||
|
|||||||
+5
-12
@@ -128,18 +128,11 @@ void stty(int fd, speed_t speed)
|
|||||||
term.c_cc[VERASE] = CERASE;
|
term.c_cc[VERASE] = CERASE;
|
||||||
tcsetattr(fd, TCSAFLUSH, &term);
|
tcsetattr(fd, TCSAFLUSH, &term);
|
||||||
|
|
||||||
/* Reset terminal to sane state - disable modes that break scrollback */
|
/* Show cursor again, if it was hidden previously */
|
||||||
dprint(fd, "\033[m", 3); /* Reset SGR attributes */
|
dprint(fd, "\033[?25h", 6);
|
||||||
dprint(fd, "\033[?1000l", 8); /* Disable X11 mouse tracking */
|
|
||||||
dprint(fd, "\033[?1002l", 8); /* Disable button event tracking */
|
/* Enable line wrap, if disabled previously */
|
||||||
dprint(fd, "\033[?1003l", 8); /* Disable all mouse tracking */
|
dprint(fd, "\033[?7h", 5);
|
||||||
dprint(fd, "\033[?1006l", 8); /* Disable SGR mouse mode */
|
|
||||||
dprint(fd, "\033[?1015l", 8); /* Disable urxvt mouse mode */
|
|
||||||
// dprint(fd, "\033[?1049l", 8); /* Exit alternate screen buffer */
|
|
||||||
dprint(fd, "\033[?1l", 6); /* Exit application cursor keys mode */
|
|
||||||
dprint(fd, "\033[?2004l", 8); /* Disable bracketed paste mode */
|
|
||||||
dprint(fd, "\033[?25h", 6); /* Show cursor */
|
|
||||||
dprint(fd, "\033[?7h", 5); /* Enable line wrap */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ typedef enum {
|
|||||||
#define MAX_STR_LEN 64
|
#define MAX_STR_LEN 64
|
||||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||||
#define MAX_USER_LEN 16
|
#define MAX_USER_LEN 16
|
||||||
#define MAX_NUM_SUPGROUPS 4
|
|
||||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||||
#define MAX_NUM_SVC_ARGS 64
|
#define MAX_NUM_SVC_ARGS 64
|
||||||
|
|
||||||
@@ -192,9 +191,6 @@ typedef struct svc {
|
|||||||
/* Identity */
|
/* Identity */
|
||||||
char username[MAX_USER_LEN];
|
char username[MAX_USER_LEN];
|
||||||
char group[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 */
|
/* Command, arguments and service description */
|
||||||
char cmd[MAX_CMD_LEN];
|
char cmd[MAX_CMD_LEN];
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@ int r_flag = 0;
|
|||||||
|
|
||||||
static int is_dir_empty(const char *path)
|
static int is_dir_empty(const char *path)
|
||||||
{
|
{
|
||||||
struct dirent **namelist = NULL;
|
struct dirent **namelist;
|
||||||
int num;
|
int num;
|
||||||
|
|
||||||
num = scandir(path, &namelist, NULL, NULL);
|
num = scandir(path, &namelist, NULL, NULL);
|
||||||
|
|||||||
+7
-44
@@ -180,34 +180,16 @@ char *str(char *fmt, ...)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int getuser(char *username, char **home)
|
||||||
* 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
|
#ifdef ENABLE_STATIC
|
||||||
if (!username || !username[0])
|
|
||||||
username = "root";
|
|
||||||
|
|
||||||
if (home)
|
if (home)
|
||||||
*home = "/"; /* XXX: Fixme */
|
*home = "/"; /* XXX: Fixme */
|
||||||
return fgetint("/etc/passwd", "x:\n", username);
|
return fgetint("/etc/passwd", "x:\n", username);
|
||||||
#else
|
#else
|
||||||
const struct passwd *usr;
|
struct passwd *usr;
|
||||||
|
|
||||||
if (!username || !username[0]) {
|
if (!username || (usr = getpwnam(username)) == NULL)
|
||||||
usr = getpwuid(0);
|
|
||||||
if (!usr)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (home)
|
|
||||||
*home = usr->pw_dir;
|
|
||||||
return usr->pw_uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
usr = getpwnam(username);
|
|
||||||
if (!usr)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (home)
|
if (home)
|
||||||
@@ -216,30 +198,14 @@ int getuser(const char *username, char **home)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int getgroup(char *group)
|
||||||
* 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
|
#ifdef ENABLE_STATIC
|
||||||
if (!group || !group[0])
|
|
||||||
group = "root";
|
|
||||||
|
|
||||||
return fgetint("/etc/group", "x:\n", group);
|
return fgetint("/etc/group", "x:\n", group);
|
||||||
#else
|
#else
|
||||||
const struct group *grp;
|
struct group *grp;
|
||||||
|
|
||||||
if (!group || !group[0]) {
|
if ((grp = getgrnam(group)) == NULL)
|
||||||
grp = getgrgid(0);
|
|
||||||
if (!grp)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return grp->gr_gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
grp = getgrnam(group);
|
|
||||||
if (!grp)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return grp->gr_gid;
|
return grp->gr_gid;
|
||||||
@@ -721,7 +687,7 @@ int fistmpfs(char *dir)
|
|||||||
* Called by initctl, and by finit at boot and shutdown, to
|
* Called by initctl, and by finit at boot and shutdown, to
|
||||||
* (re)initialize the screen size for print() et al.
|
* (re)initialize the screen size for print() et al.
|
||||||
*/
|
*/
|
||||||
int ttinit(int probe_size)
|
int ttinit(void)
|
||||||
{
|
{
|
||||||
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
|
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
|
||||||
struct winsize ws = { 0 };
|
struct winsize ws = { 0 };
|
||||||
@@ -741,9 +707,6 @@ int ttinit(int probe_size)
|
|||||||
tcsetattr(STDERR_FILENO, TCSANOW, &tc);
|
tcsetattr(STDERR_FILENO, TCSANOW, &tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (probe_size)
|
|
||||||
goto fallback;
|
|
||||||
|
|
||||||
/* 1. Try TIOCWINSZ to query window size from kernel */
|
/* 1. Try TIOCWINSZ to query window size from kernel */
|
||||||
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
|
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
|
||||||
ttrows = ws.ws_row;
|
ttrows = ws.ws_row;
|
||||||
|
|||||||
+4
-4
@@ -62,8 +62,8 @@ const char *basenm (const char *path);
|
|||||||
|
|
||||||
char *str (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
char *str (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
int getuser (const char *username, char **home);
|
int getuser (char *username, char **home);
|
||||||
int getgroup (const char *group);
|
int getgroup (char *group);
|
||||||
|
|
||||||
int getcuser (char *buf, size_t len);
|
int getcuser (char *buf, size_t len);
|
||||||
int getcgroup (char *buf, size_t len);
|
int getcgroup (char *buf, size_t len);
|
||||||
@@ -95,11 +95,11 @@ int fismnt (char *dir);
|
|||||||
int fistmpfs (char *dir);
|
int fistmpfs (char *dir);
|
||||||
|
|
||||||
#ifdef HAVE_TERMIOS_H
|
#ifdef HAVE_TERMIOS_H
|
||||||
int ttinit (int probe_size);
|
int ttinit (void);
|
||||||
int ttraw (void);
|
int ttraw (void);
|
||||||
int ttcooked (void);
|
int ttcooked (void);
|
||||||
#else
|
#else
|
||||||
#define ttinit(v) ttcols
|
#define ttinit() ttcols
|
||||||
#define ttraw() 0
|
#define ttraw() 0
|
||||||
#define ttcooked() 0
|
#define ttcooked() 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+10
-10
@@ -18,33 +18,33 @@
|
|||||||
# change the contents any way you like, it can even be empty.
|
# change the contents any way you like, it can even be empty.
|
||||||
|
|
||||||
# Check for systemd-udevd and eudev, if we find both, we opt for the latter.
|
# Check for systemd-udevd and eudev, if we find both, we opt for the latter.
|
||||||
service nowarn cgroup.system,name:udevd name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
service nowarn cgroup.system name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
||||||
[S12345789] /lib/systemd/systemd-udevd $UDEVD_ARGS -- Device event daemon (udev)
|
[S12345789] /lib/systemd/systemd-udevd $UDEVD_ARGS -- Device event daemon (udev)
|
||||||
service nowarn cgroup.system,name:udevd name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
service nowarn cgroup.system name:udevd notify:none env:-/etc/default/udevd pid:udevd log \
|
||||||
[S12345789] udevd $UDEVD_ARGS -- Device event daemon (udev)
|
[S12345789] udevd $UDEVD_ARGS -- Device event daemon (udev)
|
||||||
|
|
||||||
# Wait for udevd to start, then trigger coldplug events and module loading.
|
# Wait for udevd to start, then trigger coldplug events and module loading.
|
||||||
# The last 'settle' call waits for it to finalize processing all uevents.
|
# The last 'settle' call waits for it to finalize processing all uevents.
|
||||||
run nowarn if:udevd cgroup.init,name:udevadm :1 [S] <service/udevd/ready> log udevadm settle -t 0 --
|
run nowarn if:udevd cgroup.init :1 [S] <service/udevd/ready> log udevadm settle -t 0 --
|
||||||
run nowarn if:udevd cgroup.init,name:udevadm :2 [S] <service/udevd/ready> log udevadm control --reload --
|
run nowarn if:udevd cgroup.init :2 [S] <service/udevd/ready> log udevadm control --reload --
|
||||||
run nowarn if:udevd cgroup.init,name:udevadm :3 [S] <service/udevd/ready> log udevadm trigger -c add -t devices -- Requesting device events
|
run nowarn if:udevd cgroup.init :3 [S] <service/udevd/ready> log udevadm trigger -c add -t devices -- Requesting device events
|
||||||
run nowarn if:udevd cgroup.init,name:udevadm :4 [S] <service/udevd/ready> log udevadm trigger -c add -t subsystems -- Requesting subsystem events
|
run nowarn if:udevd cgroup.init :4 [S] <service/udevd/ready> log udevadm trigger -c add -t subsystems -- Requesting subsystem events
|
||||||
run nowarn if:udevd cgroup.init,name:udevadm :5 [S] <service/udevd/ready> log udevadm settle -t 30 -- Waiting for udev to finish
|
run nowarn if:udevd cgroup.init :5 [S] <service/udevd/ready> log udevadm settle -t 30 -- Waiting for udev to finish
|
||||||
|
|
||||||
# If udevd is not available, check for mdevd. Unlike plain 'mdev -df', the
|
# If udevd is not available, check for mdevd. Unlike plain 'mdev -df', the
|
||||||
# mdevd-coldplug process exits when mdevd has completed. So the condition
|
# mdevd-coldplug process exits when mdevd has completed. So the condition
|
||||||
# <run/coldplug/success> (same as for plain mdev below) can be relied on as a
|
# <run/coldplug/success> (same as for plain mdev below) can be relied on as a
|
||||||
# barrier for services that, e.g., need to probe for switchcore ports or PHYs
|
# barrier for services that, e.g., need to probe for switchcore ports or PHYs
|
||||||
# that require firmware loading before they show up.
|
# that require firmware loading before they show up.
|
||||||
service nowarn conflict:udevd,mdev cgroup.init,name:mdevd notify:none if:!udevd \
|
service nowarn conflict:udevd,mdev cgroup.init notify:none if:!udevd \
|
||||||
[S12345789] notify:s6 mdevd -O 4 -D %n -- Device event daemon (mdevd)
|
[S12345789] notify:s6 mdevd -O 4 -D %n -- Device event daemon (mdevd)
|
||||||
run nowarn conflict:udevd,mdev cgroup.init,name:coldplug <service/mdevd/ready> \
|
run nowarn conflict:udevd,mdev cgroup.init name:coldplug <service/mdevd/ready> \
|
||||||
[S] mdevd-coldplug -- Cold plugging system
|
[S] mdevd-coldplug -- Cold plugging system
|
||||||
|
|
||||||
# If neither udevd or mdevd are available, fall back to mdev. The new daemon
|
# If neither udevd or mdevd are available, fall back to mdev. The new daemon
|
||||||
# mode calls `mdev -s` internally but does not create a PID file when that is
|
# mode calls `mdev -s` internally but does not create a PID file when that is
|
||||||
# done, so synchronization with coldplug is fragile.
|
# done, so synchronization with coldplug is fragile.
|
||||||
service nowarn conflict:udevd,mdevd cgroup.init,name:mdev notify:none if:!udevd,!mdevd \
|
service nowarn conflict:udevd,mdevd cgroup.init notify:none if:!udevd,!mdevd \
|
||||||
[S12345789] mdev -df -S -- Device event daemon (mdev)
|
[S12345789] mdev -df -S -- Device event daemon (mdev)
|
||||||
|
|
||||||
# Trigger a replay of all uevents from boot, before userspace was up and the
|
# Trigger a replay of all uevents from boot, before userspace was up and the
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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/etc/rcS.d/S01abc.sh skel/etc/rcS.d/S02def.sh \
|
||||||
skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \
|
skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \
|
||||||
skel/etc/inittab skel/etc/hostname skel/etc/fstab \
|
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/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/.empty skel/etc/finit.d/available/.empty \
|
||||||
skel/etc/finit.d/enabled/.empty \
|
skel/etc/finit.d/enabled/.empty \
|
||||||
|
|||||||
@@ -2,14 +2,6 @@
|
|||||||
|
|
||||||
set -eu
|
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
|
# shellcheck disable=SC2154
|
||||||
make -C "$top_builddir" DESTDIR="$SYSROOT" install
|
make -C "$top_builddir" DESTDIR="$SYSROOT" install
|
||||||
|
|
||||||
@@ -29,13 +21,3 @@ done
|
|||||||
for conf in 10-hotplug.conf; do
|
for conf in 10-hotplug.conf; do
|
||||||
find "$SYSROOT" -name $conf -delete
|
find "$SYSROOT" -name $conf -delete
|
||||||
done
|
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"
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
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:
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
/usr/local/lib
|
|
||||||
/usr/lib
|
|
||||||
/lib
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
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
|
|
||||||
Reference in New Issue
Block a user