Compare commits

...
58 Commits
Author SHA1 Message Date
Joachim Wiberg d00a53dfbf Bump version for v4.0-rc3
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:53:26 +01:00
Joachim Wiberg 99a8cf3eb7 initctl: add -1,--once flag for top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 23:09:01 +01:00
Joachim Wiberg da95c7d80e initctl: ensure lines don't overflow in top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 23:07:14 +01:00
Joachim Wiberg a9f7f692a9 initctl: add more columns to top command; VmSize, RSS, VmLib, %MEM
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 16:49:08 +01:00
Joachim Wiberg ce55f88a00 cgroup: add symlinks for traditional split controller world
Even systemd have collapsed a few of the cgroup controllers into a
semi-unified hierarchy and uses this approach.  We just take it to
the extreme and have collapsed all of them (like cgroup v2).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 16:47:08 +01:00
Joachim Wiberg 74a26f559d initctl: refactor, split into multiple files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 09:23:04 +01:00
Joachim Wiberg 08144df5ea initctl: fix %CPU listing bug and use hash table for faster lookup
Two major bugs: `if (!cg)` and missing `strdup(path)`.

Also convert to use std hash table for lookup of previous cpu load value
for a given cgroup path.  The hash table is sized after the current num.
rows on the screen -- resize currently not supported.  The value given
to hcreate() should be 25% greater than the estimated num of entries,
but we take a wild guess just to make sure we don't run out of space at
runtime.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-15 13:43:37 +01:00
Joachim Wiberg 230c64927b initctl: add very bare-bones cgroup based top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-15 02:21:43 +01:00
Joachim Wiberg d1e56f300d initctl: show cgroup cpu.shares and memory.usage in ps command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 22:16:20 +01:00
Joachim Wiberg 9842786453 cgroups: fix cpu.shares assignment and set memory hierarchy
- Fix obvious refactor mistake in cpu.shares assignment
- For unified memory hierarchy we need to set .use_hierarch=1

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 12:27:46 +01:00
Joachim Wiberg 6d0c679e05 initctl: show comm and cmdline in ps output
The ps command currently only lists processes in the three main control
groups: init, system, user.  Kernel threads are not show at all.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:12:42 +01:00
Joachim Wiberg 5428dea5bf cgroup: refactor, use a unified hierarchy for finit
As of now, the default finit cgroup behavior is to use a unified
hierarchy of controllers under /sys/fs/cgroup/finit.

We mount cpu,cpuacct,cpuset,memory (if available) and gain the
ability to control our three major groups: init, system, user.

The default CPU share setup is ~10% for init and user, and 90% for
system.  These are guaranteed CPU shares to ensure we do not starve
PID 1 or user processes.  Support for configuring these limits will
be added in a later commit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:07:13 +01:00
Joachim Wiberg 4e84ecae89 Set umask(022) early, in fs_init(), and no place else
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:05:12 +01:00
Joachim Wiberg 959c9fe5aa plugins: restore previous umask() after plugin ops
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:04:29 +01:00
Joachim Wiberg 03d8ef9c8f doc: drop malplaced comment
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:45:37 +01:00
Joachim Wiberg 084b4ce8d3 cgroup: minor refactor/simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:40:52 +01:00
Joachim Wiberg 31cae6a56d plugins: dbus: simplify, use mksubsys(), run as messagebus user
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:26:14 +01:00
Joachim Wiberg 1fd45c6f2e plugins: bootmisc: refactor, use mksubys() and simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:26:14 +01:00
Joachim Wiberg 46995fe446 Add mksusbys() to helpers.c, a kind-of tmpfiles.d in C
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 00:37:47 +01:00
Joachim Wiberg 600b2874e6 initctl: add bug report address to usage text, like finit -h
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 17:08:36 +01:00
Joachim Wiberg b080b970b7 initctl: don't warn on missing env file if it's optional
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 17:08:12 +01:00
Joachim Wiberg 6106574e13 initctl: minor refactor, use systemf() instead of snprintf+system
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:49:01 +01:00
Joachim Wiberg 2385a1209b initctl: don't fall back to default command for wrong commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:48:58 +01:00
Joachim Wiberg e8b942cf95 Fix regression in starting built-in services, introduced in 5b9d990
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:28:16 +01:00
Joachim Wiberg ac17d7f5a7 Silence output from gzip in logrotate()
- system may not have gzip (we're opportunistic)
- system may not have the log file (yet)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:26:50 +01:00
Joachim Wiberg 1edc4356a6 initctl: improve error message for unprivileged users
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:26:16 +01:00
Joachim Wiberg 9f06a2db99 Silence developer API debug message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:25:50 +01:00
Joachim Wiberg cfb3a9f2b9 plugins: pidfile: reduce log noise
With the new practise of keeping record of process pid files, we no
longer need to log/update when reading back the same PID we already
have on file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 00:35:13 +01:00
Joachim Wiberg 8aba63a9e3 Track oldpid of services to fix empty pid in restart message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 00:13:15 +01:00
Joachim Wiberg 3d98ad293b Restore telinit symlink to finit, compat only
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 23:59:39 +01:00
Joachim Wiberg 156e86f395 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit d859ecd21d.
2021-03-11 22:28:41 +01:00
Joachim Wiberg d859ecd21d Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:04:56 +01:00
Joachim Wiberg cbec78dbbc initctl: show pid file and condition for foo in 'status foo' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:01:42 +01:00
Joachim Wiberg 572291a1d9 Don't guess pid file name, either there's one declared or not
The previous patch just added dynamic tracking of non-declared pid
files, so we no longer need to make stuff up.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:00:29 +01:00
Joachim Wiberg 280608f30e plugins: pidfile: track PID file in svc, if svc has none declared
Services that create their own PID files usually don't declare one with
Finit.  This patch adds support to track those PID files anywayt at
runtime for the purpose of identifying match svc_t when a PID file is
removed, i.e. when a service exits.

 - On IN_CREATE the pidfile.so plugin saves the pid file name in svc_t
 - On ON_DELETE the pidfile.so plugin finds svc_t based on pid file

Quicker tracking and less dead code, win-win.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:56:49 +01:00
Joachim Wiberg 5c4146cfe4 Export pid_file_set() function, useful to other subsystems as well
Rename, refactor, and export.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:52:44 +01:00
Joachim Wiberg 30e3f5a33c Wait only one second at shutdown/reboot for lingering processes
Transitioning to runlevel 0 or 6 handles graceful shutdown of any
managed run/task/sysv/services.  So we can replace the old 2 sec delay
with a shorter one for any non-managed still lingering process.

Also, some minor cleanup.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:48:49 +01:00
Joachim Wiberg 2c12379a30 Only reload .conf files when not going to shutdown or reboot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:47:22 +01:00
Joachim Wiberg 5f026c8149 initctl: when svc has status missing, highlight what is missing
To aid with debugging, highlight if env file, binary, or both are the
missing component(s) and causing status 'missing'.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:58:36 +01:00
Joachim Wiberg 90005235e8 initctl: minor, rename local variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:11:16 +01:00
Joachim Wiberg 6b688c4211 Refactor, share env helper fns with initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:10:10 +01:00
Joachim Wiberg bdf5f8fc90 initctl: show halted state status reason in bold everywhere
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 14:35:09 +01:00
Joachim Wiberg 79f7a8ccfb Reclaim 900 msec in boot time from final_worker() at bootstrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 14:03:15 +01:00
Joachim Wiberg 3b19db04e3 initctl: add cond, user, group and visual hints for status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:52:05 +01:00
Joachim Wiberg 93402b81d7 initctl: disable ctrl chars also in runlevel_string() when -p
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:51:23 +01:00
Joachim Wiberg 6dec4c9ca0 Set default user/group for services that don't specify any
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:49:14 +01:00
Joachim Wiberg 548acf27cf Minor refactor, rename RESPAWN_MAX -> SVC_RESPAWN_MAX
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:48:44 +01:00
Joachim Wiberg cd0bc57ca8 Clear stale PID conditions when a service stops
Protect against corner cases where pid conditions are not cleaned up.
We don't want such conditions to remain asserted when the process has
terminated.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 11a3318c02 Properly set status 'missing' for missing binaries and/or env files
When we try to start a service/run/task we call whichp() to see if the
binary exists, either tha absolute path given in the .conf file, or in
the $PATH we run with.  If binary, or the env: file, doesn't exist we
now set svc_missing() state.

On `initctl reload` we unblock the service to be able to check again.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 2711b27971 plugins: pidfile: tricky zebra doesn't close its pid file
This patch fixes a few really hard problems wrt PID files:

 1. Listening for IN_CREATE events means we get notified immediately by
    the kernel when someone calls open()/fopen() on a PID file.  Reading
    the contents returns 0, thank you atoi() ... so we drop IN_CREATE
    and instead look for IN_CLOSE_WRITE, there fixed it!  Not quite ...

 2. Some programs, like Zebra, and other Quagga/Frr daemons, don't
    close() their PID files after creation.  Instead they ftruncate()
    and keep them open, and locked.  Presumably to get a mechanism to
    detect already running instances -- messes life up a bit for the
    rest of us though.  So we need to read files after IN_MODIFY too.

 3. We also want to track IN_DELETE so we can deassert conditions when
    services exit gracefully and clean up their PID files

The rest fo the commit is debug instrumentation changes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 05ac9c18c9 Update changelog, new inictcl cond set|clear commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 1a7092f545 doc: update with new 'initctl cond set|clear foo' commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 8000d361b3 initctl: restore 'cond [set|clr] foo' for user-defined conditions
In 7447192 we dropped support for 'cond set' and 'cond clear' commands
with the motivation they were unsafe and sent the wrong message to the
user.  That was true, but mostly because it was too generic and required
the user to call `initctl reload` to apply the changes.

This patch restores the behavior, albeit in a very reduced and simple
format.  All conditions set with this command are constrained to the
'usr/...' namespace.  No subdirectories are allowed.  The argument to
the 'cond set|clear' command is disallowed if it contains '/' or '.'
but anything else is supported, for example:

    initctl cond set foo:2

creates a static/oneshot condition in /run/finit/cond/usr/foo:2

These conditions are static and are fully handled by the user.  The
initctl command is the recommended, and only supported, way of setting
and clearing usr conditions.

This patch also includes a new plugin, usr.so, which is a very simple
inotify plugin for the /run/finit/cond/usr/ directory.  When files are
created or removed here the plugin tells the Finit condition engine to
update and trigger service changes.

For instance, the following service is not started by default at boot:

    service <usr/foo> myservice -- MyService

However, as soon as `initctl cond set foo` is called, myservice starts.
Consequently, it is stopped when `initctl cond clr foo` is called.

Another major difference from the original is that this implementation
doesn't send IPC commands to create/delete the conditions.  This makes
calling these new commands non-blocking so they can be used very early
in the bootstrap, by plugins, if needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg e3c8febe3b Refactor; common nomenclature, ordering, possib. security fix
- Use _PATH_foo for all condition paths, *with* trailing /
- Read condition file first, may not exist, in which case we save time
- Change from libte makepath() to mkpath(), this changes from hard-coded
  0777 perms on all cond dirs to 0755 -- possible security fix

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 32ca117949 initctl: refactor command line parser, use concept from mroutectl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg b966d031c8 initctl: enable plain mode for ls and utmp commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-09 21:30:11 +01:00
Joachim Wiberg 905c2678a3 Fix path to fallback console and handle leading /dev from cmdline
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-08 09:33:16 +01:00
Joachim Wiberg 7d429bdbb1 initctl: restore -p,--plain output for headings and ps listing
This is the same output style as used in the mroutectl and pimctl tools.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 23:30:13 +01:00
39 changed files with 1432 additions and 471 deletions
+4 -1
View File
@@ -4,7 +4,7 @@ Change Log
All relevant changes are documented in this file.
[4.0][UNRELEASED] - 2021-02-21
[4.0][UNRELEASED] - 2021-03-xx
------------------------------
This release became v4.0, and not v3.2, because of incompatible changes
@@ -18,6 +18,9 @@ make sure to read the whole changelog when upgrading.
with a fallback to sending signals as per traditional SysV init. The
`-f` (force) flag remains, where `reboot(2)` is called directly
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
* The `inictl cond set|clear COND` have changed completely. Constrained
to a flat `<usr/...>` namespace and automatically activated by a new
`usr.so` plugin that checks services for usr condition changes
* Removed built-in inetd super server. If you need this functionality,
use an external inetd, like xinetd, instead. A pull request for a
stand-alone inetd, like watchdogd and getty, is most welcome!
+3
View File
@@ -356,6 +356,7 @@ Options:
-b, --batch Batch mode, no screen size probing
-c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt
-p, --plain Use plain table headings, no ctrl chars
-t, --no-heading Skip table headings
-v, --verbose Verbose output
-h, --help This help text
@@ -375,6 +376,8 @@ Commands:
disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined condition +usr/COND
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
cond show Show condition status
cond dump Dump all conditions and their status
+1 -1
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0-rc2, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_INIT(Finit, 4.0-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes])
+27 -1
View File
@@ -45,7 +45,31 @@ being created.
Triggering
----------
Conditions can only be triggered by built-in plugins.
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands:
initctl cond set foo
initctl cond clear foo
The purpose of user-defined conditions is to be able to start or stop
services, or run/task jobs, on external site-dependent stimuli.
Example:
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
For convenience, prefixing with `usr/` is allowed, but any other slashes
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
the same as above, can also be achieved like this:
initctl cond set usr/foo
initctl cond clear usr/foo
Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the
@@ -57,6 +81,8 @@ unchanged) state of it.
> **Note:** For `pid/` conditions it is expected that services "touch"
> or recreate their PID file on `SIGHUP`.
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions
-------------------
-3
View File
@@ -483,9 +483,6 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
Limitations
-----------
+2 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC
noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
endif
else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la
+20 -30
View File
@@ -99,13 +99,14 @@ static void bootclean(void)
*/
static void setup(void *arg)
{
int uid, gid;
mode_t prev;
/* Cleanup stale files, if any still linger on. */
bootclean();
prev = umask(0);
_d("Setting up FHS structure in /var ...");
umask(0);
makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755);
@@ -133,19 +134,21 @@ static void setup(void *arg)
makedir("/var/tmp", 0755);
makedir("/var/empty", 0755);
/*
* If /etc/group or "utmp" group is missing,
* default to "root"/"wheel" group
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
/*
* UTMP actually needs multiple db files
*/
if (has_utmp()) {
int gid;
/*
* If /etc/group or "utmp" group is missing, default to
* "root", or "wheel", group.
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
@@ -165,32 +168,19 @@ static void setup(void *arg)
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
makedir("/var/run/frr", 0755); /* Needed by FRR */
makedir("/var/log/frr", 0755); /* Needed by FRR */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
mksubsys("/var/run/frr", 0755, "frr", "frr");
mksubsys("/var/log/frr", 0755, "frr", "frr");
makedir("/var/run/sshd", 01755); /* OpenSSH */
gid = getgroup("tty");
if (gid < 0)
gid = 0;
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid);
if (!fexist("/etc/mtab"))
symlink("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
uid = getuser("uuidd", NULL);
if (uid >= 0) {
gid = getgroup("uuidd");
if (gid < 0)
gid = 0;
makedir("/var/run/uuidd", 0755);
chown("/var/run/uuidd", uid, gid);
}
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
umask(022);
umask(prev);
}
static plugin_t plugin = {
+12 -10
View File
@@ -32,13 +32,16 @@
#include "conf.h"
#define DAEMON "dbus-daemon"
#define ARGS "--nofork --system"
#define ARGS "--nofork --system --syslog-only"
#define DESC "D-Bus message bus daemon"
#define DAEMONUSER "messagebus"
static void setup(void *arg)
{
char *cmd;
char line[256];
mode_t prev;
char *cmd;
cmd = which(DAEMON);
if (!cmd) {
@@ -46,24 +49,23 @@ static void setup(void *arg)
return;
}
umask(0);
prev =umask(0);
makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
if (whichp("dbus-uuidgen"))
run("dbus-uuidgen --ensure");
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
/* Clean up from any previous pre-bootstrap run */
erase("/var/run/dbus/pid");
remove("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
snprintf(line, sizeof(line), "[S12345] @%s:%s %s %s -- %s",
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
free(cmd);
umask(022);
umask(prev);
}
static plugin_t plugin = {
+14 -15
View File
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
}
}
return iwatch_add(iw, path, IN_ONLYDIR);
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
}
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
svc_t *svc;
paste(fn, sizeof(fn), dir, name);
_d("path: %s, mask: %08x", fn, mask);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return;
_d("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn);
if (!svc) {
_d("No matching svc for %s", fn);
@@ -82,15 +82,20 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc);
if (!svc_has_pidfile(svc))
pid_file_set(svc, fn, 1);
if (svc_is_forking(svc)) {
pid_t pid;
pid = pid_file_read(pid_file(svc));
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
}
cond_set(cond);
@@ -174,8 +179,6 @@ static void pidfile_callback(void *arg, int fd, int events)
if (!ev->mask)
continue;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
if (!iwp)
@@ -186,12 +189,7 @@ static void pidfile_callback(void *arg, int fd, int events)
continue;
}
if (ev->mask & IN_DELETE) {
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
continue;
}
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
pidfile_update_conds(iwp->path, ev->name, ev->mask);
}
}
@@ -242,6 +240,7 @@ static void pidfile_init(void *arg)
*/
path = realpath(_PATH_VARRUN, NULL);
if (!path) {
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL);
if (!path) {
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
+9 -4
View File
@@ -37,10 +37,11 @@ static void setup(void *arg)
#ifdef RANDOMSEED
if (!fexist(RANDOMSEED)) {
int ret = 1;
mode_t prev;
FILE *fp;
print_desc("Bootstrapping random seed", NULL);
umask(077);
prev = umask(077);
fp = fopen(RANDOMSEED, "w");
if (fp) {
int iter = 128;
@@ -57,7 +58,7 @@ static void setup(void *arg)
ret = fclose(fp);
}
print_result(ret);
umask(0);
umask(prev);
}
print_desc("Initializing random number generator", NULL);
@@ -68,10 +69,14 @@ static void setup(void *arg)
static void save(void *arg)
{
#ifdef RANDOMSEED
umask(077);
mode_t prev;
prev = umask(077);
print_desc("Saving random seed", NULL);
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
umask(0);
umask(prev);
#endif
}
+133
View File
@@ -0,0 +1,133 @@
/* User-defined condtion event monitor for the Finit condition engine
*
* Copyright (c) 2021 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 <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_usr;
static void usr_cond(char *name, uint32_t mask)
{
char cond[MAX_COND_LEN] = COND_USR;
strlcat(cond, name, sizeof(cond));
cond_update(cond);
}
static void usr_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
if (ev->mask & IN_ISDIR)
continue; /* unsupported */
if (ev->mask & IN_DELETE)
sync(); /* dont ask */
usr_cond(ev->name, ev->mask);
}
}
static void usr_init(void *arg)
{
char usrdir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
return;
}
path = realpath(_PATH_CONDUSR, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
return;
}
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
iwatch_exit(&iw_usr);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_usr);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = usr_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_usr);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+5 -4
View File
@@ -32,13 +32,14 @@
static void setup(void *arg)
{
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE
int fd;
#endif
char line[LINE_SIZE];
const char *username = "nobody";
umask(0);
prev = umask(0);
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
@@ -58,7 +59,7 @@ static void setup(void *arg)
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
umask(022);
umask(prev);
}
static plugin_t plugin = {
+4 -4
View File
@@ -59,14 +59,14 @@ else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c client.c client.h \
reboot.c serv.c serv.h svc.h \
cond.c cond.h util.c util.h
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
client.c client.h cond.c cond.h reboot.c \
serv.c serv.h svc.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS)
INIT_LNKS = init
INIT_LNKS = init telinit
REBOOT_LNKS = reboot shutdown halt poweroff suspend
if WATCHDOGD
+1 -1
View File
@@ -381,7 +381,7 @@ static void api_cb(uev_t *w, void *arg, int events)
break;
case INIT_CMD_SVC_ITER:
_d("svc iter, first: %d", rq.runlevel);
// _d("svc iter, first: %d", rq.runlevel);
/*
* XXX: This severly limits the number of
* simultaneous client connections, but will
+2 -2
View File
@@ -2,10 +2,10 @@
# Called by kernel when last child in cgroup exists
BASE=/sys/fs/cgroup/finit
PROC=`basename $1`
PROC=$(basename "$1")
DIR="$BASE$1"
#/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir $DIR
rmdir "$DIR"
exit 0
+83 -22
View File
@@ -26,21 +26,65 @@
#include <string.h>
#include <lite/lite.h>
#include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */
#include "finit.h"
#include "log.h"
#include "util.h"
static int cg_init = 0;
static char controllers[256] = "none";
static void group_init(char *path, char *nproc, int share)
{
char file[strlen(path) + 64];
paste(file, sizeof(file), path, "cgroup.clone_children");
echo(file, 0, "1");
paste(file, sizeof(file), path, "cpuset.cpus");
echo(file, 0, nproc);
paste(file, sizeof(file), path, "cpuset.mems");
echo(file, 0, "0");
paste(file, sizeof(file), path, "cpu.shares");
echo(file, 0, "%d", share);
paste(file, sizeof(file), path, "memory.use_hierarchy");
echo(file, 0, "1");
}
static void append_ctrl(char *ctrl)
{
char *wanted[] = {
"cpu",
"cpuacct",
"cpuset",
"memory"
};
size_t i;
for (i = 0; i < NELEMS(wanted); i++) {
if (strcmp(wanted[i], ctrl))
continue;
if (strcmp(controllers, "none"))
strlcat(controllers, ",", sizeof(controllers));
else
strlcpy(controllers, "", sizeof(controllers));
strlcat(controllers, ctrl, sizeof(controllers));
}
}
/*
* Called by Finit at early boot to mount initial cgroups
*/
void cgroup_init(void)
{
FILE *fp;
char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char buf[80];
FILE *fp;
fp = fopen("/proc/cgroups", "r");
if (!fp) {
@@ -55,6 +99,8 @@ void cgroup_init(void)
/* Skip first line, header */
(void)fgets(buf, sizeof(buf), fp);
/* Create and mount traditional cgroups v1 hier */
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup;
char rc[80];
@@ -64,16 +110,28 @@ void cgroup_init(void)
continue;
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
#if 0
if (mkdir(rc, 0755) && EEXIST != errno)
continue;
if (mount("cgroup", rc, "cgroup", opts, cgroup))
_d("Failed mounting %s cgroup on %s", cgroup, rc);
#else
symlink("finit", rc);
append_ctrl(cgroup);
#endif
}
/* Default cgroups for process monitoring */
/* Finit default cgroups for process monitoring */
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
goto fail;
if (mount("none", FINIT_CGPATH, "cgroup", opts, "none,name=finit")) {
#if 0
strlcpy(controllers, "none,name=finit", sizeof(controllers));
#else
strlcat(controllers, ",name=finit", sizeof(controllers));
#endif
if (mount("none", FINIT_CGPATH, "cgroup", opts, controllers)) {
_pe("Failed mounting Finit cgroup hierarchy");
goto fail;
}
@@ -90,24 +148,33 @@ void cgroup_init(void)
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
goto fail;
/*
* Set up basic limits for our groups. Finit optimized defaults
* for embedded and server systems include limiting the groups
* init and user to a single CPU with 10% share, the system
* group gets the rest. Override this from finit.conf
*/
#if 0
#else
snprintf(buf, sizeof(buf), "0-%d", get_nprocs_conf() - 1);
group_init(FINIT_CGPATH "/init", "0", 50);
group_init(FINIT_CGPATH "/user", "0", 75);
group_init(FINIT_CGPATH "/system", buf, 900);
#endif
/* Move ourselves to init */
echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
/* We have signal, main screen turn on! */
cg_init = 1;
fail:
fclose(fp);
}
static int move_pid(char *group, char *name, char *id, int pid)
static int move_pid(char *group, char *name, int pid)
{
char path[256];
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
if (id && strlen(id)) {
strlcat(path, ":", sizeof(path));
strlcat(path, id, sizeof(path));
}
if (mkdir(path, 0755) && errno != EEXIST)
return 1;
@@ -116,25 +183,19 @@ static int move_pid(char *group, char *name, char *id, int pid)
return echo(path, 0, "%d", pid);
}
int cgroup_user(char *name)
int cgroup_user(char *name, int pid)
{
if (!cg_init)
return 0;
return move_pid("finit/user", name, "0", getpid());
return move_pid("finit/user", name, pid);
}
int cgroup_service(char *nm, char *id, int pid)
int cgroup_service(char *name, int pid)
{
if (!cg_init)
return 0;
if (pid <= 0) {
errno = EINVAL;
return 1;
}
return move_pid("finit/system", nm, id, pid);
return move_pid("finit/system", name, pid);
}
/**
+2 -2
View File
@@ -26,7 +26,7 @@
void cgroup_init (void);
int cgroup_user (char *name);
int cgroup_service (char *cmd, char *id, int pid);
int cgroup_user (char *name, int pid);
int cgroup_service (char *name, int pid);
#endif /* FINIT_CGROUP_H_ */
+483
View File
@@ -0,0 +1,483 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2019-2021 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 <err.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <inttypes.h>
#include <lite/lite.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include "initctl.h"
#define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m"
#define NONE " "
#define PIPE plain ? "| " : "│ "
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
uint64_t total_ram; /* From sysinfo() */
struct cg {
struct cg *cg_next;
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
uint64_t cg_rss; /* memory.stat */
uint64_t cg_vmlib; /* memory.stat */
uint64_t cg_vmsize; /* memory.usage_in_bytes */
float cg_mem; /* cg_rss / total_ram * 100 */
float cg_load; /* curr - prev / 10000000.0 */
};
struct cg dummy; /* empty result "NULL" */
struct cg *list;
static char *pid_cmdline(int pid, char *buf, size_t len)
{
size_t i, sz;
char *ptr;
FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) {
buf[0] = 0;
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
fclose(fp);
if (!sz)
return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
}
for (i = 0; i < sz; i++) {
if (buf[i] == 0)
buf[i] = ' ';
}
return buf;
}
static char *pid_comm(int pid, char *buf, size_t len)
{
FILE *fp;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
fgets(buf, len, fp);
fclose(fp);
return chomp(buf);
}
static uint64_t cgroup_uint64(char *path, char *file)
{
uint64_t val = 0;
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (!fp)
return val;
if (fgets(buf, sizeof(buf), fp))
val = strtoull(buf, NULL, 10);
fclose(fp);
return val;
}
static int cgroup_shares(char *path)
{
return (int)cgroup_uint64(path, "cpu.shares");
}
static uint64_t cgroup_memuse(struct cg *cg)
{
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
if (fp) {
while (fgets(buf, sizeof(buf), fp)) {
if (!strncmp(buf, "total_rss", 9)) {
cg->cg_rss = strtoull(&buf[10], NULL, 10);
continue;
}
if (!strncmp(buf, "total_mapped_file", 17)) {
cg->cg_vmlib = strtoull(&buf[18], NULL, 10);
break; /* done */
}
}
fclose(fp);
}
cg->cg_mem = (float)(cg->cg_rss * 100 / total_ram);
/* "fuzz value for efficient access", sort of like total_cache + total_rss */
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.usage_in_bytes");
}
static char *memsz(uint64_t sz, char *buf, size_t len)
{
int gb, mb, kb, b;
gb = sz / (1024 * 1024 * 1024);
sz = sz % (1024 * 1024 * 1024);
mb = sz / (1024 * 1024);
sz = sz % (1024 * 1024);
kb = sz / (1024);
b = sz % (1024);
if (gb)
snprintf(buf, len, "%d.%dG", gb, mb / 102);
else if (mb)
snprintf(buf, len, "%d.%dM", mb, kb / 102);
else
snprintf(buf, len, "%d.%dk", kb, b / 102);
return buf;
}
static float cgroup_cpuload(struct cg *cg)
{
char fn[256];
char buf[64];
FILE *fp;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
if (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
curr = strtoull(buf, NULL, 10);
if (cg->cg_prev != 0) {
uint64_t diff = curr - cg->cg_prev;
/* this expects 1 sec poll interval */
cg->cg_load = (float)(diff / 1000000);
cg->cg_load /= 10.0;
}
cg->cg_prev = curr;
}
fclose(fp);
return cg->cg_load;
}
static struct cg *append(char *path)
{
struct cg *cg;
char fn[256];
ENTRY item;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
if (access(fn, F_OK)) {
warn("not a cgroup path with cpuacct controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
cg->cg_next = list;
list = cg;
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
return cg;
}
static struct cg *find(char *path)
{
ENTRY *ep, item = { path, NULL };
ep = hsearch(item, FIND);
if (ep)
return ep->data;
return append(path);
}
static struct cg *cg_update(char *path)
{
struct cg *cg;
cg = find(path);
if (!cg)
return &dummy;
cgroup_cpuload(cg);
cgroup_memuse(cg);
return cg;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
static int cgroup_tree(char *path, char *pfx, int top)
{
struct dirent **namelist = NULL;
char s[32], r[32], l[32];
char row[screen_cols + 9]; /* + control codes */
size_t rlen = sizeof(row) - 1;
size_t rplen = rlen - 9;
struct stat st;
struct cg *cg;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (top >= screen_rows)
return 0;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
if (top) {
cg = cg_update(path);
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load, path);
} else
strlcpy(row, path, rplen);
puts(row);
}
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80], tmp[42];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (top) {
cg = cg_update(buf);
snprintf(row, rplen,
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load);
} else
row[0] = 0;
strlcat(row, pfx, rplen);
if (i + 1 == n) {
strlcat(row, END, rplen);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
strlcat(row, FORK, rplen);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
}
strlcat(row, " ", rplen);
strlcat(row, nm, rplen);
strlcat(row, "/ ", rplen);
snprintf(tmp, sizeof(tmp), "[cpu.shares: %d]", cgroup_shares(buf));
strlcat(row, tmp, rplen);
puts(row);
rc += cgroup_tree(buf, prefix, top ? top + i : 0);
free(namelist[i]);
}
free(namelist);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 };
pid_t pid;
pid = atoi(chomp(buf));
if (pid <= 0)
continue;
/* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf))) {
char proc[screen_cols];
int len;
if (top)
snprintf(row, rplen, "%37s", " ");
else
row[0] = 0;
strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen);
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
if (plain) {
strlcat(row, proc, rplen);
} else {
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);
}
if (top) {
top += i;
if (top >= screen_rows)
break;
}
}
return fclose(fp);
}
fclose(fp);
return rc;
}
int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return cgroup_tree(arg, NULL, 0);
}
int show_cgtop(char *arg)
{
struct sysinfo si = { 0 };
char path[512];
int laps = 0;
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
if (!hcreate(screen_rows + 25))
err(1, "failed creating hash table");
sysinfo(&si);
total_ram = si.totalram * si.mem_unit;
while (1) {
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
cgroup_tree(arg, NULL, 1);
if (ionce && laps++)
break;
sleep(1);
}
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+37
View File
@@ -0,0 +1,37 @@
/* Cgroup utility functions for initctl
*
* Copyright (c) 2019-2021 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_CGUTIL_H_
#define FINIT_CGUTIL_H_
int show_cgroup (char *arg);
int show_cgtop (char *arg);
#endif /* FINIT_CGUTIL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+4 -8
View File
@@ -21,6 +21,7 @@
* THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -38,17 +39,12 @@ int client_connect(void)
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
goto error;
err(1, "Failed creating UNIX domain socket");
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
close(sd);
goto error;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
err(1, "Failed connecting to finit");
return sd;
error:
perror("Failed connecting to finit");
return -1;
}
int client_disconnect(void)
+13 -11
View File
@@ -99,13 +99,13 @@ static void cond_bump_reconf(void)
unsigned int rgen;
/*
* If %COND_RECONF does not exist, cond_get_gen() returns 0
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want.
*/
rgen = cond_get_gen(COND_RECONF);
rgen = cond_get_gen(_PATH_RECONF);
rgen++;
cond_set_gen(COND_RECONF, rgen);
cond_set_gen(_PATH_RECONF, rgen);
}
static int cond_checkpath(const char *path)
@@ -119,7 +119,7 @@ static int cond_checkpath(const char *path)
return 1;
}
if (makepath(dir) && errno != EEXIST) {
if (mkpath(dir, 0755) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1;
}
@@ -134,7 +134,7 @@ int cond_set_path(const char *path, enum cond_state new)
_d("%s", path);
rgen = cond_get_gen(COND_RECONF);
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
@@ -162,7 +162,8 @@ int cond_set_path(const char *path, enum cond_state new)
return new != old;
}
static void cond_update(const char *name)
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
void cond_update(const char *name)
{
svc_t *svc, *iter = NULL;
@@ -201,7 +202,8 @@ void cond_set_oneshot(const char *name)
if (cond_checkpath(path))
return;
symlink(COND_RECONF, path);
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
_pe("Failed creating onshot cond %s", name);
cond_update(name);
}
@@ -234,13 +236,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F)
return 0;
nm = strstr((char *)fpath, COND_DIR);
nm = strstr((char *)fpath, COND_BASE);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
return 1;
}
nm += sizeof(COND_DIR);
nm += strlen(COND_BASE);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
@@ -261,8 +263,8 @@ void cond_init(void)
{
char path[MAX_ARG_LEN];
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", COND_PATH);
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND);
return;
}
+5 -5
View File
@@ -45,7 +45,7 @@ const char *cond_path(const char *name)
static char path[256];
char tmp[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
return pid_runpath(tmp, path, sizeof(path));
}
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
{
int cgen, rgen;
rgen = cond_get_gen(COND_RECONF);
if (!rgen)
return COND_OFF;
cgen = cond_get_gen(path);
if (!cgen)
return COND_OFF;
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen)
return COND_OFF;
return (cgen == rgen) ? COND_ON : COND_FLUX;
}
+7 -3
View File
@@ -4,9 +4,12 @@
#include <paths.h>
#include <svc.h>
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf"
#define COND_BASE "finit/cond"
#define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_RECONF _PATH_COND "reconf"
typedef enum cond_state {
COND_OFF = 0,
@@ -23,6 +26,7 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names);
void cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_set_oneshot (const char *name);
+3
View File
@@ -900,6 +900,9 @@ int conf_init(uev_ctx_t *ctx)
rlim2str(i), strerror(errno));
}
/* Initialize global rlimits, e.g. for built-in services */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
/* prepare /etc watcher */
fd = iwatch_init(&iw_conf);
if (fd < 0)
+5 -5
View File
@@ -199,7 +199,6 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
}
/*
@@ -222,6 +221,9 @@ static void fs_init(void)
};
size_t i;
/* mask writable bit for g and o */
umask(022);
for (i = 0; i < NELEMS(fs); i++) {
int rc;
@@ -448,7 +450,7 @@ int main(int argc, char *argv[])
};
struct wq final = {
.cb = final_worker,
.delay = 1000
.delay = 100
};
uev_ctx_t loop;
@@ -479,14 +481,13 @@ int main(int argc, char *argv[])
ctx = &loop;
/*
* Set PATH, SHELL, PWD, and umask early to something sane
* Set PATH, SHELL, and PWD early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
if (chdir("/"))
_pe("Failed cd /");
umask(0);
/* Allow progress, if enabled */
enable_progress(1);
@@ -548,7 +549,6 @@ int main(int argc, char *argv[])
_d("Starting initctl API responder ...");
api_init(&loop);
umask(022);
_d("Starting the big state machine ...");
schedule_work(&crank);
+1 -1
View File
@@ -186,7 +186,7 @@ static int do_login(char *name)
{
struct stat st;
cgroup_user(name);
cgroup_user(name, getpid());
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/*
+61 -1
View File
@@ -103,6 +103,9 @@ static void add_console(char *cons)
if (ptr)
*ptr = 0;
if (!strncmp(cons, "/dev/", 5))
cons += 5;
snprintf(path, sizeof(path), "/dev/%s", cons);
/* check for and skip duplicates */
@@ -131,7 +134,7 @@ static void find_active_consoles(void)
if (!fp) {
fallback:
_d("Using fallback /dev/console");
add_console("/dev/console");
add_console("console");
return;
}
@@ -410,6 +413,63 @@ int getgroup(char *group)
#endif
}
/* get current user */
int getcuser(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct passwd *pw = getpwuid(getuid());
if (pw)
ptr = pw->pw_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
/* get current group */
int getcgroup(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct group *gr = getgrgid(getgid());
if (gr)
ptr = gr->gr_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
int uid, gid;
int rc = 0;
omask = umask(0);
uid = getuser(user, NULL);
if (uid >= 0) {
gid = getgroup(group);
if (gid < 0)
gid = 0;
rc = makedir(dir, mode);
chown(dir, uid, gid);
}
umask(omask);
return rc;
}
void set_hostname(char **hostname)
{
FILE *fp;
+6
View File
@@ -81,6 +81,12 @@ int print_result (int fail);
int getuser (char *username, char **home);
int getgroup (char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname);
void networking (int updown);
+281 -254
View File
@@ -26,7 +26,6 @@
#include <err.h>
#include <ftw.h>
#include <ctype.h>
#include <dirent.h>
#include <getopt.h>
#include <paths.h>
#include <signal.h>
@@ -40,22 +39,18 @@
#include "cond.h"
#include "serv.h"
#include "service.h"
#include "cgutil.h"
#include "util.h"
#define _PATH_COND _PATH_VARRUN "finit/cond/"
#define NONE " "
#define PIPE plain ? "|" : "│"
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
struct command {
char *cmd;
int (*cb)(char *arg);
struct cmd {
char *cmd;
struct cmd *ctx;
int (*cb)(char *arg);
};
int icreate = 0;
int iforce = 0;
int ionce = 0;
int heading = 1;
int verbose = 0;
int plain = 0;
@@ -95,6 +90,30 @@ static void col_widths(void)
pw = 3;
}
void print_header(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (plain) {
int len;
vfprintf(stdout, fmt, ap);
fputs("\n", stdout);
for (len = 0; len < screen_cols; len++)
fputc('=', stdout);
fputs("\n", stdout);
} else {
char buf[screen_cols];
vsnprintf(buf, sizeof(buf), fmt, ap);
printheader(stdout, buf, 0);
}
va_end(ap);
}
static int runlevel_get(int *prevlevel)
{
int result;
@@ -126,7 +145,6 @@ static int toggle_debug(char *arg)
static int do_log(char *svc)
{
char cmd[128];
char *logfile = "/var/log/syslog";
if (!svc || !svc[0])
@@ -135,9 +153,7 @@ static int do_log(char *svc)
if (!fexist(logfile))
logfile = "/var/log/messages";
snprintf(cmd, sizeof(cmd), "cat %s | grep %s | tail -10", logfile, svc);
return system(cmd);
return systemf("cat %s | grep %s | tail -10", logfile, svc);
}
static int do_runlevel(char *arg)
@@ -231,7 +247,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
if (tflag != FTW_F)
return 0;
if (!strcmp(fpath, _PATH_COND "reconf"))
if (!strcmp(fpath, _PATH_RECONF))
return 0;
asserted = condstr(cond_get_path(fpath));
@@ -247,9 +263,15 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
nm = svc_ident(svc, NULL, 0);
pid = svc->pid;
}
} else if (strncmp("usr/", cond, 4) == 0) {
nm = "static";
pid = 0;
} else if (strncmp("hook/", cond, 4) == 0) {
nm = "static";
pid = 1;
}
printf("%-*d %-*s %-6s %s\n", pw, pid, iw, nm, asserted, cond);
printf("%-*d %-*s %-6s <%s>\n", pw, pid, iw, nm, asserted, cond);
return 0;
}
@@ -262,7 +284,7 @@ static int do_cond_dump(char *arg)
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION");
printheader(NULL, title, 0);
print_header(title);
}
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
@@ -273,41 +295,93 @@ static int do_cond_dump(char *arg)
return 0;
}
static void show_cond_one(const char *_conds)
static int do_cond_act(char *arg, int creat)
{
static char conds[MAX_COND_LEN];
char *cond;
char oneshot[256];
size_t off;
strlcpy(conds, _conds, sizeof(conds));
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
arg += strlen(COND_USR);
putchar('<');
if (!arg || !arg[0])
errx(1, "Invalid condition (empty)");
if (strchr(arg, '/'))
errx(1, "Invalid condition (slashes)");
if (strchr(arg, '.'))
errx(1, "Invalid condition (periods)");
snprintf(oneshot, sizeof(oneshot), _PATH_CONDUSR "%s", arg);
off = strlen(_PATH_COND);
if (creat) {
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
err(1, "Failed asserting condition <%s>", &oneshot[off]);
} else {
if (erase(oneshot))
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
}
return 0;
}
static int do_cond_set(char *arg) { return do_cond_act(arg, 1); }
static int do_cond_clr(char *arg) { return do_cond_act(arg, 0); }
static char *svc_cond(svc_t *svc, char *buf, size_t len)
{
char *cond, *conds;
memset(buf, 0, len);
if (!svc->cond[0])
return buf;
conds = strdup(svc->cond);
if (!conds)
return buf;
strlcat(buf, "<", len);
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
if (cond != conds)
putchar(',');
strlcat(buf, ",", len);
switch (cond_get(cond)) {
case COND_ON:
printf("+%s", cond);
strlcat(buf, "+", len);
strlcat(buf, cond, len);
break;
case COND_FLUX:
printf("\e[1m~%s\e[0m", cond);
if (!plain)
strlcat(buf, "\e[1m", len);
strlcat(buf, "~", len);
strlcat(buf, cond, len);
if (!plain)
strlcat(buf, "\e[0m", len);
break;
case COND_OFF:
printf("\e[1m-%s\e[0m", cond);
if (!plain)
strlcat(buf, "\e[1m", len);
strlcat(buf, "-", len);
strlcat(buf, cond, len);
if (!plain)
strlcat(buf, "\e[0m", len);
break;
}
}
putchar('>');
strlcat(buf, ">", len);
free(conds);
return buf;
}
static int do_cond_show(char *arg)
{
char ident[MAX_IDENT_LEN];
enum cond_state cond;
char buf[512];
svc_t *svc;
col_widths();
@@ -316,7 +390,7 @@ static int do_cond_show(char *arg)
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
printheader(NULL, title, 0);
print_header(title);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
@@ -325,47 +399,20 @@ static int do_cond_show(char *arg)
cond = cond_get_agg(svc->cond);
svc_ident(svc, ident, sizeof(ident));
printf("%-*d %-*s ", pw, svc->pid, iw, ident);
svc_ident(svc, buf, sizeof(buf));
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
if (cond == COND_ON)
printf("%-6.6s ", condstr(cond));
else
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
show_cond_one(svc->cond);
puts("");
puts(svc_cond(svc, buf, sizeof(buf)));
}
return 0;
}
static int do_cond(char *cmd)
{
int c;
char *arg;
struct command command[] = {
{ "show", do_cond_show },
{ "dump", do_cond_dump },
{ NULL, NULL }
};
if (!cmd)
goto fallback;
arg = strpbrk(cmd, " \0");
if (arg)
*arg++ = 0;
for (c = 0; command[c].cmd; c++) {
if (string_match(command[c].cmd, cmd))
return command[c].cb(arg);
}
fallback:
return do_cond_show(NULL);
}
static int do_cmd(int cmd)
{
struct init_request rq = {
@@ -393,15 +440,18 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
int utmp_show(char *file)
{
time_t sec;
struct utmp *ut;
static int once = 0;
struct tm *sectm;
struct utmp *ut;
time_t sec;
int pid;
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
if (heading)
printheader(NULL, file, 0);
if (heading) {
print_header("%s%s ", once ? "\n" : "", file);
once++;
}
utmpname(file);
setutent();
@@ -468,7 +518,7 @@ char *runlevel_string(int runlevel, int levels)
for (i = 0; i < 10; i++) {
if (ISSET(levels, i)) {
if (runlevel == i)
if (!plain && runlevel == i)
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
if (i == 0)
@@ -476,7 +526,7 @@ char *runlevel_string(int runlevel, int levels)
else
lvl[pos++] = '0' + i;
if (runlevel == i)
if (!plain && runlevel == i)
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
} else {
lvl[pos++] = '-';
@@ -489,45 +539,96 @@ char *runlevel_string(int runlevel, int levels)
return lvl;
}
static char *svc_command(svc_t *svc, char *cmd, size_t len)
static int missing(svc_t *svc)
{
strlcpy(cmd, svc->cmd, len);
if (svc->state == SVC_HALTED_STATE && svc_is_missing(svc))
return 1;
return 0;
}
static char *svc_command(svc_t *svc, char *buf, size_t len)
{
int bold = missing(svc);
if (plain || whichp(svc->cmd))
bold = 0;
strlcpy(buf, bold ? "\e[1m" : "", len);
strlcat(buf, svc->cmd, len);
for (int i = 1; i < MAX_NUM_SVC_ARGS; i++) {
if (!svc->args[i][0])
break;
strlcat(cmd, " ", len);
strlcat(cmd, svc->args[i], len);
strlcat(buf, " ", len);
strlcat(buf, svc->args[i], len);
}
return cmd;
strlcat(buf, bold ? "\e[0m" : "", len);
return buf;
}
static char *svc_environ(svc_t *svc, char *buf, size_t len)
{
int bold = missing(svc);
if (plain || svc_checkenv(svc) || svc->env[0] == '-')
bold = 0;
strlcpy(buf, bold ? "\e[1m" : "", len);
strlcat(buf, svc->env, len);
strlcat(buf, bold ? "\e[0m" : "", len);
return buf;
}
static char *status(svc_t *svc)
{
static char buf[25];
int bold = !plain;
char *s;
s = svc_status(svc);
if (svc->state != SVC_HALTED_STATE)
bold = 0;
snprintf(buf, sizeof(buf), "%s%-8.8s%s",
bold ? "\e[1m" : "", s, bold ? "\e[0m" : "");
return buf;
}
static int show_status(char *arg)
{
char ident[MAX_IDENT_LEN];
char cmd[512];
char buf[512];
svc_t *svc;
runlevel = runlevel_get(NULL);
if (arg && arg[0]) {
long now = jiffies();
char buf[42] = "N/A";
char uptm[42] = "N/A";
svc = client_svc_find(arg);
if (!svc)
return 1;
printf("Status : %s\n", status(svc));
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
printf("Description : %s\n", svc->desc);
printf("Origin : %s\n", svc->file[0] ? svc->file : "built-in");
printf("Environment : %s\n", svc->env);
printf("Command : %s\n", svc_command(svc, cmd, sizeof(cmd)));
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf)));
printf("PID file : %s\n", svc->pidfile);
printf("PID : %d\n", svc->pid);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
printf("User : %s\n", svc->username);
printf("Group : %s\n", svc->group);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
printf("Status : %s\n", svc_status(svc));
printf("\n");
return do_log(svc->cmd);
@@ -544,7 +645,7 @@ static int show_status(char *arg)
else
strlcat(title, "COMMAND", sizeof(title));
printheader(NULL, title, 0);
print_header(title);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
@@ -553,7 +654,7 @@ static int show_status(char *arg)
printf("%-*d ", pw, svc->pid);
svc_ident(svc, ident, sizeof(ident));
printf("%-*s %-8s ", iw, ident, svc_status(svc));
printf("%-*s %s ", iw, ident, status(svc));
lvls = runlevel_string(runlevel, svc->runlevels);
if (strchr(lvls, '\e'))
@@ -564,123 +665,12 @@ static int show_status(char *arg)
if (!verbose)
puts(svc->desc);
else
puts(svc_command(svc, cmd, sizeof(cmd)));
puts(svc_command(svc, buf, sizeof(buf)));
}
return 0;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
static int dump_cgroup(char *path, char *pfx)
{
struct dirent **namelist = NULL;
struct stat st;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
puts(path);
}
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char dir[80];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (i + 1 == n) {
printf("%s%s ", pfx, END);
snprintf(dir, sizeof(dir), "%s ", pfx);
} else {
printf("%s%s ", pfx, FORK);
snprintf(dir, sizeof(dir), "%s%s ", pfx, PIPE);
}
puts(nm);
rc += dump_cgroup(buf, dir);
free(namelist[i]);
}
free(namelist);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
FILE *cfp;
pid_t pid;
pid = atoi(chomp(buf));
cfp = fopenf("r", "/proc/%d/cmdline", pid);
if (!cfp)
continue;
if (fgets(buf, sizeof(buf), cfp))
printf("%s%s %d %s\n", pfx, ++i == num ? END : FORK, pid, buf);
fclose(cfp);
}
return fclose(fp);
}
fclose(fp);
return rc;
}
static int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return dump_cgroup(arg, NULL);
}
static int transform(char *nm)
{
char *names[] = {
@@ -725,6 +715,8 @@ static int usage(int rc)
" -b, --batch Batch mode, no screen size probing\n"
" -c, --create Create missing paths (and files) as needed\n"
" -f, --force Ignore missing files and arguments, never prompt\n"
" -1, --once Only one lap in commands like 'top'\n"
" -p, --plain Use plain table headings, no ctrl chars\n"
" -t, --no-heading Skip table headings\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n"
@@ -764,7 +756,9 @@ static int usage(int rc)
fprintf(stderr,
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
"\n"
" cond show Show condition status\n"
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
" cond status Show condition status, default cond command\n"
" cond dump Dump all conditions and their status\n"
"\n"
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
@@ -775,6 +769,7 @@ static int usage(int rc)
" status Show status of services, default command\n"
"\n"
" ps List processes based on cgroups\n"
" top Show top-like listing based on cgroups\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n"
@@ -790,6 +785,11 @@ static int usage(int rc)
else
fprintf(stderr, "\n");
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc;
}
@@ -807,62 +807,106 @@ static int do_help(char *arg)
return usage(0);
}
static int cmd_parse(int argc, char *argv[], struct cmd *command)
{
int i, j;
for (i = 0; argc > 0 && command[i].cmd; i++) {
if (!string_match(command[i].cmd, argv[0]))
continue;
if (command[i].ctx)
return cmd_parse(argc - 1, &argv[1], command[i].ctx);
if (command[i].cb) {
int rc = 0;
if (argc == 1)
return command[i].cb(NULL);
for (j = 1; j < argc; j++)
rc |= command[i].cb(argv[j]);
return rc;
}
}
if (argv[0] && strlen(argv[0]) > 0)
errx(1, "No such command. See 'initctl help' for an overview of available commands.");
return command[0].cb(NULL); /* default cmd */
}
int main(int argc, char *argv[])
{
struct command command[] = {
{ "debug", toggle_debug },
{ "devel", do_devel },
{ "help", do_help },
{ "version", show_version },
{ "list", serv_list },
{ "ls", serv_list },
{ "enable", serv_enable },
{ "disable", serv_disable },
{ "touch", serv_touch },
{ "show", serv_show },
{ "edit", serv_edit },
{ "create", serv_creat },
{ "delete", serv_delete },
{ "reload", do_reload },
{ "cond", do_cond },
{ "log", do_log },
{ "start", do_start },
{ "stop", do_stop },
{ "restart", do_restart },
{ "status", show_status },
{ "show", show_status }, /* Convenience alias, for now */
{ "ps", show_cgroup },
{ "runlevel", do_runlevel },
{ "reboot", do_reboot },
{ "halt", do_halt },
{ "poweroff", do_poweroff },
{ "suspend", do_suspend },
{ "utmp", do_utmp },
{ NULL, NULL }
};
struct option long_options[] = {
{ "batch", 0, NULL, 'b' },
{ "create", 0, NULL, 'c' },
{ "force", 0, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "once", 0, NULL, '1' },
{ "plain", 0, NULL, 'p' },
{ "no-heading", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
struct cmd cond[] = {
{ "status", NULL, do_cond_show }, /* default cmd */
{ "dump", NULL, do_cond_dump },
{ "set", NULL, do_cond_set },
{ "clr", NULL, do_cond_clr },
{ "clear", NULL, do_cond_clr },
{ NULL, NULL, NULL }
};
struct cmd command[] = {
{ "status", NULL, show_status }, /* default cmd */
{ "debug", NULL, toggle_debug },
{ "devel", NULL, do_devel },
{ "help", NULL, do_help },
{ "version", NULL, show_version },
{ "list", NULL, serv_list },
{ "ls", NULL, serv_list },
{ "enable", NULL, serv_enable },
{ "disable", NULL, serv_disable },
{ "touch", NULL, serv_touch },
{ "show", NULL, serv_show },
{ "edit", NULL, serv_edit },
{ "create", NULL, serv_creat },
{ "delete", NULL, serv_delete },
{ "reload", NULL, do_reload },
{ "cond", cond, NULL },
{ "log", NULL, do_log },
{ "start", NULL, do_start },
{ "stop", NULL, do_stop },
{ "restart", NULL, do_restart },
{ "ps", NULL, show_cgroup },
{ "top", NULL, show_cgtop },
{ "runlevel", NULL, do_runlevel },
{ "reboot", NULL, do_reboot },
{ "halt", NULL, do_halt },
{ "poweroff", NULL, do_poweroff },
{ "suspend", NULL, do_suspend },
{ "utmp", NULL, do_utmp },
{ NULL, NULL, NULL }
};
int interactive = 1, c;
char *cmd;
if (transform(progname(argv[0])))
return reboot_main(argc, argv);
while ((c = getopt_long(argc, argv, "bcfh?tv", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "1bcfh?ptv", long_options, NULL)) != EOF) {
switch(c) {
case '1':
ionce = 1;
break;
case 'b':
interactive = 0;
break;
@@ -879,6 +923,10 @@ int main(int argc, char *argv[])
case '?':
return usage(0);
case 'p':
plain = 1;
break;
case 't':
heading = 0;
break;
@@ -892,10 +940,8 @@ int main(int argc, char *argv[])
if (interactive)
screen_init();
if (optind >= argc)
return show_status(NULL);
if (!has_utmp()) {
/* system w/o utmp support, disable 'utmp' command */
for (c = 0; command[c].cmd; c++) {
if (!string_compare(command[c].cmd, "utmp"))
continue;
@@ -905,26 +951,7 @@ int main(int argc, char *argv[])
}
}
cmd = argv[optind++];
for (c = 0; command[c].cmd; c++) {
int rc = 0;
if (!string_match(command[c].cmd, cmd))
continue;
if (!command[c].cb)
continue;
/* no arg, just a command, e.g. initctl ls */
if (optind >= argc)
return command[c].cb(NULL);
/* >=1 arg, e.g., initctl del foo bar baz */
while (optind < argc)
rc |= command[c].cb(argv[optind++]);
return rc;
}
return usage(1);
return cmd_parse(argc - optind, &argv[optind], command);
}
void logit(int prio, const char *fmt, ...)
+46
View File
@@ -0,0 +1,46 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2015-2021 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_INITCTL_H_
#define FINIT_INITCTL_H_
#include "finit.h"
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
extern int ionce; /* initctl -1 */
extern int heading; /* initctl -t */
extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */
extern void print_header(const char *fmt, ...);
#endif /* FINIT_INITCTL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -80,7 +80,7 @@ int logrotate(char *file, int num, off_t sz)
}
if (cnt == 2 && fexist(nfile)) {
if (systemf("gzip %s", nfile))
if (systemf("gzip %s 2>/dev/null", nfile))
continue; /* no gzip, probably */
(void)remove(nfile);
+10 -12
View File
@@ -95,18 +95,15 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
char *pid_file(svc_t *svc)
{
static char path[256];
char fn[MAX_ARG_LEN];
if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!')
return &svc->pidfile[1];
return svc->pidfile;
}
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
errno = ENOENT;
return pid_runpath(fn, path, sizeof(path));
return NULL;
}
pid_t pid_file_read(const char *fn)
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
return fclose(fp);
}
static int pid_realpath(svc_t *svc, char *file)
int pid_file_set(svc_t *svc, char *file, int not)
{
int not = 0;
if (!file)
if (!file) {
file = pid_file(svc);
if (!file)
return -1;
}
if (file[0] == '!') {
not = 1;
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_realpath(svc, arg);
return pid_file_set(svc, arg, 0);
if (arg[0] == '!') {
arg++;
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
if (len > 4 && strcmp(&path[len - 4], ".pid"))
strlcat(path, ".pid", sizeof(path));
return pid_realpath(svc, path);
return pid_file_set(svc, path, 0);
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid"))
return pid_realpath(svc, NULL);
return pid_file_set(svc, NULL, 0);
return 1;
}
+1
View File
@@ -32,6 +32,7 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc);
int pid_file_set (svc_t *svc, char *file, int not);
pid_t pid_file_read (const char *fn);
int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
+22 -7
View File
@@ -30,10 +30,7 @@
#include <stdlib.h>
#include <lite/lite.h>
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
#include "initctl.h"
static int calc_width(char *arr[], size_t len)
@@ -53,7 +50,7 @@ static int calc_width(char *arr[], size_t len)
static void do_list(const char *path)
{
char buf[screen_cols];
static int once = 0;
int width, num, prev;
glob_t gl;
size_t i;
@@ -67,8 +64,26 @@ static void do_list(const char *path)
if (gl.gl_pathc <= 0)
goto done;
snprintf(buf, sizeof(buf), "%s ", path);
printheader(NULL, buf, 0);
if (plain) {
if (heading)
print_header("%s%s ", once ? "\n" : "", path);
once++;
for (i = 0; i < gl.gl_pathc; i++) {
if (!heading) {
char buf[512];
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
puts(buf);
} else
puts(gl.gl_pathv[i]);
}
goto done;
}
if (heading)
print_header("%s ", path);
width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0)
+54 -44
View File
@@ -49,8 +49,6 @@
#include "utmp-api.h"
#include "schedule.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static struct wq work = {
.cb = service_worker,
};
@@ -262,37 +260,6 @@ static int redirect(svc_t *svc)
return 0;
}
/*
* non-zero env, no checking if file exists or not
*/
static char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static int check_env(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env)
return 1;
return fexist(env);
}
/*
* Source environment file, if it exists
* Note: must be called from privsepped child
@@ -309,7 +276,7 @@ static void source_env(svc_t *svc)
if (!fn)
return;
/* Warning in service_start() after check_env() */
/* Warning in service_start() after svc_checkenv() */
fp = fopen(fn, "r");
if (!fp)
return;
@@ -384,6 +351,30 @@ static int is_norespawn(void)
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;
}
/**
* service_start - Start service
* @svc: Service to start
@@ -395,6 +386,7 @@ static int service_start(svc_t *svc)
{
int result = 0, do_progress = 1;
sigset_t nmask, omask;
char grnam[80];
pid_t pid;
size_t i;
@@ -407,14 +399,15 @@ static int service_start(svc_t *svc)
/* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd)) {
print(1, "%s: does not exist", svc->cmd);
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
svc_missing(svc);
return 1;
}
/* Unlike systemd we do not allow starting service if env is missing, unless - */
if (!check_env(svc)) {
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
if (!svc_checkenv(svc)) {
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
svc_missing(svc);
return 1;
}
@@ -440,8 +433,6 @@ static int service_start(svc_t *svc)
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork();
cgroup_service(svc->name, svc->id, pid);
if (pid == 0) {
int status;
char *home = NULL;
@@ -579,6 +570,8 @@ static int service_start(svc_t *svc)
_d("Starting %s %s", svc->cmd, buf);
}
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid);
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
svc->pid = pid;
@@ -648,11 +641,12 @@ static void service_cleanup(svc_t *svc)
char *fn;
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
if (fn && remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
basename(svc->cmd), fn);
/* No longer running, update books. */
svc->oldpid = svc->pid;
svc->start_time = svc->pid = 0;
}
@@ -1127,6 +1121,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc);
} else {
/* e.g., if missing cmd or env before */
svc_unblock(svc);
}
/* Always clear svc PID file, for now. See TODO */
@@ -1143,6 +1140,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
strlcpy(svc->group, ptr, sizeof(svc->group));
}
strlcpy(svc->username, username, sizeof(svc->username));
} else {
getcuser(svc->username, sizeof(svc->username));
getcgroup(svc->group, sizeof(svc->group));
}
parse_cmdline_args(svc, cmd);
@@ -1229,6 +1229,7 @@ void service_monitor(pid_t lost, int status)
if (svc_is_daemon(svc)) {
service_cleanup(svc);
} else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */
if (WIFEXITED(status) && !WEXITSTATUS(status))
svc->started = 1;
else
@@ -1296,7 +1297,7 @@ static void service_retry(svc_t *svc)
return;
}
if (*restart_cnt >= RESPAWN_MAX) {
if (*restart_cnt >= SVC_RESPAWN_MAX) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0));
svc_crashing(svc);
@@ -1309,12 +1310,12 @@ static void service_retry(svc_t *svc)
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
svc_unblock(svc);
service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
service_timeout_after(svc, timeout, service_retry);
}
@@ -1368,10 +1369,15 @@ restart:
case SVC_STOPPING_STATE:
if (!svc->pid) {
char cond[MAX_COND_LEN];
/* PID was collected normally, no need to kill it */
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
_d("%s: stopped normally, no need to send SIGKILL :)", svc->cmd);
service_timeout_cancel(svc);
_d("%s: clearing pid condition ...", svc->name);
cond_clear(mkcond(svc, cond, sizeof(cond)));
switch (svc->type) {
case SVC_TYPE_SERVICE:
svc_set_state(svc, SVC_HALTED_STATE);
@@ -1400,6 +1406,10 @@ restart:
err = service_start(svc);
if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++;
break;
}
+9 -9
View File
@@ -192,10 +192,10 @@ void do_kill(int signo)
if (fgets(file, sizeof(file), fp)) {
if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file);
else if (file[0] != '@')
kill(pid, signo);
else
else if (file[0] == '@')
_d("Skipping %s ...", &file[1]);
else
kill(pid, signo);
}
fclose(fp);
}
@@ -214,11 +214,11 @@ void do_shutdown(shutop_t op)
utmp_set_halt();
/*
* Tell all remaining non-monitored processes to exit, give them
* some time to exit gracefully, 2 sec is customary.
* Tell remaining non-monitored processes to exit, give them
* time to exit gracefully, 2 sec was customary, we go for 1.
*/
do_kill(SIGTERM);
do_sleep(2);
do_sleep(1);
do_kill(SIGKILL);
/* Exit plugins and API gracefully */
@@ -274,15 +274,15 @@ void do_shutdown(shutop_t op)
do_sleep(1);
}
_e("Rebooting ...");
_d("Rebooting ...");
reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) {
_e("Powering down ...");
_d("Powering down ...");
reboot(RB_POWER_OFF);
}
/* Also fallback if any of the other two fails */
_e("Halting ...");
_d("Halting ...");
reboot(RB_HALT_SYSTEM);
}
+5 -3
View File
@@ -156,9 +156,11 @@ restart:
/* Disable login in single-user mode as well as shutdown/reboot */
nologin();
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
if (runlevel != 0 && runlevel != 6) {
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
}
/* Reset once flag of runtasks */
service_runtask_clean();
+16 -1
View File
@@ -415,8 +415,20 @@ svc_t *svc_find_by_pidfile(char *fn)
pid_t pid;
pid = pid_file_read(fn);
if (pid == -1)
if (pid <= 0) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
int v = 0;
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue;
return svc;
}
return NULL;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid != pid)
@@ -532,6 +544,9 @@ int svc_enabled(svc_t *svc)
if (svc_is_removed(svc) || svc_is_blocked(svc))
return 0;
if (svc_is_missing(svc))
return 0;
return 1;
}
+40 -4
View File
@@ -77,6 +77,9 @@ typedef enum {
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
#define SVC_TERM_TIMEOUT 3000
/* Prevent endless respawn of faulty services. */
#define SVC_RESPAWN_MAX 10
/*
* Default enable for all services, can be stopped by means
* of issuing an initctl call. E.g.
@@ -95,7 +98,7 @@ typedef struct svc {
/* Service details */
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
int killdelay; /* Delay in msec before sending SIGKILL */
pid_t pid;
pid_t oldpid, pid;
char pidfile[256];
long start_time; /* Start time, as seconds since boot, from sysinfo() */
int started; /* Set for run/task/sysv to track if started */
@@ -194,12 +197,14 @@ static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1;
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
#define svc_start(svc) svc_unblock(svc)
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
@@ -347,6 +352,37 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
return buf;
}
/*
* non-zero env, no checking if file exists or not
*/
static inline char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static inline int svc_checkenv(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env)
return 1;
return fexist(env);
}
#endif /* FINIT_SVC_H_ */
/**