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 |
@@ -1,12 +1,13 @@
|
||||
name: Bob the Builder
|
||||
|
||||
# Run on all branches, including all pull requests, except the 'dev'
|
||||
# branch since that's where we run Coverity Scan (limited tokens/day)
|
||||
# Run on push to release branches (master and maintenance N.x), and
|
||||
# on all pull requests. The 'dev' branch is where we run Coverity
|
||||
# Scan (limited tokens/day)
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- '!dev'
|
||||
- master
|
||||
- '[0-9]+.x'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
@@ -22,8 +23,6 @@ jobs:
|
||||
# Verify we can build on latest Ubuntu with both gcc and clang
|
||||
name: ${{ matrix.compiler }}
|
||||
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:
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
@@ -43,7 +42,7 @@ jobs:
|
||||
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
||||
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- name: Static Finit
|
||||
run: |
|
||||
./autogen.sh
|
||||
@@ -81,7 +80,7 @@ jobs:
|
||||
make -j1 check || (cat test/test-suite.log; false)
|
||||
- name: Upload Test Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: finit-test-${{ matrix.compiler }}
|
||||
path: test/*.log
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
coverity:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- name: Fetch latest Coverity Scan MD5
|
||||
id: var
|
||||
env:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
|
||||
-O coverity-latest.tar.gz.md5
|
||||
echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v6
|
||||
id: cache
|
||||
with:
|
||||
path: coverity-latest.tar.gz
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
|
||||
https://scan.coverity.com/builds?project=${COVERITY_PROJ}
|
||||
- name: Upload build.log
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverity-build.log
|
||||
path: cov-int/build-log.txt
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 4.x
|
||||
- '[0-9]+.x'
|
||||
paths:
|
||||
- 'doc/**'
|
||||
- 'README.md'
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0 # Needed for git-revision-date-localized plugin
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
run: mkdocs build --clean
|
||||
|
||||
- name: Upload site artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: site
|
||||
path: site/
|
||||
@@ -72,13 +72,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download site artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: site
|
||||
path: site/
|
||||
|
||||
- name: Checkout pages repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: finit-project/finit-project.github.io
|
||||
path: pages
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- name: Installing dependencies ...
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
@@ -31,12 +31,17 @@ jobs:
|
||||
run: |
|
||||
ver=${GITHUB_REF#refs/tags/}
|
||||
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
|
||||
else
|
||||
echo "pre=false" >> $GITHUB_OUTPUT
|
||||
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
|
||||
else
|
||||
echo "latest=false" >> $GITHUB_OUTPUT
|
||||
@@ -64,7 +69,7 @@ jobs:
|
||||
mv ../*.tar.* artifacts/
|
||||
- name: Upload Test Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: finit-release-test-logs
|
||||
path: finit-*/_build/sub/test/*.log
|
||||
|
||||
@@ -14,22 +14,31 @@ jobs:
|
||||
schedule:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
run: ${{ steps.schedule.outputs.run }}
|
||||
branches: ${{ steps.schedule.outputs.branches }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check for any commits since last week
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for branches with no commits since last week
|
||||
id: schedule
|
||||
run: |
|
||||
commits="$(git log --format=%H --since='1 week ago')"
|
||||
if [ -z "$commits" ]; then
|
||||
echo "run=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "run=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
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
|
||||
branches="$branches\"$branch\","
|
||||
fi
|
||||
done
|
||||
echo "branches=[${branches%,}]" >> $GITHUB_OUTPUT
|
||||
distcheck:
|
||||
needs: schedule
|
||||
if: ${{ needs.schedule.outputs.run == 'true' }}
|
||||
if: ${{ needs.schedule.outputs.branches != '[]' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJSON(needs.schedule.outputs.branches) }}
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -42,7 +51,9 @@ jobs:
|
||||
(cd libuev-2.4.1 && ./configure && make -j9 && sudo make install-strip)
|
||||
(cd libite-2.6.2 && ./configure && make -j9 && sudo make install-strip)
|
||||
sudo ldconfig
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ matrix.branch }}
|
||||
- name: Base run
|
||||
run: |
|
||||
./autogen.sh
|
||||
@@ -61,7 +72,7 @@ jobs:
|
||||
make -j1 distcheck || (cat $dir/test/test-suite.log; false)
|
||||
- name: Upload Test Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: finit-weekly-test-logs
|
||||
name: finit-weekly-test-logs-${{ matrix.branch }}
|
||||
path: finit-*/_build/sub/test/*.log
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
AC_INIT([Finit], [4.17], [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/])
|
||||
AC_CONFIG_AUX_DIR(aux)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign subdir-objects])
|
||||
|
||||
@@ -12,6 +12,14 @@ All relevant changes are documented in this file.
|
||||
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
|
||||
|
||||
@@ -27,6 +35,19 @@ All relevant changes are documented in this file.
|
||||
- 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
|
||||
---------------------
|
||||
|
||||
+6
-2
@@ -121,15 +121,19 @@ void unmount_tmpfs(void)
|
||||
}
|
||||
}
|
||||
|
||||
void unmount_regular(void)
|
||||
int unmount_regular(void)
|
||||
{
|
||||
const struct mntent *mnt;
|
||||
FILE *fp = NULL;
|
||||
int rc = 0;
|
||||
|
||||
while ((mnt = iterator("/proc/mounts", &fp))) {
|
||||
if (!unmount(mnt->mnt_dir))
|
||||
rc = unmount(mnt->mnt_dir);
|
||||
if (!rc)
|
||||
iterator_end(&fp); /* Restart iteration */
|
||||
}
|
||||
|
||||
return !!rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+8
-4
@@ -1654,7 +1654,8 @@ static void parse_script(svc_t *svc, char *type, char *script, int *tmo, char *b
|
||||
script, errstr);
|
||||
goto err;
|
||||
}
|
||||
*tmo = (int)(sec * 1000);
|
||||
if (tmo)
|
||||
*tmo = (int)(sec * 1000);
|
||||
} else {
|
||||
path = script;
|
||||
if (tmo)
|
||||
@@ -2336,11 +2337,14 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
if (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)
|
||||
svc_mark_dirty(svc);
|
||||
else
|
||||
svc_mark_clean(svc);
|
||||
|
||||
svc_enable(svc);
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ static struct sigmap {
|
||||
|
||||
void mdadm_wait(void);
|
||||
void unmount_tmpfs(void);
|
||||
void unmount_regular(void);
|
||||
int unmount_regular(void);
|
||||
|
||||
static void fs_swapoff(void)
|
||||
{
|
||||
@@ -302,6 +302,7 @@ void do_shutdown(shutop_t op)
|
||||
struct sched_param sched_param = { .sched_priority = 99 };
|
||||
int in_cont = in_container();
|
||||
int signo = SIGTERM;
|
||||
int rc;
|
||||
|
||||
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 ... */
|
||||
print(0, "Unmounting filesystems ...");
|
||||
print(-1, "Unmounting filesystems ...");
|
||||
unmount_tmpfs();
|
||||
fs_swapoff();
|
||||
|
||||
/* ... 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!
|
||||
@@ -386,9 +388,9 @@ void do_shutdown(shutop_t op)
|
||||
* - https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/29187
|
||||
*/
|
||||
sync();
|
||||
run("mount -n -o remount,ro -t dummytype dummydev /", NULL);
|
||||
run("mount -n -o remount,ro dummydev /", NULL);
|
||||
run("mount -n -o remount,ro /", "mount");
|
||||
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 /", "mount");
|
||||
|
||||
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
||||
mdadm_wait();
|
||||
|
||||
@@ -100,7 +100,7 @@ typedef enum {
|
||||
#define MAX_IDENT_LEN (MAX_ARG_LEN + MAX_ID_LEN + 1)
|
||||
#define MAX_STR_LEN 64
|
||||
#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_FDS 64 /* Max number of I/O plugins */
|
||||
#define MAX_NUM_SVC_ARGS 64
|
||||
|
||||
+50
-42
@@ -24,6 +24,7 @@
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <ftw.h>
|
||||
#include <getopt.h>
|
||||
#include <glob.h>
|
||||
@@ -586,67 +587,74 @@ static void tmpfiles(char *line)
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(path, mode ?: 0755);
|
||||
if (rc && errno == EEXIST)
|
||||
rc = chmod(path, mode ?: 0755);
|
||||
if (chown(path, uid, gid))
|
||||
warn("Failed chown(%s, %d, %d)", path, uid, gid);
|
||||
if (!mode)
|
||||
mode = 0755;
|
||||
rc = makedir(path, mode);
|
||||
if (!rc && dirperm(path, mode, uid, gid))
|
||||
warn("Failed setting mode/owner on %s", path);
|
||||
}
|
||||
umask(omask);
|
||||
break;
|
||||
}
|
||||
case 'e':
|
||||
case 'e': {
|
||||
int uid, gid;
|
||||
|
||||
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
|
||||
break;
|
||||
|
||||
uid = parse_uid(user);
|
||||
gid = parse_gid(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
|
||||
for (size_t i = 0; i < gl.gl_pathc; i++) {
|
||||
char *p = gl.gl_pathv[i];
|
||||
int uid, gid;
|
||||
|
||||
/* e only adjusts existing directories */
|
||||
if (!fisdir(p))
|
||||
continue;
|
||||
|
||||
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);
|
||||
/* skip non-directories */
|
||||
if (dirperm(p, mode, uid, gid) && errno != ENOTDIR)
|
||||
warn("Failed setting mode/owner on %s", p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
case 'F':
|
||||
mkparent(path, 0755);
|
||||
case 'F': {
|
||||
int flags = O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC;
|
||||
int fd, uid, gid;
|
||||
|
||||
if (type[1] == '+' || type[0] == 'F') {
|
||||
/* f+/F will create or truncate the file */
|
||||
fp = fopen(path, "w+");
|
||||
flags |= O_TRUNC; /* create or truncate */
|
||||
} else {
|
||||
/* f will create the file if it doesn't exist */
|
||||
if (strc)
|
||||
fp = fopen(path, "w");
|
||||
if (!strc)
|
||||
break; /* create only if missing */
|
||||
flags |= O_EXCL;
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
int uid, gid;
|
||||
|
||||
write_arg(fp, arg);
|
||||
rc = fclose(fp);
|
||||
|
||||
/* Apply mode and ownership */
|
||||
if (mode)
|
||||
chmod(path, mode);
|
||||
uid = parse_uid(user);
|
||||
gid = parse_gid(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
if (uid >= 0 && chown(path, uid, gid))
|
||||
warn("Failed chown(%s, %d, %d)", path, uid, gid);
|
||||
mkparent(path, 0755);
|
||||
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);
|
||||
|
||||
uid = parse_uid(user);
|
||||
gid = parse_gid(group);
|
||||
if (gid < 0)
|
||||
gid = 0;
|
||||
if (fdperm(fd, mode, uid, gid))
|
||||
warn("Failed setting mode/owner on %s", path);
|
||||
|
||||
rc = fclose(fp);
|
||||
break;
|
||||
}
|
||||
case 'l': /* Finit extension, like 'L' but only if target exists */
|
||||
if (!arg) {
|
||||
paste(buf, sizeof(buf), "/usr/share/factory", path);
|
||||
|
||||
+40
-4
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <ctype.h> /* isprint() */
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#ifdef HAVE_MNTENT_H
|
||||
@@ -280,6 +281,43 @@ int getcgroup(char *buf, size_t len)
|
||||
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)
|
||||
{
|
||||
mode_t omask;
|
||||
@@ -295,10 +333,8 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
|
||||
gid = 0;
|
||||
|
||||
rc = makedir(dir, mode);
|
||||
if (rc && errno == EEXIST)
|
||||
rc = chmod(dir, mode);
|
||||
if (chown(dir, uid, gid))
|
||||
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
|
||||
if (!rc && dirperm(dir, mode, uid, gid))
|
||||
warn("Failed setting mode/owner on %s", dir);
|
||||
}
|
||||
|
||||
umask(omask);
|
||||
|
||||
@@ -68,6 +68,8 @@ int getgroup (const char *group);
|
||||
int getcuser (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 fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
|
||||
+4
-1
@@ -30,9 +30,10 @@ EXTRA_DIST += setup-sysroot.sh
|
||||
EXTRA_DIST += add-remove-dynamic-service.sh
|
||||
EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
||||
EXTRA_DIST += bootstrap-crash.sh
|
||||
EXTRA_DIST += cond-start-task.sh
|
||||
EXTRA_DIST += script-timeout.sh
|
||||
EXTRA_DIST += crashing.sh
|
||||
EXTRA_DIST += dep-chain-reload.sh
|
||||
EXTRA_DIST += reload-while-paused.sh
|
||||
EXTRA_DIST += depserv.sh
|
||||
EXTRA_DIST += devmon.sh
|
||||
EXTRA_DIST += failing-sysv.sh
|
||||
@@ -75,8 +76,10 @@ TESTS += add-remove-dynamic-service.sh
|
||||
TESTS += add-remove-dynamic-service-sub-config.sh
|
||||
TESTS += bootstrap-crash.sh
|
||||
TESTS += cond-start-task.sh
|
||||
TESTS += script-timeout.sh
|
||||
TESTS += crashing.sh
|
||||
TESTS += dep-chain-reload.sh
|
||||
TESTS += reload-while-paused.sh
|
||||
TESTS += depserv.sh
|
||||
TESTS += devmon.sh
|
||||
TESTS += failing-sysv.sh
|
||||
|
||||
+17
-1
@@ -255,8 +255,12 @@ wdstart()
|
||||
|
||||
wdkill()
|
||||
{
|
||||
[ -n "${wdpid:-}" ] || return 0
|
||||
|
||||
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()
|
||||
@@ -305,6 +309,18 @@ trap teardown EXIT
|
||||
SYSROOT="${SYSROOT:-$(pwd)/${TEST_DIR}/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
|
||||
|
||||
# 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'
|
||||
Reference in New Issue
Block a user