mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70a44d3860 | ||
|
|
f29a3db86c | ||
|
|
f6b394e0a2 | ||
|
|
ccaa4caf48 | ||
|
|
77d199f1a4 | ||
|
|
0448151c0a | ||
|
|
75739fbeef | ||
|
|
3a72d1263c | ||
|
|
7e2b154f9b | ||
|
|
547e59b2ad | ||
|
|
7f37d0c4e9 | ||
|
|
dd77f63ad3 | ||
|
|
e0f9df1f27 | ||
|
|
b56ffc155e | ||
|
|
4596e27eb0 | ||
|
|
a3eac3a85e | ||
|
|
7d09e34b80 | ||
|
|
3febc7d513 | ||
|
|
309ad1bea7 | ||
|
|
30f2ca3b2e | ||
|
|
4a53f610cd | ||
|
|
e74dff96ba | ||
|
|
9ed4407b20 |
@@ -1,12 +1,13 @@
|
|||||||
name: Bob the Builder
|
name: Bob the Builder
|
||||||
|
|
||||||
# Run on all branches, including all pull requests, except the 'dev'
|
# Run on push to release branches (master and maintenance N.x), and
|
||||||
# branch since that's where we run Coverity Scan (limited tokens/day)
|
# on all pull requests. The 'dev' branch is where we run Coverity
|
||||||
|
# Scan (limited tokens/day)
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '**'
|
- master
|
||||||
- '!dev'
|
- '[0-9]+.x'
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened, labeled]
|
types: [opened, synchronize, reopened, labeled]
|
||||||
|
|
||||||
@@ -22,8 +23,6 @@ jobs:
|
|||||||
# Verify we can build on latest Ubuntu with both gcc and clang
|
# Verify we can build on latest Ubuntu with both gcc and clang
|
||||||
name: ${{ matrix.compiler }}
|
name: ${{ matrix.compiler }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip redundant builds for PRs - prefer PR builds over push builds
|
|
||||||
if: github.event_name != 'push' || github.ref == 'refs/heads/master'
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
compiler: [gcc, clang]
|
compiler: [gcc, clang]
|
||||||
@@ -43,7 +42,7 @@ jobs:
|
|||||||
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
||||||
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Static Finit
|
- name: Static Finit
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
@@ -81,7 +80,7 @@ jobs:
|
|||||||
make -j1 check || (cat test/test-suite.log; false)
|
make -j1 check || (cat test/test-suite.log; false)
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: finit-test-${{ matrix.compiler }}
|
name: finit-test-${{ matrix.compiler }}
|
||||||
path: test/*.log
|
path: test/*.log
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
coverity:
|
coverity:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Fetch latest Coverity Scan MD5
|
- name: Fetch latest Coverity Scan MD5
|
||||||
id: var
|
id: var
|
||||||
env:
|
env:
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
|
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
|
||||||
-O coverity-latest.tar.gz.md5
|
-O coverity-latest.tar.gz.md5
|
||||||
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
|
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v6
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: coverity-latest.tar.gz
|
path: coverity-latest.tar.gz
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
|
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
|
||||||
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
|
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
|
||||||
- name: Upload build.log
|
- name: Upload build.log
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: coverity-build.log
|
name: coverity-build.log
|
||||||
path: cov-int/build-log.txt
|
path: cov-int/build-log.txt
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- '[0-9]+.x'
|
||||||
paths:
|
paths:
|
||||||
- 'doc/**'
|
- 'doc/**'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
- 'mkdocs.yml'
|
- 'mkdocs.yml'
|
||||||
|
- 'configure.ac'
|
||||||
- '.github/workflows/docs.yml'
|
- '.github/workflows/docs.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened, labeled]
|
types: [opened, synchronize, reopened, labeled]
|
||||||
@@ -15,6 +17,7 @@ on:
|
|||||||
- 'doc/**'
|
- 'doc/**'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
- 'mkdocs.yml'
|
- 'mkdocs.yml'
|
||||||
|
- 'configure.ac'
|
||||||
- '.github/workflows/docs.yml'
|
- '.github/workflows/docs.yml'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -23,14 +26,23 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Needed for git-revision-date-localized plugin
|
fetch-depth: 0 # Needed for git-revision-date-localized plugin
|
||||||
|
|
||||||
|
- name: Determine docs version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
version=$(sed -n 's/^AC_INIT(\[Finit\], *\[\([0-9]*\)\..*/\1.x/p' configure.ac)
|
||||||
|
test -n "$version"
|
||||||
|
echo "version=$version" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
@@ -46,25 +58,27 @@ jobs:
|
|||||||
run: mkdocs build --clean
|
run: mkdocs build --clean
|
||||||
|
|
||||||
- name: Upload site artifact
|
- name: Upload site artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: site
|
name: site
|
||||||
path: site/
|
path: site/
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.build.outputs.version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download site artifact
|
- name: Download site artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: site
|
name: site
|
||||||
path: site/
|
path: site/
|
||||||
|
|
||||||
- name: Checkout pages repo
|
- name: Checkout pages repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: finit-project/finit-project.github.io
|
repository: finit-project/finit-project.github.io
|
||||||
path: pages
|
path: pages
|
||||||
@@ -79,7 +93,56 @@ jobs:
|
|||||||
|
|
||||||
- name: Sync site to pages repo
|
- name: Sync site to pages repo
|
||||||
run: |
|
run: |
|
||||||
rsync -a --delete --exclude .git site/ pages/
|
rsync -a --delete site/ "pages/$VERSION/"
|
||||||
|
|
||||||
|
- name: Update version index and redirects
|
||||||
|
run: |
|
||||||
|
cd pages/
|
||||||
|
|
||||||
|
# The root holds only version directories and the files
|
||||||
|
# generated below, drop anything else (pre-versioned site)
|
||||||
|
find . -maxdepth 1 ! -name . ! -name .git ! -name CNAME \
|
||||||
|
! -name '[0-9]*.x' -exec rm -rf {} +
|
||||||
|
|
||||||
|
latest=$(ls -d [0-9]*.x | sort -rV | head -1)
|
||||||
|
|
||||||
|
sep=
|
||||||
|
printf '[' > versions.json
|
||||||
|
for v in $(ls -d [0-9]*.x | sort -rV); do
|
||||||
|
printf '%s{"version": "%s", "title": "%s", "aliases": []}' \
|
||||||
|
"$sep" "$v" "$v" >> versions.json
|
||||||
|
sep=', '
|
||||||
|
done
|
||||||
|
printf ']\n' >> versions.json
|
||||||
|
|
||||||
|
cat > index.html <<EOF
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="refresh" content="0; url=$latest/">
|
||||||
|
<link rel="canonical" href="https://finit-project.github.io/$latest/">
|
||||||
|
</head>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > 404.html <<EOF
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Finit — page not found</title>
|
||||||
|
<script>
|
||||||
|
var path = window.location.pathname;
|
||||||
|
if (!/^\/[0-9]+\.x(\/|$)/.test(path))
|
||||||
|
window.location.replace("/$latest" + path);
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Page not found. Try the <a href="/$latest/">latest User Guide</a>.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
|
||||||
- name: Commit and push
|
- name: Commit and push
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Installing dependencies ...
|
- name: Installing dependencies ...
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
@@ -31,12 +31,17 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
ver=${GITHUB_REF#refs/tags/}
|
ver=${GITHUB_REF#refs/tags/}
|
||||||
echo "ver=${ver}" >> $GITHUB_OUTPUT
|
echo "ver=${ver}" >> $GITHUB_OUTPUT
|
||||||
if echo $ver | grep -qE '^[0-9]+\.[0-9]+(\.[0-9]+)?(-alpha|-beta|-rc)[0-9]*$'; then
|
stable='[0-9]+\.[0-9]+(\.[0-9]+)?'
|
||||||
|
if echo $ver | grep -qE "^${stable}(-alpha|-beta|-rc)[0-9]*$"; then
|
||||||
echo "pre=true" >> $GITHUB_OUTPUT
|
echo "pre=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "pre=false" >> $GITHUB_OUTPUT
|
echo "pre=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
if echo $ver | grep -qE '^[0-9.]+\.[0-9.]+(\.[0-9]+)?$'; then
|
# Mark as latest release only if this is the highest stable
|
||||||
|
# tag, so a 4.x maintenance release doesn't outrank 5.x
|
||||||
|
newest=$(git ls-remote --tags --refs origin | awk -F/ '{print $NF}' \
|
||||||
|
| grep -E "^${stable}$" | sort -V | tail -1)
|
||||||
|
if [ "$ver" = "$newest" ]; then
|
||||||
echo "latest=true" >> $GITHUB_OUTPUT
|
echo "latest=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "latest=false" >> $GITHUB_OUTPUT
|
echo "latest=false" >> $GITHUB_OUTPUT
|
||||||
@@ -64,7 +69,7 @@ jobs:
|
|||||||
mv ../*.tar.* artifacts/
|
mv ../*.tar.* artifacts/
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: finit-release-test-logs
|
name: finit-release-test-logs
|
||||||
path: finit-*/_build/sub/test/*.log
|
path: finit-*/_build/sub/test/*.log
|
||||||
|
|||||||
@@ -14,22 +14,31 @@ jobs:
|
|||||||
schedule:
|
schedule:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
run: ${{ steps.schedule.outputs.run }}
|
branches: ${{ steps.schedule.outputs.branches }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Check for any commits since last week
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Check for branches with no commits since last week
|
||||||
id: schedule
|
id: schedule
|
||||||
run: |
|
run: |
|
||||||
commits="$(git log --format=%H --since='1 week ago')"
|
branches=
|
||||||
|
maint=$(git branch -r --format='%(refname:lstrip=3)' | grep -E '^[0-9]+\.x$')
|
||||||
|
for branch in master $maint; do
|
||||||
|
commits="$(git log --format=%H --since='1 week ago' origin/$branch)"
|
||||||
if [ -z "$commits" ]; then
|
if [ -z "$commits" ]; then
|
||||||
echo "run=true" >> $GITHUB_OUTPUT
|
branches="$branches\"$branch\","
|
||||||
else
|
|
||||||
echo "run=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
echo "branches=[${branches%,}]" >> $GITHUB_OUTPUT
|
||||||
distcheck:
|
distcheck:
|
||||||
needs: schedule
|
needs: schedule
|
||||||
if: ${{ needs.schedule.outputs.run == 'true' }}
|
if: ${{ needs.schedule.outputs.branches != '[]' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(needs.schedule.outputs.branches) }}
|
||||||
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -42,7 +51,9 @@ jobs:
|
|||||||
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
||||||
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
ref: ${{ matrix.branch }}
|
||||||
- name: Base run
|
- name: Base run
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
@@ -61,7 +72,7 @@ jobs:
|
|||||||
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: finit-weekly-test-logs
|
name: finit-weekly-test-logs-${{ matrix.branch }}
|
||||||
path: finit-*/_build/sub/test/*.log
|
path: finit-*/_build/sub/test/*.log
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([Finit], [4.17-rc1], [https://github.com/finit-project/finit/issues],
|
AC_INIT([Finit], [4.18-pre], [https://github.com/finit-project/finit/issues],
|
||||||
[finit], [https://http://finit-project.github.io/])
|
[finit], [https://http://finit-project.github.io/])
|
||||||
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])
|
||||||
|
|||||||
+47
-1
@@ -3,7 +3,53 @@ Change Log
|
|||||||
|
|
||||||
All relevant changes are documented in this file.
|
All relevant changes are documented in this file.
|
||||||
|
|
||||||
[4.17][] - 2026-04-xx
|
[Unreleased]
|
||||||
|
------------
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
- Restart log now spells out the signal name and flags core dumps,
|
||||||
|
e.g. `killed by SIGKILL` or `killed by SIGSEGV, core dumped`, in
|
||||||
|
place of the bare numeric `by signal: N`. Gives operators a much
|
||||||
|
stronger breadcrumb when a daemon dies unexpectedly
|
||||||
|
- Service `user:` and `group:` names, including supplementary groups,
|
||||||
|
can now be up to 63 characters, up from 15. Longer names were
|
||||||
|
silently truncated before the lookup, so the service never started.
|
||||||
|
By Anees Rehman
|
||||||
|
- Shutdown now stops after the first successful remount of `/` read
|
||||||
|
only. The plain `mount -n -o remount,ro /`, which reads
|
||||||
|
`/etc/fstab`, is only used when both device-agnostic forms fail,
|
||||||
|
the same order as Debian's `umountroot`. By Anees Rehman
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Remove stale daemon-owned (`pid:!`) pidfiles after unclean exits.
|
||||||
|
When a daemon dies via SIGKILL/OOM/segfault, or exits early during
|
||||||
|
startup, its pidfile lingers and can prevent the next instance from
|
||||||
|
starting -- `dbus-daemon` for example refuses to start when its
|
||||||
|
pidfile already exists, so Finit's restart loop gives up. Finit now
|
||||||
|
drops the file when it still names the just-reaped PID and that PID
|
||||||
|
is no longer alive (the liveness check guards against PID reuse).
|
||||||
|
This is a controlled exception to the long-standing rule that Finit
|
||||||
|
does not touch service-owned pidfiles
|
||||||
|
- Fix misspelled `SIGUNKOWN` returned by `sig_name()` for unknown
|
||||||
|
signal numbers, now spelled correctly as `SIGUNKNOWN`. Surfaced by
|
||||||
|
the new restart log above
|
||||||
|
- Fix mode of existing `d`/`D` directories in tmpfiles, the chmod branch
|
||||||
|
never ran because the directory creation swallowed `EEXIST`. Mode and
|
||||||
|
owner of `d`/`D`, `e`, and `f`/`F` entries, and of the directories
|
||||||
|
`mksubsys()` creates for the dbus plugin, are now set through an open fd
|
||||||
|
and only when they differ, so an immutable directory like `/var/empty`
|
||||||
|
no longer warns on every boot. By Sam Brkopac
|
||||||
|
- Fix crash at config load for a `stop:` or `reload:` script written
|
||||||
|
with a timeout, e.g. `stop:5,/bin/true`, which took PID 1 down
|
||||||
|
- Fix a lost reload for a service paused mid-reload by a condition in
|
||||||
|
flux. A second reload arriving meanwhile re-parsed the unchanged .conf
|
||||||
|
and cleared the pending mark, so the service resumed without ever being
|
||||||
|
reloaded. Seen on Infix, where sshd kept its old listen addresses after
|
||||||
|
a change that landed as two reloads in a row
|
||||||
|
|
||||||
|
[4.17][] - 2026-04-28
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ basename of the binary to guess the PID file to watch for the PID:
|
|||||||
`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
|
`/var/run/ntpd.pid`. If Finit guesses wrong, you have to submit the
|
||||||
full `pid:!/path/to/file.pid`.
|
full `pid:!/path/to/file.pid`.
|
||||||
|
|
||||||
|
With `pid:!/path`, the file belongs to the service: Finit reads it
|
||||||
|
but does not create or remove it. The one exception is *stale*
|
||||||
|
cleanup — if the service dies without removing its own pidfile
|
||||||
|
(SIGKILL, OOM, segfault), and the file still names the just-reaped
|
||||||
|
PID, Finit removes it before the next retry. This prevents daemons
|
||||||
|
that refuse to start on an existing pidfile (e.g. `dbus-daemon`)
|
||||||
|
from getting stuck in a crash-restart loop.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
In the case of `ospfd` (below), we omit the `-d` flag (daemonize) to
|
In the case of `ospfd` (below), we omit the `-d` flag (daemonize) to
|
||||||
|
|||||||
+71
-40
@@ -1,9 +1,9 @@
|
|||||||
site_name: ""
|
site_name: User's Guide
|
||||||
site_description: Fast Init for Linux Systems
|
site_description: Fast Init for Linux Systems
|
||||||
site_url: http://finit-project.github.io
|
site_url: https://finit-project.github.io
|
||||||
repo_url: https://github.com/finit-project/finit
|
repo_url: https://github.com/finit-project/finit
|
||||||
repo_name: Finit Project
|
repo_name: Finit Project
|
||||||
copyright: Copyright © 2008-2025 Joachim Wiberg
|
copyright: Copyright © 2008-2026 Joachim Wiberg
|
||||||
docs_dir: doc/
|
docs_dir: doc/
|
||||||
edit_uri: edit/master/doc/
|
edit_uri: edit/master/doc/
|
||||||
extra_css:
|
extra_css:
|
||||||
@@ -13,42 +13,6 @@ exclude_docs: |
|
|||||||
TODO.md
|
TODO.md
|
||||||
ChangeLog.md
|
ChangeLog.md
|
||||||
|
|
||||||
theme:
|
|
||||||
logo: img/logo.png
|
|
||||||
name: material
|
|
||||||
features:
|
|
||||||
- toc.follow
|
|
||||||
- toc.integrate
|
|
||||||
- navigation.path
|
|
||||||
- navigation.instant
|
|
||||||
- navigation.instant.progress
|
|
||||||
- navigation.tracking
|
|
||||||
- navigation.tabs
|
|
||||||
# - navigation.sections
|
|
||||||
# - navigation.expand
|
|
||||||
# - navigation.top
|
|
||||||
- navigation.footer
|
|
||||||
- search.highlight
|
|
||||||
- search.share
|
|
||||||
# - content.action.edit
|
|
||||||
- content.code.copy
|
|
||||||
- content.code.annotate
|
|
||||||
palette:
|
|
||||||
- media: "(prefers-color-scheme: light)"
|
|
||||||
scheme: default
|
|
||||||
primary: orange
|
|
||||||
accent: orange
|
|
||||||
toggle:
|
|
||||||
icon: material/weather-night
|
|
||||||
name: Switch to dark mode
|
|
||||||
- media: "(prefers-color-scheme: dark)"
|
|
||||||
scheme: slate
|
|
||||||
primary: black
|
|
||||||
accent: black
|
|
||||||
toggle:
|
|
||||||
icon: material/weather-sunny
|
|
||||||
name: Switch to light mode
|
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Getting Started:
|
- Getting Started:
|
||||||
- Introduction: index.md
|
- Introduction: index.md
|
||||||
@@ -94,14 +58,62 @@ nav:
|
|||||||
- Distributions: distro.md
|
- Distributions: distro.md
|
||||||
- Requirements: requirements.md
|
- Requirements: requirements.md
|
||||||
|
|
||||||
|
theme:
|
||||||
|
logo: img/logo.png
|
||||||
|
name: material
|
||||||
|
features:
|
||||||
|
- toc.follow
|
||||||
|
# - toc.integrate
|
||||||
|
- navigation.path
|
||||||
|
- navigation.instant
|
||||||
|
- navigation.instant.progress
|
||||||
|
- navigation.tracking
|
||||||
|
- navigation.indexes
|
||||||
|
# - navigation.tabs
|
||||||
|
# - navigation.sections
|
||||||
|
# - navigation.expand
|
||||||
|
# - navigation.top
|
||||||
|
# - navigation.footer
|
||||||
|
- search.highlight
|
||||||
|
- search.share
|
||||||
|
# - content.action.edit
|
||||||
|
- content.code.copy
|
||||||
|
- content.code.annotate
|
||||||
|
- content.footnote.tooltips
|
||||||
|
palette:
|
||||||
|
- media: "(prefers-color-scheme: light)"
|
||||||
|
scheme: default
|
||||||
|
primary: orange
|
||||||
|
accent: orange
|
||||||
|
toggle:
|
||||||
|
icon: material/weather-night
|
||||||
|
name: Switch to dark mode
|
||||||
|
- media: "(prefers-color-scheme: dark)"
|
||||||
|
scheme: slate
|
||||||
|
primary: black
|
||||||
|
accent: black
|
||||||
|
toggle:
|
||||||
|
icon: material/weather-sunny
|
||||||
|
name: Switch to light mode
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- admonition
|
- admonition
|
||||||
- attr_list
|
- attr_list
|
||||||
|
- footnotes
|
||||||
|
- pymdownx.blocks.caption
|
||||||
|
- pymdownx.critic
|
||||||
|
- pymdownx.caret
|
||||||
|
- pymdownx.keys
|
||||||
|
- pymdownx.mark
|
||||||
|
- pymdownx.tilde
|
||||||
- pymdownx.details
|
- pymdownx.details
|
||||||
- pymdownx.superfences
|
- pymdownx.superfences
|
||||||
- pymdownx.highlight:
|
- pymdownx.highlight:
|
||||||
anchor_linenums: true
|
anchor_linenums: true
|
||||||
- pymdownx.inlinehilite
|
- pymdownx.inlinehilite
|
||||||
|
- pymdownx.emoji:
|
||||||
|
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||||
|
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||||
- pymdownx.snippets
|
- pymdownx.snippets
|
||||||
- pymdownx.tabbed:
|
- pymdownx.tabbed:
|
||||||
alternate_style: true
|
alternate_style: true
|
||||||
@@ -112,10 +124,29 @@ markdown_extensions:
|
|||||||
plugins:
|
plugins:
|
||||||
- search
|
- search
|
||||||
- callouts
|
- callouts
|
||||||
- glightbox
|
- glightbox:
|
||||||
|
touchNavigation: true
|
||||||
|
loop: false
|
||||||
|
effect: zoom
|
||||||
|
slide_effect: slide
|
||||||
|
width: 100%
|
||||||
|
height: auto
|
||||||
|
zoomable: true
|
||||||
|
draggable: true
|
||||||
|
skip_classes:
|
||||||
|
- custom-skip-class-name
|
||||||
|
auto_themed: true
|
||||||
|
auto_caption: false
|
||||||
|
caption_position: bottom
|
||||||
|
background: black
|
||||||
|
shadow: false
|
||||||
|
manual: false
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
|
generator: false
|
||||||
homepage: https://finit-project.github.io/
|
homepage: https://finit-project.github.io/
|
||||||
|
version:
|
||||||
|
provider: mike
|
||||||
social:
|
social:
|
||||||
- icon: fontawesome/brands/github
|
- icon: fontawesome/brands/github
|
||||||
link: https://github.com/finit-project/finit
|
link: https://github.com/finit-project/finit
|
||||||
|
|||||||
+9
-4
@@ -379,7 +379,6 @@ void print(int rc, const char *fmt, ...)
|
|||||||
/* Pending state: show description with spinner, no final status yet */
|
/* Pending state: show description with spinner, no final status yet */
|
||||||
if (!buf[0])
|
if (!buf[0])
|
||||||
return;
|
return;
|
||||||
delline();
|
|
||||||
if (progress_style == PROGRESS_CLASSIC)
|
if (progress_style == PROGRESS_CLASSIC)
|
||||||
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
|
cprintf("\r%s ", pad(buf, sizeof(buf), ".", sizeof(buf)));
|
||||||
else
|
else
|
||||||
@@ -391,15 +390,21 @@ void print(int rc, const char *fmt, ...)
|
|||||||
* Final status. Emit description + status in a single cprintf() so
|
* Final status. Emit description + status in a single cprintf() so
|
||||||
* both reach the console in one write(), preventing kernel messages or
|
* both reach the console in one write(), preventing kernel messages or
|
||||||
* command output from splitting the description from its [ OK ]/[FAIL].
|
* command output from splitting the description from its [ OK ]/[FAIL].
|
||||||
|
*
|
||||||
|
* status() returns a pointer to a static buffer, so we must copy the
|
||||||
|
* result for rc before calling status(3) — the second call overwrites
|
||||||
|
* the same buffer, corrupting the first result.
|
||||||
*/
|
*/
|
||||||
last_desc[0] = 0;
|
last_desc[0] = 0;
|
||||||
|
|
||||||
if (buf[0]) {
|
if (buf[0]) {
|
||||||
delline();
|
char st[64];
|
||||||
|
|
||||||
|
strlcpy(st, status(rc), sizeof(st));
|
||||||
if (progress_style == PROGRESS_CLASSIC)
|
if (progress_style == PROGRESS_CLASSIC)
|
||||||
cprintf("\r%s %s\n", pad(buf, sizeof(buf), ".", sizeof(buf)), status(rc));
|
cprintf("\r%s %s\n", pad(buf, sizeof(buf), ".", sizeof(buf)), st);
|
||||||
else
|
else
|
||||||
cprintf("\r\e[K%s%s\r%s\n", status(3), buf, status(rc));
|
cprintf("\r\e[K%s%s\r%s\n", status(3), buf, st);
|
||||||
} else {
|
} else {
|
||||||
if (progress_style == PROGRESS_CLASSIC)
|
if (progress_style == PROGRESS_CLASSIC)
|
||||||
cprintf("%s\n", status(rc));
|
cprintf("%s\n", status(rc));
|
||||||
|
|||||||
+6
-2
@@ -121,15 +121,19 @@ void unmount_tmpfs(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unmount_regular(void)
|
int unmount_regular(void)
|
||||||
{
|
{
|
||||||
const struct mntent *mnt;
|
const struct mntent *mnt;
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
while ((mnt = iterator("/proc/mounts", &fp))) {
|
while ((mnt = iterator("/proc/mounts", &fp))) {
|
||||||
if (!unmount(mnt->mnt_dir))
|
rc = unmount(mnt->mnt_dir);
|
||||||
|
if (!rc)
|
||||||
iterator_end(&fp); /* Restart iteration */
|
iterator_end(&fp); /* Restart iteration */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return !!rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -201,7 +201,10 @@ int pid_file_set(svc_t *svc, char *file, int not)
|
|||||||
* pid:!foo --> !/run/foo.pid
|
* pid:!foo --> !/run/foo.pid
|
||||||
* pid:!/run/foo.pid --> !/run/foo.pid
|
* pid:!/run/foo.pid --> !/run/foo.pid
|
||||||
*
|
*
|
||||||
* Note, nothing is created or removed by Finit in this latter form.
|
* Nothing is created or removed by Finit in this latter form, with one
|
||||||
|
* exception: a verifiably stale pidfile (still names the just-reaped
|
||||||
|
* PID, and that PID is no longer alive) is removed so the next instance
|
||||||
|
* can start. See service_clean_pidfile() in src/service.c.
|
||||||
*/
|
*/
|
||||||
int pid_file_parse(svc_t *svc, char *arg)
|
int pid_file_parse(svc_t *svc, char *arg)
|
||||||
{
|
{
|
||||||
|
|||||||
+52
-9
@@ -1120,6 +1120,35 @@ static void service_notify_stop(svc_t *svc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drop a daemon-owned (pid:!) pidfile if it still names the just-reaped
|
||||||
|
* PID and that PID is gone. The liveness check guards against reuse.
|
||||||
|
*/
|
||||||
|
static void service_clean_pidfile(svc_t *svc, pid_t reaped)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
char *fn;
|
||||||
|
|
||||||
|
if (reaped <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fn = pid_file(svc);
|
||||||
|
if (!fn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pid = pid_file_read(fn);
|
||||||
|
if (pid != reaped || pid_alive(pid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (remove(fn) && errno != ENOENT) {
|
||||||
|
logit(LOG_CRIT, "Failed removing stale service %s pidfile %s",
|
||||||
|
svc_ident(svc, NULL, 0), fn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg("Removed stale service %s pidfile %s", svc_ident(svc, NULL, 0), fn);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clean up any lingering state from dead/killed services
|
* Clean up any lingering state from dead/killed services
|
||||||
*/
|
*/
|
||||||
@@ -1137,6 +1166,8 @@ static void service_cleanup(svc_t *svc)
|
|||||||
if (remove(fn) && errno != ENOENT)
|
if (remove(fn) && errno != ENOENT)
|
||||||
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
|
||||||
svc_ident(svc, NULL, 0), fn);
|
svc_ident(svc, NULL, 0), fn);
|
||||||
|
} else if (svc->pidfile[0] == '!') {
|
||||||
|
service_clean_pidfile(svc, svc->pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1623,6 +1654,7 @@ static void parse_script(svc_t *svc, char *type, char *script, int *tmo, char *b
|
|||||||
script, errstr);
|
script, errstr);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
if (tmo)
|
||||||
*tmo = (int)(sec * 1000);
|
*tmo = (int)(sec * 1000);
|
||||||
} else {
|
} else {
|
||||||
path = script;
|
path = script;
|
||||||
@@ -2305,11 +2337,14 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
|||||||
if (cgroup)
|
if (cgroup)
|
||||||
parse_cgroup(svc, cgroup);
|
parse_cgroup(svc, cgroup);
|
||||||
|
|
||||||
/* New, recently modified or unchanged ... used on reload. */
|
/*
|
||||||
|
* New or modified since the last reload. The mark is cleared when
|
||||||
|
* the change has been applied, on start or reload, so one that is
|
||||||
|
* still set here is a change that has not been applied yet, e.g.
|
||||||
|
* the service is paused waiting for a condition. Leave it.
|
||||||
|
*/
|
||||||
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)) || svc->args_dirty)
|
if ((file && conf_changed(file)) || conf_changed(svc_getenv(svc)) || svc->args_dirty)
|
||||||
svc_mark_dirty(svc);
|
svc_mark_dirty(svc);
|
||||||
else
|
|
||||||
svc_mark_clean(svc);
|
|
||||||
|
|
||||||
svc_enable(svc);
|
svc_enable(svc);
|
||||||
|
|
||||||
@@ -2405,6 +2440,9 @@ void service_monitor(pid_t lost, int status)
|
|||||||
if (svc_is_forking(svc)) {
|
if (svc_is_forking(svc)) {
|
||||||
/* Likely start script exiting */
|
/* Likely start script exiting */
|
||||||
if (svc_is_starting(svc)) {
|
if (svc_is_starting(svc)) {
|
||||||
|
/* Daemon died before clearing 'starting'; drop any stale pidfile. */
|
||||||
|
service_clean_pidfile(svc, lost);
|
||||||
|
svc->oldpid = lost; /* So service_retry() logs the real PID */
|
||||||
svc->pid = 0; /* Expect no more activity from this one */
|
svc->pid = 0; /* Expect no more activity from this one */
|
||||||
goto cont;
|
goto cont;
|
||||||
}
|
}
|
||||||
@@ -2794,13 +2832,18 @@ static void service_retry(svc_t *svc)
|
|||||||
timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000;
|
timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000;
|
||||||
/* If a longer timeout was specified in the conf, use that instead. */
|
/* If a longer timeout was specified in the conf, use that instead. */
|
||||||
svc->restart_tmo = max(svc->restart_tmo, timeout);
|
svc->restart_tmo = max(svc->restart_tmo, timeout);
|
||||||
logit(LOG_CONSOLE|LOG_WARNING, "Service %s[%d] died (%s%d), restarting (retry in %d msec) (attempt: %d/%d)",
|
if (WIFEXITED(svc->status))
|
||||||
|
logit(LOG_CONSOLE|LOG_WARNING,
|
||||||
|
"Service %s[%d] died (exit status: %d), restarting (retry in %d msec) (attempt: %d/%d)",
|
||||||
|
svc_ident(svc, NULL, 0), svc->oldpid, WEXITSTATUS(svc->status),
|
||||||
|
svc->restart_tmo, *restart_cnt, svc->restart_max);
|
||||||
|
else
|
||||||
|
logit(LOG_CONSOLE|LOG_WARNING,
|
||||||
|
"Service %s[%d] died (killed by %s%s), restarting (retry in %d msec) (attempt: %d/%d)",
|
||||||
svc_ident(svc, NULL, 0), svc->oldpid,
|
svc_ident(svc, NULL, 0), svc->oldpid,
|
||||||
WIFEXITED(svc->status) ? "with exit status: " : "by signal: ",
|
sig_name(WTERMSIG(svc->status)),
|
||||||
WIFEXITED(svc->status) ? WEXITSTATUS(svc->status) : WTERMSIG(svc->status),
|
WCOREDUMP(svc->status) ? ", core dumped" : "",
|
||||||
svc->restart_tmo,
|
svc->restart_tmo, *restart_cnt, svc->restart_max);
|
||||||
*restart_cnt,
|
|
||||||
svc->restart_max);
|
|
||||||
|
|
||||||
svc_unblock(svc);
|
svc_unblock(svc);
|
||||||
service_step(svc);
|
service_step(svc);
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ static struct sigmap {
|
|||||||
|
|
||||||
void mdadm_wait(void);
|
void mdadm_wait(void);
|
||||||
void unmount_tmpfs(void);
|
void unmount_tmpfs(void);
|
||||||
void unmount_regular(void);
|
int unmount_regular(void);
|
||||||
|
|
||||||
static void fs_swapoff(void)
|
static void fs_swapoff(void)
|
||||||
{
|
{
|
||||||
@@ -302,6 +302,7 @@ void do_shutdown(shutop_t op)
|
|||||||
struct sched_param sched_param = { .sched_priority = 99 };
|
struct sched_param sched_param = { .sched_priority = 99 };
|
||||||
int in_cont = in_container();
|
int in_cont = in_container();
|
||||||
int signo = SIGTERM;
|
int signo = SIGTERM;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (SHUTDOWN_DEBUG) cprintf("do_shutdown: entered, op=%d\n", op);
|
if (SHUTDOWN_DEBUG) cprintf("do_shutdown: entered, op=%d\n", op);
|
||||||
|
|
||||||
@@ -372,12 +373,13 @@ void do_shutdown(shutop_t op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Unmount any tmpfs before unmounting swap ... */
|
/* Unmount any tmpfs before unmounting swap ... */
|
||||||
print(0, "Unmounting filesystems ...");
|
print(-1, "Unmounting filesystems ...");
|
||||||
unmount_tmpfs();
|
unmount_tmpfs();
|
||||||
fs_swapoff();
|
fs_swapoff();
|
||||||
|
|
||||||
/* ... unmount remaining regular file systems. */
|
/* ... unmount remaining regular file systems. */
|
||||||
unmount_regular();
|
rc = unmount_regular();
|
||||||
|
print(rc, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We sit on / so we must remount it ro, try all the things!
|
* We sit on / so we must remount it ro, try all the things!
|
||||||
@@ -386,8 +388,8 @@ void do_shutdown(shutop_t op)
|
|||||||
* - https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/29187
|
* - https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/29187
|
||||||
*/
|
*/
|
||||||
sync();
|
sync();
|
||||||
run("mount -n -o remount,ro -t dummytype dummydev /", NULL);
|
if (run("mount -n -o remount,ro -t dummytype dummydev /", NULL) &&
|
||||||
run("mount -n -o remount,ro dummydev /", NULL);
|
run("mount -n -o remount,ro dummydev /", NULL))
|
||||||
run("mount -n -o remount,ro /", "mount");
|
run("mount -n -o remount,ro /", "mount");
|
||||||
|
|
||||||
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
||||||
@@ -630,7 +632,7 @@ const char *sig_name(int signo)
|
|||||||
return signames[i].name;
|
return signames[i].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "SIGUNKOWN";
|
return "SIGUNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ typedef enum {
|
|||||||
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
|
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
|
||||||
#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 MAX_ARG_LEN
|
||||||
#define MAX_NUM_SUPGROUPS 4
|
#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
|
||||||
|
|||||||
+44
-36
@@ -24,6 +24,7 @@
|
|||||||
#include "config.h" /* Generated by configure script */
|
#include "config.h" /* Generated by configure script */
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <ftw.h>
|
#include <ftw.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
@@ -586,67 +587,74 @@ static void tmpfiles(char *line)
|
|||||||
if (gid < 0)
|
if (gid < 0)
|
||||||
gid = 0;
|
gid = 0;
|
||||||
|
|
||||||
rc = makedir(path, mode ?: 0755);
|
if (!mode)
|
||||||
if (rc && errno == EEXIST)
|
mode = 0755;
|
||||||
rc = chmod(path, mode ?: 0755);
|
rc = makedir(path, mode);
|
||||||
if (chown(path, uid, gid))
|
if (!rc && dirperm(path, mode, uid, gid))
|
||||||
warn("Failed chown(%s, %d, %d)", path, uid, gid);
|
warn("Failed setting mode/owner on %s", path);
|
||||||
}
|
}
|
||||||
umask(omask);
|
umask(omask);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'e':
|
case 'e': {
|
||||||
|
int uid, gid;
|
||||||
|
|
||||||
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
|
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
uid = parse_uid(user);
|
||||||
|
gid = parse_gid(group);
|
||||||
|
if (gid < 0)
|
||||||
|
gid = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < gl.gl_pathc; i++) {
|
for (size_t i = 0; i < gl.gl_pathc; i++) {
|
||||||
char *p = gl.gl_pathv[i];
|
char *p = gl.gl_pathv[i];
|
||||||
int uid, gid;
|
|
||||||
|
|
||||||
/* e only adjusts existing directories */
|
/* skip non-directories */
|
||||||
if (!fisdir(p))
|
if (dirperm(p, mode, uid, gid) && errno != ENOTDIR)
|
||||||
continue;
|
warn("Failed setting mode/owner on %s", p);
|
||||||
|
|
||||||
uid = parse_uid(user);
|
|
||||||
gid = parse_gid(group);
|
|
||||||
if (gid < 0)
|
|
||||||
gid = 0;
|
|
||||||
|
|
||||||
if (mode)
|
|
||||||
chmod(p, mode);
|
|
||||||
if (uid >= 0 && chown(p, uid, gid))
|
|
||||||
warn("Failed chown(%s, %d, %d)", p, uid, gid);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'f':
|
case 'f':
|
||||||
case 'F':
|
case 'F': {
|
||||||
mkparent(path, 0755);
|
int flags = O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC;
|
||||||
|
int fd, uid, gid;
|
||||||
|
|
||||||
if (type[1] == '+' || type[0] == 'F') {
|
if (type[1] == '+' || type[0] == 'F') {
|
||||||
/* f+/F will create or truncate the file */
|
flags |= O_TRUNC; /* create or truncate */
|
||||||
fp = fopen(path, "w+");
|
|
||||||
} else {
|
} else {
|
||||||
/* f will create the file if it doesn't exist */
|
if (!strc)
|
||||||
if (strc)
|
break; /* create only if missing */
|
||||||
fp = fopen(path, "w");
|
flags |= O_EXCL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fp) {
|
mkparent(path, 0755);
|
||||||
int uid, gid;
|
fd = open(path, flags, 0666);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (errno != EEXIST)
|
||||||
|
warn("Failed creating %s", path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fdopen(fd, "w");
|
||||||
|
if (!fp) {
|
||||||
|
close(fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
write_arg(fp, arg);
|
write_arg(fp, arg);
|
||||||
rc = fclose(fp);
|
|
||||||
|
|
||||||
/* Apply mode and ownership */
|
|
||||||
if (mode)
|
|
||||||
chmod(path, mode);
|
|
||||||
uid = parse_uid(user);
|
uid = parse_uid(user);
|
||||||
gid = parse_gid(group);
|
gid = parse_gid(group);
|
||||||
if (gid < 0)
|
if (gid < 0)
|
||||||
gid = 0;
|
gid = 0;
|
||||||
if (uid >= 0 && chown(path, uid, gid))
|
if (fdperm(fd, mode, uid, gid))
|
||||||
warn("Failed chown(%s, %d, %d)", path, uid, gid);
|
warn("Failed setting mode/owner on %s", path);
|
||||||
}
|
|
||||||
|
rc = fclose(fp);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'l': /* Finit extension, like 'L' but only if target exists */
|
case 'l': /* Finit extension, like 'L' but only if target exists */
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
paste(buf, sizeof(buf), "/usr/share/factory", path);
|
paste(buf, sizeof(buf), "/usr/share/factory", path);
|
||||||
|
|||||||
+40
-4
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <ctype.h> /* isprint() */
|
#include <ctype.h> /* isprint() */
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#ifdef HAVE_MNTENT_H
|
#ifdef HAVE_MNTENT_H
|
||||||
@@ -280,6 +281,43 @@ int getcgroup(char *buf, size_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set mode and owner on an open fd, only what differs is touched, the
|
||||||
|
* file may be immutable. mode 0 skips the chmod, uid -1 the chown.
|
||||||
|
*/
|
||||||
|
int fdperm(int fd, mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = fstat(fd, &st);
|
||||||
|
if (!rc && mode && (st.st_mode & 07777) != mode)
|
||||||
|
rc = fchmod(fd, mode);
|
||||||
|
if (!rc && uid != (uid_t)-1 && (st.st_uid != uid || st.st_gid != gid))
|
||||||
|
rc = fchown(fd, uid, gid);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same for a directory that may already exist. Opened with
|
||||||
|
* O_NOFOLLOW | O_DIRECTORY, so the change lands on the directory
|
||||||
|
* itself and not on whatever a link at @path points to.
|
||||||
|
*/
|
||||||
|
int dirperm(const char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
int fd, rc;
|
||||||
|
|
||||||
|
fd = open(path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rc = fdperm(fd, mode, uid, gid);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||||
{
|
{
|
||||||
mode_t omask;
|
mode_t omask;
|
||||||
@@ -295,10 +333,8 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
|||||||
gid = 0;
|
gid = 0;
|
||||||
|
|
||||||
rc = makedir(dir, mode);
|
rc = makedir(dir, mode);
|
||||||
if (rc && errno == EEXIST)
|
if (!rc && dirperm(dir, mode, uid, gid))
|
||||||
rc = chmod(dir, mode);
|
warn("Failed setting mode/owner on %s", dir);
|
||||||
if (chown(dir, uid, gid))
|
|
||||||
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
umask(omask);
|
umask(omask);
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ int getgroup (const 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);
|
||||||
|
|
||||||
|
int fdperm (int fd, mode_t mode, uid_t uid, gid_t gid);
|
||||||
|
int dirperm (const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||||
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
|
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
|
||||||
|
|
||||||
int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
|
|||||||
+6
-1
@@ -30,9 +30,10 @@ EXTRA_DIST += setup-sysroot.sh
|
|||||||
EXTRA_DIST += add-remove-dynamic-service.sh
|
EXTRA_DIST += add-remove-dynamic-service.sh
|
||||||
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
||||||
EXTRA_DIST += bootstrap-crash.sh
|
EXTRA_DIST += bootstrap-crash.sh
|
||||||
EXTRA_DIST += cond-start-task.sh
|
EXTRA_DIST += script-timeout.sh
|
||||||
EXTRA_DIST += crashing.sh
|
EXTRA_DIST += crashing.sh
|
||||||
EXTRA_DIST += dep-chain-reload.sh
|
EXTRA_DIST += dep-chain-reload.sh
|
||||||
|
EXTRA_DIST += reload-while-paused.sh
|
||||||
EXTRA_DIST += depserv.sh
|
EXTRA_DIST += depserv.sh
|
||||||
EXTRA_DIST += devmon.sh
|
EXTRA_DIST += devmon.sh
|
||||||
EXTRA_DIST += failing-sysv.sh
|
EXTRA_DIST += failing-sysv.sh
|
||||||
@@ -41,6 +42,7 @@ EXTRA_DIST += global-envs.sh
|
|||||||
EXTRA_DIST += initctl-status-subset.sh
|
EXTRA_DIST += initctl-status-subset.sh
|
||||||
EXTRA_DIST += notify.sh
|
EXTRA_DIST += notify.sh
|
||||||
EXTRA_DIST += pidfile.sh
|
EXTRA_DIST += pidfile.sh
|
||||||
|
EXTRA_DIST += stale-pidfile.sh
|
||||||
EXTRA_DIST += pre-post-serv.sh
|
EXTRA_DIST += pre-post-serv.sh
|
||||||
EXTRA_DIST += pre-fail.sh
|
EXTRA_DIST += pre-fail.sh
|
||||||
EXTRA_DIST += process-depends.sh
|
EXTRA_DIST += process-depends.sh
|
||||||
@@ -74,8 +76,10 @@ TESTS += add-remove-dynamic-service.sh
|
|||||||
TESTS += add-remove-dynamic-service-sub-config.sh
|
TESTS += add-remove-dynamic-service-sub-config.sh
|
||||||
TESTS += bootstrap-crash.sh
|
TESTS += bootstrap-crash.sh
|
||||||
TESTS += cond-start-task.sh
|
TESTS += cond-start-task.sh
|
||||||
|
TESTS += script-timeout.sh
|
||||||
TESTS += crashing.sh
|
TESTS += crashing.sh
|
||||||
TESTS += dep-chain-reload.sh
|
TESTS += dep-chain-reload.sh
|
||||||
|
TESTS += reload-while-paused.sh
|
||||||
TESTS += depserv.sh
|
TESTS += depserv.sh
|
||||||
TESTS += devmon.sh
|
TESTS += devmon.sh
|
||||||
TESTS += failing-sysv.sh
|
TESTS += failing-sysv.sh
|
||||||
@@ -84,6 +88,7 @@ TESTS += global-envs.sh
|
|||||||
TESTS += initctl-status-subset.sh
|
TESTS += initctl-status-subset.sh
|
||||||
TESTS += notify.sh
|
TESTS += notify.sh
|
||||||
TESTS += pidfile.sh
|
TESTS += pidfile.sh
|
||||||
|
TESTS += stale-pidfile.sh
|
||||||
TESTS += pre-post-serv.sh
|
TESTS += pre-post-serv.sh
|
||||||
TESTS += pre-fail.sh
|
TESTS += pre-fail.sh
|
||||||
TESTS += process-depends.sh
|
TESTS += process-depends.sh
|
||||||
|
|||||||
+17
-1
@@ -255,8 +255,12 @@ wdstart()
|
|||||||
|
|
||||||
wdkill()
|
wdkill()
|
||||||
{
|
{
|
||||||
|
[ -n "${wdpid:-}" ] || return 0
|
||||||
|
|
||||||
say "Stopping test watchdog, pid $wdpid"
|
say "Stopping test watchdog, pid $wdpid"
|
||||||
kill -KILL $wdpid
|
# Reap the sleep first, killing its subshell orphans it to PID 1.
|
||||||
|
pkill -KILL -P "$wdpid" 2>/dev/null || true
|
||||||
|
kill -KILL "$wdpid" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown()
|
teardown()
|
||||||
@@ -305,6 +309,18 @@ trap teardown EXIT
|
|||||||
SYSROOT="${SYSROOT:-$(pwd)/${TEST_DIR}/sysroot}"
|
SYSROOT="${SYSROOT:-$(pwd)/${TEST_DIR}/sysroot}"
|
||||||
export SYSROOT
|
export SYSROOT
|
||||||
|
|
||||||
|
# 'make check' refreshes the sysroot through the setup-chroot rule, but
|
||||||
|
# running a test script by hand does not, and then the test silently
|
||||||
|
# exercises whatever finit was installed last. Compare and refuse.
|
||||||
|
top_builddir="${top_builddir:-$TEST_DIR/..}"
|
||||||
|
sysroot_finit="$SYSROOT/sbin/finit"
|
||||||
|
built_finit="$top_builddir/src/finit"
|
||||||
|
[ -x "$top_builddir/src/.libs/finit" ] && built_finit="$top_builddir/src/.libs/finit"
|
||||||
|
|
||||||
|
if [ -x "$built_finit" ] && [ -e "$sysroot_finit" ] && ! cmp -s "$built_finit" "$sysroot_finit"; then
|
||||||
|
fail "Stale $sysroot_finit, run 'make -C test setup-chroot' or use 'make check'"
|
||||||
|
fi
|
||||||
|
|
||||||
TEST_TIMEOUT=300
|
TEST_TIMEOUT=300
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
|
|||||||
Executable
+76
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# A reload must not be lost on a service paused by a condition
|
||||||
|
#
|
||||||
|
# svc_s writes its PID file one second after a SIGHUP, so a service
|
||||||
|
# with <pid/svc_s> is paused for that second whenever svc_s reloads.
|
||||||
|
# svc_x counts the SIGHUPs it gets. Both .conf files are touched and
|
||||||
|
# two reloads are requested back to back, so the second one arrives
|
||||||
|
# while svc_x is paused with its own reload still pending. svc_x must
|
||||||
|
# get exactly one SIGHUP and keep its PID.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")
|
||||||
|
|
||||||
|
test_teardown()
|
||||||
|
{
|
||||||
|
say "Running test teardown."
|
||||||
|
run "rm -f $FINIT_RCSD/svc_s.conf $FINIT_RCSD/svc_x.conf /tmp/hup.sh /tmp/hup.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
pidof()
|
||||||
|
{
|
||||||
|
texec initctl -j status "$1" | jq .pid
|
||||||
|
}
|
||||||
|
|
||||||
|
test_setup()
|
||||||
|
{
|
||||||
|
run "cat > /tmp/hup.sh" <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
trap 'echo HUP >> /tmp/hup.log' HUP
|
||||||
|
trap 'exit 0' TERM
|
||||||
|
while true; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
run "cat > $FINIT_RCSD/svc_s.conf" <<EOF
|
||||||
|
service log:stdout notify:pid pid:!/run/service.pid name:svc_s service.sh -- Slow to reassert
|
||||||
|
EOF
|
||||||
|
run "cat > $FINIT_RCSD/svc_x.conf" <<EOF
|
||||||
|
service log:stdout <pid/svc_s> name:svc_x /bin/sh /tmp/hup.sh -- Paused while svc_s reloads
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib/setup.sh"
|
||||||
|
|
||||||
|
sep "Configuration"
|
||||||
|
run "cat $FINIT_RCSD/svc_s.conf"
|
||||||
|
run "cat $FINIT_RCSD/svc_x.conf"
|
||||||
|
|
||||||
|
say "Reload Finit to start both services"
|
||||||
|
run "initctl reload"
|
||||||
|
retry 'assert_status "svc_x" "running"' 10 1
|
||||||
|
|
||||||
|
pid_x=$(pidof svc_x)
|
||||||
|
say "svc_x PID before: $pid_x"
|
||||||
|
|
||||||
|
sep "Touch both, reload twice"
|
||||||
|
run "rm -f /tmp/hup.log"
|
||||||
|
run "initctl touch svc_s.conf"
|
||||||
|
run "initctl touch svc_x.conf"
|
||||||
|
run "initctl reload"
|
||||||
|
run "initctl reload"
|
||||||
|
|
||||||
|
say "Wait for services to settle"
|
||||||
|
retry 'assert_status "svc_x" "running"' 15 1
|
||||||
|
run "initctl status"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
retry 'assert "svc_x got its SIGHUP" "$(texec sh -c "cat /tmp/hup.log 2>/dev/null | wc -l")" -eq 1' 10 1
|
||||||
|
|
||||||
|
new_pid_x=$(pidof svc_x)
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
assert "svc_x was not restarted" $new_pid_x -eq $pid_x
|
||||||
|
|
||||||
|
return 0
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# A timeout on a stop: or reload: script must not take PID 1 with it.
|
||||||
|
# Those two hooks passed a NULL timeout pointer to parse_script(),
|
||||||
|
# which wrote through it whenever the script was prefixed with a
|
||||||
|
# valid number. Only a valid number reached the store, so
|
||||||
|
# 'stop:abc,/bin/true' was harmless while 'stop:5,/bin/true' was not.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")
|
||||||
|
|
||||||
|
test_teardown()
|
||||||
|
{
|
||||||
|
say "Running test teardown."
|
||||||
|
run "rm -f $FINIT_CONF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib/setup.sh"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
assert "Finit survived $1" "$(kill -0 "$finit_pid" 2>/dev/null && echo yes)" = "yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
for hook in stop reload post pre; do
|
||||||
|
say "Timeout on a $hook: script"
|
||||||
|
run "echo 'service $hook:5,/bin/true service.sh -- Timeout test' > $FINIT_CONF"
|
||||||
|
run "initctl reload" || true
|
||||||
|
assert_alive "$hook:5,/bin/true"
|
||||||
|
done
|
||||||
|
|
||||||
|
say 'The service still runs afterwards'
|
||||||
|
run "echo 'service stop:5,/bin/true service.sh -- Timeout test' > $FINIT_CONF"
|
||||||
|
run "initctl reload"
|
||||||
|
retry 'assert_num_children 1 service.sh'
|
||||||
+16
-3
@@ -34,6 +34,7 @@ volatile sig_atomic_t reloading = 1;
|
|||||||
volatile sig_atomic_t running = 1;
|
volatile sig_atomic_t running = 1;
|
||||||
static char *ident = PROGNM;
|
static char *ident = PROGNM;
|
||||||
static char fn[80];
|
static char fn[80];
|
||||||
|
static int exclusive = 0; /* -x: refuse to start if pidfile exists (dbus-style) */
|
||||||
|
|
||||||
static void verify_env(char *arg)
|
static void verify_env(char *arg)
|
||||||
{
|
{
|
||||||
@@ -122,7 +123,7 @@ static void writefn(char *fn, int val)
|
|||||||
|
|
||||||
static int checkfn(char *fn)
|
static int checkfn(char *fn)
|
||||||
{
|
{
|
||||||
return !access(fn, R_OK);
|
return !access(fn, F_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mine(char *fn)
|
static void mine(char *fn)
|
||||||
@@ -133,12 +134,18 @@ static void mine(char *fn)
|
|||||||
|
|
||||||
static void pidfile(char *pidfn)
|
static void pidfile(char *pidfn)
|
||||||
{
|
{
|
||||||
|
static int once = 0;
|
||||||
|
|
||||||
if (!pidfn) {
|
if (!pidfn) {
|
||||||
if (fn[0] == 0)
|
if (fn[0] == 0)
|
||||||
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, ident);
|
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, ident);
|
||||||
pidfn = fn;
|
pidfn = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exclusive && !once && checkfn(pidfn))
|
||||||
|
errx(EX_SOFTWARE, "PID file %s exists, refusing to start", pidfn);
|
||||||
|
once = 1;
|
||||||
|
|
||||||
if (!checkfn(pidfn)) {
|
if (!checkfn(pidfn)) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ static void pidfile(char *pidfn)
|
|||||||
atexit(cleanup);
|
atexit(cleanup);
|
||||||
} else {
|
} else {
|
||||||
inf("Touching PID file %s", pidfn);
|
inf("Touching PID file %s", pidfn);
|
||||||
utimensat(0, fn, NULL, 0);
|
utimensat(AT_FDCWD, pidfn, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +189,7 @@ static int usage(int rc)
|
|||||||
" -p Create PID file despite running in foreground\n"
|
" -p Create PID file despite running in foreground\n"
|
||||||
" -P FILE Create PID file using FILE\n"
|
" -P FILE Create PID file using FILE\n"
|
||||||
" -r SVC Call initctl to restart service SVC (self)\n"
|
" -r SVC Call initctl to restart service SVC (self)\n"
|
||||||
|
" -x Refuse to start if PID file already exists (dbus-style)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"By default this program daemonizes itself to the background, and,\n"
|
"By default this program daemonizes itself to the background, and,\n"
|
||||||
"when it's done setting up its signal handler(s), creates a PID file\n"
|
"when it's done setting up its signal handler(s), creates a PID file\n"
|
||||||
@@ -212,7 +220,7 @@ int main(int argc, char *argv[])
|
|||||||
char cmd[80];
|
char cmd[80];
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "cCe:E:f:F:hi:nN:pP:r:")) != EOF) {
|
while ((c = getopt(argc, argv, "cCe:E:f:F:hi:nN:pP:r:x")) != EOF) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
do_crash = 1;
|
do_crash = 1;
|
||||||
@@ -249,12 +257,17 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
pidfn = optarg;
|
pidfn = optarg;
|
||||||
|
if ((size_t)snprintf(fn, sizeof(fn), "%s", optarg) >= sizeof(fn))
|
||||||
|
errx(EX_USAGE, "-P path too long (max %zu)", sizeof(fn) - 1);
|
||||||
do_pidfile++;
|
do_pidfile++;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
snprintf(cmd, sizeof(cmd), "initctl restart %s", optarg);
|
snprintf(cmd, sizeof(cmd), "initctl restart %s", optarg);
|
||||||
do_restart = 1;
|
do_restart = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'x':
|
||||||
|
exclusive = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return usage(1);
|
return usage(1);
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Regression: Finit must remove a daemon-owned (pid:!) stale pidfile
|
||||||
|
# left behind by an unclean exit (SIGKILL), so the next instance can
|
||||||
|
# start. Simulates the dbus-daemon pattern: 'serv -x' refuses to
|
||||||
|
# start if its pidfile already exists.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TEST_DIR=$(dirname "$0")
|
||||||
|
PIDFN="/run/serv.pid"
|
||||||
|
|
||||||
|
test_teardown()
|
||||||
|
{
|
||||||
|
say "Running test teardown."
|
||||||
|
run "rm -f $FINIT_CONF $PIDFN"
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$TEST_DIR/lib/setup.sh"
|
||||||
|
|
||||||
|
say "Add service stanza '$FINIT_CONF' with pid:!$PIDFN"
|
||||||
|
run "echo 'service pid:!$PIDFN serv -np -P $PIDFN -x' > $FINIT_CONF"
|
||||||
|
run "initctl reload"
|
||||||
|
|
||||||
|
retry "assert_num_children 1 serv"
|
||||||
|
assert_is_pidfile "serv" "$PIDFN"
|
||||||
|
|
||||||
|
PID=$(texec cat "$PIDFN")
|
||||||
|
say "SIGKILL serv ($PID) -- leaves stale pidfile"
|
||||||
|
run "kill -9 $PID"
|
||||||
|
|
||||||
|
# Without the fix, 'serv -x' refuses to start on each retry until
|
||||||
|
# Finit hits restart_max and marks the service crashed. With the fix
|
||||||
|
# Finit removes the stale pidfile and the next instance comes up.
|
||||||
|
retry "assert_pidiff serv $PID"
|
||||||
|
retry "assert_num_children 1 serv"
|
||||||
|
|
||||||
|
sep
|
||||||
Reference in New Issue
Block a user