Compare commits

..
288 Commits
Author SHA1 Message Date
Joachim Nilsson 6d41725387 Bump version for v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:51:55 +01:00
Joachim Nilsson ceb91f746c Revert "Disable clang build during Coverity Scan run"
This reverts commit 22a69d8cca.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:45:24 +01:00
Joachim Nilsson a8802ab0d2 Mention Void Linux tutorial in README, change to bullet list
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:31:03 +01:00
Joachim Nilsson ad98f69560 Ignore return value from rename(), ofile may not exist yet.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 14:03:07 +01:00
Joachim Nilsson 5953090688 Document some relevant cmdline options
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 13:45:22 +01:00
Joachim Nilsson 22a69d8cca Disable clang build during Coverity Scan run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 11:45:55 +01:00
Joachim Nilsson 710a3d2c8f Fix Coverity warning, array cannot be NULL
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 11:45:15 +01:00
Joachim Nilsson 658fb9b037 Check return value from rename(), fall back to truncate() on error
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 11:39:50 +01:00
Joachim Nilsson a84bb9042f Update and add ignore for WeOS build system files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:51:39 +01:00
Joachim Nilsson eeb13dba74 Toggle silent mode (progress) when toggling debug mode
Starting in debug mode and disabling debug at runtime did not exhibit
the same behavior as if starting in normal mode and toggling debug mode
at runtime.  This patch adds toggling of silent mode (progress output)
to runtime debug toggling.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:44:44 +01:00
Joachim Nilsson 3d20237959 Follow-up to f858d94, only reinitialize screen when toggling debug mode
The screen_init() call for each print() & C:o caused serious screen
artefacts in debug mode.  This patch reworks f858d94 to only re-init
screen when toggling debug.

Also, avoid screen_init() completely if started in debug mode.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:33:24 +01:00
Joachim Nilsson ddf1bce642 Add screen_exit() fn to restore screen defaults in debug mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 09:31:44 +01:00
Joachim Nilsson 93cd4116db Update ChangeLog and prepare for v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 01:21:37 +01:00
Joachim Nilsson 1df6a91a94 Document sufficiently complex, yet deceptively simple, function
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 00:44:40 +01:00
Joachim Nilsson c64a71ff30 Follow-up to ba2ab4b, crashing services may not have a PID rn
- Don't warn of missing PID, even if only _d(), perfectly normal
- Allow stopping restart timer of non-services (e.g. task/run)
- Allow stopping services with no PID rn, i.e. stop libuEv timer

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-23 00:17:02 +01:00
Joachim Nilsson b2b6fd25d8 Relocate service helper script to contrib/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 22:28:18 +01:00
Joachim Nilsson 3ca548b681 Add list of major contributors, from Westermo
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 21:31:05 +01:00
Joachim Nilsson ba2ab4b446 Follow-up to 2683049, only stop running, non-inet, services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 20:52:37 +01:00
Joachim Nilsson a8183b0559 Check if symlink exists before trying to create symlink
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 20:45:05 +01:00
Joachim Nilsson 227e4fa097 Create destination contrib/*/finit.d dir if missing
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 19:03:06 +01:00
Joachim Nilsson 3d68ebbf54 Fix off-by-one in condition reassert()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 15:24:11 +01:00
Joachim Nilsson 6e017025aa Fix bad string pointer arithmetic in condition reassert
On `initctl reload` we reassert conditions, i.e. bring each still viable
condition back in sync with the new reconf generation.  This patch fixes
an assumption in the reassert() callback that caused the following nasty
transformation:

	/run/finit/cond/net/lo/up --> /run/finit/cond/lo/up

The transformation was caused by the reassert() code assuming a /var/run
prefix rather than /run.  We must check the actual runpath, or like this
patch does, use a path neutral way to find the base condition string.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 15:17:07 +01:00
Joachim Nilsson 5f818f917e cond_init(): Create correct condition path, /var/run might not exist
The cond_init() function is called before the bootmisc.so plugin has
run.  It is responsble for setting up the /var/run symlink to /run on
systems with the new layout.  Therefore we must wrap COND_PATH with
pid_runpath() to create the correct runtime path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 15:17:07 +01:00
Joachim Nilsson 0da1deb487 Add docs/*.md to distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:02:19 +01:00
Joachim Nilsson 0337985422 Ignore generated files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:02:19 +01:00
Joachim Nilsson f59d41cdec Add silly process monitor script to distribution.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:02:18 +01:00
Joachim Nilsson 1cc998eb60 Distribute and install contrib/ directory with all examples
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 09:01:58 +01:00
Joachim Nilsson 8c1d84305d Minor, refactor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-22 08:38:52 +01:00
Joachim Nilsson 7e4e5127c7 Preserve Markdown links on installed .md files in docs/ sub-directory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-21 10:22:25 +01:00
Joachim Nilsson 3b813bca68 Ignore generated files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-20 20:48:26 +01:00
Joachim Nilsson a1f210bc70 Distribute and install relevant docs/*.md
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-20 20:47:33 +01:00
Joachim Nilsson 0ecf46d7a9 Prefer _exit() over exit() in forked service handlers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:35:15 +01:00
Joachim Nilsson 268304927d Fix segfault, stop a crashing service before deleting it
Finit tries to restart crashing services.  If a service is removed
before Finit has given up restarting it, then the background timer
responsible the restart may dereference a free():d svc_t.

Hence, make sure to properly stop (and step) a service and its timer
callbacks in service_unregister() before calling svc_del().

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:27:25 +01:00
Joachim Nilsson fa02b59db5 Refactor, set STOPPING state in service_stop()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:24:39 +01:00
Joachim Nilsson 2c904b5b44 service_unregister(): Also call inetd_del() for inetd services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:07:22 +01:00
Joachim Nilsson 4191abc79f service_timeout_cb(): Check callback pointer before calling it
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:07:22 +01:00
Joachim Nilsson abb928f60b service_stop(): Also stop any pending restart timers
In service_stop() we call svc_del() which, as of Finit v3.1, free():s
the svc_t, so we don't want any lingering restart timers to wake up
after free() and start dereferencing the svc_t.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 16:07:22 +01:00
Joachim Nilsson 3c52118a8e svc_clean_dynamic(): Fix access after free()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-19 13:20:25 +01:00
Joachim Nilsson 5354a57313 plugin_unregister(): Stop I/O watcher before free():ing watcher
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 18:35:18 +01:00
Joachim Nilsson 92b1d882e5 do_kill(): Also skip gdbserver from being killed
To improve debug possibilities at shutdown, keep gdbserver running even
during shutdown.

Note: The gdbserver service runlevels must be sth like [123456789] for
      this to work in the first place.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 18:21:37 +01:00
Joachim Nilsson fd7a6c5020 cond.c: Normalize COND_PATH, some systems have /run not /var/run
On most modern Linux systems /run is a tmpfs that replaces /var/run.
The latter is set up as a symlink to /run by the bootmisc.so plugin.

However, Finit conditions rely on the /var/run/finit/cond prefix, which
does not exist until bootmisc.so has run, which is *after* `mount -a`
has run.  Therefore, to have working conditions before we run `mount -a`
we must normalize the path constructed by cond_path() to use either the
/run or /var/run (default) prefix.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 14:44:25 +01:00
Joachim Nilsson a61551d15f pid.c: Relocate pid_runpath() to a static inline fn
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 14:44:00 +01:00
Joachim Nilsson 7f62a69f74 New hook/mount/post, runs after mount -a but before hook/mount/all
This gives the operator a way to add a post-mount script, e.g. to peform
a 2nd stage mount or mount preparations, before the bootmisc.so plugin
and others run in hook/mount/all

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 13:34:28 +01:00
Joachim Nilsson c969bda62d bootmisc.so: Create /etc/mtab symlink if missing
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 13:34:28 +01:00
Joachim Nilsson af41fcd35e api_exit(): Must stop libuEv watcher before closing descriptor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-18 13:34:28 +01:00
Joachim Nilsson 84e9569be6 initctl: Adjust field widths in show/status
The job:id field can now be used to display job:INET_PORT_NUMBER, e.g.
100:16161 for job 100 localhost SNMP port 16161.

The service name field has been reduced from 20 --> 16 bytes since the
full path has been reduced to display the basename.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-17 18:42:24 +01:00
Joachim Nilsson a4ece0fe3f Add log rotate, log, condition and non-SIGHUP example .conf sample
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 17:41:03 +01:00
Joachim Nilsson b2f66da1f9 Cleanup TODO, last item turned out to be bogus
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 17:06:55 +01:00
Joachim Nilsson abbc0a1d5c Fix Ctrl-C, and signals in general, on "logins" in nologin mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 16:25:11 +01:00
Joachim Nilsson 37e420bd35 Minor, itemize example files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 16:03:36 +01:00
Joachim Nilsson d72eb45d6e Refactor, simplify text and rename section
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 16:02:06 +01:00
Joachim Nilsson 30c593f64e Minor, reorder debug messages to not mess up progress text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:55:57 +01:00
Joachim Nilsson f858d94d2a Reinitialize screen at shutdown and when in debug mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:47:14 +01:00
Joachim Nilsson 992bab2f8f Allow empty -- run/task/service descriptions => no progress
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:38:55 +01:00
Joachim Nilsson 72c9681ffb Change debug messages to use svc->cmd rather than empty svc->desc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:15:49 +01:00
Joachim Nilsson e439b79e82 Refactor, only show process progress if -- Description is set
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:15:10 +01:00
Joachim Nilsson 36e001d636 Refactor, rindex() --> strrchr(), LEGACY as of POSIX.1-2001
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 14:13:48 +01:00
Joachim Nilsson 75aa14466f Allow disabling log rotation, log size:0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 07:50:52 +01:00
Joachim Nilsson 7f899b7a91 Clarify size:200k and count:5 global log rotation options
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 07:46:27 +01:00
Joachim Nilsson 1c177fd953 TODO before v3.1 GA release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-16 07:41:20 +01:00
Joachim Nilsson 617ec7c147 Document global log rotation setting log size:200k count:5
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 19:42:33 +01:00
Joachim Nilsson 192d3aafe7 Add support for global logfile max size and max num. rotated
# Max file size for each log file: 100 kiB, rotate max 4 copies:
    # log => log.1 => log.2.gz => log.3.gz => log.4.gz
    log size=100k count=4

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 19:42:33 +01:00
Joachim Nilsson f11ebb0046 New function, strtobytes(), allows k/M/G ISO size modifiers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 19:42:33 +01:00
Joachim Nilsson b5e260b9e0 Minor refactor
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 18:30:41 +01:00
Joachim Nilsson 45c10edad6 Fix static build, libplug.la is now in ../plugins/, not plugins/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 16:05:39 +01:00
Joachim Nilsson b60d8ce4b6 Fix bootstrap ordering problem, inotify watchers *after* HOOK_BASEFS_UP
An external plugin to set up /etc can use HOOK_BASEFS_UP, but the Finit
conf_monitor() must then be called *after* not before that hook, or the
inotify watchers will listen on the wrong inode.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 15:23:25 +01:00
Joachim Nilsson d35a8bbda9 Fix problem building external plugins
The file plugin.h is installed to $prefix/include and used by external
plugins.  Therefore it must not include, or depend on, features set by
the configure script in config.h

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 15:07:24 +01:00
Joachim Nilsson c616270ccb Bump version for v3.1-rc2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 890ac2f4e1 Update ChangeLog for upcoming v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 8063b1bfe1 Include rescue.conf in distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 0bcfb464ce make release: Call distcheck target verify build from tarball
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson dc5b1a5980 Increase size of intermediate buffers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 12:19:51 +01:00
Joachim Nilsson 14555d7a7b configure: Fix misspelled --enable-redirect option
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:17:50 +01:00
Joachim Nilsson a06127b990 Document new log:... option to task/run/services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:10:20 +01:00
Joachim Nilsson 3b8e7d677f rlimit can be set globally or locally depending on .conf file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:06:19 +01:00
Joachim Nilsson 097fce4170 Ignore logit binary
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 10:03:12 +01:00
Joachim Nilsson 306bf0f191 Add support for configurable redirect to /dev/null, issue #21
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson f25390004b Add support for redirecting output to a file using logit, issue #21
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson cbd4cc8d34 Extend .conf syntax for 'log' option, issue #21
This is the first patch in a series to add support for

    log:/path/to/logfile,priority:facility.level,tag:ident

In this patch support for 'priority:' and 'tag:', log file support
coming later.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson 44591c6e51 logit: Sync each log entry to file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 07:44:33 +01:00
Joachim Nilsson 0e51a1eed8 logit: Hard code program name and clarify distribution scope
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 06:58:44 +01:00
Joachim Nilsson c315fb5637 Add logit v1.0 to Finit distribution
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-15 06:58:44 +01:00
Joachim Nilsson 1017e7b8e5 Disable conditions for some non-oneshot (runtime) hooks
The SVC_RECONF, SVC_LOST, SVC_START, and RUNLEVEL_CHANGE hooks are not
one-shot, they are also not regular conditions since there exist no
mechanism to reset them from flux.

One idea was to turn them into actions, but the lost + start hooks need
to be called multiple times per trigger, e.g. `initctl reload`, which
turned out to be non-trivial to implement right now.

Therefore, for (at least) the Finit v3.1 release these conditions have
been disabled ("nop") and ignored by Finit.  Only actual C-style plugins
will be called.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson 8ef216af62 Let hook conditions be oneshot, prevents flux on reload
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson a9b9631098 Minor, reduce debug noise from plugin recursion
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson cc186334e0 Add support for one-shot conditions, like most hooks
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-14 11:24:29 +01:00
Joachim Nilsson e1b87d707f Support touching custom PID files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 22:03:17 +01:00
Joachim Nilsson 68e7bcba4c Refactor and document pid_file_parse()
This patch cleans up the implmentation, adds documentation and also
makes sure to adjust the prefix path of a pid:/prefix/path argument.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:52:06 +01:00
Joachim Nilsson f9783b4b41 pidfile: Use realpath() to figure out of _PATH_VARRUN is a symlink
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:49:18 +01:00
Joachim Nilsson 32d9afc7bb Use pid_runpath() in svc_find_by_pidfile(), used by pidfile plugin
The pidfile plugin just listens to changes in files in /run or /var/run,
so when trying to locate an svc_t from a file change we need to compare
using the proper prefix path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:47:11 +01:00
Joachim Nilsson 91b550662e New function, pid_runpath(), figures out /var/run or /run prefix
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-13 21:42:13 +01:00
Joachim Nilsson d87af8270e docs/config.md: Document new pid:/optional/file.pid argument
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-12 21:09:55 +01:00
Joachim Nilsson 366c23f5d1 TODO: Remember to handle PID files in subdirectories to /run
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-12 21:07:15 +01:00
Joachim Nilsson 0d4bc69584 pidfile: Use new svc_find_by_pidfile() in plugin
A service may now have a custom PID file, possibly because it doesn't
really create one itself.  This needs to be taken into account also in
the pidfile plugin.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 10:50:19 +01:00
Joachim Nilsson cccf199280 New function, svc_find_by_pidfile()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 10:49:58 +01:00
Joachim Nilsson a1c33dec21 Refactor, move all PID file related functions to the same module
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 10:48:17 +01:00
Joachim Nilsson df303c6243 Nota bene, changing name of PID file of running process
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-11 09:05:24 +01:00
Joachim Nilsson 6a6ae6fa3b Fix #95: Create PID file if program does not
This patch adds support for creating, and removing, a PID file for
programs (services) that for some reason does not create one.  With
Finit this is very useful since it allows us to use our condition
support even for "broken" daemons.

To enable this for a service, use the following syntax:

    service pid[:[/path/to/]file[.pid]] /path/to/daemon

I.e., `pid` is a required argument if you want to enable the feature,
but the rest is optional:

    pid             --> /run/`basename $CMD`.pid
    pid:file        --> /run/file.pid
    pid:file.dip    --> /run/file.dip
    pid:/mnt/k      --> /mnt/k.pid
    pid:/mnt/k.foo  --> /mnt/k.foo

There is an additional feature with this patch.  For daemons that run
with a different identiy one usually wants to create the PID file with
that identity, e.g.:

    service inadyn -I dyn     -- Dyn.com DDNS client
    service inadyn -I freedns -- FreeDNS DDNS client

Here inadyn will look for /etc/dyn.conf and create /run/dyn.pid, and
/etc/freedns.conf and create /run/freedns.pid, respetively.  While Finit
will in both cases assume the PID file is /run/inadyn.pid ... err fail!

To get out of this mess the `pid` argument can be used to tell Finit
what the real PID file the daemon creates is called:

    service pid:!dyn     inadyn -I dyn     -- Dyn.com DDNS client
    service pid:!freedns inadyn -I freedns -- FreeDNS DDNS client

The same composition rules listed above applies also to the `!` variant.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 23:27:05 +01:00
Joachim Nilsson 4011b5a3b8 Relocate all docs from doc/* to docs/*, integrates better w/ GitHub
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 02:28:33 +01:00
Joachim Nilsson 68ef6ec108 Fix v3.1 regression, segfault when inetd service connection exits
The service_step() function may call svc_del() on an svc from the
service_monitor(), so we need a way to tell the latter that it no
longer can dereference the pointer.

This regression is limited to v3.1, due to the introduction of the
new non-shared-memory store of svc_t.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 02:10:14 +01:00
Joachim Nilsson 89a9d92d6c Refactor construction of inetd service connection description
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 01:41:14 +01:00
Joachim Nilsson 15cb576293 Fix v3.1 regression in start of inetd services, missing rlimit
Per service rlimit is new in v3.1.  When an inetd service connection is
created we do a selective copy from the parent inetd.  We also need to
copy the parent's rlimits, otherwise the child will be killed as soon
as the kernel starts it.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-10 01:36:24 +01:00
Joachim Nilsson 90734eee16 reboot: Reboot via Finit + watchdogd may take a while
This patch increases the default wait, 2 --> 5 sec, while pending for
Finit + watchdogd controlled reboot/shutdown.  We don't want to hand
back the prompt when we're not really ready yet.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:20:08 +01:00
Joachim Nilsson 71915e0e14 Make sure log_exit() restores progress output on shutdown/reboot
This patch builds on top of the recently introduced "silent by default"
and support for kernel cmdline `splash` to disable silent operation.

At shutdown/reboot `log_exit()` is called, the intention is to re-enable
progress output so the user can see what's going on.  So, we need to re-
set `silent` to the same setting as log_init() started with, hence the
new `muffler()`.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:16:58 +01:00
Joachim Nilsson 112ab2c22f initctl: Refactor, use struct command rather than command_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:15:55 +01:00
Joachim Nilsson 8f00ac2e90 Do *not* redirect stdout/stderr of services by default
Automatic redirection of output was introduced in v3.0 and for the
upcoming v3.1 a configure option was added to disable it.

By popular demand this patch flips the default.  One now has to
explicitly enable this feature to get redirection.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2018-01-04 11:12:59 +01:00
Joachim Nilsson 6fb070c75c TODO: Update and remove recently fixed items \o/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:31:02 +01:00
Joachim Nilsson 8fd431f3e9 initctl: Show uptime of running services in show <NAME> output
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:30:08 +01:00
Joachim Nilsson 50f128cd1d Track service start time using new jiffies() function
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:29:43 +01:00
Joachim Nilsson f6fa576270 New functions: jiffies() and uptime(), implemented with sysinfo()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 18:28:29 +01:00
Joachim Nilsson b26d30757d Document new initctl status <JOB|NAME> command
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 17:11:15 +01:00
Joachim Nilsson dc9d0b6fca Add service wrapper script, sometimes more convenient to use
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 17:08:35 +01:00
Joachim Nilsson ff95ca92b1 Update cron syntax proposal: @hourly, @daily, @weekly, @reboot, etc.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 16:52:09 +01:00
Joachim Nilsson c939aed87f Add net/IFNAME/running to list of supported conditions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 15:01:11 +01:00
Joachim Nilsson 71ea9e2ea3 Support for restarting initctl API socket on SIGHUP
This patch adds support for restarting the API socket on SIGHUP, similar
to how SysV init and systemd use SIGUSR1 to restart their FIFO or D-Bus.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 13:28:27 +01:00
Joachim Nilsson b6bd10d960 Follow-up to c1344e8, never shutdown() API socket, or segfault
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 13:27:25 +01:00
Joachim Nilsson 87d8157175 fixup! netlink.so: Add support for IFF_RUNNING => net/IFNAME/running
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 13:26:58 +01:00
Joachim Nilsson cf6faa9eeb netlink.so: Refactor, break out condition set/clear func.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 12:41:27 +01:00
Joachim Nilsson 80a71aff07 netlink.so. Refactor (simplify) and fix problem with loss of events
An interface may come up with IFF_RUNNING already set, but we would
only detect IFF_UP.  This patch simplfies the code a lot by pivoting
the qualifier from using ifi_change to ifi_flags, which is the actual
current status of an interface.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 12:25:50 +01:00
Joachim Nilsson 04a4653e9b netlink.so: Add support for IFF_RUNNING => net/IFNAME/running
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-31 12:07:47 +01:00
Joachim Nilsson 3aa9d9f451 README: Document how to re-enable progress with configure or splash
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 22:10:10 +01:00
Joachim Nilsson c1afca9c33 README: Clarify that runparts runs *before* networking is up
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 22:01:56 +01:00
Joachim Nilsson 205915d42f Document new rescue (recovery) mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:58:26 +01:00
Joachim Nilsson 631f89b58c conf_parse_cmdline(): Relax check for 'debug' flag
Relax check for 'debug' flag, we want to catch '--debug' and 'finit_debug'

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:57:36 +01:00
Joachim Nilsson 6d1c245985 Update documentation, debug replaces --debug and finit_debug
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:56:24 +01:00
Joachim Nilsson fc08b0c65b README: Clarify runlevel section a bit, new 'single' user mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:46:03 +01:00
Joachim Nilsson 83021b11d8 No need to take down network if it's already down (at boot)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:33:45 +01:00
Joachim Nilsson 5309ece2f9 Add support for splash progress mode from kernel cmdline
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:28:30 +01:00
Joachim Nilsson 6480850ee2 Enforce *no networking* in single-user mode, runlevel 1
This is a quite intrusive change.  In addition to the already reserved
runlevels 0 and 6, halt and reboot, respectively, this patch reserves
runlevel 1 as *no networking*, like the traditional SysV init did.  One
of the reasons for this change is the new 'single' user mode, introduced
earlier.  Most users when entering 'S' or 'single' on the kernel cmdline
expect the single user mode (runlevel 1) to be without networking.

This leaves 2, 3, 4, 5, and 7, 8, 9 as fully user-configurable runlevels
with networking.

Of course, networking can still be enabled in runlevel 1 using ifconfig
or ifupdown, or change runlevel.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson eaa82377c4 Add support for single (S) user mode from kernel cmdline
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 5b3df3654c conf_parse_cmdline(): Use strict string comparison
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 55cb50ef90 In rescue mode, always set hostname from /lib/finit/rescue.conf
If hostname is missing from /lib/finit/rescue.conf the system will
default to the configured default hostname (noname).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 3f7e03e813 Refactor, re-order a bit so screen_init() doesn't run in rescue mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson ab723d2f31 Add support for a rescue (recovery) mode from kernel cmdline
This patch adds a rescue (revovery) mode to Finit.  Simply add `rescue`
to the kernel cmdline at boot and the following steps at bootstrap will
be skipped:

- Load of services/run/task/etc in /etc/finit.conf + /etc/finit.d/*.conf
- fsck of filesystems in /etc/fstab
- Remount of / to read-write
- Mount of all filesystems in /etc/fstab
- swapon
- A few plugin hooks:
  - HOOK_ROOTFS_UP
  - HOOK_MOUNT_ERROR
  - HOOK_BASEFS_UP
- `runparts DIR`
- /etc/rc.local

Instead of loading /etc/finit.conf and /etc/finit.d/*.conf Finit loads
the file /lib/finit/rescue.conf, which can be overridden by the operator
if needed.  If this file is removed (or lost), Finit falls back to start
a simple root shell, without any login.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson bc149d95fc conf: Add hostname alias to host
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson c605874b26 Remove --with-plugindir=DIR, use --prefix or --libdir instead
The /lib/finit/plugins path is as of this patch not longer possible to
change.  Use --prefix or --libdir instead to change the leading path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson c33ba6a477 Remove reboot symlinks properly on uninstall
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson 63199d368f conf: lim2str(): Fix buffer overflow in local debug function
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:20:06 +01:00
Joachim Nilsson db3263c441 Refactor conf_parse_cmdline(), support 'debug' as well as '--debug'
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 21:19:55 +01:00
Joachim Nilsson 925212f7cf Merge pull request #94 from joerg-krause/patch-1
doc/config.md: Add entry about using wrapper scripts to execute pre commands
2017-12-30 21:03:03 +01:00
Jörg Krause ad401280c2 doc/config.md: Add entry about using wrapper scripts to execute pre commands
See: https://github.com/troglobit/finit/issues/93
2017-12-30 17:55:12 +01:00
Joachim Nilsson f3d2586417 Fix build warning on musl libc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 13:12:05 +01:00
Joachim Nilsson 6fc1d27bcf conf: Check if a kernel module has been loaded first
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 13:09:34 +01:00
Joachim Nilsson f3305e75b7 conf_reload(): Exit properly, even if no /etc/finit.d/*.conf exitsts
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-30 12:47:27 +01:00
Joachim Nilsson e4c12314b1 doc/config.md: Mention /etc/finit.d/available/ and initctl enable
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 21:00:46 +01:00
Joachim Nilsson c9a1a64ce5 doc/config.md: Update with changes to finit.conf and stanza limits
Finit v3.1 adds support for reloading /etc/finit.conf, document what
actually is read at runtime, compared to at bootstrap, and clarify
which stanzas are for what part of the boot process.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:52:43 +01:00
Joachim Nilsson 3843f62aa8 README.md: Update with difference between runparts & /etc/rc.local
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:51:42 +01:00
Joachim Nilsson ef54c4826b bootstrap.md: runparts goes before the runlevel change
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:51:14 +01:00
Joachim Nilsson 2fac65becb Fix long-standing bug, runparts() should run before runlevel change
The difference between `runparts DIR` and `/etc/rc.local` is that one
should run just before the first runlevel change (to the configured
runlevel) at the end of bootstrap, whereas the other should run just
before launcing the TTYs, i.e. after all tasks in the configured
runlevel have been started.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:48:47 +01:00
Joachim Nilsson 46b253dea6 doc: The module configuration stanza supports optional args
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:15:29 +01:00
Joachim Nilsson b04983c3f3 conf: Make rlimit [hard|soft] optional => possible to set both
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 20:15:12 +01:00
Joachim Nilsson f2d142ef57 Only reload .conf and services if any .conf file has changed
This patch is an optimization.  Now that we monitor for any .conf file
changes (as soon as the event loop starts) we can skip automatic reload
of all .conf files and services if no .conf file has changed.  Unless
the user issues an `initctl reload`, `init q`, or sends us SIGHUP.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 19:22:01 +01:00
Joachim Nilsson 33ce746e58 parse_conf(): Increase log with actual limits in case of problems.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 16:57:36 +01:00
Joachim Nilsson 6ecb86b4a7 conf_reload(): No need to reload global rlimits again
This patch removes duplicate reload of global_rlimit[], which
parse_conf() already has done.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 16:56:30 +01:00
Joachim Nilsson 338810b886 Fix #92: Load .conf files and initialize global_rlimit[] earlier
As reported in #92, systems with an udevd very early register a service
using global_rlimit[].  The change was introduced in b68c5c1 but did
not take the ordering problem into consideration, global_rlimit[] was
left uninitialized, which likely led to severely limited udevd that
was continously crashing.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-29 16:51:49 +01:00
Joachim Nilsson c1344e89ff api_exit(): Only shutdown() of API socket at runtime
Not needed at shutdown/reboot.  Also, it may segfault at that stage.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:48:44 +01:00
Joachim Nilsson bc35b42bd2 inet_stop(): Check argument and skip shutdown(), just close() socket
- Validate function argument
- Skip¹ shutdown() when closing a TCP socket, not needed

___
¹ also, shutdown() segfaults a late shutdown/reboot

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:46:19 +01:00
Joachim Nilsson dcf8fbe235 do_shutdown(): Advise watchdog using SIGPWR, system is going down
On shutdown/reboot, send SIGPR as a heads-up to watchdogd that the
system is going down.  All file systems are about to be unmounted.
It's just about the last process left running in the system, so we
give it two seconds to complete its work before we continue.

When everything has been unmounted and we're ready to call reboot(),
we send SIGTERM to the registered watchdog, to give ti the chance to
let the WDT reset the system.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:45:02 +01:00
Joachim Nilsson c82a2ce215 Built-in watchdog: on SIGPWR, exit main loop and prepare for SIGTERM
When Finit enters shutdown/reboot we need to advise any watchdogd that
we are going down *before* we unmount any file systems.  This to let
the user save any state, e.g. reboot counters, to disk.  Right before
we call reboot() in Finit we send SIGTERM to the watchdogd to give it
the chance to use the WDT to reboot the system.

The WDT cricuitry on embedded systems is often connected to the RESETn
logic of all relevant board compoents, whereas reboot() only reboots the
SoC with the CPU.  Hence, a WDT reboot is more efficient and more like a
regular power cycle, which users of embedded systems often want.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:24:32 +01:00
Joachim Nilsson e1bb5e3396 Add --disable-redirect-output to configure script
This patch makes it possible to control the default redirection of
(misbehaving) services output to /dev/null.  With this disabled,
a service may write to its stdout/stderr and mess up the console.

By default all service/run/tasks stdout/stderr is redirected.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-28 20:22:06 +01:00
Joachim Nilsson 8198582c10 Revert "Travis-CI: Disable clang temporarily, for Coverity Scan run"
This reverts commit cbafbaea1d.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 11:01:24 +01:00
Joachim Nilsson 0d19842aa1 conf_changed(): Fix possible NULL ptr deref, found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:35:07 +01:00
Joachim Nilsson 6f665ab283 inet_dgram_drop(): Fix control flow logic, found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:31:19 +01:00
Joachim Nilsson 3f731aa5a3 inet_dgram_drop(): Check return value from recv()
Found by Coverity Scan.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:28:22 +01:00
Joachim Nilsson 9e8a01669e api: Fix possible descriptor leak, found by Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 10:02:49 +01:00
Joachim Nilsson cbafbaea1d Travis-CI: Disable clang temporarily, for Coverity Scan run
... so we don't lose tokens ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 09:53:14 +01:00
Joachim Nilsson f534368b33 The @console and nologin flags deserve mentioning early
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 09:51:16 +01:00
Joachim Nilsson 78fc9fccc5 Update initctl usage text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 09:51:04 +01:00
Joachim Nilsson 3f7df3d35e Update ChangeLog with new nologin option to tty configs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 03:29:38 +01:00
Joachim Nilsson 9fc33418f9 Document nologin option flag to tty configuration directive
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 03:26:31 +01:00
Joachim Nilsson 72bc3b1a91 Fix #91: Add support for tty nologin, to start /bin/sh immediately
This patch adds a `nologin` flag to the `tty` configuration directive,
making it possible to set up really simple embedded systems with no
login.

Example, put the following line in `/etc/finit.d/getty.conf` to get
a "Please press Enter to ..." prompt before being presented with a
root shell prompt.

    tty [12345] @console noclear nologin

Needless to say, this is not very secure, but can be really useful
for developer setups, during board bringup, or similar.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-24 01:54:07 +01:00
Joachim Nilsson d6ea1c0cab Protect internal services like dbus-daemon and udevd
In f3669c7 the difference between static and dynamic services was
removed.  This led to a regression in handling internally created
services for, e.g., dbus-daemon and udevd, mentioned in issue #90.

This patch introduces a "protected" flag for svc_t to prevent the
mark-and-sweep handling of regularly loaded services.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 21:57:31 +01:00
Joachim Nilsson 523a270f22 Update README slightly, there's systemd-udevd and eudev these days
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 19:33:27 +01:00
Joachim Nilsson 3258edc246 Fix #90: Restore tmpfs mount of /dev/shm, removed in d7401b2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 19:02:06 +01:00
Joachim Nilsson 32b7a14cb3 Add ismnt() helper function, wrap fismnt()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 19:00:41 +01:00
Joachim Nilsson 55479cc511 Bump version for first -rc, v3.1-rc1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 11:52:40 +01:00
Joachim Nilsson b702f7eeef Update ChangeLog for upcoming v3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 11:52:40 +01:00
Joachim Nilsson 0e37bb0924 Follow-up to b0663d0, cond_init() must run before first hooks
The new condition triggered hooks requrie /var/run/finit/cond to
be set up before calling the first user-configurable hooks.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 11:19:35 +01:00
Joachim Nilsson 360f3e72bc Audit b0663d0, add newline to generation ID in cond files
- Add newline to cond generation ID, like PID files, easier when
  debugging.  Does not affect fscanf() in cond_get_gen()
- Update copyright years

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 10:05:45 +01:00
Joachim Nilsson c1023c03c7 Merge pull request #89 from westermo/cond-generation
Cond generation
2017-12-23 09:37:16 +01:00
Joachim Nilsson e94d2a8c08 Merge branch 'master' into cond-generation 2017-12-23 09:31:41 +01:00
Joachim Nilsson f7c0366fa2 Fix segfault on X86_64, call va_end()+va_start() for each vfprintf()
... varargs is a special kind of hell.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-23 00:38:01 +01:00
Joachim Nilsson 8992e7bf2b Refactor svc_iterator() into a proper iterator, add first flag
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 23:56:14 +01:00
Joachim Nilsson 95518df62d Some systems rely on /dev/shm being there for mount -a
This was accidentally removed in d7401b2, when svc_t was refactored
from using shared memory.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 16:44:52 +01:00
Joachim Nilsson 85fe95835a doc/build.md: Mention required library versions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 13:17:39 +01:00
Joachim Nilsson baf9c4f61f Travis-CI: Update build deps, libite v1.9.2 --> v2.0.1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 13:12:04 +01:00
Joachim Nilsson b439ac670d .conf: don't rely on mtime to determine if service needs reloading
Until now Finit has used mtime to determine if a file has been changed,
or simply touched to request reload, when `initctl reload` is called.
Using mtime for this purpose is a monumentally bad idea since time can
be changed at any point: a user may adjust the time, NTP continously
tunes the clock, and time stamps are stored as the walltime.  So if we
compare timestamps against the last time we (booted or) did reload, we
would miss .conf file changes.

This patch replaces the mtime mistake with an inotify watcher for the
following files: /etc/finit.d/*.conf, /etc/finit.d/available/*.conf,
and /etc/finit.conf.  All file changes between (bootstraps and) calls
to `initctl reload` are tracked, like the mtime backend it replaces.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 13:08:38 +01:00
Joachim Nilsson bb3d9c8670 initctl: Add support for touch <CONF>, mark .conf for reload
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 12:50:18 +01:00
Joachim Nilsson 2f9b97b3a7 Update libite (-lite) requried version: 1.9.2 --> 2.0.1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 12:49:04 +01:00
Joachim Nilsson c59f7d23f4 Add and improve debug messages, clean out old dev. comments
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-22 12:40:43 +01:00
Tobias Waldekranz b0663d04e4 cond: don't rely on mtime for condition management
TIL, using mtimes for tracking event orderings is a monumentally bad
idea (queue the nodding UNIX-beards). Mtimes are in wallclock time which
is not necessarily monotonically increasing. A user may adjust the time,
an NTP daemon will continously tune the clock and so on.

Instead, store an explicit generation number in each condition file,
which will be monotonically increased by finit on each reconf.
2017-12-21 11:10:47 +01:00
Joachim Nilsson 6f26f2f9d0 TODO: Idea about addding support for an optional rescue mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:30:49 +01:00
Joachim Nilsson e7cca05fe3 Reserve certain configuration directives for bootstrap only
The following configuration directives, previously only allowed
in /etc/finit.conf, are now also only allowed in runlevel S, i.e.
at bootstrap:

- host
- mknod
- network
- runparts
- runlevel

This change is in preparation for allowing reload of finit.conf
as well as /etc/finit.d/*.conf, which we already support.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:16:55 +01:00
Joachim Nilsson f3669c7537 svc: Remove svc_dynamic_iterator(), soon no difference between svc's
This is a prelude to a major behavioral change.  The difference between
a static and dynamic service will be removed in Finit v3.1.  This means
all .conf files, including /etc/finit.conf, will be re-read on reload.

However, not all configuration directives will be re-read, some will
only be applicable at bootstrap, i.e. runlevel S.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:04:14 +01:00
Joachim Nilsson a9456cec41 svc: Remove unused function, svc_foreach_dynamic()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 09:02:38 +01:00
Joachim Nilsson 61a9926442 Minor, staticify
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 00:13:21 +01:00
Joachim Nilsson 7784480abe Postpone networking stuff, + hooks, a bit to the finalize() step
This patch moves the networking bit to the latter part of bootstrap,
called finalize(), in favor of starting the event loop earlier.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-20 00:10:29 +01:00
Joachim Nilsson db50c09ec5 initctl: Improve show SVC command output a bit, provide summary
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 21:04:17 +01:00
Joachim Nilsson 0f1f51b5ad Refactor, change from static array of svc_t to linked list
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 17:50:02 +01:00
Joachim Nilsson d7401b2a96 Follow-up to issue #81: remove SysV shared memory IPC altogether
This change removes a long standing kernel requirement and architectural
mishap in the design of Finit v1.  It is no longer necessary to include
CONFIG_SYSVIPC in the kernel to use Finit and its command line tool
initctl.  It is also no longer possible to connect to the internals of
PID 1 using shmat(), the initctl tool has already been updated to use
the domain socket API.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 11:20:21 +01:00
Joachim Nilsson c0515d112e New client side API to access svc_t entries without shared memory
This change is one of the required intermediate steps to remove the
shared memory store for all svc_t, this in turn to avoid any external
programs manipulating the internal memory of PID 1.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 11:06:19 +01:00
Joachim Nilsson 9a8b55737f Move svc_status() + svc_dirtystr() to header file as static inline
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 11:03:15 +01:00
Joachim Nilsson d7d8b8487d Refactor other iterators to use new re-entrant svc_iterator1() API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 07:49:45 +01:00
Joachim Nilsson 7f04382eee Refactor, use new re-entrant svc_iterator1() API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 00:21:07 +01:00
Joachim Nilsson bf004d9ec4 svc: Re-entrant replacement to svc_iterator() --> svc_iterator1()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 00:18:13 +01:00
Joachim Nilsson 23e0928e5c Minor, unused arguments no longer need magic tricks or hand waving
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-18 00:17:00 +01:00
Joachim Nilsson d6b2ac94dd Fix regression in 32b9096, only pick next :ID for inetd services
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 14:54:46 +01:00
Joachim Nilsson 7362e6d00a initctl: Fix error handling for intictl start/stop if missing arg
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 12:12:57 +01:00
Joachim Nilsson d94d11e00a initctl: Allow short forms of commands
For ease of use, allow short forms of commands, like:

   initctl h
   initctl c d
   initctl l

Also, default to show conditions if no argument to cond is given.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 12:09:41 +01:00
Joachim Nilsson f06a0fcb71 initctl: Support printing previous runlevel
This patch adds support for "PREVLEVEL RUNLEVEL" output to the initctl
runlevel command.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-17 10:54:06 +01:00
Joachim Nilsson cae61ad6df initctl refactor, break out domain socket/client API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-16 12:15:28 +01:00
Joachim Nilsson 27e4af5ed8 Rename, client.c --> telinit.c
We need the name client.c for the client side of api.c, so let's rename
the client to its traditional name.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-15 14:00:11 +01:00
Joachim Nilsson f10a724503 Add mention of dropping UDP connections from blocked interfaces
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-14 17:23:54 +01:00
Joachim Nilsson 33037b012a Merge pull request #88 from ccb3/master
Drop UDP packets from unwanted interfaces
2017-12-14 10:44:29 +01:00
Lennart Eriksson 0c75e59b77 Drop UDP packets from unwanted interfaces
Previously UDP packets were never dropped and if a request came
from a not valid interface inetd got stuck in an inifinite
loop looking at the same packet every time and not dropping it.

Signed-off-by: Lennart Eriksson <lennart.eriksson@westermo.se>
2017-12-14 10:34:07 +01:00
Joachim Nilsson 8e55a76732 Initial draft of v3.1 release notes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-14 09:13:13 +01:00
Joachim Nilsson 32b9096e31 Fix #87: Allow inetd services to be registered with an id != 1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 22:20:30 +01:00
Joachim Nilsson f51723e8b7 Fix #86: Ensure that run/tasks are run at least once per runlevel
This patch fixes the problem with run/task jobs with conditions, run at
runlevel S (bootstrap), not being given enough time to actually start
and run even once.

The new svc counter 'once' is now inspected before transitioning away
from runlevel S to the configured runlevel.  A global (currently hard
coded) timeout of 10 sec is given to all run/tasks.

As a spin-off, this patch starts up the event loop much earlier than
before.  Meaning the initial runlevel change at boot is now done in
a finalize() callback when all run/tasks have completed, or when the
global timeout has expired.  Starting the event loop earlier gives us
the possibility to rely on the pidfile plugin to provide our run/tasks
with the conditions necessary to start.

TODO: What remains is to add similar handling to all runlevel
      transitions, not just the initial one at boot.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 9e31bd8a23 Fix #18: Add support for calling run/tasks on HOOK points
This patch implements support for calling run/task jobs on the built-in
`HOOK_*` points in Finit.  To map each HOOK point to a condition a new
classification was made, as shown in plugin.h and in the example below.

task <hook/mount/root> /sbin/attachubi.sh      # run in parallel with other tasks/services
run  <hook/net/up>     /sbin/dosomething.sh    # run in sequence with other commands

Please note, only rudimentary tested.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson e841984a4e service_step(): Fix problem with double start of service/run/tasks
Make svc state transition to RUNNING *before* service_start(), because
in that function we may call service_step() as well, which would start
the svc again (since it was still in READY).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 594476c4fc Fix progress print-out ordering bug, and run HOOK_SVC_START only on success
Only run HOOK_SVC_START on successful start, and *after* having printed
the result of the started service, otherwise any print-outs from plugins
or tasks we start may overwrite the started service's progress.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 9c423dfca4 Disable toggling of log/debug in fsck() loop
Don't really see the point of this code anymore, was possibly an issue
before we refactored the logging subsystem.  Comment out for now, and
remove later.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 77cd0f0243 Fix problem with duplicate log messages before syslogd has started
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 5dc4e7c058 cond_update(): Minor, log service description as well as cmd
If you have lots of service/run/tasks using the same cmd it's impossible
to tell them apart in debug mode.  This patch logs description as well
as cmd to improve on this situation a bit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 3c015f384d Track run/tasks, should run at least once per declared runlevel
This patch adds a 'once' counter to the svc_t for run/task jobs.  This
counter is incremented when Finit calls service_start() and is reset on
runlevel changes.

Also, at the end of a runlevel each run/task have their state reset to
SVC_HALTED_STATE so they can be launched again for the new runlevel, if
so declared.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 5117b723c2 Minor, fix missing rlim2str() declaration
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson b55a609834 Handle /etc overlayfs: reload /etc/finit.d/*.conf after mount -a
Systems running OverlayFS, or similar, on /etc may have new/modified
.conf files in /etc/finit.d.  This patch makes sure to reload them
after `mount -a` has run so any configured bootstrap tasks and the
new hook callback scripts may run as expected.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 60eccbac31 Refactor, relocate+rename service_enabled() --> svc_enabled()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson b182e6bcc5 Minor update and refactor, use kernel doc style comments
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson b8bf26b107 Refactor, rename restart counter and change type to char, MAX=10
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-12-13 17:58:48 +01:00
Joachim Nilsson 14c0d80f63 Merge pull request #85 from reverendhomer/doc-fix-typo
doc: fix couple of typos in config.md
2017-12-01 18:45:20 +01:00
Reverend Homer 1f55f677d1 doc: fix couple of typos in config.md 2017-12-01 20:07:58 +03:00
Joachim Nilsson 40267a5f0f Minor, comment fixes and additions
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-28 08:07:06 +01:00
Joachim Nilsson 9f50b033d9 Abort service kill if already terminated, otherwise kill(0, SIGKILL)
This patch fixes a rather nasty bug in the SIGTERM+SIGKILL logic
employed by Finit when stopping tasks, e.g. at a runlevel change.

When a process terminates, after Finit having sent it SIGTERM, and the
SIGKILL timer expires *before* the timer has been cancelled, then the
timer callback would cause kill(0, SIGKILL).

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-28 08:00:46 +01:00
Joachim Nilsson bbaa7a1403 TODO: Process callbacks replaced with conditions in Finit 3.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-27 22:37:38 +01:00
Joachim Nilsson b68c5c1291 Fix #45: add support for rlimit per service/run/task/inetd/tty
This patch extends the existing rlimit implementation to support setting
limits per service, run/task, inetd, and tty.

Use rlimit in /etc/finit.conf to change the global setting, which is
then inherited to each /etc/finit.d/*.conf.  For each .conf file the
rlimit is reinitialized to the global finit.conf settings.

Also, add `unlimited` keyword, to replace the now deprecated `infinity`
keyword.  The latter is however kept, for compatibility with previous
releases, for the foreseeable future.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-26 16:35:37 +01:00
Joachim Nilsson 44007c86f2 initctl: Fix problem with start/stop using job ID
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-24 08:51:50 +01:00
Joachim Nilsson cd73c1c951 Replace libite:fmode() with stat(), removed in libite v2
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-22 14:40:02 +01:00
Joachim Nilsson 5406af2054 watchdog: Adjust WDT timeout (30 sec), use half that as kick period
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:51:33 +01:00
Joachim Nilsson 3c800f6508 Refactor, simplify registration of built-in watchdog daemon
This patch not only greatly simplifies the registration of the built-in
watchdog daemon, it also fixes a nasty race condition with an external
watchdog daemon.  The INIT_CMD_WDOG_HELLO could reach finit in reversed
order, leading to the built-in daemon shutting down, but finit killing
the just registered external daemon.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:48:32 +01:00
Joachim Nilsson e0fb2b036d Improve debug of initctl api
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:45:07 +01:00
Joachim Nilsson 0ee4657f1c Follow-up to 6c563a2, clean up unused variables
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 16:39:27 +01:00
Joachim Nilsson ec2382b6d4 Silence, remove "Starting ..." of inetd services from boot progress
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:30:04 +01:00
Joachim Nilsson 0fc2df1a65 Silence, remove "Loading %sconfiguration ..." from boot progress
Most Linux distributions, embedded or otherwise, do not need this
and only confuses the user of an OS with Finit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:29:31 +01:00
Joachim Nilsson 6c563a2b22 Silence, remove "Loading plugins ..." from boot progress
Most Linux distributions, embedded or otherwise, do not need this
and only confuses the user of an OS with Finit.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:27:57 +01:00
Joachim Nilsson 6a2911223d Fix screen_init() regression introduced in a6290da
A simple missing semicolon caused initscr() to not be called properly.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-19 12:26:45 +01:00
Joachim Nilsson 7f499ed1d0 watchdog: Kick one last time before handover to external watchdogd
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-18 14:48:46 +01:00
Joachim Nilsson 2d2807bc65 Change log message for crashing services, remove dev. debug info
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-18 14:48:08 +01:00
Joachim Nilsson 4af7974fb9 Travis-CI: Install new required version of libuEv, v2.1.0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:26:12 +01:00
Joachim Nilsson 3fd45eac7c bootmisc: Handle _PATH_VARDB inconsistency gracefully
Some systems use /var/db while others use /var/lib/misc, we need to
support both and cannot rely on _PATH_VARDB, since it too might not
be set properly.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:24:25 +01:00
Joachim Nilsson bb4578292f Bump version for v3.1 release cycle
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:01:02 +01:00
Joachim Nilsson 656fa3d0e7 Bump required libuEv version to v2.1.0, fixed failure mode support
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:00:36 +01:00
Joachim Nilsson 9a75813350 api.c: Handle domain socket errors more gracefully, restart socket
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 15:00:05 +01:00
Joachim Nilsson 5af1f05606 plugin: Only close() PID file descriptors >0, skip -1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-14 14:34:08 +01:00
Joachim Nilsson fcdb5c2965 initctl: Refactor, reduce code duplication
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 22:58:50 +01:00
Joachim Nilsson af3e560d79 tty.c: Refactor, move LIST_REMOVE() to tty_unregister()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 21:33:22 +01:00
Joachim Nilsson b6e695b656 Bump required libuEv version to v2.0.0, with failure mode support
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 19:40:54 +01:00
Joachim Nilsson 123df18847 Travis-CI: Bump required libuEv version to v2.0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 19:40:28 +01:00
Joachim Nilsson dae21d9d64 Simplify rlimit documentation a bit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 19:18:32 +01:00
Joachim Nilsson bf4a0abe5c conf_parse_rlimit(): Refactor, replace 'new' with 'cfg'
'new' is a reserved keyword in C++, it is recommnded to avoid it also in C.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 14:57:22 +01:00
Joachim Nilsson 6826d7ee3f Handle event loop watcher failure modes
When alibuEv event watcher callback has `UEV_ERROR` in the events mask
we must handle the error.  Unfortunately, in most cases there is not
much we can do but ignore it or log the error.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 11:18:12 +01:00
Joachim Nilsson 168da2a5c6 plugin: Update list of available events for I/O plugins
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 02:47:33 +01:00
Joachim Nilsson c82dc54205 initctl: Minor refactor, handle errors first
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-12 02:46:19 +01:00
Joachim Nilsson eb0a759371 TTY: If parsing speed argument fails, do NOT default to B38400
If the speed argument to the `tty` stanza is invalid we should not
default to some other random speed.  Instead, leave the line speed
as-is, like we do when starting an external getty.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-02 10:48:04 +01:00
Joachim Nilsson 830abc1971 getty: Remove leading \n in do_issue(), not needed
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-31 20:57:36 +01:00
Joachim Nilsson 4c3a11131f Minor cleanup + use strlen() rather than sizeof() on const strings
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-31 20:56:48 +01:00
Joachim Nilsson 796701c854 getty: Ignore SIGINT & C:o earlier
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-31 20:54:56 +01:00
Joachim Nilsson ab760aa3ff Mention @console TTYs in top-level README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 17:21:21 +01:00
Joachim Nilsson 2b9eecbef3 Spell check and mention use of logger tool in service stanza
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 17:17:21 +01:00
Joachim Nilsson b8190c8a2a Refactor, move speed parser from getty() to prepare_tty()
This patch relocates the speed parser so that exec.c can set the TTY
speed properly as soon as possible.  For external getty we make sure
to not touch the TTY speed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 16:53:26 +01:00
Joachim Nilsson adb4670d2f Clear current line before printing \nPlease press Enter ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 16:52:14 +01:00
Joachim Nilsson d31575538c getty: Disable VINTR + VEOF, re-enable before calling /bin/login
This patch makes the Finit built-in getty behave a bit more like other
getty implementations.  We now not only disable ISIG but also VEOF and
VINTR when prearing the TTY for the getty (built-in or external).

Like before, it is up to the getty process to leave the TTY in a
usable state to /bin/login, so we also move the stty() call to
just before calling /bin/login in getty.c

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 16:11:59 +01:00
Joachim Nilsson faf0c680b8 getty: Disable ECHO, XON/OFF while waiting for initial <CR>
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 12:14:04 +01:00
Joachim Nilsson 250e26f2f6 Fix #84: Reset TTY w/ stty() before taking any pre-getty action
This patch moves the stty() function from getty.c to a separate file so
it can be called from exec.c as well.  The fix to #84 is simply to call
stty() in prepare_tty(), which is shared by run_getty() and run_getty2()
and called before "Please press Enter ...".

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-29 11:59:48 +01:00
Joachim Nilsson b45c3d5cb8 timespec_newer(): time a should be > or >= to b
Time comparision should be >= not just >.  May seem a bit silly to
nitpick like this since we're comparing nano seconds, but on systems
with no high-res timers this happens more often than you would like.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 16:04:18 +02:00
Joachim Nilsson 6b2678c2ed pidfile: On HOOK_SVC_RECONF the netlink plugin must run first
When performing an `initctl reload` with one (unchanged) service
depending on, e.g. `net/iface/lo`, its condition will not be set
to ON by the pidfile plugin unless the netlink plugin hook runs
first.

Example:

    service <net/iface/lo> /sbin/dropbear ...

Which provides the <svc/sbin/dropbear> condition, will not be
set by pidfile.so during `initctl reload` because dropbear is
still SIGSTP:ed waiting for <net/iface/lo>.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 15:55:35 +02:00
Joachim Nilsson 0d6f203e0b tty_reload(): When reloading a single TTY dev, remember to clean it
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 13:48:25 +02:00
Joachim Nilsson 781ba66905 Revert "Remove reconf condition when .conf reload completes"
This reverts commit 06479aa5f1.
2017-10-24 13:07:48 +02:00
Joachim Nilsson 9a97ab29dc Revert "cond_reassert(): Use cond_set_path(), not cond_clear(), for reconf"
This reverts commit afb3fada50.
2017-10-24 13:07:15 +02:00
Joachim Nilsson b2b17ea4fb doc/config.md: Document new tty @console support in finit.conf
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:30:24 +02:00
Joachim Nilsson bfa99266e7 Minor, cleanup old unused function prototype
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:30:04 +02:00
Joachim Nilsson c76a58ef7a Remove long since deprecated finit.conf: console <DEV> support
All this code did was trigger prctl() to set process name to "console",
which you could use as a simple means of figuring out if a process was
started from the system console.

There are other ways to do this, which Finit should not be involved in.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:28:11 +02:00
Joachim Nilsson 51c5f3cbfa Length of the ANSI escaped runlevels string is 20 chars, need 21
Problem reported by @wkz

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:20:25 +02:00
Joachim Nilsson a6290da210 screen_init(): Verify that STDOUT is a TTY
Before we call screen_init(), which in turn calls libite:initscr(),
which in turn does a lot of tcgetattr() and termios to STDOUT to figure
out the size, we need to check that we're not backgrounded, or don't
actually have a TTY.  Problem reported by @wkz

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 19:16:51 +02:00
Joachim Nilsson 949f02eff4 tty: Add support for auto-detecting serial console from SysFS
This patch adds support for a new TTY device keyword `@console` which
auto-detects the system console by reading the SysFS file:

    /sys/class/tty/console/active

Embedded systems with one of /dev/ttyAMA0, /tty/ttyMXC0 or the original
/dev/ttyS0, as their active serial console can now instead use a single
line to /etc/finit.d/getty.conf to start the built-in getty on the same
TTY as the kernel already runs on:

    tty [12345789] @console 115200 noclear

With no TTY defined Finit will either do nothing (default), or start a
fallback shell if enabled in the configure script.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 17:24:46 +02:00
Joachim Nilsson 7970e45f45 Follow-up to cf60cdd, make sure to disable ISIG before calling getty
Disable INTR, QUIT, SUSP, and DSUSP while handing over to getty.  It is
up to the getty process to allow ISIG again.

This patch is due to the differences in control line setup between ARM,
x86/x86_64, and most notably PPC.  PPC in particular seem to have ISIG
set by default from the kernel.

Without this patch pressing Ctrl-C before getty is started will lead to
Finit receiving SIGINT and defaulting to a reboot, as introduced in the
BusyBox init signal adoption in eeb59eb.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-23 11:57:58 +02:00
Joachim Nilsson a36dafaa6c README: Update initctl usage
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-19 20:16:37 +02:00
Joachim Nilsson c092d22d48 initctl: Default to show services listing, not usage text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-19 20:01:15 +02:00
88 changed files with 4134 additions and 1547 deletions
+8 -1
View File
@@ -2,13 +2,15 @@
*.o
*.so
*.gz
ID
GPATH
GRTAGS
GSYMS
GTAGS
# GNU configure and build system
/autom4te.cache/*
/.deps
/autom4te.cache
/aclocal.m4
/ar-lib
/compile
@@ -33,3 +35,8 @@ GTAGS
/.config
/.stamp
/.unpacked
# WeOS build system
/.autoconf
/.automake
/.built
+6 -6
View File
@@ -33,12 +33,12 @@ addons:
branch_pattern: dev
install:
- wget https://github.com/troglobit/libuev/releases/download/v1.5.2/libuev-1.5.2.tar.xz
- wget https://github.com/troglobit/libite/releases/download/v1.9.2/libite-1.9.2.tar.xz
- tar xf libuev-1.5.2.tar.xz
- tar xf libite-1.9.2.tar.xz
- (cd libuev-1.5.2 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-1.9.2 && ./configure --prefix=/tmp && make && make install-strip)
- wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
- tar xf libuev-2.1.0.tar.xz
- tar xf libite-2.0.1.tar.xz
- (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip)
- (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip)
# Custom build script for the time being, no "make test" yet
script:
+5
View File
@@ -1,6 +1,11 @@
Current Maintainer(s):
* Joachim Nilsson <troglobit@gmail.com>
Major Contributors:
* Tobias Waldekranz <tobias@waldekranz.com>
* Mattias Walström <mattias.walstrom@westermo.se>
* Jonas Johansson <jonas.johansson@westermo.se>
Original Author(s):
* Claudio Matsuoka <cmatsuoka@gmail.com>
+114 -2
View File
@@ -3,6 +3,117 @@ Change Log
All relevant changes are documented in this file.
[3.1][] - 2018-01-23
--------------------
Improvements to `netlink.so` plugin, per-service `rlimit` support,
improved integration with `watchdogd`, auto-detect TTY console. Much
improved debug, rescue and logging support. Also, many fixes to both
big and small issues, most notably in the condition handling, which no
longer is sensitive to time skips.
This version requires at least libuEv v2.1.0 and libite v2.0.1
### Changes
* Support for more kernel command line settings:
- splash, enable boot progress
- debug, like `--debug` but also enable kernel debug
- single, single user mode (no network)
- rescue, new rescue mode
* Support for `IFF_RUNNING` to netlink plugin => `net/IFNAME/running`
* Support for restarting `initctl` API socket on `SIGHUP`
* Greatly updated `initctl status <JOB|NAME>` command
* Support for `rlimit` per service/run/task/inetd/tty, issue #45
* Support for setting `hard` and `soft` rlimit for a resource at once
* Support for auto-detecting serial console using Linux SysFS, the new
`tty @console` eliminates the need to keep track of different console
devices across embedded platforms: `/dev/ttyS0`, `/dev/ttyAMA0`, etc.
* Add TTY `nologin` option. Bypasses login and skips immediately to a
root shell. Useful during board bringup, in developer builds, etc.
* Support for calling run/tasks on Finit internal HOOK points, issue #18
* Removed support for long-since deprecated `console DEV` setting
* Cosmetic change to login, pressing enter at the `Press enter to ...`
prompt will now replace that line with the login issue text
* Calling `initctl` without any arguments or options now defaults to
show status of all enabled services, and run/task/inetd jobs
* Cosmetic change to boot messages, removed `Loading plugins ...`, start
of inetd services, and `Loading configuration ...`. No end user knows
what those plugins and configurations are, i.e. internal state+config
* Change kernel WDT timeout (3 --> 30 sec) for built-in watchdog daemon
* Advise watchdog dawmon on shutdown and reboot using `SIGPWR` and then
`SIGTERM`. It is recommended the daemon start a timer on the first
signal, in case the shutdown process somehow hangs.
* Handle `/etc/` OverlayFS, reload /etc/finit.d/*.conf after `mount -a`
* initctl: Add support for printing previous runlevel
* initctl: Support short forms of all commands
* initctl: Support for `initctl touch <CONF>` to be used with `reload`
* initctl: Improved output of `initctl show <SVC>`
* Support reloading `/etc/finit.conf`. The main finit.conf file
previously did not support reloading at runtime, as of v3.1 all
configuration directives supported in `/etc/finit.d/*.conf` are now
supported in `/etc/finit.conf`
* Change `.conf` dependency + reload handling. Finit no longer relies
on mtime of `.conf` files, instead an inotify handler tracks file
changes for time *insensitive* dependency tracking
* Change condition handling to not rely on mtime but a generation id.
* New configure script option `--enable-redirect` to automatically
redirect `stdout` and `stderr` of all applications to `/dev/null`
* New `pid` sub-option to services when a service does not create a PID
file, or when the PID file has another name. Issue #95
* Greatly improved `log` sub-option to service/run/tasks, selectively
redirect `stdout` and `stderr` using the new tool `logit` to either
syslog or a logfile. Issue #44
* Support for automatic log rotation of logfiles created by `log`
option. Use `configure --disable-logrotate` on systems with a
dedicated log rotation service. Issue #44
* Support for disabling service/run/task progress with empty ` --`
description. Note: no description separator gives a default desc.
* Create `/etc/mtab` symlink if missing on system (bootmisc plugin)
* New hook: `hook/mount/post` runs after `mount -a` but before the
`hook/mount/all`, where `bootmisc.so` runs. This provides the
possibility of running a second stage mount command before files in
`/run` and similar are created
* Skip `gdbserver` when unleashing the grim reaper at shutdown
* Distribute and install `docs/` and `contrib/` directories
### Fixes
* Reset TTY before restarting it. A program may manipulate the TTY in
various ways before the user logs out, Finit needs to reset the TTY to
a sane state before restarting it. Issue #84
* On .conf parse errors, do *not* default to set TTY speed 38400, reuse
current TTY speed instead
* Fix run/tasks, must be guaranteed to run once per declared runlevel.
All run/tasks on `[S]` with a condition `<...>` failed to run. Finit
now tracks run/tasks more carefully, waiting for them to finish before
switching to the configured runlevel at boot. Issue #86
* Allow inetd services to be registered with a unique ID, e.g. `:161`,
issue #87. Found by Westermo
* inetd: drop UDP packets from blocked interfaces, issue #88
* Handle obscure inter-plugin dependency issue by calling the netlink
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
* Handle event loop failure modes, issue found by Westermo
* Handle API socket errors more gracefully, restart socket
* Do not attempt to load kernel modules more than once at bootstrap
* Remove reboot symlinks properly on uninstall
* Fix regression in condition handling, reconf condition must be kept
as a reference point to previous reconfiguration, or bootstrap.
* Fix nasty race condition with internal service stop, abort kill if the
service has already terminated, otherwise we may do `kill(0, SIGKILL)`
* Fix reconfiguration issue with (very quick) systems that don't have
highres timers
* Fix formatting of runlevel string in `initctl show`
* Allow running `initctl` with `STDOUT` redirected
* Fix regression in `initctl start/stop <ID>`, using name worked not id
* Fix error handling in `initctl start/stop` without any argument
* Fix issue #81 properly, remove use of SYSV shm IPC completely. Finit
now use the API socket for all communication between PID 1 and initctl
* Fix segfault on x86_64 when started with kernel cmdline `--debug`
* Normalize condition paths on systems with `/run` instead of `/var/run`
[3.0][] - 2017-10-19
--------------------
@@ -28,7 +139,7 @@ distributions.
* Ensure plugins always have a default name, file name
* Reorganization, move all source files to a `src/` sub-directory
* Add support for `initctl <list|enable|disable> <SVC>`, much needed by
distributions. See [doc/distro.md](doc/distro.md) for details
distributions. See [docs/distro.md](docs/distro.md) for details
* Remove `UNUSED()` macro, mentioned here because it may have been used
by external plugin developers. Set `-Wno-unused-parameter` instead
* New table headings in `initctl`, using `top` style inverted text
@@ -598,7 +709,8 @@ Major bug fix release.
* Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.0...HEAD
[UNRELEASED]: https://github.com/troglobit/finit/compare/3.1...HEAD
[3.1]: https://github.com/troglobit/finit/compare/3.0...3.1
[3.0]: https://github.com/troglobit/finit/compare/2.4...3.0
[2.4]: https://github.com/troglobit/finit/compare/2.3...2.4
[2.3]: https://github.com/troglobit/finit/compare/2.2...2.3
+2 -2
View File
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = plugins src
SUBDIRS = contrib docs plugins src
doc_DATA = README.md LICENSE contrib/finit.conf
EXTRA_DIST = README.md LICENSE ChangeLog.md contrib/finit.conf
@@ -8,7 +8,7 @@ install-dev:
@make -C src install-pkgincludeHEADERS
# Target to run when building a release
release: dist
release: distcheck
@for file in $(DIST_ARCHIVES); do \
md5sum $$file > ../$$file.md5; \
done
+121 -40
View File
@@ -2,17 +2,19 @@
* [Introduction](#introduction)
* [Features](#features)
* [Bootstrap](doc/bootstrap.md#bootstrap)
* [Bootstrap](docs/bootstrap.md#bootstrap)
* [Runlevels](#runlevels)
* [Syntax](doc/config.md#syntax)
* [Inetd](doc/inetd.md#inetd)
* [Syntax](docs/config.md#syntax)
* [Inetd](docs/inetd.md#inetd)
* [Runparts & /etc/rc.local](#runparts--etcrclocal)
* [Hooks, Callbacks & Plugins](doc/plugins.md#hooks-callbacks--plugins)
* [Hooks, Callbacks & Plugins](docs/plugins.md#hooks-callbacks--plugins)
* [Rebooting & Halting](#rebooting--halting)
* [Commands & Status](#commands--status)
* [Building](doc/build.md#building)
* [Running](doc/build.md#running)
* [Debugging](doc/build.md#debugging)
* [Tips & Tricks with the kernel cmdline](docs/cmdline.md)
* [Building](docs/build.md#building)
* [Running](docs/build.md#running)
* [Recovery](docs/build.md#recovery)
* [Debugging](docs/build.md#debugging)
* [Requirements](#requirements)
* [Origin & References](#origin--references)
@@ -27,8 +29,11 @@ was reverse engineered from the [EeePC fastinit][] ten years ago by
Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section where both [Debian](contrib/debian/) and
up to date [Alpine Linux](contrib/alpine/) tutorials can be found.
[contrib/](contrib/) section with tutorials for:
* [Alpine Linux](contrib/alpine/),
* [Debian GNU/Linux](contrib/debian/), and
* [Void Linux](contrib/void/)
![Alpine screenshot](images/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
@@ -49,12 +54,23 @@ host default
# Runlevel to start after bootstrap, 'S', default: 2
runlevel 2
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size=100k count=4
# Services to be monitored and respawned as needed
service [S12345] /sbin/watchdogd -L -f -- System watchdog daemon
service [S12345] /sbin/syslogd -n -b 3 -D -- System log daemon
service [S12345] /sbin/klogd -n -- Kernel log daemon
service [2345] /sbin/lldpd -d -c -M1 -H0 -i -- LLDP daemon (IEEE 802.1ab)
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. A single
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
# For multiple instances of the same service, add :ID somewhere between
# the service/run/task keyword and the command.
service :1 [2345] /sbin/merecat -n -p 80 /var/www -- Web server
@@ -87,10 +103,13 @@ tty [12345] /sbin/getty -L 115200 /dev/tty3 linux
# Use built-in getty for serial port and USB serial
tty [12345] /dev/ttyAMA0 115200 noclear nowait
tty [12345] /dev/ttyUSB0 115200 noclear
# Just give me a shell, I need to debug this embedded system!
tty [12345] @console noclear nologin
```
The `service` stanza, as well as `task`, `run`, `inetd` and others are
described in full in [doc/config.md](doc/config.md). Here's a quick
described in full in [docs/config.md](docs/config.md). Here's a quick
overview of some of the most common components needed to start a UNIX
daemon:
@@ -111,7 +130,7 @@ advanced uses as well:
service /usr/sbin/sshd -D
Dependencies are handled using [conditions](doc/conditions.md). On of
Dependencies are handled using [conditions](docs/conditions.md). One of
the most common conditions is to wait for basic networking to become
available:
@@ -124,6 +143,10 @@ to create its PID file, by default `/var/run/syslogd.pid`.
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
service [S12345] /sbin/syslogd -n -- Syslog daemon
Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
run in the foreground. With `log` Finit redirects `stdout` + `stderr`
to the system log daemon using the command line `logger(1)` tool.
A service, or task, can have multiple dependencies listed. Here we wait
for *both* `syslogd` to have started and basic networking to be up:
@@ -148,7 +171,7 @@ Start, monitor and restart services should they fail.
**Inetd**
Finit comes with a built-in [inetd server](doc/inetd.md). No need to
Finit comes with a built-in [inetd server](docs/inetd.md). No need to
maintain a separate config file for services that you want to start on
demand.
@@ -163,7 +186,7 @@ Built-in optional inetd services:
- discard [RFC863][]
- time (rdate) [RFC868][]
For more information, see [doc/inetd.md](doc/inetd.md).
For more information, see [docs/inetd.md](docs/inetd.md).
**Getty**
@@ -179,7 +202,18 @@ tty [12345] /dev/ttyAMA0 115200 noclear vt100
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
```
For more information, see [doc/config.md](doc/config.md#syntax).
Users of embedded systems may want to enable automatic serial console
with the special `@console` device. This works regardless weather the
system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
figures it out by querying sysfs: `/sys/class/tty/console/active`.
```conf
tty [12345] @console 115200 linux noclear
```
Notice the optional `noclear`, `nowait`, and `nologin` flags. The
latter is for skipping the login process entirely. For more information,
see [docs/config.md](docs/config.md#syntax).
**Runlevels**
@@ -188,10 +222,18 @@ Support for SysV init-style [runlevels][5] is available, in the same
minimal style as everything else in Finit. The `[2345]` syntax can be
applied to service, task, run, inetd, and TTY stanzas.
All services in runlevel S(1) are started first, followed by the desired
run-time runlevel. Runlevel S can be started in sequence by using `run
[S] cmd`. Changing runlevels at runtime is done like any other init,
e.g. <kbd>init 4</kbd>, but also using the more advanced `intictl` tool.
Reserved runlevels are 0 and 6, halt and reboot, respectively just like
SysV init. Runlevel 1 can be configured freely, but is recommended to
be kept as the system single-user runlevel since Finit will not start
networking here. The configured `runlevel NUM` from `/etc/finit.conf`
is what Finit changes to after bootstrap, unless 'single' (or 'S') is
given on the kernel cmdline, in which case runlevel 1 is started.
All services in runlevel S(0) are started first, followed by the desired
run-time runlevel. Run tasks in runlevel S can be started in sequence
by using `run [S] cmd`. Changing runlevels at runtime is done like any
other init, e.g. <kbd>init 4</kbd>, but also using the more advanced
`intictl` tool.
**Plugins**
@@ -216,25 +258,49 @@ Extensions and functionality not purely related to what an `/sbin/init`
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
For more information, see [doc/plugins.md](doc/plugins.md).
For more information, see [docs/plugins.md](docs/plugins.md).
**Progress**
Finit prior to v3.0 showed the progress at boot: plugins and modules
being loaded, run/tasks/services being started, etc. By default Finit
is now silent, but progress can still be enabled at build-time using:
configure --enable-progress
or at boot from the kernel cmdline using the boot parameter `splash`:
```shell
append="init=/sbin/finit splash"
```
For a really nice boot, add `quiet` as well. This silences the output
from the kernel, leaving only warnings and errors. For other kernel
command line parameters, see [docs/build.md](docs/build.md#recovery).
Runparts & /etc/rc.local
------------------------
At the end of the boot, when networking and all services are up, Finit
calls its built-in [run-parts(8)][] on the `runparts <DIR>` directory,
and `/etc/rc.local`, in that order if they exist.
At the end of the boot, when all bootstrap (`S`) tasks and services have
started, but not networking, Finit calls its built-in [run-parts(8)][]
command on any configured `runparts <DIR>` directory. This happens just
before changing to the configured runlevel (default 2). (Networking is
enabled just prior to changing from single user mode.)
```shell
runparts /etc/rc.d/
```
Right after the runlevel change when all services have started properly,
`/etc/rc.local` is called.
No configuration stanza in `/etc/finit.conf` is required for `rc.local`.
If it exists and is an executable shell script, finit calls it at the
very end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on
hooks in [doc/plugins.md](doc/plugins.md#hooks), and about the system
bootstrap in [doc/bootstrap.md](doc/bootstrap.md).
If it exists and is an executable shell script Finit calls it at the very
end of the boot, before calling the `HOOK_SYSTEM_UP`. See more on hooks
in [docs/plugins.md](docs/plugins.md#hooks), and about the system bootstrap
in [docs/bootstrap.md](docs/bootstrap.md).
Runlevels
@@ -312,7 +378,7 @@ section.
For compatibility reasons Finit listens to the same set of signals as
BusyBox init. This is not 100% compatible with SysV init, but clearly
the more common combination for Finit. For more details, see
[doc/signals.md](doc/signals.md).
[docs/signals.md](docs/signals.md).
Finit also listens to the classic SysV init FIFO, used by `telinit`.
Support for this is implemented by the `initctl.so` plugin. Hence,
@@ -346,33 +412,46 @@ not return until the given command has fully completed.
```shell
~ $ initctl -h
Usage: initctl [OPTIONS] <COMMAND>
Usage: initctl [OPTIONS] [COMMAND]
Options:
-d, --debug Debug initctl (client)
-v, --verbose Verbose output
-h, --help This help text
Commands:
debug Toggle Finit (daemon) debug
help This help text
reload Reload *.conf in /etc/finit.d/ and activate changes
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
status | show Show status of services
version Show Finit version
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes)
cond set <COND> Set (assert) condition => +COND
cond clear <COND> Clear (deassert) condition => -COND
cond show Show condition status
cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name
version Show Finit version
status <JOB|NAME>[:ID] Show service status, by job# or name
status | show Show status of services, default command
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system
halt Halt system
poweroff Halt and power off system
utmp show Raw dump of UTMP/WTMP db
```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
file must be used to tell Finit to stop and start it on `reload` and
`runlevel` changes. If `<>` holds more [conditions](doc/conditions.md),
`runlevel` changes. If `<>` holds more [conditions](docs/conditions.md),
these will also affect how a service is maintained.
**Note:** even though it is possible to start services not belonging in
@@ -398,11 +477,13 @@ Requirements
------------
Finit is capable of running on both desktop/server systems with udev and
embedded systems that usually come with BusyBox mdev. Finit probes for
them at runtime and expects `/dev/` to be writable, usually `devtmpfs`.
It is also possible to run on a statically set up `/dev` if needed. It
is however not a good idea to have both udev and mdev installed at the
same time, this will lead to unpredictable results.
embedded systems that usually come with BusyBox mdev. Some systems have
systemd-udev or eudev today instead of the original udev, Finit probes
for all of them at runtime and expects `/dev/` to be a writable file
system using `devtmpfs`. It is also possible to run on a statically set
up `/dev` if needed. It is however not a good idea to have both udev
and mdev installed at the same time, this will lead to unpredictable
results.
At boot Finit calls either `mdev` or `udevd` to populate `/dev`, this is
done slightly differently and on systems with udev you might want to add
+31 -9
View File
@@ -1,10 +1,17 @@
AC_INIT(Finit, 3.0, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AC_INIT(Finit, 3.1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([src/finit.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile plugins/Makefile src/Makefile])
AC_CONFIG_FILES([Makefile
contrib/Makefile
contrib/alpine/Makefile contrib/alpine/finit.d/Makefile contrib/alpine/finit.d/available/Makefile
contrib/debian/Makefile contrib/debian/finit.d/Makefile contrib/debian/finit.d/available/Makefile contrib/debian/grub.d/Makefile
contrib/void/Makefile contrib/void/finit.d/Makefile contrib/void/finit.d/available/Makefile
docs/Makefile
plugins/Makefile
src/Makefile])
# Older versions of autoconf (<2.58) do not have AC_CONFIG_MACRO_DIR()
#m4_include([m4/plugin.m4])
@@ -21,6 +28,7 @@ LT_INIT([shared disable-static dlopen aix-soname=both disable-fast-install])
AC_PROG_CC
AC_PROG_LN_S
AC_PROG_INSTALL
AC_PROG_MKDIR_P
# Configuration.
AC_HEADER_STDC
@@ -33,8 +41,8 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 1.5.2])
PKG_CHECK_MODULES([lite], [libite >= 1.9.2])
PKG_CHECK_MODULES([uev], [libuev >= 2.1.0])
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
# Check for configured Finit features
AC_ARG_ENABLE(emergency_shell,
@@ -58,6 +66,10 @@ AC_ARG_ENABLE(watchdog,
AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog]),,[
enable_watchdog=no])
AC_ARG_ENABLE(redirect,
AS_HELP_STRING([--enable-redirect], [Redirect service output to /dev/null, default: no]),,[
enable_redirect_output=no])
AC_ARG_ENABLE(inetd,
AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled]),,[
enable_inetd=yes])
@@ -89,11 +101,6 @@ AC_ARG_WITH(fifo,
AS_HELP_STRING([--with-fifo=FIFO], [FIFO for init/telinit clients, default /var/run/initctl]),
[fifo=$withval], [fifo=$localstatedir/run/initctl])
AC_ARG_WITH(plugindir,
AS_HELP_STRING([--with-plugindir=DIR], [Finit plugin directory, default /lib/finit/plugins]),
[pkglibdir=$withval], [pkglibdir=$libdir/finit/plugins])
AC_SUBST([pkglibdir])
AC_ARG_WITH(config,
AS_HELP_STRING([--with-config=CONFIG], [Finit bootstrap config file, default /etc/finit.conf]),
[conf=$withval], [conf=$sysconfdir/finit.conf])
@@ -143,6 +150,9 @@ AS_IF([test "x$enable_progress" = "xyes"], [
AS_IF([test "x$enable_watchdog" = "xyes"], [
AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])])
AS_IF([test "x$enable_redirect" = "xyes"], [
AC_DEFINE(REDIRECT_OUTPUT, 1, [Enable redirection of service output to /dev/null])])
### Disable features ###########################################################################
AS_IF([test "x$enable_inetd" != "xno"], [
enable_inetd="yes"
@@ -207,6 +217,17 @@ AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"])
AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"])
AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"])
# Override default libdir, used for plugins and rescue.conf
#pkglibdir=$libdir/finit
#AC_SUBST(pkglibdir)
#AC_EXPAND_DIR(pkglibdir, "$libdir/finit")
AC_EXPAND_DIR(plugin_path, "$libdir/finit/plugins")
AC_SUBST(plugin_path)
AC_DEFINE_UNQUOTED(PLUGIN_PATH, "$plugin_path", [Finit plugin path])
AC_EXPAND_DIR(rescue_conf, "$libdir/finit/rescue.conf")
AC_DEFINE_UNQUOTED(RESCUE_CONF, "$rescue_conf", [Finit rescue mode .conf])
# Generate all files
AC_OUTPUT
@@ -220,6 +241,7 @@ cat <<EOF
FIFO path.............: $fifo_path
Finit config file.....: $conf_path
Finit config.d path...: $rcsd_path
Finit plugin path.....: $plugin_path
Compat rc.local path..: $rclocal_path
Random seed path......: $random_path
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
+2
View File
@@ -0,0 +1,2 @@
Makefile
Makefile.in
+5
View File
@@ -0,0 +1,5 @@
SUBDIRS = alpine debian void
bin_SCRIPT = service
docsdir := @docdir@/contrib
docs_DATA = README.md finit.conf procmon.sh
EXTRA_DIST = $(docs_DATA)
+5
View File
@@ -0,0 +1,5 @@
SUBDIRS = finit.d
docsdir := @docdir@/contrib/alpine
docs_DATA = README.md finit.conf alpine-release build.sh install.sh rc.local
EXTRA_DIST = $(docs_DATA)
+27
View File
@@ -0,0 +1,27 @@
SUBDIRS = available
docsdir := @docdir@/contrib/alpine/finit.d
d_LINKS = dropbear.conf getty.conf keymap.conf modules.conf klogd.conf \
syslogd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ -L $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) $$src $$lnk; \
fi; \
done
uninstall-hook:
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ "`readlink $$lnk`" != "$$src" ]; then \
continue; \
fi; \
$(RM) $$lnk; \
done
@@ -0,0 +1,4 @@
docsdir := @docdir@/contrib/alpine/finit.d/available
docs_DATA = acpid.conf crond.conf dropbear.conf getty.conf keymap.conf \
lxdm.conf modules.conf ntpd.conf klogd.conf syslogd.conf
EXTRA_DIST = $(docs_DATA)
+4
View File
@@ -0,0 +1,4 @@
SUBDIRS = finit.d grub.d
docsdir := @docdir@/contrib/debian
docs_DATA = README.md finit.conf os-release build.sh install.sh
EXTRA_DIST = $(docs_DATA)
+27
View File
@@ -0,0 +1,27 @@
SUBDIRS = available
docsdir := @docdir@/contrib/debian/finit.d
d_LINKS = console-setup.conf getty.conf keyboard-setup.conf \
modules.conf rsyslogd.conf sshd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ -L $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) $$src $$lnk; \
fi; \
done
uninstall-hook:
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ "`readlink $$lnk`" != "$$src" ]; then \
continue; \
fi; \
$(RM) $$lnk; \
done
@@ -0,0 +1,5 @@
docsdir := @docdir@/contrib/debian/finit.d/available
docs_DATA = acpid.conf console-setup.conf getty.conf \
keyboard-setup.conf lightdm.conf modules.conf \
ntpclient.conf rsyslogd.conf sshd.conf xdm.conf
EXTRA_DIST = $(docs_DATA)
+3
View File
@@ -0,0 +1,3 @@
docsdir := @docdir@/contrib/debian/grub.d
docs_DATA = 40_custom
EXTRA_DIST = $(docs_DATA)
+11
View File
@@ -17,6 +17,10 @@ runlevel 2
# Network bringup script
network service networking start
# Max file size for each log file: 100 kiB, rotate max 4 copies:
# log => log.1 => log.2.gz => log.3.gz => log.4.gz
log size=100k count=4
# Virtual consoles to start getty on
tty /dev/tty1
tty /dev/tty2
@@ -33,6 +37,13 @@ service [2345] /sbin/klogd -n -- Kernel logging server
service [2345] /sbin/syslogd -n -- Syslog server
service [3] /usr/sbin/gdm -- GNOME Display Manager
# The BusyBox ntpd does not use syslog when running in the foreground
# So we use this trick to redirect stdout/stderr to a log file. The
# log file is rotated with the above settings. The condition declares
# a dependency on a system default route (gateway) to be set. A single
# <!> at the beginning means ntpd does not respect SIGHUP for restart.
service [2345] log:/var/log/ntpd.log <!net/route/default> /sbin/ntpd -n -l -I eth0 -- NTP daemon
# Run start scripts from this directory
# runparts /etc/start.d
+51
View File
@@ -0,0 +1,51 @@
#!/bin/sh
USAGE="Usage: $0 processName"
LOG_FILE="/tmp/memusage.csv"
ELAPSED_TIME=0
PERIOD=1 # seconds
if [ $# -ne 1 ]; then
echo $USAGE
exit 1
fi
echo "ElapsedTime,VmSize,VmRSS" > $LOG_FILE
# Monitor memory usage forever until script is killed
while :
do
SUM_VM_SIZE=0
SUM_RSS_SIZE=0
# In case the monitored process has not yet started
# keep searching until its PID is found
PROCESS_PIDS=""
while :
do
PROCESS_PIDS=`/bin/pidof $1`
if [ "$PROCESS_PIDS.X" != ".X" ]; then
break
fi
done
for PID in ${PROCESS_PIDS} ; do
VM_SIZE=`awk '/VmSize/ {print $2}' < /proc/$PID/status`
if [ "$VM_SIZE.X" = ".X" ]; then
continue
fi
#echo exprVM_ $SUM_VM_SIZE + $VM_SIZE
SUM_VM_SIZE=`expr $SUM_VM_SIZE + $VM_SIZE`
VM_RSS=`awk '/VmRSS/ {print $2}' < /proc/$PID/status`
if [ "$VM_RSS.X" = ".X" ]; then
continue
fi
SUM_RSS_SIZE=`expr $SUM_RSS_SIZE + $VM_RSS`
done
echo "$ELAPSED_TIME sec, $SUM_VM_SIZE KB, $SUM_RSS_SIZE KB"
echo "$ELAPSED_TIME,$SUM_VM_SIZE,$SUM_RSS_SIZE" >> $LOG_FILE
sleep $PERIOD
VM_SIZE=""
VM_RSS=""
# Needs to get actual elapsed time instead of doing this
ELAPSED_TIME=`expr $ELAPSED_TIME + $PERIOD`
done
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
JOB=$1
CMD=$2
usage() {
echo "Usage: service [job# | name] <stop | start | restart | status | show>"
exit 1
}
if [ $# -eq 0 ]; then
initctl show
exit 0
fi
case $CMD in
stop)
;;
start)
;;
restart)
;;
status)
;;
show)
;;
*)
usage
;;
esac
initctl $2 $1
+4
View File
@@ -0,0 +1,4 @@
SUBDIRS = finit.d
docsdir := @docdir@/contrib/void
docs_DATA = README.md finit.conf build.sh install.sh rc.local xbps
EXTRA_DIST = $(docs_DATA)
+26
View File
@@ -0,0 +1,26 @@
SUBDIRS = available
docsdir := @docdir@/contrib/void/finit.d
d_LINKS = acpid.conf dhcpcd.conf getty.conf sshd.conf uuidd.conf
install-exec-hook:
@$(MKDIR_P) $(DESTDIR)$(docsdir)
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ -L $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) $$src $$lnk; \
fi; \
done
uninstall-hook:
@for file in $(d_LINKS); do \
lnk=$(DESTDIR)$(docsdir)/$$file; \
src=available/$$file; \
if [ "`readlink $$lnk`" != "$$src" ]; then \
continue; \
fi; \
$(RM) $$lnk; \
done
@@ -0,0 +1,5 @@
docsdir := @docdir@/contrib/void/finit.d/available
docs_DATA = acpid.conf crond.conf dhcpcd.conf dmeventd.conf getty.conf \
keymap.conf klogd.conf lxdm.conf modules.conf ntpd.conf \
sshd.conf syslogd.conf uuidd.conf
EXTRA_DIST = $(docs_DATA)
+2
View File
@@ -0,0 +1,2 @@
Makefile
Makefile.in
+4
View File
@@ -0,0 +1,4 @@
docsdir := @docdir@/docs
docs_DATA = config.md conditions.md distro.md inetd.md plugins.md \
service.md signals.md svc-machine.png cmdline.md
EXTRA_DIST = $(docs_DATA)
+15 -20
View File
@@ -21,29 +21,15 @@ can also be used.
General
-------
* Remove shm, let `initctl` call `api.c` for each `svc_t` instead of
opening up the inner parts of PID 1 to other processes. Also, this
would prevent issues like #81 from happening.
* Add support for timed shutdown in Finit, including cancelled shutdown.
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
API restart to SIGHUP callback for the same functionality in Finit.
* Add support for timed shutdown in Finit, including cancelled shutdown,
possibly using an at-job, see [crond section](#Crond) below
* Add `finit.conf` support for UPS notification (SIGPWR) to start a task
using, e.g. <sys/power/{ok,fail,low}> conditions. More info in sig.c
* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest,
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
* Add `IFF_RUNNING` support to netlink plugin along `IFF_UP`
* Allow scripts, instead of C code, as process callbacks
* Add support for a pipe/popen where we can listen for `STOP`, `START`
and `RELOAD` on `STDOUT` from a script ... call it for example
`.monitor=` in `svc_t` ... the script named as the basename of the
service it monitors + `.sh`.
* Implement `initctl stop|start|restart|reload|status <SVC>` and
`service <SVC> stop|start|restart|reload|status` on top
* Add support for JSON output, or similar, from `initctl show`, e.g.
`initctl show --json`
* Improve `initctl show SVC` output. Add service uptime, last ten SVC
logs/history (historian), etc. Similar to `systemctl show SVC`
* [Solaris SMF][] (Service Management Facility) has some interesting
features that may be well worth looking into adopting:
@@ -54,6 +40,13 @@ General
* `initctl add /sbin/service -n -- Service description`, for details
see above [Solaris SMF][] featuer, issue #55 and issue #69,
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
* Changing the name of a pidfile, `pid:[/path/]file.pid`, after a
service has been started is not supported atm. The previous name will
not be (never) removed and the new name will not be created until the
process has been stopped and started again.
* PID files created in subdirectories to `/run` is not supported right
now inotify listeners should be automatically created (and removed)
when a subdirectory is added (or removed).
Init
@@ -90,14 +83,16 @@ system-level timed periodic tasks:
Proposed syntax:
# Crontab
cron @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
cron @SPEC [LVLS] /path/to/cmd [ARGS] -- Optional descr
# One-shot 'at' command
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
Notice the ISO date in notation. The runlevels is extra filtering
sugar. E.g., if cron service is only allowed in runlevels two or three
it will not start if system currently is in runlevel four.
Notice the ISO date in the `at` notation. The `cron @SPEC` could use
are mo loose notation, e.g. `@hourly`, `@midnight`, `@daily`, `@weekly`,
`@reboot`, etc. The runlevels is extra filtering sugar. E.g., if cron
service is only allowed in runlevels two or three it will not start if
system currently is in runlevel four.
To run a command as another user the `.conf` file must have a that
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
+4 -4
View File
@@ -5,7 +5,7 @@ Bootstrap
2. Set up initial signal handlers
3. Set up default $PATH early
4. Mount `/proc` and `/sys`
5. Check kernel command line for `--debug` to figure out log level
5. Check kernel command line for `debug` to figure out log level
6. Load all `.so` plugins
7. Call `fsck` on file systems listed in `/etc/fstab`
8. Populate `/dev` using either udev or mdev, depending on system type
@@ -25,13 +25,13 @@ Bootstrap
20. Bring up loopback interface and all `/etc/network/interfaces`, if
the `.conf` setting `network <SCRIPT>` is set, it is called instead
21. Call 3rd level hooks, `HOOK_NETWORK_UP`
22. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
22. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
23. Switch to the configured runlevel from `/etc/finit.conf`, default 2.
At every runlevel change all `*.conf` files in `/etc/finit.d/` are
(re)loaded and new services, tasks, and blocking run commands are
started. Provided they are allowed in the new runlevel and all of
their conditions, if any, are set.
23. Call 4th level hooks, `HOOK_SVC_UP`
24. If `runparts <DIR>` is set, [run-parts(8)][] is called on `<DIR>`
24. Call 4th level hooks, `HOOK_SVC_UP`
25. Call `/etc/rc.local`, if it exists and is an executable shell script
26. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
27. Start all configured TTYs, or a fallback shell on `/dev/console`
+27 -3
View File
@@ -26,6 +26,12 @@ optional plugins to enable. It depends on two external libraries:
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
The configure script checks for all dependencies, including the correct
version of the above mentioned libraries. Currently requried versions:
- libite v2.0.1
- libuEv v2.1.0
Configure
---------
@@ -118,14 +124,32 @@ append="init=/sbin/finit"
Remember to also set up an initial `/etc/finit.conf` before rebooting!
Recovery
--------
To rescue a system with Finit, append the following to the kernel
command line:
```shell
append="init=/sbin/finit rescue"
```
This tells Finit to start in a very limited recovery mode, no services
are loaded, no filesystems are mounted or checked, and no networking is
enabled. The default Finit rescue mode configuration is installed into
`/lib/finit/rescue.conf`, which can be safely removed or changed.
By default the a root shell, without login, is started.
Debugging
---------
Add `finit_debug`, or `--debug`, to the kernel command line to enable
debug messages.
Edit, or append to, the kernel command line: remove `quiet` to enable
kernel messages and add `debug` to enable Finit debug messages.
```shell
append="init=/sbin/finit --debug"
append="init=/sbin/finit debug"
```
To debug startup issues, in particular issues with getty/login, try
+38
View File
@@ -0,0 +1,38 @@
Tips & Tricks with the kernel cmdline
=====================================
This document summarizes the different boot parameters that can be
passed on the Linux kernel command line. Not just limited to Finit.
* `debug`, or `--debug`
Enable debug mode. The former syntax also enable kernel debug, and
the latter only in Finit. Debug messages are printed to the console
until a syslog daemon has been started.
* `init=/bin/sh`
Bypass system default init and tell kernel to start a shell. Note,
this shell is very limited and does not support signals and has no
job control. Recommend using, and modifying, `rescue` mode instead.
* `panic=SEC`
By default the kernel does not reboot after a kernel panic. This
setting will cause a kernel reboot after SEC seconds.
* `quiet`
Suppress kernel logging to console, except for warnings and errors.
* `rescue`
Start Finit rescue mode; no network, no `fsck` of file systems in
`/etc/fstab`, no `/etc/rc.local`, no `runparts`. The configuration
is read from `/etc/rescue.conf`, can be modified by the operator.
* `single`, or `S`
Single user mode, runlevel 1, in Finit. Useful to debug problems with
the regular runlevel. All services and TTYs in `[1]` will be started,
so a `tty [1] @console nologin` configuration presents you with a
root console without login.
* `splash`
Display Finit splash screen with progress at bootstrap and shutdown.
For more on kernel boot paramaters, see the man page `bootparam(7)`.
+4 -3
View File
@@ -95,10 +95,11 @@ Built-in conditions:
- `net/route/default`
- `net/<IFNAME>/exist`
- `net/<IFNAME>/up`
- `net/<IFNAME>/running`
**Note:** `up` means administratively up, the interface flag `IFF_UP`,
not link up, `IFF_RUNNING`. This latter conditions is currently not
implemented by the `plugins/netlink.c` plugin.
**Note:** `up` means administratively up, the interface flag `IFF_UP`.
`running` is the `IFF_RUNNING` flag, meaning operatively up. The
difference is that `running` tells if the NIC has link.
Debugging
+166 -49
View File
@@ -2,6 +2,7 @@ Configuration
=============
* [Introduction](#introduction)
* [Service Wrapper Scripts](#service-wrapper-scripts)
* [Syntax](#syntax)
* [Limitations](#limitations)
* [/etc/finit.conf](#etcfinitconf)
@@ -13,10 +14,10 @@ Introduction
Finit can be configured using only the original `/etc/finit.conf` file
or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a
system using only /etc/finit.d/*.conf`, highly useful for package-based
system using only `/etc/finit.d/*.conf`, highly useful for package-based
Linux distributions -- each package can provide its own "script" file.
- `/etc/finit.conf`: main configuration file, read only once at boot
- `/etc/finit.conf`: main configuration file
- `/etc/finit.d/*.conf`: snippets, usually one service per file
Not all configuration directives are available in `/etc/finit.d/*.conf`
@@ -26,8 +27,15 @@ the section [Limitations](#limitations) below for details.
To add a new service, simply drop a `.conf` file in `/etc/finit.d` and
run `initctl reload`. (It is also possible to `SIGHUP` to PID 1, or
call `finit q`, but that has been deprecated with the `initctl` tool).
Any service read from this directory is flagged as a dynamic service, so
changes to or removal of `/etc/finit.d/*.conf` files, is detected.
Finit monitors all known active `.conf` files, so if you want to force
a restart of any service you can simply touch its corresponding `.conf`
file in `/etc/finit.d` and call `initctl reload`. Finit handle any
and all conditions and dependencies between services automatically.
It is also possible to drop `.conf` files in `/etc/finit.d/available/`
and use `initctl enable` to enable a service `.conf` file. This may be
useful in particular to Linux distributions that may want to install all
files for a package and let the user decide when to enable a service.
On `initctl reload` the following is checked for all services:
@@ -42,24 +50,63 @@ For more info on the different states of a service, see the separate
document [Finit Services](service.md).
Service Wrapper Scripts
-----------------------
If your service requires to run additional commands, executed before the
service is actually started, like the systemd `ExecStartPre`, you can
use a wrapper shell script to start your service.
The Finit service `.conf` file can be put into `/etc/finit.d/available`,
so you can control the service using `initctl`. Then use the path to
the wrapper script in the Finit `.conf` service stanza. The following
example employs a wrapper script in `/etc/start.d`.
**Example:**
* `/etc/finit.d/available/program.conf`:
```conf
service [235] <!> /etc/start.d/program -- Example Program
```
* `/etc/start.d/program:`
```shell
#!/bin/sh
# Prepare the command line options
OPTIONS="-u $(cat /etc/username)"
# Execute the program
exec /usr/bin/program $OPTIONS
```
**Note:**, that the example sets `<!>` to denote that it doesn't support
`SIGHUP`. That way Finit will stop/start such the service instead of
sending SIGHUP at restart/reload.
Syntax
------
* `module <MODULE>`
* `host <NAME>`, or `hostname <NAME>`
Set system hostname to NAME, unless `/etc/hostname` exists in which
case the contents of that file is used.
* `module <MODULE> [ARGS]`
Load a kernel module, with optional arguments
* `network <PATH>`
Script or program to bring up networking, with optional arguments
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>`
Modify the specified resource's hard or soft limit to be the
specifed limit. `RESOURCE` is a lower-case string matching the
constants in `setrlimit(2)` with the `RLIMIT_` prefix
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
`cpu`. `LIMIT` is an integer whose unit depends on the resource
being modified, see `setrlimit(2)` for more information. The special
limit `infinity` means the there should be no limit on the resource,
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`.
* `rlimit [hard|soft] RESOURCE <LIMIT|unlimited>` Set the hard or soft
limit for a resource, or both if that argument is omitted. `RESOURCE`
is the lower-case `RLIMIT_` string constants from `setrlimit(2)`,
without the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
LIMIT is an integer that depends on the resource being modified, see
the man page, or the kernel `/proc/PID/limits` file, for details.
Finit versions before v3.1 used `infinity` for `unlimited`, which is
still supported, albeit deprecated.
```shell
# No process is allowed more than 8MB of address space
@@ -67,8 +114,14 @@ Syntax
# Core dumps may be arbitrarily large
rlimit soft core infinity
# CPU limit for all services, soft & hard = 10 sec
rlimit cpu 10
```
`rlimit` can be set globally, in `/etc/finit.conf`, or locally for
a set of task/run/services, in `/etc/finit.d/*.conf`.
* `runlevel <N>`
N is the runlevel number 1-9, where 6 is reserved for reboot.
Default is 2.
@@ -107,8 +160,30 @@ Syntax
Finit waits for another service, `/sbin/zebra`, to have created its
PID file in `/var/run/zebra.pid` before starting `ospfd`.
For a detailed description of conditions, and how to debug them,
see the [Finit Conditions](conditions.md) document.
Some services do not maintain a PID file and rather than patching each
application Finit provides a workaround. A `pid` keyword can be set
to have Finit automatically create (when starting) and later remove
(when stopping) the PID file. The file is created in the `/var/run`
directory using the `basename(1)` of the service. The default can be
modified with an optional argument:
pid[:[/path/to/]filename[.pid]]
For example, by adding `pid:/run/foo.pid` to the service `bar` that
PID file will, not only be created and removed automatically, but also
be used by the Finit condition subsystem. So another service/run/task
can depend on `<svc/foo>`.
If a service `bar` *does* maintain a PID file, but using `foo.pid`, we
can inform Finit of this by simplly prepending an `!` to the argument.
pid:!/run/foo.pid
Here Finit will *not* create/remove/touch the PID file, only use it
for the condition handling instead of the default PID file name.
For a detailed description of conditions, and how to debug them, see
the [Finit Conditions](conditions.md) document.
* `inetd service/proto[@iflist] <wait|nowait> [LVLS] /path/to/daemon args`
Launch a daemon when a client initiates a connection on an Internet
@@ -140,31 +215,48 @@ Syntax
* `runparts <DIR>`
Call [run-parts(8)][] on `DIR` to run start scripts. All executable
files, or scripts, in the directory are called, in alphabetic order.
The scripts in this directory are executed at the very end of runlevel
`S`, bootstrap.
It can be beneficial to use `S01name`, `S02othername`, etc. if there
is a dependency order between the scripts. Symlinks to existing
daemons can talso be used, but make sure they daemonize by default.
As the optional `/etc/rc.local` shell script, make sure that all your
Similar to the `/etc/rc.local` shell script, make sure that all your
services and programs either terminate or start in the background or
you will block Finit.
* `include <CONF>`
Include another configuration file. Absolute path required.
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [TERM]`
* `log size:200k count:5`
Log rotation for run/task/services using the `log` sub-option with
redirection to a log file. Global setting, applies to all services.
The size can be given as bytes, without a specifier, or in `k`, `M`,
or `G`, e.g. `size:10M`, or `size:3G`. A value of `size:0` disables
log rotation. The default is `200k`.
The count value is recommended to be between 1-5, with a default 5.
Setting count to 0 means the logfile will be truncated when the MAX
size limit is reached.
* `tty [LVLS] <DEV> [BAUD] [noclear] [nowait] [nologin] [TERM]`
`tty [LVLS] <CMD> <ARGS> [noclear] [nowait]`
The first variant of this option uses the built-in getty on the given
TTY device DEV, in the given runlevels. Default baud rate is `38400`.
TTY device DEV, in the given runlevels. The DEV may be the special
keyword `@console`, which is very useful on embedded systems. Default
baud rate is `38400`.
**Example:**
```conf
tty [12345] /dev/ttyAMA0 115200 noclear vt220
```
The second variant is for using an external getty, like agetty or the
BusyBox getty.
The second `tty` syntax variant is for using an external getty, like
agetty or the BusyBox getty.
By default both variants *clear* the TTY and *wait* for the user to
press enter before starting getty.
@@ -182,10 +274,28 @@ Syntax
embedded systems running multiple unused getty wastes both memory
and CPU cycles, so `wait` is the preferred default.
The `nologin` option disables getty and `/bin/login`, and gives the
user a root (login) shell on the given TTY `<DEV>` immediately.
Needless to say, this is a rather insecure option, but can be very
useful for developer builds, during board bringup, or similar.
Notice the ordering, the `TERM` option to the built-in getty must be
the last argument.
On really bare bones systems Finit offers a fallback shell:
Embedded systems may want to enable automatic `DEV` by supplying the
special `@console` device. This works regardless weather the system
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
it out by querying sysfs: `/sys/class/tty/console/active`.
**Example:**
```conf
tty [12345] @console 115200 noclear vt220
```
On really bare bones systems Finit offers a fallback shell, which
should not be enabled on production systems since. This because it
may give a user root access without having to log in. However, for
bringup and system debugging it can come in handy:
```shell
configure --enable-fallback-shell
@@ -197,16 +307,10 @@ Syntax
service [12345] /bin/sh -l
```
* `console <DEV>`
Use this, along with a matching `tty DEV` line, to mark this TTY as a
special "console" port with `prctl()`. Useful in use-cases when
all logins except the console must be stopped, e.g. when performing a
flash upgrade on an embedded system.
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
be installed since system requirements differ too much. Try out the
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
is capable of service monitoring SSH, sysklogd, gdm and getty!
Every `run`, `task`, `service`, or `inetd` can also list the privileges
the `/path/to/cmd` should be executed with. Simply prefix the path with
@@ -229,8 +333,19 @@ Without the `:ID` to the service the latter will overwrite the former
and only the old web server would be started and supervised.
The `run`, `task`, `service`, or `inetd` stanzas also allow the keyword
`log` to redirect `stderr` and `stdout` of the application to syslog,
using `logger`.
`log` to redirect `stderr` and `stdout` of the application to a file or
syslog using the native `logit` tool. The full syntax is:
log:/path/to/file
log:prio:facility.level,tag:ident
log:null
log
Log rotation is controlled using the global `log` setting.
**Example:**
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.
@@ -241,40 +356,42 @@ Limitations
To understand the limitations of `finit.conf` vs `finit.d` it is useful
to picture the different phases of the system: bootstrap, runtime, and
shutdown. The `finit.conf` file can be used for all possible settings,
but `finit.d/*.conf` can only be used for runtime settings.
shutdown.
### /etc/finit.conf
This file was the only way to set up and boot a system using Finit. It
is used for pre-runtime settings like system hostname, network bringup
and shutdown:
This file used to be the only way to set up and boot a Finit system.
Today it is mainly used for pre-runtime settings like system hostname,
network bringup and shutdown:
- `host`
- `mkdod`
- `network`
- `runparts`
- `host`, only at bootstrap, (runlevel `S`)
- `mknod`, only at bootstrap
- `network`, only at bootstrap
- `runparts`, only at bootstrap
- `include`
- `shtudown`
- `runlevel`
- `log`, global setting
- `shutdown`
- `runlevel`, only at bootstrap
- ... and all configuration stanzas from `/etc/finit.d` below
### /etc/finit.d
Support for partial `.conf` files in `/etc/finit.d` was added to handle
changes of the system configuration at runtime. It does *not support*
the above `finit.conf` settings described above, only the following:
Support for per-service `.conf` files in `/etc/finit.d` was added in
v3.0 to handle changes of the system configuration at runtime. As of
v3.1 `finit.conf` is also handled at runtime, except of course for any
stanza that only runs at bootstrap. However, a `/etc/finit.d/*.conf`
does *not support* the above `finit.conf` settings described above, only
the following:
- `module`, but only in runlevel `S`
- `module`, but only at bootstrap
- `service`
- `task`
- `run`
- `inetd`
- `rlimit`
- `tty`
- `console`
**NOTE:*** The `/etc/finit.d` directory was previously the default Finit
**NOTE:** The `/etc/finit.d` directory was previously the default Finit
`runparts` directory. Finit no longer has a default `runparts`, make
sure to update your setup, or the finit configuration, accordingly.
View File
View File
View File
View File
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+1
View File
@@ -1,3 +1,4 @@
pkglibdir = $(plugin_path)
AM_LDFLAGS = -module -avoid-version -shared
AM_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
AM_CPPFLAGS = -I$(top_srcdir)/src -U_FORTIFY_SOURCE
+5 -1
View File
@@ -106,9 +106,10 @@ static void setup(void *arg)
_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);
makedir("/var/lib", 0755);
makedir("/var/lib/misc", 0755);
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
makedir("/var/lib/alarm", 0755);
makedir("/var/lib/urandom",0755);
if (fisdir("/run")) {
@@ -169,6 +170,9 @@ static void setup(void *arg)
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) {
+2 -1
View File
@@ -29,6 +29,7 @@
#include "helpers.h"
#include "plugin.h"
#include "service.h"
#include "conf.h"
#define DAEMON "dbus-daemon"
#define ARGS "--nofork --system"
@@ -58,7 +59,7 @@ static void setup(void *arg)
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] %s %s -- %s", cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, NULL))
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
free(cmd);
+20 -23
View File
@@ -94,6 +94,17 @@ static void nl_route(struct nlmsghdr *nlmsg)
}
}
static void net_cond_set(char *ifname, char *cond, int set)
{
char msg[MAX_ARG_LEN];
snprintf(msg, sizeof(msg), "net/%s/%s", ifname, cond);
if (set)
cond_set(msg);
else
cond_clear(msg);
}
static void nl_link(struct nlmsghdr *nlmsg)
{
int la;
@@ -112,39 +123,25 @@ static void nl_link(struct nlmsghdr *nlmsg)
while (RTA_OK(a, la)) {
if (a->rta_type == IFLA_IFNAME) {
char msg[MAX_ARG_LEN];
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK:
/*
* New interface has appearad, or interface flags has changed.
* New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN
*/
_d("%s: New link, ifi_change 0x%x", ifname, i->ifi_change);
if (i->ifi_change & IFF_UP) {
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
if (i->ifi_flags & IFF_UP)
cond_set(msg);
else
cond_clear(msg);
if (string_compare("lo", ifname)) {
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
cond_set(msg);
}
} else {
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
cond_set(msg);
}
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
break;
case RTM_DELLINK:
/* NOTE: Interface has dissapeared, not link down ... */
/* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname);
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
cond_clear(msg);
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
break;
case RTM_NEWADDR:
+23 -19
View File
@@ -50,7 +50,6 @@ static void pidfile_callback(void *arg, int fd, int events)
struct inotify_event *ev;
ssize_t sz, len;
char *basename;
svc_t *svc;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
@@ -60,23 +59,16 @@ static void pidfile_callback(void *arg, int fd, int events)
}
ev_buf[sz] = 0;
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
for (ev = (void *)ev_buf;
sz > (ssize_t)sizeof(*ev);
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
if (!ev->mask || !strstr(ev->name, ".pid"))
continue;
basename = strtok(ev->name, ".");
if (!basename)
continue;
svc = svc_find_by_nameid(basename, 1);
svc = svc_find_by_pidfile(ev->name);
if (!svc)
continue;
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
_d("%s: match %s", basename, svc->cmd);
mkcond(cond, sizeof(cond), svc->cmd);
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY)) {
svc_started(svc);
@@ -92,17 +84,16 @@ static void pidfile_callback(void *arg, int fd, int events)
*
* We must wait for the service to create/touch its pidfile.
*/
static void pidfile_reconf(void *_null)
static void pidfile_reconf(void *arg)
{
static char cond[MAX_COND_LEN];
svc_t *svc;
(void)(_null);
svc_t *svc, *iter = NULL;
int restart = 0;
do {
restart = 0;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
mkcond(cond, sizeof(cond), svc->cmd);
if (svc->state == SVC_RUNNING_STATE &&
!svc_is_changed(svc) &&
@@ -114,17 +105,30 @@ static void pidfile_reconf(void *_null)
}
if (restart)
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
} while (restart);
}
static void pidfile_init(void *arg)
{
char *path;
struct context *ctx = arg;
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
/*
* The bootmisc plugin is responsible for setting up /var/run.
* and/or /run, with proper symlinks etc. We depend on bootmisc
* so it's safe here to query realpath() and set up inotify.
*/
path = realpath(_PATH_VARRUN, NULL);
if (!path) {
_pe("Failed ");
return;
}
ctx->wd = inotify_add_watch(ctx->fd, path, IN_CREATE | IN_ATTRIB | IN_DELETE | IN_MODIFY);
free(path);
if (ctx->wd < 0) {
_pe("inotify_add_watch()");
close(ctx->fd);
@@ -148,7 +152,7 @@ static plugin_t plugin = {
.cb = pidfile_callback,
.flags = PLUGIN_IO_READ,
},
.depends = { "bootmisc", },
.depends = { "bootmisc", "netlink" },
};
PLUGIN_INIT(plugin_init)
+1 -1
View File
@@ -45,7 +45,7 @@ static plugin_t plugin = {
static void setup(void)
{
if (plugin.io.fd)
if (plugin.io.fd > 0)
close(plugin.io.fd);
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+1
View File
@@ -1,6 +1,7 @@
.deps/*
finit
initctl
logit
reboot
Makefile
Makefile.in
+25 -14
View File
@@ -1,23 +1,29 @@
EXTRA_DIST = rescue.conf
AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC
AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs
else
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
AM_LDFLAGS = -export-dynamic
endif
bin_PROGRAMS = logit
sbin_PROGRAMS = finit initctl reboot
logit_SOURCES = logit.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_SOURCES = api.c \
cond.c cond-w.c cond.h \
client.c \
telinit.c \
conf.c conf.h \
exec.c finit.c finit.h \
getty.c \
getty.c stty.c \
helpers.c helpers.h \
log.c log.h \
mdadm.c mount.c pid.c \
mdadm.c mount.c \
pid.c pid.h \
plugin.c plugin.h private.h \
service.c service.h \
sig.c sig.h \
@@ -38,12 +44,13 @@ finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
if STATIC
finit_LDADD += plugins/libplug.la
finit_LDADD += ../plugins/libplug.la
else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c serv.c serv.h svc.c svc.h \
initctl_SOURCES = initctl.c client.c client.h \
serv.c serv.h svc.h \
cond.c cond.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS)
@@ -58,10 +65,11 @@ REBOOT_LNKS = shutdown halt poweroff suspend
# Hook in install to add finit and reboot symlink(s)
install-exec-hook:
@$(INSTALL_DATA) $(srcdir)/rescue.conf $(DESTDIR)$(pkglibdir)
@for file in $(INIT_LNKS); do \
lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$lnk ]; then \
echo "Skipping $$lnk exists already."; \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) finit $$lnk; \
fi; \
@@ -70,16 +78,17 @@ install-exec-hook:
echo "Non-finit reboot, skipping install."; \
else \
for file in $(REBOOT_LNKS); do \
link=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$link ]; then \
echo "$$link exists, skipping.";\
lnk=$(DESTDIR)$(sbindir)/$$file; \
if [ -e $$lnk ]; then \
echo "Skipping $$lnk exists."; \
else \
$(LN_S) reboot $$link; \
$(LN_S) reboot $$lnk; \
fi; \
done; \
fi
uninstall-hook:
@$(RM) $(DESTDIR)$(pkglibdir)/rescue.conf
@for file in $(INIT_LNKS); do \
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
if [ "$$lnk" != "finit" ]; then \
@@ -92,9 +101,11 @@ uninstall-hook:
echo "Non-finit reboot, skipping uninstall."; \
else \
for file in $(REBOOT_LNKS); do \
if [ "`readlink $$file`" != "reboot" ]; \
then continue; \
lnk=$(DESTDIR)$(sbindir)/$$file; \
dst=`readlink $$lnk`; \
if [ "$$dst" != "reboot" ]; then \
continue; \
fi; \
$(RM) $(DESTDIR)$(sbindir)/$$file; \
$(RM) $$lnk; \
done; \
fi
+114 -16
View File
@@ -21,6 +21,7 @@
* THE SOFTWARE.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -38,11 +39,12 @@
#include "helpers.h"
#include "log.h"
#include "plugin.h"
#include "private.h"
#include "sig.h"
#include "service.h"
#include "util.h"
uev_t api_watcher;
static uev_t api_watcher;
static int call(int (*action)(svc_t *), char *buf, size_t len)
{
@@ -86,15 +88,42 @@ static int do_start (char *buf, size_t len) { return call(service_start, buf,
static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); }
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
#ifdef INETD_ENABLED
static int do_query_inetd(char *buf, size_t len)
static char query_buf[368];
static int missing(char *job, int id)
{
char buf[20];
char idstr[10] = "";
if (!job)
job = "";
if (id > 1)
snprintf(idstr, sizeof(idstr), ":%d", id);
snprintf(buf, sizeof(buf), "%s%s ", job, idstr);
strlcat(query_buf, buf, sizeof(query_buf));
return 1;
}
static int do_query(struct init_request *rq, size_t len)
{
memset(query_buf, 0, sizeof(query_buf));
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) {
memcpy(rq->data, query_buf, sizeof(rq->data));
return 1;
}
return 0;
}
static svc_t *do_find(char *buf, size_t len)
{
int id = 1;
char *ptr, *input = sanitize(buf, len);
svc_t *svc;
char *ptr, *input;
input = sanitize(buf, len);
if (!input)
return -1;
return NULL;
ptr = strchr(input, ':');
if (ptr) {
@@ -102,11 +131,20 @@ static int do_query_inetd(char *buf, size_t len)
id = atonum(ptr);
}
svc = svc_find_by_jobid(atonum(input), id);
if (!svc || !svc_is_inetd(svc)) {
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
if (isdigit(input[0]))
return svc_find_by_jobid(atonum(input), id);
return svc_find_by_nameid(input, id);
}
#ifdef INETD_ENABLED
static int do_query_inetd(char *buf, size_t len)
{
svc_t *svc;
svc = do_find(buf, len);
if (!svc || !svc_is_inetd(svc))
return 1;
}
return inetd_filter_str(&svc->inetd, buf, len);
}
@@ -163,20 +201,38 @@ static int do_handle_emit(char *buf, size_t len)
return result;
}
static void send_svc(int sd, svc_t *svc)
{
svc_t empty;
size_t len;
if (!svc) {
empty.pid = -1;
svc = &empty;
}
len = write(sd, svc, sizeof(*svc));
if (len != sizeof(*svc))
_d("Failed sending svc_t to client");
}
/*
* In contrast to the SysV compat handling in plugins/initctl.c, when
* `initctl runlevel 0` is issued we default to POWERDOWN the system
* instead of just halting.
*/
static void cb(uev_t *w, void *arg, int events)
static void api_cb(uev_t *w, void *arg, int events)
{
int sd, lvl;
svc_t *svc;
static svc_t *iter = NULL;
struct init_request rq;
sd = accept(w->fd, NULL, NULL);
if (sd < 0) {
_pe("Failed serving API request");
return;
goto error;
}
while (1) {
@@ -228,37 +284,46 @@ static void cb(uev_t *w, void *arg, int events)
break;
case INIT_CMD_DEBUG:
_d("debug");
log_debug();
break;
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
_d("reload");
service_reload_dynamic();
break;
case INIT_CMD_START_SVC:
_d("start %s", rq.data);
result = do_start(rq.data, sizeof(rq.data));
break;
case INIT_CMD_STOP_SVC:
_d("stop %s", rq.data);
result = do_stop(rq.data, sizeof(rq.data));
break;
case INIT_CMD_RESTART_SVC:
_d("restart %s", rq.data);
result = do_restart(rq.data, sizeof(rq.data));
break;
#ifdef INETD_ENABLED
case INIT_CMD_QUERY_INETD:
_d("query inetd");
result = do_query_inetd(rq.data, sizeof(rq.data));
break;
#endif
case INIT_CMD_EMIT:
_d("emit %s", rq.data);
result = do_handle_emit(rq.data, sizeof(rq.data));
break;
case INIT_CMD_GET_RUNLEVEL:
rq.runlevel = runlevel;
_d("get runlevel");
rq.runlevel = runlevel;
rq.sleeptime = prevlevel;
break;
case INIT_CMD_ACK:
@@ -266,18 +331,42 @@ static void cb(uev_t *w, void *arg, int events)
goto leave;
case INIT_CMD_WDOG_HELLO:
_d("wdog hello");
if (rq.runlevel <= 0) {
result = 1;
break;
}
if (wdogpid > 0 && wdogpid != rq.runlevel) {
_d("Sending SIGTERM to %d", wdogpid);
kill(wdogpid, SIGTERM);
do_sleep(1);
}
_d("wdog was %d, now %d is in charge", wdogpid, rq.runlevel);
wdogpid = rq.runlevel;
break;
case INIT_CMD_SVC_ITER:
_d("svc iter, first: %d", rq.runlevel);
/*
* XXX: This severly limits the number of
* simultaneous client connections, but will
* have to do for now.
*/
svc = svc_iterator(&iter, rq.runlevel);
send_svc(sd, svc);
goto leave;
case INIT_CMD_SVC_QUERY:
_d("svc query: %s", rq.data);
result = do_query(&rq, len);
break;
case INIT_CMD_SVC_FIND:
_d("svc find: %s", rq.data);
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
goto leave;
default:
_d("Unsupported cmd: %d", rq.cmd);
break;
@@ -294,6 +383,13 @@ static void cb(uev_t *w, void *arg, int events)
leave:
close(sd);
if (UEV_ERROR == events)
goto error;
return;
error:
api_exit();
if (api_init(w->ctx))
_e("Unrecoverable error on API socket");
}
int api_init(uev_ctx_t *ctx)
@@ -305,6 +401,7 @@ int api_init(uev_ctx_t *ctx)
.sun_path = INIT_SOCKET,
};
_d("Setting up external API socket ...");
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (-1 == sd) {
_pe("Failed starting external API socket");
@@ -320,7 +417,7 @@ int api_init(uev_ctx_t *ctx)
goto error;
umask(oldmask);
if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ))
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
return 0;
error:
@@ -332,8 +429,9 @@ error:
int api_exit(void)
{
return shutdown(api_watcher.fd, SHUT_RDWR) ||
close(api_watcher.fd);
uev_io_stop(&api_watcher);
return close(api_watcher.fd);
}
/**
+109 -103
View File
@@ -1,7 +1,6 @@
/* Simple telinit client
/* External client API, using UNIX domain socket.
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2015-2017 Joachim Nilsson <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
@@ -22,133 +21,140 @@
* THE SOFTWARE.
*/
#include "config.h" /* Generated by configure script */
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <lite/lite.h>
#include "client.h"
#include "finit.h"
#include "util.h"
static int sd = -1;
static int do_send(struct init_request *rq)
int client_connect(void)
{
static int fd = -1;
int result = 255;
ssize_t len;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
if (fd == -1) {
if (!fexist(FINIT_FIFO)) {
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
return 1;
}
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
goto error;
fd = open(FINIT_FIFO, O_RDWR);
if (-1 == fd) {
perror("Failed opening " FINIT_FIFO);
return 1;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
close(sd);
goto error;
}
len = write(fd, rq, sizeof(*rq));
if (sizeof(*rq) == len)
result = 0;
return result;
return sd;
error:
perror("Failed connecting to finit");
return -1;
}
static int do_reload(void)
int client_disconnect(void)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RELOAD,
};
int rc;
return do_send(&rq);
}
static int set_env(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SETENV,
};
if (runlevel == 0) {
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
return do_send(&rq);
if (sd < 0) {
errno = EINVAL;
return -1;
}
return 0;
}
static int set_runlevel(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = runlevel,
};
return set_env(runlevel) || do_send(&rq);
}
/* telinit q | telinit <0-9> */
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n"
" -h, --help This help text\n"
" -V, --version Show Finit version\n\n"
"Commands:\n"
" 0 Power-off the system, same as initctl poweroff\n"
" 6 Reboot the system, same as initctl reboot\n"
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
" services in prev. runlevel that are not allowed in new.\n"
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
" sending SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm);
rc = close(sd);
sd = -1;
return rc;
}
int client(int argc, char *argv[])
int client_send(struct init_request *rq, ssize_t len)
{
int c;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0}
int sd, result = 255;
sd = client_connect();
if (-1 == sd)
return -1;
if (write(sd, rq, len) != len)
goto error;
if (read(sd, rq, len) != len)
goto error;
result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit:
client_disconnect();
return result;
}
svc_t *client_svc_iterator(int first)
{
int sd = -1;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_ITER,
};
static svc_t svc;
progname(argv[0]);
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
switch(c) {
case 'v':
return puts("v" VERSION) == EOF;
sd = client_connect();
if (sd == -1)
return NULL;
case 'h':
case '?':
return usage(0);
}
}
if (first)
rq.runlevel = 1;
else
rq.runlevel = 0;
if (optind < argc) {
int req = (int)argv[optind][0];
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
goto error;
if (isdigit(req))
return set_runlevel(req);
client_disconnect();
if (svc.pid < 0)
return NULL;
if (req == 'q' || req == 'Q')
return do_reload();
return &svc;
error:
perror("Failed communicating with finit");
client_disconnect();
sd = -1;
if (req == 's' || req == 'S')
return set_runlevel(1);
}
return NULL;
}
return usage(1);
svc_t *client_svc_find(char *arg)
{
int sd = -1;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_FIND,
};
static svc_t svc;
sd = client_connect();
if (sd == -1)
return NULL;
strlcpy(rq.data, arg, sizeof(rq.data));
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
goto error;
client_disconnect();
if (svc.pid < 0)
return NULL;
return &svc;
error:
client_disconnect();
perror("Failed communicating with finit");
sd = -1;
return NULL;
}
/**
+37
View File
@@ -0,0 +1,37 @@
/* External client API, using UNIX domain socket.
*
* Copyright (c) 2015-2017 Joachim Nilsson <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_CLIENT_H_
#define FINIT_CLIENT_H_
#include "finit.h"
#include "svc.h"
int client_connect (void);
int client_disconnect (void);
int client_send (struct init_request *rq, ssize_t len);
svc_t *client_svc_iterator (int first);
svc_t *client_svc_find (char *arg);
#endif /* FINIT_CLIENT_H_ */
+108 -26
View File
@@ -1,6 +1,6 @@
/* Condition engine (write)
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.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
@@ -25,33 +25,80 @@
#include <libgen.h>
#include <lite/lite.h>
#include <stdio.h>
#include <sys/stat.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "service.h"
static int cond_set_gen(const char *path, unsigned int gen)
{
FILE *fp;
int ret;
fp = fopen(path, "w");
if (!fp)
return -1;
ret = fprintf(fp, "%u\n", gen);
fclose(fp);
return (ret > 0) ? 0 : ret;
}
static void cond_bump_reconf(void)
{
unsigned int rgen;
/*
* If %COND_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_set_gen(COND_RECONF, rgen);
}
static int cond_checkpath(const char *path)
{
char buf[MAX_ARG_LEN], *dir;
strlcpy(buf, path, sizeof(buf));
dir = dirname(buf);
if (!dir) {
_e("Invalid path '%s' for condition", path);
return 1;
}
if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1;
}
return 0;
}
int cond_set_path(const char *path, enum cond_state new)
{
char buf[MAX_ARG_LEN], *dir;
enum cond_state old;
unsigned int rgen;
_d("%s", path);
rgen = cond_get_gen(COND_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
}
old = cond_get_path(path);
switch (new) {
case COND_ON:
strlcpy(buf, path, sizeof(buf));
dir = dirname(buf);
if (!dir) {
_e("Invalid path '%s' for condition", path);
return 0;
}
if (makepath(dir) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 0;
}
touch(path);
utimensat(0, path, NULL, 0);
if (cond_checkpath(path))
return 0;
cond_set_gen(path, rgen);
break;
case COND_OFF:
@@ -88,14 +135,14 @@ static int svc_has_cond(svc_t *svc)
static void cond_update(const char *name)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
_d("%s", name);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
continue;
_d("%s: match <%s> %s", name ?: "nil", svc->cond, svc->cmd);
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
service_step(svc);
}
}
@@ -103,15 +150,38 @@ static void cond_update(const char *name)
void cond_set(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_ON))
return;
cond_update(name);
}
void cond_set_oneshot(const char *name)
{
const char *path;
if (string_compare(name, "nop"))
return;
path = cond_path(name);
_d("s => %s", name, path);
if (cond_checkpath(path))
return;
symlink(COND_RECONF, path);
cond_update(name);
}
void cond_clear(const char *name)
{
_d("%s", name);
if (string_compare(name, "nop"))
return;
if (!cond_set_path(cond_path(name), COND_OFF))
return;
@@ -121,8 +191,8 @@ void cond_clear(const char *name)
void cond_reload(void)
{
_d("");
cond_set_path(COND_RECONF, COND_ON);
cond_bump_reconf();
cond_update(NULL);
}
@@ -136,7 +206,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F)
return 0;
nm = (char *)fpath + sizeof(COND_PATH);
nm = strstr((char *)fpath, COND_DIR);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
return 1;
}
nm += sizeof(COND_DIR);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
@@ -149,15 +225,21 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
*/
void cond_reassert(const char *type)
{
_d("%s", type ?: "reload done");
if (!type) {
cond_set_path(COND_RECONF, COND_OFF);
return;
}
_d("%s", type);
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
}
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);
return;
}
cond_bump_reconf();
}
/**
* Local Variables:
+28 -17
View File
@@ -1,6 +1,6 @@
/* Condition engine (read)
*
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.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
@@ -26,16 +26,9 @@
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "service.h"
static inline int timespec_newer(const struct timespec *a,
const struct timespec *b)
{
if (a->tv_sec != b->tv_sec)
return a->tv_sec > b->tv_sec;
return a->tv_nsec > b->tv_nsec;
}
const char *condstr(enum cond_state s)
{
@@ -50,25 +43,43 @@ const char *condstr(enum cond_state s)
const char *cond_path(const char *name)
{
char tmp[MAX_ARG_LEN];
static char file[MAX_ARG_LEN];
snprintf(file, sizeof(file), COND_PATH "/%s", name);
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
return file;
return pid_runpath(tmp, file, sizeof(file));
}
unsigned int cond_get_gen(const char *path)
{
unsigned int gen;
FILE *fp;
int ret;
fp = fopen(path, "r");
if (!fp)
return 0;
ret = fscanf(fp, "%u", &gen);
fclose(fp);
return (ret == 1) ? gen : 0;
}
enum cond_state cond_get_path(const char *path)
{
struct stat st, st_reconf;
int cgen, rgen;
if (stat(path, &st))
rgen = cond_get_gen(COND_RECONF);
if (!rgen)
return COND_OFF;
if (stat(COND_RECONF, &st_reconf) ||
timespec_newer(&st.st_mtim, &st_reconf.st_mtim))
return COND_ON;
cgen = cond_get_gen(path);
if (!cgen)
return COND_OFF;
return COND_FLUX;
return (cgen == rgen) ? COND_ON : COND_FLUX;
}
enum cond_state cond_get(const char *name)
+10 -7
View File
@@ -3,8 +3,8 @@
#include <paths.h>
#define COND_PATH _PATH_VARRUN "finit/cond"
#define COND_SVC_PATH COND_PATH "/svc/"
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf"
typedef enum cond_state {
@@ -15,16 +15,19 @@ typedef enum cond_state {
const char *condstr (enum cond_state s);
const char *cond_path (const char *name);
unsigned int cond_get_gen (const char *path);
enum cond_state cond_get_path(const char *path);
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);
int cond_set_path(const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
void cond_reassert(const char *pat);
int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_set_oneshot (const char *name);
void cond_clear (const char *name);
void cond_reload (void);
void cond_reassert (const char *pat);
void cond_init (void);
#endif /* FINIT_COND_H_ */
+466 -130
View File
@@ -25,8 +25,10 @@
#include <dirent.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/resource.h>
#include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include <sys/time.h>
#include "finit.h"
@@ -34,35 +36,111 @@
#include "service.h"
#include "tty.h"
#include "helpers.h"
#include "util.h"
#define BOOTSTRAP (runlevel == 0)
#define MATCH_CMD(l, c, x) \
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
static int parse_conf(char *file);
int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
struct rlimit global_rlimit[RLIMIT_NLIMITS];
struct conf_change {
TAILQ_ENTRY(conf_change) link;
char *name;
};
static uev_t w1, w2, w3;
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
static int parse_conf(char *file);
static void drop_changes(void);
void conf_parse_cmdline(void)
{
int dbg = 0;
FILE *fp;
char line[LINE_SIZE], *cmdline, *tok;
fp = fopen("/proc/cmdline", "r");
if (fp) {
char line[LINE_SIZE];
fgets(line, sizeof(line), fp);
chomp(line);
_d("%s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
dbg = 1;
if (!fp)
goto done;
if (!fgets(line, sizeof(line), fp)) {
fclose(fp);
goto done;
}
cmdline = chomp(line);
_d("%s", cmdline);
while ((tok = strtok(cmdline, " \t"))) {
cmdline = NULL;
/* Catches finit_debug (deprecated), --debug, and debug */
if (strstr(tok, "debug"))
dbg = 1;
if (string_compare(tok, "rescue") || string_compare(tok, "recover"))
rescue = 1;
if (string_compare(tok, "single") || string_compare(tok, "S"))
single = 1;
if (string_compare(tok, "splash"))
splash = 1;
}
fclose(fp);
done:
log_init(dbg);
}
static int kmod_exists(char *mod)
{
int found = 0;
FILE *fp;
char buf[256];
size_t len;
for (len = 0; mod[len]; len++) {
if (mod[len] == ' ' || mod[len] == '\t')
break;
}
fp = fopen("/proc/modules", "r");
if (!fp)
return 0;
while (!found && (fgets(buf, sizeof(buf), fp))) {
if (!strncmp(buf, mod, len))
found = 1;
}
fclose(fp);
return found;
}
static void kmod_load(char *line)
{
char *mod;
char cmd[CMD_SIZE];
if (runlevel != 0)
return;
mod = strip_line(line);
if (kmod_exists(mod))
return;
strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
}
/* Convert optional "[!123456789S]" string into a bitmask */
int conf_parse_runlevels(char *runlevels)
{
@@ -163,59 +241,101 @@ static const struct rlimit_name rlimit_names[] = {
{ NULL, 0 }
};
void conf_parse_rlimit(char *line)
int str2rlim(char *str)
{
struct rlimit rlim;
rlim_t new, *set;
const struct rlimit_name *name;
int resource = -1;
const struct rlimit_name *rn;
char *tok = strtok(line, " \t");
if (tok && !strcmp(tok, "soft"))
set = &rlim.rlim_cur;
else if (tok && !strcmp(tok, "hard"))
set = &rlim.rlim_max;
else
goto error;
tok = strtok(NULL, " \t");
if (!tok)
goto error;
for (name = rlimit_names; name->name; name++) {
if (!strcmp(tok, name->name))
resource = name->val;
for (rn = rlimit_names; rn->name; rn++) {
if (!strcmp(str, rn->name))
return rn->val;
}
if (resource < 0)
goto fail;
return -1;
}
tok = strtok(NULL, " \t");
if (!tok)
goto fail;
char *rlim2str(int rlim)
{
const struct rlimit_name *rn;
if (!strcmp(tok, "infinity")) {
new = RLIM_INFINITY;
for (rn = rlimit_names; rn->name; rn++) {
if (rn->val == rlim)
return rn->name;
}
return "unknown";
}
char *lim2str(struct rlimit *rlim)
{
char tmp[20];
static char buf[42];
memset(buf, 0, sizeof(buf));
if (RLIM_INFINITY == rlim->rlim_cur)
snprintf(tmp, sizeof(tmp), "unlimited, ");
else
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_cur);
strlcat(buf, tmp, sizeof(buf));
if (RLIM_INFINITY == rlim->rlim_max)
snprintf(tmp, sizeof(tmp), "unlimited, ");
else
snprintf(tmp, sizeof(tmp), "%llu, ", (unsigned long long)rlim->rlim_max);
strlcat(buf, tmp, sizeof(buf));
return buf;
}
/* First form: `rlimit <hard|soft> RESOURCE LIMIT` */
void conf_parse_rlimit(char *line, struct rlimit arr[])
{
char *level, *limit, *val;
int resource = -1;
rlim_t cfg;
level = strtok(line, " \t");
if (!level)
goto error;
limit = strtok(NULL, " \t");
if (!limit)
goto error;
val = strtok(NULL, " \t");
if (!val) {
/* Second form: `rlimit RESOURCE LIMIT` */
val = limit;
limit = level;
level = "both";
}
resource = str2rlim(limit);
if (resource < 0 || resource > RLIMIT_NLIMITS)
goto error;
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
cfg = RLIM_INFINITY;
} else {
const char *err = NULL;
new = strtonum(tok, 0, (long long)2 << 31, &err);
if (err)
goto fail;
cfg = strtonum(val, 0, (long long)2 << 31, &err);
if (err) {
logit(LOG_WARNING, "rlimit: invalid %s value: %s",
rlim2str(resource), val);
return;
}
}
if (getrlimit(resource, &rlim))
goto fail;
if (!strcmp(level, "soft"))
arr[resource].rlim_cur = cfg;
else if (!strcmp(level, "hard"))
arr[resource].rlim_max = cfg;
else if (!strcmp(level, "both"))
arr[resource].rlim_max = arr[resource].rlim_cur = cfg;
else
goto error;
*set = new;
if (setrlimit(resource, &rlim))
goto fail;
return;
fail:
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
return;
error:
logit(LOG_WARNING, "rlimit: parse error");
@@ -226,13 +346,13 @@ static void parse_static(char *line)
char *x;
char cmd[CMD_SIZE];
if (MATCH_CMD(line, "host ", x)) {
if (BOOTSTRAP && (MATCH_CMD(line, "host ", x) || MATCH_CMD(line, "hostname ", x))) {
if (hostname) free(hostname);
hostname = strdup(strip_line(x));
return;
}
if (MATCH_CMD(line, "mknod ", x)) {
if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) {
char *dev = strip_line(x);
strcpy(cmd, "mknod ");
@@ -242,13 +362,13 @@ static void parse_static(char *line)
return;
}
if (MATCH_CMD(line, "network ", x)) {
if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) {
if (network) free(network);
network = strdup(strip_line(x));
return;
}
if (MATCH_CMD(line, "runparts ", x)) {
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
if (runparts) free(runparts);
runparts = strdup(strip_line(x));
return;
@@ -267,16 +387,38 @@ static void parse_static(char *line)
return;
}
if (MATCH_CMD(line, "log ", x)) {
char *tok;
static int size = 200000, count = 5;
tok = strtok(x, ":= ");
while (tok) {
if (!strncmp(tok, "size", 4))
size = strtobytes(strtok(NULL, ":= "));
else if (!strncmp(tok, "count", 5))
count = strtobytes(strtok(NULL, ":= "));
tok = strtok(NULL, ":= ");
}
if (size >= 0)
logfile_size_max = size;
if (count >= 0)
logfile_count_max = count;
}
if (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown);
sdown = strdup(strip_line(x));
return;
}
/* The desired runlevel to start when leaving bootstrap (S).
/*
* The desired runlevel to start when leaving bootstrap (S).
* Finit supports 1-9, but most systems only use 1-6, where
* 6 is reserved for reboot */
if (MATCH_CMD(line, "runlevel ", x)) {
* 6 is reserved for reboot and 0 for halt/poweroff.
*/
if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) {
char *token = strip_line(x);
const char *err = NULL;
@@ -289,10 +431,9 @@ static void parse_static(char *line)
}
}
static void parse_dynamic(char *line, struct timeval *mtime)
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
{
char *x;
char cmd[CMD_SIZE];
/* Skip comments, i.e. lines beginning with # */
if (MATCH_CMD(line, "#", x))
@@ -300,41 +441,32 @@ static void parse_dynamic(char *line, struct timeval *mtime)
/* Kernel module to load at bootstrap */
if (MATCH_CMD(line, "module ", x)) {
char *mod;
if (runlevel != 0)
return;
mod = strip_line(x);
strcpy(cmd, "modprobe ");
strlcat(cmd, mod, sizeof(cmd));
run_interactive(cmd, "Loading kernel module %s", mod);
kmod_load(x);
return;
}
/* Monitored daemon, will be respawned on exit */
if (MATCH_CMD(line, "service ", x)) {
service_register(SVC_TYPE_SERVICE, x, mtime);
service_register(SVC_TYPE_SERVICE, x, rlimit, file);
return;
}
/* One-shot task, will not be respawned */
if (MATCH_CMD(line, "task ", x)) {
service_register(SVC_TYPE_TASK, x, mtime);
service_register(SVC_TYPE_TASK, x, rlimit, file);
return;
}
/* Like task but waits for completion, useful w/ [S] */
if (MATCH_CMD(line, "run ", x)) {
service_register(SVC_TYPE_RUN, x, mtime);
service_register(SVC_TYPE_RUN, x, rlimit, file);
return;
}
/* Classic inetd service */
if (MATCH_CMD(line, "inetd ", x)) {
#ifdef INETD_ENABLED
service_register(SVC_TYPE_INETD, x, mtime);
service_register(SVC_TYPE_INETD, x, rlimit, file);
#else
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
#endif
@@ -343,20 +475,13 @@ static void parse_dynamic(char *line, struct timeval *mtime)
/* Read resource limits */
if (MATCH_CMD(line, "rlimit ", x)) {
conf_parse_rlimit(x);
return;
}
/* Special TTY console powers */
if (MATCH_CMD(line, "console ", x)) {
if (console) free(console);
console = strdup(strip_line(x));
conf_parse_rlimit(x, rlimit);
return;
}
/* Regular or serial TTYs to run getty */
if (MATCH_CMD(line, "tty ", x)) {
tty_register(strip_line(x), mtime);
tty_register(strip_line(x), rlimit, file);
return;
}
}
@@ -372,9 +497,10 @@ static void tabstospaces(char *line)
}
}
static int parse_conf_dynamic(char *file, struct timeval *mtime)
static int parse_conf_dynamic(char *file)
{
FILE *fp;
struct rlimit rlimit[RLIMIT_NLIMITS];
fp = fopen(file, "r");
if (!fp) {
@@ -382,7 +508,10 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
return 1;
}
_d("Parsing %s", file);
/* Prepare default limits for each service */
memcpy(rlimit, global_rlimit, sizeof(rlimit));
_d("Parsing %s <<<<<<", file);
while (!feof(fp)) {
char line[LINE_SIZE] = "";
@@ -393,7 +522,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
tabstospaces(line);
_d("%s", line);
parse_dynamic(line, mtime);
parse_dynamic(line, rlimit, file);
}
fclose(fp);
@@ -405,34 +534,19 @@ static int parse_conf(char *file)
{
FILE *fp;
char line[LINE_SIZE] = "";
char *x;
/*
* Get current global limits, which may be overridden from both
* finit.conf, for Finit and its services like inetd+getty, and
* *.conf in finit.d/, for each service(s) listed there.
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
fp = fopen(file, "r");
if (!fp)
return 1;
/*
* If not standard finit.conf, then we want to show just the base name
* Loading configuration ............. vs
* Loading services configuration ....
*/
if (!string_match (file, FINIT_CONF)) {
/* Remove leading path */
x = strrchr(file, '/');
if (!x) x = file;
else x++;
/* Remove ending .conf */
strlcpy(line, x, sizeof(line));
x = strstr(line, ".conf");
if (x) *x = 0;
/* Add empty space. */
strcat(line, " ");
}
print(0, "Loading %sconfiguration", line);
_d("Parsing %s", file);
while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp))
@@ -443,29 +557,54 @@ static int parse_conf(char *file)
_d("%s", line);
parse_static(line);
parse_dynamic(line, NULL);
parse_dynamic(line, global_rlimit, NULL);
}
fclose(fp);
/* Set global limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &global_rlimit[i]) == -1)
logit(LOG_WARNING, "rlimit: Failed setting %s: %s",
rlim2str(i), lim2str(&global_rlimit[i]));
}
return 0;
}
/* Reload all *.conf in /etc/finit.d/ */
int conf_reload_dynamic(void)
/*
* Reload /etc/finit.conf and all *.conf in /etc/finit.d/
*/
int conf_reload(void)
{
int i, num;
char *dir = rcsd;
struct dirent **e;
/* Mark and sweep */
svc_mark_dynamic();
tty_mark();
num = scandir(dir, &e, NULL, alphasort);
if (rescue) {
int rc;
char line[80] = "tty [12345] @console noclear nologin";
/* If rescue.conf is missing, fall back to a root shell */
rc = parse_conf(RESCUE_CONF);
if (rc)
tty_register(line, global_rlimit, NULL);
print(rc, "Entering rescue mode");
goto done;
}
/* First, read /etc/finit.conf */
parse_conf(FINIT_CONF);
/* Next, read all *.conf in /etc/finit.d/ */
num = scandir(rcsd, &e, NULL, alphasort);
if (num < 0) {
_d("Skipping %s, no files found ...", dir);
return 1;
_d("Skipping %s, no files found ...", rcsd);
goto done;
}
for (i = 0; i < num; i++) {
@@ -473,13 +612,11 @@ int conf_reload_dynamic(void)
char path[LINE_SIZE];
size_t len;
struct stat st;
struct timeval mtime;
snprintf(path, sizeof(path), "%s/%s", dir, name);
snprintf(path, sizeof(path), "%s/%s", rcsd, name);
/* Check that it's an actual file ... beyond any symlinks */
if (lstat(path, &st)) {
lost:
_d("Skipping %s, cannot access: %s", path, strerror(errno));
continue;
}
@@ -496,10 +633,6 @@ int conf_reload_dynamic(void)
rp = realpath(path, NULL);
if (!rp) {
/*
* XXX: Prune from rcsd?
* XXX: Possibly temporary service from last boot
*/
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
continue;
}
@@ -514,27 +647,230 @@ int conf_reload_dynamic(void)
continue;
}
if (stat(path, &st))
goto lost; /* Weird, lost file between here and lstat() */
TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim);
parse_conf_dynamic(path, &mtime);
parse_conf_dynamic(path);
}
while (num--)
free(e[num]);
free(e);
done:
/* Drop record of all .conf changes */
drop_changes();
/* Override configured runlevel, user said 'S' on /proc/cmdline */
if (BOOTSTRAP && single)
cfglevel = 1;
/*
* Set host name, from %DEFHOST, *.conf or /etc/hostname. The
* latter wins, if neither exists we default to "noname"
*/
set_hostname(&hostname);
return 0;
}
int conf_parse_config(void)
static struct conf_change *conf_find(char *file)
{
struct conf_change *node, *tmp;
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) {
if (string_compare(node->name, file))
return node;
}
return NULL;
}
static void drop_change(struct conf_change *node)
{
if (!node)
return;
TAILQ_REMOVE(&conf_change_list, node, link);
free(node->name);
free(node);
}
static void drop_changes(void)
{
struct conf_change *node, *tmp;
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp)
drop_change(node);
}
static int do_change(char *name, uint32_t mask)
{
struct conf_change *node;
node = conf_find(name);
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
drop_change(node);
return 0;
}
if (node) {
_d("Event already registered for %s ...", name);
return 0;
}
node = malloc(sizeof(*node));
if (!node)
return 1;
node->name = strdup(name);
if (!node->name) {
free(node);
return 1;
}
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
return 0;
}
int conf_any_change(void)
{
if (TAILQ_EMPTY(&conf_change_list))
return 0;
return 1;
}
int conf_changed(char *file)
{
char *ptr;
if (!file)
return 0;
if ((ptr = strrchr(file, '/')))
file = ++ptr;
if (conf_find(file))
return 1;
return 0;
}
static void conf_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz, len;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
ev = (struct inotify_event *)ev_buf;
if (arg) {
do_change(arg, ev->mask);
return;
}
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
if (do_change(ev->name, ev->mask)) {
_pe("conf_monitor: Out of memory");
break;
}
}
}
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
{
struct stat st;
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE;
char *arg = NULL;
int fd, wd;
if (!ctx)
return 0;
if (stat(path, &st)) {
_d("No such file or directory, skipping %s", path);
w->fd = -1;
return 0;
}
if (!S_ISDIR(st.st_mode)) {
arg = strrchr(path, '/');
if (!arg)
arg = path;
else
arg++;
}
if (w->fd >= 0)
close(w->fd);
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
_pe("Failed creating inotify descriptor");
w->fd = -1;
return 1;
}
/*
* Only forward error, don't report error,
* user may not have @path and that's OK
*/
wd = inotify_add_watch(fd, path, mask | opt);
if (wd < 0) {
w->fd = -1;
close(fd);
return 1;
}
if (uev_io_init(ctx, w, conf_cb, arg, fd, UEV_READ)) {
_pe("Failed setting up I/O callback for %s watcher", path);
w->fd = -1;
close(fd);
return 1;
}
return 0;
}
/*
* Set up inotify watcher and load all *.conf in /etc/finit.d/
*/
int conf_monitor(uev_ctx_t *ctx)
{
int rc = 0;
/* Skip second run, when called from finit.c in rescue mode */
if (ctx && rescue)
return 0;
/*
* If only one watcher fails, that's OK. A user may have only
* one of /etc/finit.conf or /etc/finit.d in use, and may also
* have or not have symlinks in place. We need to monitor for
* changes to either symlink or target.
*/
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW);
rc += add_watcher(ctx, &w3, FINIT_CONF, 0);
return rc + conf_reload();
}
/*
* Prepare .conf parser and load all .conf files
*/
int conf_init(void)
{
hostname = strdup(DEFHOST);
w1.fd = w2.fd = w3.fd = -1;
return parse_conf(FINIT_CONF) || conf_reload_dynamic();
return conf_monitor(NULL);
}
/**
+14 -2
View File
@@ -26,11 +26,23 @@
#include "svc.h"
extern int logfile_size_max;
extern int logfile_count_max;
extern struct rlimit global_rlimit[];
int str2rlim(char *str);
char *rlim2str(int rlim);
int conf_init (void);
void conf_reload (void);
int conf_any_change (void);
int conf_changed (char *file);
int conf_monitor (uev_ctx_t *ctx);
void conf_parse_cmdline (void);
int conf_parse_runlevels (char *runlevels);
void conf_parse_cond (svc_t *svc, char *cond);
int conf_parse_config (void);
void conf_reload_dynamic (void);
#endif /* FINIT_CONF_H_ */
+121 -17
View File
@@ -30,17 +30,18 @@
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
#include <lite/lite.h>
#include "finit.h"
#include "conf.h"
#include "helpers.h"
#include "sig.h"
#include "utmp-api.h"
#define NUM_ARGS 16
int getty(char *tty, char *baud, char *term, char *user);
/* Wait for process completion, returns status of waitpid(2) syscall */
int complete(char *cmd, int pid)
@@ -234,30 +235,77 @@ int exec_runtask(char *cmd, char *args[])
return execvp(_PATH_BSHELL, argv);
}
static void prepare_tty(char *tty, char *procname, int console)
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
{
struct sigaction sa;
struct termios term;
/*
* Reset to sane defaults in case of messup from prev. session
*/
stty(STDIN_FILENO, speed);
/*
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
* It is up to the getty process to allow them again.
*/
tcdrain(STDIN_FILENO);
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag &= ~ISIG;
term.c_cc[VEOF] = _POSIX_VDISABLE;
term.c_cc[VINTR] = _POSIX_VDISABLE;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
/* Reset signal handlers that were set by the parent process */
sig_unblock();
/*
* Ignore a few signals, needed to prevent Ctrl-C at login:
* prompt and to prevent QUIT from dumping core.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &rlimit[i]) == -1)
logit(LOG_WARNING, "%s: rlimit: Failed setting %s", tty, rlim2str(i));
}
/* Create new session and process group */
setsid();
/* Set INIT_PROCESS UTMP entry */
/* Finit is responsible for the UTMP INIT_PROCESS record */
utmp_set_init(tty, 0);
if (console)
procname = "console";
prctl(PR_SET_NAME, procname, 0, 0, 0);
}
static int activate_console(int noclear, int nowait)
{
int ret = 0;
struct termios term;
if (!noclear)
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
if (nowait)
return 1;
/* Disable ECHO, XON/OFF while waiting for <CR> */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_iflag &= ~(IXON|IXOFF);
term.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
while (!fexist(SYNC_SHUTDOWN)) {
char c;
static const char clr[] = "\r\e[2K";
static const char msg[] = "\nPlease press Enter to activate this console.";
if (fexist(SYNC_STOPPED)) {
@@ -265,26 +313,63 @@ static int activate_console(int noclear, int nowait)
continue;
}
(void)write(STDERR_FILENO, msg, sizeof(msg));
(void)write(STDERR_FILENO, clr, strlen(clr));
(void)write(STDERR_FILENO, msg, strlen(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
continue;
if (fexist(SYNC_STOPPED))
continue;
return 1;
(void)write(STDERR_FILENO, clr, strlen(clr));
ret = 1;
break;
}
return 0;
/* Restore ECHO, XON/OFF */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_iflag |= IXON|IXOFF;
term.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
return ret;
}
pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int console)
/*
* Start a getty on @tty
*
* At the login: prompt, no signals are allowed, both Ctrl-C and Ctrl-D
* should be disabled. Ctrl-S and Ctrl-Q are optional, but most getty
* allow them.
*
* Prior to getty is called and login: is printed, Finit may display the
* "Please press Enter ..." if @nowait is unset. This mode must be RAW,
* only accepting <CR> and not echoing anything, this also means no
* signals are allowed. For ease of implementation Finit will call the
* stty() function to reset the TTY and then force RAW mode until a <CR>
* has been received. This is handled in the same fashion for both this
* function and run_getty2(), which is used for external getty.
*
* When handing over to /bin/login, Ctrl-C and Ctrl-D must be enabled
* since /bin/login usually only disables ECHO until a password line has
* been entered. Upon starting the user's $SHELL the ISIG flag is reset
*/
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
pid = fork();
if (!pid) {
prepare_tty(tty, "finit-getty", console);
speed_t speed = B38400;
if (baud) {
speed = stty_parse_speed(baud);
if (B0 == speed)
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
}
prepare_tty(tty, speed, "finit-getty", rlimit);
if (activate_console(noclear, nowait))
_exit(getty(tty, speed, term, NULL));
}
@@ -292,7 +377,7 @@ pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int
return pid;
}
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, int console)
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
@@ -321,7 +406,8 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
prepare_tty(tty, "getty", console);
/* Dunno speed, tell stty() to not mess with it */
prepare_tty(tty, B0, "getty", rlimit);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
_pe("Failed TIOCSCTTY");
@@ -337,6 +423,20 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
return pid;
}
pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[])
{
pid_t pid;
pid = fork();
if (!pid) {
prepare_tty(tty, B0, "finit-sh", rlimit);
if (activate_console(noclear, nowait))
_exit(sh(tty));
}
return pid;
}
int run_parts(char *dir, char *cmd)
{
struct dirent **e;
@@ -351,14 +451,18 @@ int run_parts(char *dir, char *cmd)
for (i = 0; i < num; i++) {
int j = 0;
pid_t pid = 0;
mode_t mode;
struct stat st;
char *args[NUM_ARGS];
char *name = e[i]->d_name;
char path[LINE_SIZE];
snprintf(path, sizeof(path), "%s/%s", dir, name);
mode = fmode(path);
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
if (stat(path, &st)) {
_d("Failed stat(%s): %s", path, strerror(errno));
continue;
}
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
_d("Skipping %s ...", path);
continue;
}
+150 -145
View File
@@ -36,6 +36,7 @@
#include <lite/lite.h>
#include "finit.h"
#include "cond.h"
#include "conf.h"
#include "helpers.h"
#include "private.h"
@@ -52,12 +53,14 @@ int wdogpid = 0; /* No watchdog by default */
int runlevel = 0; /* Bootstrap 'S' */
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
int prevlevel = -1;
int rescue = 0; /* rescue mode from kernel cmdline */
int single = 0; /* single user mode from kernel cmdline */
int splash = 0; /* splash + progress enabled on kernel cmdline */
char *sdown = NULL;
char *network = NULL;
char *hostname = NULL;
char *rcsd = FINIT_RCSD;
char *runparts = NULL;
char *console = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
@@ -81,14 +84,13 @@ static void banner(void)
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, SCREEN_WIDTH - (int)strlen(buf) - 2, separator);
}
/* Requires /proc to be mounted */
static int fismnt(char *dir)
static int ismnt(char *file, char *dir)
{
FILE *fp;
int found = 0;
struct mntent *mnt;
fp = setmntent("/proc/mounts", "r");
fp = setmntent(file, "r");
if (!fp)
return 0; /* Dunno, maybe not */
@@ -103,12 +105,18 @@ static int fismnt(char *dir)
return found;
}
/* Requires /proc to be mounted */
static int fismnt(char *dir)
{
return ismnt("/proc/mounts", dir);
}
/*
* Check all filesystems in /etc/fstab with a fs_passno > 0
*/
static int fsck(int pass)
{
int save;
// int save;
struct fstab *fs;
if (!setfsent()) {
@@ -116,8 +124,8 @@ static int fsck(int pass)
return 1;
}
if ((save = log_is_debug()))
log_debug();
// if ((save = log_is_debug()))
// log_debug();
while ((fs = getfsent())) {
char cmd[80];
@@ -143,61 +151,13 @@ static int fsck(int pass)
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
}
if (save)
log_debug();
// if (save)
// log_debug();
endfsent();
return 0;
}
static void networking(void)
{
FILE *fp;
/* Run user network start script if enabled */
if (network) {
run_interactive(network, "Starting networking: %s", network);
goto done;
}
/* Debian/Ubuntu/Busybox/RH/Suse */
if (!whichp("ifup"))
goto done;
fp = fopen("/etc/network/interfaces", "r");
if (fp) {
int i = 0;
char buf[160];
/* Bring up all 'auto' interfaces */
while (fgets(buf, sizeof(buf), fp)) {
char cmd[80];
char *line, *ifname = NULL;
chomp(buf);
line = strip_line(buf);
if (!strncmp(line, "auto", 4))
ifname = &line[5];
if (!strncmp(line, "allow-hotplug", 13))
ifname = &line[14];
if (!ifname)
continue;
snprintf(cmd, 80, "ifup %s", ifname);
run_interactive(cmd, "Bringing up interface %s", ifname);
i++;
}
fclose(fp);
}
done:
/* Fall back to bring up at least loopback */
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
}
/*
* If everything goes south we can use this to give the operator an
* emergency shell to debug the problem -- Finit should not crash!
@@ -227,7 +187,7 @@ static void emergency_shell(void)
}
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
fprintf(stderr, "=> To debug, add '--debug' to the kernel command line.\n\n");
fprintf(stderr, "=> To debug, add 'debug' to the kernel command line.\n\n");
/*
* Become session leader and set controlling TTY
@@ -241,11 +201,71 @@ static void emergency_shell(void)
#endif /* EMERGENCY_SHELL */
}
/*
* Handle bootstrap transition to configured runlevel, start TTYs
*
* This is the final stage of bootstrap. It changes to the default
* (configured) runlevel, calls all external start scripts and final
* bootstrap hooks before bringing up TTYs.
*
* We must ensure that all declared `task [S]` and `run [S]` jobs in
* finit.conf, or *.conf in finit.d/, run to completion before we
* finalize the bootstrap process by calling this function.
*/
static void finalize(void)
{
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts) && !rescue) {
_d("Running startup scripts in %s ...", runparts);
run_parts(runparts, NULL);
if (conf_any_change())
service_reload_dynamic();
}
/*
* Start all tasks/services in the configured runlevel
*/
_d("Change to default runlevel, start all services ...");
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */
_d("Clean up all bootstrap-only tasks/services ...");
svc_prune_bootstrap();
/* All services/tasks/inetd/etc. in configure runlevel have started */
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
service_step_all(SVC_TYPE_ANY);
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK) && !rescue) {
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
if (conf_any_change())
service_reload_dynamic();
}
/* Hooks that should run at the very end */
_d("Calling all system up hooks ...");
plugin_run_hooks(HOOK_SYSTEM_UP);
service_step_all(SVC_TYPE_ANY);
/* Enable silent mode before starting TTYs */
_d("Going silent ...");
log_silent();
/* Delayed start of TTYs at bootstrap */
_d("Launching all getty services ...");
tty_runlevel();
}
int main(int argc, char* argv[])
{
char *path;
char cmd[256];
int ret, udev = 0;
int udev = 0;
uev_t timer; /* Bootstrap timer, on timeout call finalize() */
uev_ctx_t loop;
/*
@@ -255,19 +275,6 @@ int main(int argc, char* argv[])
if (getpid() != 1)
return client(argc, argv);
/* Set up canvas */
screen_init();
/*
* In case of emergency.
*/
emergency_shell();
/*
* Initial setup of signals, ignore all until we're up.
*/
sig_init();
/*
* Initalize event context.
*/
@@ -290,32 +297,50 @@ int main(int argc, char* argv[])
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
/*
* Parse kernel parameters, including log_init()
* Parse kernel command line (debug, rescue, splash, etc.)
* Also calls log_init() to set correct log level
*/
conf_parse_cmdline();
/* Set up canvas */
if (!rescue && !log_is_debug())
screen_init();
/*
* In case of emergency.
*/
emergency_shell();
/*
* Initial setup of signals, ignore all until we're up.
*/
sig_init();
/*
* Load plugins early, finit.conf may contain references to
* features implemented by plugins.
*/
ret = plugin_init(&loop, PLUGIN_PATH);
plugin_init(&loop);
/*
* Hello world.
*/
banner();
/* Show results after banner() */
print(ret, "Loading plugins");
/*
* Check file filesystems in /etc/fstab
*/
for (int pass = 1; pass < 10; pass++) {
for (int pass = 1; pass < 10 && !rescue; pass++) {
if (fsck(pass))
break;
}
/*
* Initialize .conf system and load static /etc/finit.conf
* Also initializes global_rlimit[] for udevd, below.
*/
conf_init();
/*
* Some non-embedded systems without an initramfs may not have /dev mounted yet
* If they do, check if system has udevadm and perform cleanup from initramfs
@@ -329,8 +354,14 @@ int main(int argc, char* argv[])
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
/*
* Some systems rely on us to both create /dev/shm and, to mount
* a tmpfs there. Any system with dbus needs shared memory, so
* mount it, unless its already mounted, but not if listed in
* the /etc/fstab file already.
*/
makedir("/dev/shm", 0755);
if (!fismnt("/dev/shm"))
if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
/*
@@ -361,7 +392,7 @@ int main(int argc, char* argv[])
/* Register udevd as a monitored service, started much later */
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path);
if (service_register(SVC_TYPE_SERVICE, cmd, NULL)) {
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
_pe("Failed registering %s", path);
udev = 0;
}
@@ -385,49 +416,58 @@ int main(int argc, char* argv[])
}
}
/*
* Parse /etc/finit.conf and all *.conf in /etc/finit.d/ to
* figure out how to bootstrap the system.
*/
conf_parse_config();
/*
* Start built-in watchdog as soon as possible, if enabled
*/
watchdog(argv[0]);
/* Set hostname as soon as possible, for syslog et al. */
set_hostname(&hostname);
wdogpid = watchdog(argv[0]);
/*
* Mount filesystems
*/
if (!rescue) {
#ifdef REMOUNT_ROOTFS
run("mount -n -o remount,rw /");
run("mount -n -o remount,rw /");
#endif
#ifdef SYSROOT
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif
}
/* Debian has this little script to copy generated rules while the system was read-only */
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
/* Bootstrap conditions, needed for hooks */
cond_init();
umask(0);
if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
if (!rescue) {
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
run("swapon -ea");
umask(0022);
umask(0);
if (run_interactive("mount -na", "Mounting filesystems"))
plugin_run_hooks(HOOK_MOUNT_ERROR);
_d("Calling extra mount hook, after mount -a ...");
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
}
/* Base FS up, enable standard SysV init signals */
sig_setup(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
if (!rescue) {
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
}
/*
* Set up inotify watcher for /etc/finit.d and read all .conf
* files to figure out how to bootstrap the system.
*/
conf_monitor(&loop);
/*
* Initalize state machine and start all bootstrap tasks
@@ -436,56 +476,21 @@ int main(int argc, char* argv[])
sm_init(&sm);
sm_step(&sm);
/*
* Network stuff
*/
networking();
umask(022);
/* Hooks that rely on loopback, or basic networking being up. */
plugin_run_hooks(HOOK_NETWORK_UP);
/*
* Start all tasks/services in the configured runlevel
*/
service_runlevel(cfglevel);
/* Clean up bootstrap-only tasks/services that never started */
svc_prune_bootstrap();
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
/*
* Run startup scripts in the runparts directory, if any.
*/
if (runparts && fisdir(runparts)) {
_d("Running startup scripts in %s ...", runparts);
run_parts(runparts, NULL);
service_reload_dynamic();
}
/* Convenient SysV compat for when you just don't care ... */
if (!access(FINIT_RC_LOCAL, X_OK)) {
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
service_reload_dynamic();
}
/* Hooks that should run at the very end */
plugin_run_hooks(HOOK_SYSTEM_UP);
/* Enable silent mode before starting TTYs */
log_silent();
/* Delayed start of TTYs at bootstrap */
tty_runlevel();
/* Start new initctl API responder */
api_init(&loop);
umask(022);
/*
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
* [S], or timeout, before calling finalize()
*/
_d("Starting bootstrap finalize timer ...");
uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);
/*
* Enter main loop to monior /dev/initctl and services
*/
_d("Entering main loop ...");
return uev_run(&loop, 0);
}
+6 -1
View File
@@ -78,6 +78,9 @@
#define INIT_CMD_EMIT 15
#define INIT_CMD_GET_RUNLEVEL 16
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
#define INIT_CMD_SVC_ITER 129
#define INIT_CMD_SVC_QUERY 130
#define INIT_CMD_SVC_FIND 131
#define INIT_CMD_NACK 254
#define INIT_CMD_ACK 255
@@ -93,12 +96,14 @@ extern int wdogpid;
extern int runlevel;
extern int cfglevel;
extern int prevlevel;
extern int rescue;
extern int single;
extern int splash;
extern char *rcsd;
extern char *sdown;
extern char *network;
extern char *hostname;
extern char *runparts;
extern char *console;
extern uev_ctx_t *ctx;
#endif /* FINIT_H_ */
+63 -117
View File
@@ -64,42 +64,6 @@ static int readch(char *tty)
return ch1 & 0xFF;
}
static void stty(int fd, speed_t speed)
{
struct termios term;
tcdrain(fd);
if (tcgetattr(fd, &term))
return;
cfsetispeed(&term, speed);
cfsetospeed(&term, speed);
tcsetattr(fd, TCSAFLUSH, &term);
tcflush(fd, TCIOFLUSH);
/* Disable modem specific flags */
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
term.c_cflag &= ~CRTSCTS;
term.c_cflag |= CLOCAL;
/* Timeouts, minimum chars and default flags */
term.c_cc[VTIME] = 0;
term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF;
term.c_oflag = OPOST|ONLCR;
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
/* Reset special characters to defaults */
term.c_cc[VINTR] = CTRL('C');
term.c_cc[VQUIT] = CTRL('\\');
term.c_cc[VEOF] = CTRL('D');
term.c_cc[VEOL] = '\n';
term.c_cc[VKILL] = CTRL('U');
term.c_cc[VERASE] = CERASE;
tcsetattr(fd, TCSANOW, &term);
}
/*
* Parse and display a line from /etc/issue
*/
@@ -162,7 +126,6 @@ static void do_issue(char *tty)
*/
uname(&uts);
print("\n");
fp = fopen("/etc/issue", "r");
if (fp) {
while (fgets(buf, sizeof(buf), fp))
@@ -244,72 +207,10 @@ static int do_login(char *name)
return 1; /* We shouldn't get here ... */
}
static speed_t do_parse_speed(char *baud)
{
char *ptr;
size_t i;
unsigned long val;
struct { unsigned long val; speed_t speed; } v2s[] = {
{ 0, B0 },
{ 50, B50 },
{ 75, B75 },
{ 110, B110 },
{ 134, B134 },
{ 150, B150 },
{ 200, B200 },
{ 300, B300 },
{ 600, B600 },
{ 1200, B1200 },
{ 1800, B1800 },
{ 2400, B2400 },
{ 4800, B4800 },
{ 9600, B9600 },
{ 19200, B19200 },
{ 38400, B38400 },
{ 57600, B57600 },
{ 115200, B115200 },
{ 230400, B230400 },
{ 460800, B460800 },
{ 500000, B500000 },
{ 576000, B576000 },
{ 921600, B921600 },
{ 1000000, B1000000 },
{ 1152000, B1152000 },
{ 1500000, B1500000 },
{ 2000000, B2000000 },
{ 2500000, B2500000 },
{ 3000000, B3000000 },
{ 3500000, B3500000 },
{ 4000000, B4000000 },
};
errno = 0;
val = strtoul(baud, &ptr, 10);
if (errno || ptr == baud)
return B0;
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
if (v2s[i].val == val)
return v2s[i].speed;
}
return B0;
}
int getty(char *tty, char *baud, char *term, char *user)
int getty(char *tty, speed_t speed, char *term, char *user)
{
int fd;
char name[30];
speed_t speed = B38400;
struct sigaction sa;
if (baud) {
speed = do_parse_speed(baud);
if (speed == B0) {
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
return 1;
}
}
/* Detach from initial controlling TTY */
vhangup();
@@ -325,32 +226,77 @@ int getty(char *tty, char *baud, char *term, char *user)
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
warn("Failed TIOCSCTTY");
/*
* Ignore a few signals, needed to prevent Ctrl-C at login:
* prompt and to prevent QUIT from dumping core.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
/* Set up TTY */
stty(fd, speed);
close(fd);
/* The getty process is responsible for the UTMP login record */
utmp_set_login(tty, NULL);
if (term)
setenv("TERM", term, 1);
if (!user)
do_getty(tty, name, sizeof(name));
else
strlcpy(name, user, sizeof(name));
/* Set up TTY, re-enabling ISIG et al. */
stty(fd, speed);
close(fd);
if (term)
setenv("TERM", term, 1);
return do_login(name);
}
int sh(char *tty)
{
int fd;
char *arg0;
char *args[2] = {
NULL,
NULL
};
size_t len;
struct termios term;
/* Detach from initial controlling TTY */
vhangup();
fd = open(tty, O_RDWR);
if (fd < 0)
err(1, "Failed opening %s", tty);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
warn("Failed TIOCSCTTY");
/* The getty process is usually responsible for the UTMP login record */
utmp_set_login(tty, NULL);
/* Set up TTY, re-enabling ISIG et al. */
stty(fd, B0);
close(fd);
/* Start /bin/sh as a login shell, i.e. with a prefix '-' */
len = strlen(_PATH_BSHELL) + 2;
arg0 = malloc(len);
if (!arg0)
err(1, "Failed allocating memory");
snprintf(arg0, len, "-%s", _PATH_BSHELL);
args[0] = arg0;
/* Reenable Ctrl-D and Ctrl-C, and ... */
if (!tcgetattr(STDIN_FILENO, &term)) {
term.c_lflag |= ISIG;
term.c_cc[VEOF] = CTL('D');
term.c_cc[VINTR] = CTL('C');
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
/* ... unblock signals in general */
sig_unblock();
return execv(_PATH_BSHELL, args);
}
/**
* Local Variables:
* indent-tabs-mode: t
+79
View File
@@ -184,6 +184,9 @@ void set_hostname(char **hostname)
{
FILE *fp;
if (rescue)
goto done;
fp = fopen("/etc/hostname", "r");
if (fp) {
struct stat st;
@@ -204,10 +207,86 @@ void set_hostname(char **hostname)
fclose(fp);
}
done:
if (*hostname)
sethostname(*hostname, strlen(*hostname));
}
static void ifup(char *ifname, int updown)
{
char cmd[80];
if (updown) {
snprintf(cmd, sizeof(cmd), "ifup %s", ifname);
run_interactive(cmd, "Bringing up interface %s", ifname);
} else {
snprintf(cmd, sizeof(cmd), "ifdown -f %s", ifname);
run_interactive(cmd, "Taking down interface %s", ifname);
}
}
/*
* Bring up networking, but only if not single-user or rescue mode
*/
void networking(int updown)
{
FILE *fp;
/* No need to report errors if network is already down */
if (!prevlevel && !updown)
return;
if (updown)
_d("Setting up networking ...");
else
_d("Taking down networking ...");
/* Run user network start script if enabled */
if (updown && network) {
run_interactive(network, "Starting networking: %s", network);
goto done;
}
/* Debian/Ubuntu/Busybox/RH/Suse */
if (!whichp("ifup"))
goto done;
fp = fopen("/etc/network/interfaces", "r");
if (fp) {
char buf[160];
/* Bring up, or down, all 'auto' interfaces */
while (fgets(buf, sizeof(buf), fp)) {
char *line, *ifname = NULL;
chomp(buf);
line = strip_line(buf);
if (!strncmp(line, "auto", 4))
ifname = &line[5];
if (!strncmp(line, "allow-hotplug", 13))
ifname = &line[14];
if (!ifname)
continue;
ifup(ifname, updown);
}
fclose(fp);
}
done:
/* Fall back to bring up at least loopback */
ifconfig("lo", "127.0.0.1", "255.0.0.0", updown);
/* Hooks that rely on loopback, or basic networking being up. */
if (updown) {
_d("Calling all network up hooks ...");
plugin_run_hooks(HOOK_NETWORK_UP);
}
}
#ifndef HAVE_GETFSENT
static lfile_t *fstab = NULL;
+12 -5
View File
@@ -26,7 +26,10 @@
#define FINIT_HELPERS_H_
#include <stdarg.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
#include <lite/lite.h>
#include "log.h"
@@ -48,25 +51,29 @@ struct fstab *getfsent (void);
char *strip_line (char *line);
int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
int getty (char *tty, speed_t speed, char *term, char *user);
int sh (char *tty);
int stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud);
void printv (const char *fmt, va_list ap);
void print (int action, const char *fmt, ...);
void print_desc (char *action, char *desc);
int print_result (int fail);
int start_process (char *cmd, char *args[], int console);
int getuser (char *username, char **home);
int getgroup (char *group);
void set_hostname (char **hostname);
void networking (int updown);
int complete (char *cmd, int pid);
int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...);
int exec_runtask (char *cmd, char *args[]);
pid_t run_getty (char *tty, char *speed, char *term, int noclear, int nowait, int console);
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, int console);
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
pid_t run_sh (char *tty, int noclear, int nowait, struct rlimit rlimit[]);
int run_parts (char *dir, char *cmd);
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
+42 -10
View File
@@ -70,6 +70,23 @@ static int inetd_dgram_peek(int sd, char *ifname)
return -1;
}
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static void inetd_dgram_drop(int sd, const char *ifname)
{
char pkt_interface[17];
char buf[BUFSIZ];
while (1) {
inetd_dgram_peek(sd, pkt_interface);
if (string_compare(pkt_interface, ifname)) {
if (recv(sd, buf, sizeof(buf), 0) < 0)
break;
continue;
}
break;
}
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
static int inetd_stream_peek(int sd, char *ifname)
{
@@ -129,6 +146,8 @@ static int get_stdin(svc_t *svc)
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
if (svc->inetd.type == SOCK_STREAM)
close(stdin);
else
inetd_dgram_drop(stdin, ifname);
return -1;
}
@@ -140,9 +159,15 @@ static int get_stdin(svc_t *svc)
static void socket_cb(uev_t *w, void *arg, int events)
{
svc_t *svc = (svc_t *)arg, *task;
const char *conn = " connection";
int stdin;
_d("Got event on %s socket ...", svc->cmd);
_d("%s: Got socket event ...", svc->cmd);
if (UEV_ERROR == events) {
logit(LOG_INFO, "%s: Socket error, aborting: %m", svc->cmd);
return;
}
stdin = get_stdin(svc);
if (stdin < 0) {
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
@@ -184,11 +209,13 @@ static void socket_cb(uev_t *w, void *arg, int events)
task->inetd.cmd = svc->inetd.cmd;
task->inetd.type = svc->inetd.type;
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
memcpy(task->cond, svc->cond, sizeof(task->cond));
memcpy(task->username, svc->username, sizeof(task->username));
memcpy(task->group, svc->group, sizeof(task->group));
memcpy(task->args, svc->args, sizeof(task->args));
snprintf(task->desc, sizeof(task->desc), "%52.52s connection", svc->desc);
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
strlcat(task->desc, conn, sizeof(task->desc));
task->stdin_fd = stdin;
service_step(task);
@@ -202,10 +229,10 @@ static void socket_cb(uev_t *w, void *arg, int events)
*/
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
char pname[NI_MAXHOST];
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
inetd_t *i = &svc->inetd;
if (!i->builtin || i->type != SOCK_DGRAM)
@@ -310,16 +337,21 @@ int inetd_start(inetd_t *inetd)
void inetd_stop(inetd_t *inetd)
{
if (!inetd || !inetd->svc) {
_e("Invalid inetd, cannot stop it...");
return;
}
if (inetd->watcher.fd != -1) {
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
uev_io_stop(&inetd->watcher);
/* For dgram inetd services we block the parent SVC
* and halt the watcher, so don't close the socket! */
/*
* For dgram inetd services we block the parent SVC
* and halt the watcher, so don't close the socket!
*/
if (!svc_is_busy(inetd->svc)) {
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
if (inetd->type == SOCK_STREAM)
shutdown(inetd->watcher.fd, SHUT_RDWR);
close(inetd->watcher.fd);
inetd->watcher.fd = -1;
}
@@ -642,9 +674,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
svc_t *inetd_find_svc(char *path, char *service, char *proto)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
continue;
+97 -149
View File
@@ -33,11 +33,9 @@
#include <time.h>
#include <utmp.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <lite/lite.h>
#include "finit.h"
#include "client.h"
#include "cond.h"
#include "serv.h"
#include "service.h"
@@ -45,78 +43,30 @@
#define _PATH_COND _PATH_VARRUN "finit/cond/"
typedef struct {
struct command {
char *cmd;
int (*cb)(char *arg);
} command_t;
};
int verbose = 0;
int runlevel = 0;
static int do_send(struct init_request *rq, ssize_t len)
static int runlevel_get(int *prevlevel)
{
int sd, result = 255;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
return -1;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto error;
if (write(sd, rq, len) != len)
goto error;
if (read(sd, rq, len) != len)
goto error;
result = 0;
goto exit;
error:
perror("Failed communicating with finit");
exit:
close(sd);
return result;
}
static int runlevel_get(void)
{
int sd, result = 255;
int result;
struct init_request rq;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
memset(&rq, 0, sizeof(rq));
rq.cmd = INIT_CMD_GET_RUNLEVEL;
rq.magic = INIT_MAGIC;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
return -1;
result = client_send(&rq, sizeof(rq));
if (!result) {
result = rq.runlevel;
if (prevlevel)
*prevlevel = rq.sleeptime;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto error;
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
if (read(sd, &rq, sizeof(rq)) != sizeof(rq))
goto error;
result = rq.runlevel;
goto exit;
error:
perror("Failed communicating with finit");
result = -1;
exit:
close(sd);
return result;
}
@@ -127,7 +77,7 @@ static int toggle_debug(char *arg)
.cmd = INIT_CMD_DEBUG,
};
return do_send(&rq, sizeof(rq));
return client_send(&rq, sizeof(rq));
}
static int do_log(char *svc)
@@ -146,6 +96,9 @@ static int do_log(char *svc)
return system(cmd);
}
/*
* XXX: Convert to read UTMP instead, like runlevel(8) does
*/
static int do_runlevel(char *arg)
{
struct init_request rq = {
@@ -154,14 +107,25 @@ static int do_runlevel(char *arg)
.runlevel = (int)arg[0],
};
/* Not compatible with the SysV runlevel(8) command, it prints
* "PREVLEVEL RUNLEVEL", we just print the current runlevel. */
if (!rq.runlevel) {
printf("%d\n", runlevel_get());
char prev;
int runlevel, prevlevel;
runlevel = runlevel_get(&prevlevel);
if (255 == runlevel) {
printf("unknown\n");
return 0;
}
prev = prevlevel + '0';
if (prev <= '0' || prev > '9')
prev = 'N';
printf("%c %d\n", prev , runlevel);
return 0;
}
return do_send(&rq, sizeof(rq));
return client_send(&rq, sizeof(rq));
}
static int do_svc(int cmd, char *arg)
@@ -173,27 +137,12 @@ static int do_svc(int cmd, char *arg)
strlcpy(rq.data, arg, sizeof(rq.data));
return do_send(&rq, sizeof(rq));
return client_send(&rq, sizeof(rq));
}
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
static int missing(char *job, int id)
{
char idstr[10] = "";
if (!job)
job = "";
if (id > 1)
snprintf(idstr, sizeof(idstr), ":%d", id);
if (verbose)
fprintf(stderr, "No such service %s%s\n", job, idstr);
return 1;
}
/*
* This is a wrapper for do_svc() that adds a simple sanity check of
* the service(s) provided as argument. If a service does not exist
@@ -201,28 +150,20 @@ static int missing(char *job, int id)
*/
static int do_startstop(int cmd, char *arg)
{
int ret;
char *svcs;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SVC_QUERY
};
if (!arg) {
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]",
strlcpy(rq.data, arg, sizeof(rq.data));
if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
cmd == INIT_CMD_START_SVC ? "start" :
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
return 1;
}
svcs = strdup(arg);
if (!svcs) {
perror("Failed validating service(s) name");
return 1;
}
/* Include \0 in len, needed by parser */
ret = svc_parse_jobstr(svcs, strlen(svcs) + 1, NULL, missing);
free(svcs);
if (ret)
return ret;
return do_svc(cmd, arg);
}
@@ -265,6 +206,9 @@ static int do_cond_magic(char op, char *cond)
{
char event[368]; /* sizeof(init_request.data) */
if (!cond || strlen(cond) < 1)
return 1;
event[0] = op;
strlcpy(&event[1], cond, sizeof(event) - 1);
@@ -281,7 +225,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_COND "reconf"))
return 0;
len = strlen(_PATH_COND);
@@ -309,7 +253,7 @@ static int do_cond_show(char *arg)
printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
if (!svc->cond[0])
continue;
@@ -333,10 +277,10 @@ static int do_cond(char *cmd)
{
int c;
char *arg;
command_t command[] = {
struct command command[] = {
{ "show", do_cond_show },
{ "set", do_cond_set },
{ "clear", do_cond_clear },
{ "show", do_cond_show },
{ "dump", do_cond_dump },
{ NULL, NULL }
};
@@ -346,7 +290,7 @@ static int do_cond(char *cmd)
*arg++ = 0;
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd))
if (string_match(command[c].cmd, cmd))
return command[c].cb(arg);
}
@@ -431,7 +375,7 @@ static int show_version(char *arg)
char *runlevel_string(int runlevel, int levels)
{
int i, pos = 1;
static char lvl[20];
static char lvl[21];
memset(lvl, 0, sizeof(lvl));
lvl[0] = '[';
@@ -459,53 +403,49 @@ char *runlevel_string(int runlevel, int levels)
return lvl;
}
/* In verbose mode we skip the header and each service description.
/*
* In verbose mode we skip the header and each service description.
* This in favor of having all info on one line so a machine can more
* easily parse it. */
* easily parse it.
*/
static int show_status(char *arg)
{
svc_t *svc;
/* Fetch UTMP runlevel, needed for svc_status() call below */
runlevel = runlevel_get();
runlevel = runlevel_get(NULL);
if (arg && arg[0]) {
int id = 1;
char *ptr, *next;
ptr = strchr(arg, ':');
if (ptr) {
*ptr++ = 0;
next = strchr(ptr, ' ');
if (next)
*next = 0;
id = atonum(ptr);
}
if (isdigit(arg[0]))
svc = svc_find_by_jobid(atonum(arg), id);
else
svc = svc_find_by_nameid(arg, id);
long now = jiffies();
char buf[42] = "N/A";
svc = client_svc_find(arg);
if (!svc)
return 1;
printf("%s\n", svc_status(svc));
return 0;
printf("Service : %s\n", svc->cmd);
printf("Description : %s\n", svc->desc);
printf("PID : %d\n", svc->pid);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
printf("Status : %s\n", svc_status(svc));
printf("\n");
return do_log(svc->cmd);
}
if (!verbose)
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char jobid[10], args[512] = "", *lvls;
if (svc_is_unique(svc))
snprintf(jobid, sizeof(jobid), "%d", svc->job);
else
// if (svc_is_unique(svc))
// snprintf(jobid, sizeof(jobid), "%d", svc->job);
// else
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
printf("%-5s %7s ", jobid, svc_status(svc));
printf("%-9s %7s ", jobid, svc_status(svc));
if (svc_is_inetd(svc))
printf("inetd ");
else
@@ -526,7 +466,7 @@ static int show_status(char *arg)
else
name++;
printf("%-20.20s %-*.*s\n", name, adj, adj, svc->desc);
printf("%-16.16s %-*.*s\n", name, adj, adj, svc->desc);
continue;
}
@@ -539,7 +479,7 @@ static int show_status(char *arg)
};
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
if (do_send(&rq, sizeof(rq))) {
if (client_send(&rq, sizeof(rq))) {
snprintf(args, sizeof(args), "Unknown inetd");
info = args;
} else {
@@ -548,7 +488,7 @@ static int show_status(char *arg)
info = rq.data;
info[len - 1] = 0;
if (strcmp("internal", svc->cmd)) {
if (!string_match("internal", svc->cmd)) {
char *ptr;
ptr = strchr(info, ' ');
@@ -579,7 +519,7 @@ static int show_status(char *arg)
static int usage(int rc)
{
fprintf(stderr,
"Usage: %s [OPTIONS] <COMMAND>\n"
"Usage: %s [OPTIONS] [COMMAND]\n"
"\n"
"Options:\n"
" -v, --verbose Verbose output\n"
@@ -593,7 +533,9 @@ static int usage(int rc)
" list List all .conf in /etc/finit.d/\n"
" enable <CONF> Enable .conf in /etc/finit.d/available/\n"
" disable <CONF> Disable .conf in /etc/finit.d/[enabled/]\n"
" touch <CONF> Mark .conf in /etc/finit.d/ for reload\n"
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
"\n"
" cond set <COND> Set (assert) condition => +COND\n"
" cond clear <COND> Clear (deassert) condition => -COND\n"
@@ -604,7 +546,8 @@ static int usage(int rc)
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
" status | show Show status of services\n"
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
" status | show Show status of services, default command\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n"
@@ -625,7 +568,8 @@ static int do_help(char *arg)
int main(int argc, char *argv[])
{
int c;
command_t command[] = {
char *cmd, arg[120];
struct command command[] = {
{ "debug", toggle_debug },
{ "help", do_help },
{ "version", show_version },
@@ -633,6 +577,7 @@ int main(int argc, char *argv[])
{ "list", serv_list },
{ "enable", serv_enable },
{ "disable", serv_disable },
{ "touch", serv_touch },
{ "reload", do_reload },
{ "cond", do_cond },
@@ -673,20 +618,23 @@ int main(int argc, char *argv[])
}
screen_init();
if (optind < argc) {
char *cmd = argv[optind++];
char arg[120] = "";
while (optind < argc) {
strlcat(arg, argv[optind++], sizeof(arg));
if (optind < argc)
strlcat(arg, " ", sizeof(arg));
}
if (optind >= argc)
return show_status(NULL);
for (c = 0; command[c].cmd; c++) {
if (!strcmp(command[c].cmd, cmd))
return command[c].cb(arg);
}
memset(arg, 0, sizeof(arg));
cmd = argv[optind++];
while (optind < argc) {
strlcat(arg, argv[optind++], sizeof(arg));
if (optind < argc)
strlcat(arg, " ", sizeof(arg));
}
for (c = 0; command[c].cmd; c++) {
if (!string_match(command[c].cmd, cmd))
continue;
return command[c].cb(arg);
}
return usage(1);
+55 -29
View File
@@ -29,12 +29,22 @@
#include "finit.h"
#include "log.h"
#include "util.h"
static int up = 0;
static int debug = 0;
static int silent = SILENT_MODE; /* Completely silent, including boot */
static int loglevel = LOG_NOTICE;
static void muffler(void)
{
silent = SILENT_MODE;
/* User override from kernel cmdline */
if (splash)
silent = 0;
}
void log_init(int dbg)
{
if (dbg)
@@ -44,16 +54,27 @@ void log_init(int dbg)
loglevel = LOG_DEBUG;
else
loglevel = LOG_NOTICE;
muffler();
}
/* If we enabled terse mode at boot, restore to previous setting at shutdown */
void log_exit(void)
{
silent = SILENT_MODE;
muffler();
if (!silent) {
sched_yield();
fputs("\n", stderr);
}
/*
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (debug)
screen_exit();
else
screen_init();
}
void log_open(void)
@@ -63,7 +84,7 @@ void log_open(void)
closelog();
if (debug)
opts |= LOG_CONS | LOG_PERROR;
opts |= LOG_PERROR; /* LOG_CONS | */
openlog("finit", opts, LOG_DAEMON);
setlogmask(LOG_UPTO(loglevel));
@@ -91,12 +112,19 @@ void log_debug(void)
if (debug) {
silent = 0;
loglevel = LOG_DEBUG;
screen_exit();
} else {
silent = SILENT_MODE;
loglevel = LOG_NOTICE;
screen_init();
}
log_open();
/* Also toggle silent mode (progress) */
log_silent();
logit(LOG_NOTICE, "Debug mode %s", debug ? "enabled" : "disabled");
}
@@ -105,45 +133,43 @@ int log_is_debug(void)
return debug;
}
static void early_logit(int prio, const char *fmt, va_list ap)
{
FILE *fp;
if (!debug && prio > loglevel)
return;
fp = fopen("/dev/kmsg", "w");
if (fp) {
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
vfprintf(fp, fmt, ap);
fclose(fp);
if (debug)
vfprintf(stderr, fmt, ap);
} else {
vfprintf(stderr, fmt, ap);
}
}
/*
* Log to /dev/kmsg until syslogd has started, then openlog()
* and continue logging as a regular daemon.
*/
void logit(int prio, const char *fmt, ...)
{
FILE *fp;
va_list ap;
va_start(ap, fmt);
if (!up) {
if (!fexist("/dev/log")) {
early_logit(prio, fmt, ap);
goto done;
}
log_open();
if (up || fexist("/dev/log")) {
if (!up)
log_open();
vsyslog(prio, fmt, ap);
goto done;
}
if (!debug && prio > loglevel)
goto done;
fp = fopen("/dev/kmsg", "w");
if (!fp) {
vfprintf(stderr, fmt, ap);
goto done;
}
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
vfprintf(fp, fmt, ap);
fclose(fp);
if (debug) {
va_end(ap);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
}
vsyslog(prio, fmt, ap);
done:
va_end(ap);
}
+1 -1
View File
@@ -33,7 +33,7 @@
* errors and debug messages. For all other user messages, see logit()
*
* The default log level is LOG_NOTICE. To toggle LOG_DEBUG messages,
* use `initctl debug` or add `--debug` to the kernel cmdline.
* use `initctl debug` or add `debug` to the kernel cmdline.
*/
#define _d(fmt, args...) logit(LOG_DEBUG, "%s():" fmt "\n", __func__, ##args)
#define _w(fmt, args...) logit(LOG_WARNING, "%s():" fmt "\n", __func__, ##args)
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright (c) 2018 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SYSLOG_NAMES
#include <syslog.h>
#include <unistd.h>
#include <sys/stat.h>
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
{
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
static int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
if (cnt == 2 && !access(nfile, F_OK)) {
size_t len = 5 + strlen(nfile) + 1;
char cmd[len];
snprintf(cmd, len, "gzip %s", nfile);
system(cmd);
remove(nfile);
}
}
if (rename(file, nfile))
(void)truncate(file, 0);
else
create(file, st.st_mode, st.st_uid, st.st_gid);
} else {
if (truncate(file, 0))
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
}
}
return 0;
}
static int checksz(FILE *fp, off_t sz)
{
struct stat st;
if (sz <= 0)
return 0;
if (!fstat(fileno(fp), &st) && st.st_size > sz) {
fclose(fp);
return 1;
}
return 0;
}
static int flogit(char *logfile, int num, off_t sz, char *buf, size_t len)
{
FILE *fp;
reopen:
fp = fopen(logfile, "a");
if (!fp) {
syslog(LOG_ERR | LOG_PERROR, "Failed opening %s: %s", logfile, strerror(errno));
return 1;
}
if (buf[0]) {
fprintf(fp, "%s\n", buf);
fsync(fileno(fp));
if (checksz(fp, sz))
return logrotate(logfile, num, sz);
} else {
while ((fgets(buf, len, stdin))) {
fputs(buf, fp);
fsync(fileno(fp));
if (checksz(fp, sz)) {
logrotate(logfile, num, sz);
buf[0] = 0;
goto reopen;
}
}
}
return fclose(fp);
}
static int logit(int level, char *buf, size_t len)
{
if (buf[0]) {
syslog(level, "%s", buf);
return 0;
}
while ((fgets(buf, len, stdin)))
syslog(level, "%s", buf);
return 0;
}
static int parse_prio(char *arg, int *f, int *l)
{
char *ptr;
ptr = strchr(arg, '.');
if (ptr) {
*ptr++ = 0;
for (int i = 0; facilitynames[i].c_name; i++) {
if (!strcmp(facilitynames[i].c_name, arg)) {
*f = facilitynames[i].c_val;
break;
}
}
arg = ptr;
}
for (int i = 0; prioritynames[i].c_name; i++) {
if (!strcmp(prioritynames[i].c_name, arg)) {
*l = prioritynames[i].c_val;
break;
}
}
return 0;
}
static int usage(int code)
{
fprintf(stderr, "Usage: logit [OPTIONS] [MESSAGE]\n"
"\n"
"Write MESSAGE (or stdin) to syslog, or file (with logrotate)\n"
"\n"
" -h This help text\n"
" -p PRIO Priority (numeric or facility.level pair)\n"
" -t TAG Log using the specified tag (defaults to user name)\n"
" -s Log to stderr as well as the system log\n"
"\n"
" -f FILE File to write log messages to, instead of syslog\n"
" -n SIZE Number of bytes before rotating, default: 200 kB\n"
" -r NUM Number of rotated files to keep, default: 5\n"
" -v Show program version\n"
"\n"
"This version of logit is distributed as part of Finit.\n"
"Bug report address: %s\n", PACKAGE_BUGREPORT);
return code;
}
int main(int argc, char *argv[])
{
int c, rc, num = 5;
int facility = LOG_USER;
int level = LOG_INFO;
int log_opts = LOG_NOWAIT;
off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL;
char buf[512] = "";
while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) {
switch (c) {
case 'f':
logfile = optarg;
break;
case 'h':
return usage(0);
case 'n':
size = atoi(optarg);
break;
case 'p':
if (parse_prio(optarg, &facility, &level))
return usage(1);
break;
case 'r':
num = atoi(optarg);
break;
case 's':
log_opts |= LOG_PERROR;
break;
case 't':
ident = optarg;
break;
case 'v': /* version */
fprintf(stderr, "%s\n", version_info);
return 0;
default:
return usage(1);
}
}
if (!ident)
ident = getenv("LOGNAME") ?: getenv("USER");
if (optind < argc) {
size_t pos = 0, len = sizeof(buf);
while (optind < argc) {
size_t bytes;
bytes = snprintf(&buf[pos], len, "%s ", argv[optind++]);
pos += bytes;
len -= bytes;
}
}
openlog(ident, log_opts, facility);
if (logfile)
rc = flogit(logfile, num, size, buf, sizeof(buf));
else
rc = logit(level, buf, sizeof(buf));
closelog();
return rc;
}
+116 -2
View File
@@ -1,7 +1,7 @@
/* Misc. utility functions and C-library extensions for finit and its plugins
/* PID and PID file helpers
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2008-2018 Joachim Nilsson <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
@@ -23,11 +23,14 @@
*/
#include <errno.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <lite/lite.h>
#include "pid.h"
#include "svc.h"
#include "helpers.h"
@@ -90,6 +93,117 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
return pname;
}
char *pid_file(svc_t *svc)
{
char fn[MAX_ARG_LEN];
static char path[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));
return pid_runpath(fn, path, sizeof(path));
}
int pid_file_create(svc_t *svc)
{
FILE *fp;
if (!svc->pidfile[0] || svc->pidfile[0] == '!')
return 1;
fp = fopen(svc->pidfile, "w");
if (!fp)
return 1;
fprintf(fp, "%d\n", svc->pid);
return fclose(fp);
}
static int pid_realpath(svc_t *svc, char *file)
{
int not = 0;
if (!file)
file = pid_file(svc);
if (file[0] == '!') {
not = 1;
file++;
}
pid_runpath(file, &svc->pidfile[not], sizeof(svc->pidfile) - not);
if (not)
svc->pidfile[0] = '!';
return 0;
}
/*
* This function parses a PID file @arg for @svc.
*
* The logic is explained below. Please note that using the first form
* of the syntax not only creates and removes the PID file, but it also
* calls touch to update the mtime on service_restart(), only applicable
* to processes that accept SIGHUP. This to ensure dependant services
* are sent SIGCONT properly on reload, otherwise their condition would
* never be asserted again after `initctl reload`.
*
* pid --> /run/$(basename).pid
* pid:foo --> /run/foo.pid
* pid:foo.pid --> /run/foo.pid
* pid:foo.tla --> /run/foo.tla.pid
* pid:/tmp/foo --> /tmp/foo.pid (Not handled by pidfile plugin!)
*
* An exclamation mark, or logial NOT, in the first character position
* indicates that Finit should *not* manage the PID file, and that the
* process uses a "non-standard" location. Eg. if service is 'bar'
* Finit would assume the PID file is /run/bar.pid and the below simply
* 'redirects' Finit to use the correct PID file.
*
* pid:!foo --> !/run/foo.pid
* pid:!/run/foo.pid --> !/run/foo.pid
*
* Note, nothing is created or removed by Finit in this latter form.
*/
int pid_file_parse(svc_t *svc, char *arg)
{
/* Sanity check ... */
if (!arg || !arg[0])
return 1;
/* 'pid:' implies argument following*/
if (!strncmp(arg, "pid:", 4)) {
int len, not = 0;
char path[MAX_ARG_LEN];
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_realpath(svc, arg);
if (arg[0] == '!') {
arg++;
not++;
}
len = snprintf(path, sizeof(path), "%s%s%s", not ? "!" : "", _PATH_VARRUN, arg);
if (len > 4 && strcmp(&path[len - 4], ".pid"))
strlcat(path, ".pid", sizeof(path));
return pid_realpath(svc, path);
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid"))
return pid_realpath(svc, NULL);
return 1;
}
/**
* Local Variables:
* indent-tabs-mode: t
+65
View File
@@ -0,0 +1,65 @@
/* PID and PID file helpers
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2018 Joachim Nilsson <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_PID_H_
#define FINIT_PID_H_
#include "svc.h"
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_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
static inline char *pid_runpath(char *file, char *path, size_t len)
{
static int unknown = 1;
static char *prefix = "/var/run";
if (unknown) {
if (fisdir("/run"))
prefix = "/run";
unknown = 0;
}
if (!strncmp(file, "/var/run/", 9))
file += 9;
else if (!strncmp(file, "/run/", 5))
file += 5;
snprintf(path, len, "%s/%s", prefix, file);
return path;
}
#endif /* FINIT_PID_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+41 -5
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2012-2017 Joachim Nilsson <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
@@ -30,10 +30,12 @@
#include <lite/queue.h> /* BSD sys/queue.h API */
#include "config.h"
#include "cond.h"
#include "finit.h"
#include "private.h"
#include "helpers.h"
#include "plugin.h"
#include "private.h"
#include "service.h"
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
#define SEARCH_PLUGIN(str) \
@@ -106,6 +108,9 @@ int plugin_register(plugin_t *plugin)
/* Not called, at the moment plugins cannot be unloaded. */
int plugin_unregister(plugin_t *plugin)
{
if (is_io_plugin(plugin))
uev_io_stop(&plugin->watcher);
#ifndef ENABLE_STATIC
TAILQ_REMOVE(&plugins, plugin, link);
@@ -163,6 +168,15 @@ plugin_t *plugin_find(char *name)
}
/* Private daemon API *******************************************************/
#define CHOOSE(x, y) y
static const char *hook_cond[] = HOOK_TYPES;
#undef CHOOSE
const char *plugin_hook_str(hook_point_t no)
{
return hook_cond[no];
}
int plugin_exists(hook_point_t no)
{
plugin_t *p, *tmp;
@@ -178,14 +192,32 @@ int plugin_exists(hook_point_t no)
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
void plugin_run_hook(hook_point_t no, void *arg)
{
static int last = -1;
plugin_t *p, *tmp;
/*
* End recursion: any plugin hook => start service => SVC start
* hook => start service ... err ... wait a second
*/
if (HOOK_SVC_START == last)
return;
PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) {
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
p->hook[no].cb(arg ? arg : p->hook[no].arg);
}
}
/* Guard against infinite recursion */
if (HOOK_SVC_START == no)
last = no;
cond_set_oneshot(hook_cond[no]);
service_step_all(SVC_TYPE_RUNTASK);
if (HOOK_SVC_START == no)
last = -1;
}
/* Regular hooks are called with the registered plugin's argument */
@@ -194,7 +226,11 @@ void plugin_run_hooks(hook_point_t no)
plugin_run_hook(no, NULL);
}
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
/*
* Generic libev I/O callback, looks up correct plugin and calls its
* callback. libuEv might return UEV_ERROR in events, it is up to the
* plugin callback to handle this.
*/
static void generic_io_cb(uev_t *w, void *arg, int events)
{
plugin_t *p = (plugin_t *)arg;
@@ -349,11 +385,11 @@ static int load_plugins(char *path)
}
#endif /* ENABLE_STATIC */
int plugin_init(uev_ctx_t *ctx, char *path)
int plugin_init(uev_ctx_t *ctx)
{
int fail = 1;
if (!load_plugins(path))
if (!load_plugins(PLUGIN_PATH))
fail = init_plugins(ctx);
return fail;
+48 -25
View File
@@ -1,6 +1,6 @@
/* Plugin based services architecture for finit
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
* Copyright (c) 2012-2017 Joachim Nilsson <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
@@ -29,13 +29,17 @@
#include "svc.h"
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
#define PLUGIN_PATH "/dev/null"
#endif
#define PLUGIN_DEP_MAX 10
/*
* Event flags for I/O plugins
*/
#define PLUGIN_IO_ERROR UEV_ERROR
#define PLUGIN_IO_READ UEV_READ
#define PLUGIN_IO_WRITE UEV_WRITE
#define PLUGIN_IO_PRI UEV_PRI
#define PLUGIN_IO_HUP UEV_HUP
#define PLUGIN_IO_RDHUP UEV_RDHUP
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
@@ -43,28 +47,47 @@
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
/*
* Predefined hook points for easier plugin debugging
* Predefined hook points and corresponding conditions in Finit,
* for use by plugins and scripts. Recommended to use the task
* or run stanzas: task <hook/mount/error> /bin/rescue.sh
*
* Some of the below hooks cannot (currently) be realized as conditions.
* The idea was to rename them action scripts, with optional argument,
* according to the following scheme:
*
* - HOOK_SVC_RECONF :: action/svc/reconf
* - HOOK_SVC_LOST :: action/svc/lost
* - HOOK_SVC_START :: action/svc/start
* - HOOK_RUNLEVEL_CHANGE :: action/sys/runlevel
*
* However, the implementation did not turn out to be stable enough for
* general release, so it was pulled.
*/
typedef enum {
/* Bootstrap hooks */
HOOK_BANNER = 0,
HOOK_ROOTFS_UP,
HOOK_MOUNT_ERROR,
HOOK_BASEFS_UP,
HOOK_NETWORK_UP,
HOOK_SVC_UP,
HOOK_SYSTEM_UP,
#define HOOK_TYPES { \
/* Bootstrap hooks, runlevel [S] */ \
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
CHOOSE(HOOK_MOUNT_POST, "hook/mount/post"), \
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
\
/* Runtime hooks, runlevel [S1-9] */ \
CHOOSE(HOOK_SVC_RECONF, "nop"), \
CHOOSE(HOOK_SVC_LOST, "nop"), \
CHOOSE(HOOK_SVC_START, "nop"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "nop"), \
\
/* Shutdown hooks, runlevel [06] */ \
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
CHOOSE(HOOK_MAX_NUM, "nop") \
}
/* Runtime hooks */
HOOK_SVC_RECONF,
HOOK_SVC_LOST,
HOOK_SVC_START,
HOOK_RUNLEVEL_CHANGE,
/* Shutdown hooks */
HOOK_SHUTDOWN,
HOOK_MAX_NUM
} hook_point_t;
#define CHOOSE(x, y) x
typedef enum HOOK_TYPES hook_point_t;
#undef CHOOSE
/**
* plugin_t - Finit &plugin_t object
+2 -1
View File
@@ -40,11 +40,12 @@ int client (int argc, char *argv[]);
void service_monitor (pid_t lost);
const char *plugin_hook_str(hook_point_t no);
int plugin_exists (hook_point_t no);
void plugin_run_hook (hook_point_t no, void *arg);
void plugin_run_hooks (hook_point_t no);
int plugin_init (uev_ctx_t *ctx, char *path);
int plugin_init (uev_ctx_t *ctx);
void plugin_exit (void);
#endif /* FINIT_PRIVATE_H_ */
+1 -1
View File
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
if (c)
err(1, "Failed signalling init to %s", msg);
do_sleep(2);
do_sleep(5);
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
runlevel 1
hostname rescue
tty [12345] @console noclear nologin
+25
View File
@@ -24,6 +24,7 @@
#include "config.h"
#include <err.h>
#include <fcntl.h> /* Definition of AT_* constants */
#include <glob.h>
#include <stdio.h>
#include <lite/lite.h>
@@ -168,6 +169,30 @@ int serv_disable(char *arg)
return remove(link) != 0;
}
int serv_touch(char *arg)
{
char corr[40];
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
}
pushd(FINIT_RCSD);
if (!fexist(arg)) {
popd();
if (!strstr(arg, "finit.conf"))
errx(1, "Service %s is not enabled", arg);
arg = FINIT_CONF;
}
/* libite:touch() follows symlinks */
if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW))
err(1, "Failed marking %s for reload", arg);
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
+1
View File
@@ -27,6 +27,7 @@
int serv_list (char *arg);
int serv_enable (char *arg);
int serv_disable (char *arg);
int serv_touch (char *arg);
#endif /* FINIT_SERV_H_ */
+383 -142
View File
@@ -25,7 +25,9 @@
#include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */
#include <sched.h> /* sched_yield() */
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <net/if.h>
#include <lite/lite.h>
@@ -35,66 +37,47 @@
#include "finit.h"
#include "helpers.h"
#include "inetd.h"
#include "pid.h"
#include "private.h"
#include "sig.h"
#include "service.h"
#include "sm.h"
#include "tty.h"
#include "util.h"
#include "utmp-api.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static void svc_set_state(svc_t *svc, svc_state_t new);
/**
* service_enabled - Should the service run?
* @svc: Pointer to &svc_t object
*
* Returns:
* 1, if the service is allowed to run in the current runlevel and the
* user has not manually requested that this service should not run. 0
* otherwise.
*/
int service_enabled(svc_t *svc)
{
if (!svc)
return 0;
if (!svc_in_runlevel(svc, runlevel))
return 0;
if (svc_is_removed(svc) || svc_is_blocked(svc))
return 0;
return 1;
}
/**
* service_timeout_cb - libuev callback wrapper for service timeouts
* @w: Watcher
* @arg: Callback argument, from init
* @events: Error, or ready to read/write (N/A for relative timers)
*
* Calls the callback registered with the call to
* service_timeout_after().
* Run callback registered when calling service_timeout_after().
*/
static void service_timeout_cb(uev_t *w, void *arg, int events)
{
svc_t *svc = arg;
svc->timer_cb(svc);
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
if (svc->timer_cb)
svc->timer_cb(svc);
}
/**
* service_timeout_after - Call a function after some time has elapsed
* @param svc Service to use as argument to the callback
* @param timeout Timeout, in milliseconds
* @param cb Callback function
* @svc: Service to use as argument to the callback
* @timeout: Timeout, in milliseconds
* @cb: Callback function
*
* After @param timeout milliseconds has elapsed, calls @param cb with
* @param svc as the argument.
* After @timeout milliseconds has elapsed, call @cb() with @svc as the
* argument.
*
* @return 0 on success, non-zero on error.
* Returns:
* POSIX OK(0) on success, non-zero on error.
*/
static int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc))
{
@@ -107,11 +90,12 @@ static int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc)
/**
* service_timeout_cancel - Cancel timeout associated with service
* @param svc Service whose timeout to cancel
* @svc: Service whose timeout to cancel
*
* If a timeout is associated with @param svc, cancel it.
* If a timeout is associated with @svc, cancel it.
*
* @return 0 on success, non-zero on error.
* Returns:
* POSIX OK(0) on success, non-zero on error.
*/
static int service_timeout_cancel(svc_t *svc)
{
@@ -126,6 +110,18 @@ static int service_timeout_cancel(svc_t *svc)
return err;
}
static void redirect_null(void)
{
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
fclose(fp);
}
}
static int is_norespawn(void)
{
return sig_stopped() ||
@@ -142,7 +138,7 @@ static int is_norespawn(void)
*/
static int service_start(svc_t *svc)
{
int i, result = 0;
int i, result = 0, do_progress = 1;
pid_t pid;
sigset_t nmask, omask;
@@ -160,16 +156,21 @@ static int service_start(svc_t *svc)
return 1;
}
if (svc_is_daemon(svc) || svc_is_inetd(svc))
print_desc("Starting ", svc->desc);
else
print_desc("", svc->desc);
#ifdef INETD_ENABLED
if (svc_is_inetd(svc))
return print_result(inetd_start(&svc->inetd));
return inetd_start(&svc->inetd);
#endif
if (!svc->desc[0])
do_progress = 0;
if (do_progress) {
if (svc_is_daemon(svc))
print_desc("Starting ", svc->desc);
else
print_desc("", svc->desc);
}
/* Declare we're waiting for svc to create its pidfile */
svc_starting(svc);
@@ -191,6 +192,14 @@ static int service_start(svc_t *svc)
#endif
char *args[MAX_NUM_SVC_ARGS];
/* Set configured limits */
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (setrlimit(i, &svc->rlimit[i]) == -1)
logit(LOG_WARNING,
"%s: rlimit: Failed setting %s",
svc->cmd, rlim2str(i));
}
/* Set desired user+group */
if (gid >= 0)
setgid(gid);
@@ -222,30 +231,38 @@ static int service_start(svc_t *svc)
} else
#endif
if (svc->log) {
if (svc->log.enabled) {
int fd;
if (svc->log.null) {
redirect_null();
goto logit_done;
}
/*
* Open PTY to connect to logger. A pty isn't buffered
* like a pipe, and it eats newlines so they aren't logged
*/
fd = posix_openpt(O_RDWR);
if (fd == -1) {
svc->log = 0;
goto logger_err;
svc->log.enabled = 0;
goto logit_done;
}
if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
close(fd);
svc->log = 0;
goto logger_err;
svc->log.enabled = 0;
goto logit_done;
}
/* SIGCHLD is still blocked for grantpt() and fork() */
sigprocmask(SIG_BLOCK, &nmask, NULL);
pid = fork();
if (pid == 0) {
int fds = open(ptsname(fd), O_RDONLY);
int fds;
char *tag = basename(svc->cmd);
char *prio = "daemon.info";
fds = open(ptsname(fd), O_RDONLY);
close(fd);
if (fds == -1)
_exit(0);
@@ -254,7 +271,22 @@ static int service_start(svc_t *svc)
/* Reset signals */
sig_unblock();
execlp("logger", "logger", "-t", svc->cmd, "-p", "daemon.info", NULL);
if (svc->log.file[0] == '/') {
char sz[20], num[3];
snprintf(sz, sizeof(sz), "%d", logfile_size_max);
snprintf(num, sizeof(num), "%d", logfile_count_max);
execlp("logit", "logit", "-f", svc->log.file, "-n", sz, "-r", num, NULL);
_exit(0);
}
if (svc->log.ident[0])
tag = svc->log.ident;
if (svc->log.prio[0])
prio = svc->log.prio;
execlp("logit", "logit", "-t", tag, "-p", prio, NULL);
_exit(0);
}
@@ -270,23 +302,18 @@ static int service_start(svc_t *svc)
dup2(fd, STDERR_FILENO);
close(fd);
}
} else {
FILE *fp;
fp = fopen("/dev/null", "w");
if (fp) {
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
fclose(fp);
}
}
#ifdef REDIRECT_OUTPUT
else
redirect_null();
#endif
logger_err:
logit_done:
sig_unblock();
if (svc->inetd.cmd)
status = svc->inetd.cmd(svc->inetd.type);
else if (svc->type & SVC_TYPE_RUNTASK)
else if (svc_is_runtask(svc))
status = exec_runtask(svc->cmd, args);
else
status = execv(svc->cmd, args);
@@ -300,9 +327,9 @@ static int service_start(svc_t *svc)
}
} else
#endif
if (svc->log)
if (svc->log.enabled && !svc->log.null)
waitpid(pid, NULL, 0);
exit(status);
_exit(status);
} else if (log_is_debug()) {
char buf[CMD_SIZE] = "";
@@ -317,25 +344,37 @@ static int service_start(svc_t *svc)
}
svc->pid = pid;
svc->start_time = jiffies();
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc) && svc->inetd.type == SOCK_STREAM)
close(svc->stdin_fd);
#endif
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
if (SVC_TYPE_RUN == svc->type) {
result = WEXITSTATUS(complete(svc->cmd, pid));
if (!svc_clean_bootstrap(svc)) {
svc->pid = 0;
svc->start_time = svc->pid = 0;
svc_set_state(svc, SVC_STOPPING_STATE);
}
}
sigprocmask(SIG_SETMASK, &omask, NULL);
return print_result(result);
if (svc_is_daemon(svc))
pid_file_create(svc);
sigprocmask(SIG_SETMASK, &omask, NULL);
if (do_progress)
print_result(result);
/*
* Only run hook on successful start, and *after* having printed
* the result, otherwise any hook tasks may overwrite it and the
* result would be like double "[ OK ]" but only one service.
*/
if (!result)
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
return result;
}
/**
@@ -348,10 +387,16 @@ static void service_kill(svc_t *svc)
{
service_timeout_cancel(svc);
if (svc->pid <= 1) {
/* Avoid killing ourselves or all processes ... */
_d("%s: Aborting SIGKILL, already terminated.", svc->cmd);
return;
}
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
if (runlevel != 1)
print_desc("Killing ", svc->desc);
_d("Sending SIGKILL to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
kill(svc->pid, SIGKILL);
/* Let SIGKILLs stand out, show result as [WARN] */
@@ -375,31 +420,31 @@ static int service_stop(svc_t *svc)
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
int do_print = runlevel != 1 && !svc_is_busy(svc);
int do_progress = runlevel != 1 && !svc_is_busy(svc);
if (do_print)
if (do_progress)
print_desc("Stopping ", svc->desc);
inetd_stop(&svc->inetd);
if (do_print)
if (do_progress)
print_result(0);
svc_set_state(svc, SVC_STOPPING_STATE);
return 0;
}
} else
#endif
service_timeout_cancel(svc);
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
if (svc->pid <= 1)
return 1;
}
if (SVC_TYPE_SERVICE != svc->type)
return 0;
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
svc_set_state(svc, SVC_STOPPING_STATE);
if (runlevel != 1)
print_desc("Stopping ", svc->desc);
_d("Sending SIGTERM to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
res = kill(svc->pid, SIGTERM);
if (runlevel != 1)
@@ -420,6 +465,8 @@ static int service_stop(svc_t *svc)
*/
static int service_restart(svc_t *svc)
{
int rc;
/* Ignore if finit is SIGSTOP'ed */
if (is_norespawn())
return 1;
@@ -429,17 +476,29 @@ static int service_restart(svc_t *svc)
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
svc->pid = 0;
svc->start_time = svc->pid = 0;
return 1;
}
print_desc("Restarting ", svc->desc);
_d("Sending SIGHUP to PID %d", svc->pid);
if (svc->desc[0])
print_desc("Restarting ", svc->desc);
rc = kill(svc->pid, SIGHUP);
/* Declare we're waiting for svc to re-assert/touch its pidfile */
svc_starting(svc);
_d("Sending SIGHUP to PID %d", svc->pid);
return print_result(kill(svc->pid, SIGHUP));
/* Service does not maintain a PID file on its own */
if (svc_has_pidfile(svc)) {
sched_yield();
touch(pid_file(svc));
}
if (svc->desc[0])
print_result(rc);
return rc;
}
/**
@@ -464,15 +523,46 @@ void service_reload_dynamic(void)
*/
void service_runlevel(int newlevel)
{
if (!rescue && runlevel <= 1 && newlevel > 1)
networking(1);
sm_set_runlevel(&sm, newlevel);
sm_step(&sm);
if (!rescue && runlevel <= 1)
networking(0);
}
/*
* log:/path/to/logfile,priority:facility.level,tag:ident
*/
static void parse_log(svc_t *svc, char *arg)
{
char *tok;
tok = strtok(arg, ":, ");
while (tok) {
if (!strcmp(tok, "log"))
svc->log.enabled = 1;
else if (!strcmp(tok, "null") || !strcmp(tok, "/dev/null"))
svc->log.null = 1;
else if (tok[0] == '/')
strlcpy(svc->log.file, tok, sizeof(svc->log.file));
else if (!strcmp(tok, "priority") || !strcmp(tok, "prio"))
strlcpy(svc->log.prio, strtok(NULL, ","), sizeof(svc->log.prio));
else if (!strcmp(tok, "tag") || !strcmp(tok, "identity") || !strcmp(tok, "ident"))
strlcpy(svc->log.ident, strtok(NULL, ","), sizeof(svc->log.ident));
tok = strtok(NULL, ":=, ");
}
}
/**
* service_register - Register service, task or run commands
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
* @svcline: A complete command line with -- separated description text
* @mtime: The modification time if service is loaded from /etc/finit.d
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
* @cfg: Configuration, complete command, with -- for description text
* @rlimit: Limits for this service/task/run/inetd, may be global limits
* @file: The file name service was loaded from
*
* This function is used to register commands to be run on different
* system runlevels with optional username. The @type argument details
@@ -505,7 +595,7 @@ void service_runlevel(int newlevel)
*
* Service conditions can be: svc/<PATH> for PID files, net/<IFNAME>/up
* and net/<IFNAME>/exists. The condition handling is further described
* doc/conditions.md.
* docs/conditions.md.
*
* For multiple instances of the same command, e.g. multiple DHCP
* clients, the user must enter an ID, using the :ID syntax.
@@ -519,26 +609,27 @@ void service_runlevel(int newlevel)
* Returns:
* POSIX OK(0) on success, or non-zero errno exit status on failure.
*/
int service_register(int type, char *svcline, struct timeval *mtime)
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
{
int i = 0;
int id = 1; /* Default to ID:1 */
int id = -1;
#ifdef INETD_ENABLED
int forking = 0;
#endif
int log = 0, levels = 0;
char *username = NULL, *line;
int levels = 0;
char *line;
char *username = NULL, *log = NULL, *pid = NULL;
char *service = NULL, *proto = NULL, *ifaces = NULL;
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
svc_t *svc;
plugin_t *plugin = NULL;
if (!svcline) {
if (!cfg) {
_e("Invalid input argument");
return errno = EINVAL;
}
line = strdup(svcline);
line = strdup(cfg);
if (!line)
return 1;
@@ -549,18 +640,27 @@ int service_register(int type, char *svcline, struct timeval *mtime)
while (*desc && isblank(*desc))
desc++;
} else {
int pos;
/* Find "--\n" to denote empty/no description */
pos = (int)strlen(line) - 2;
if (pos > 0 && !strcmp(&line[pos], "--")) {
line[pos] = 0;
desc = &line[pos];
}
}
cmd = strtok(line, " ");
if (!cmd) {
incomplete:
_e("Incomplete service '%s', cannot register", svcline);
_e("Incomplete service '%s', cannot register", cfg);
free(line);
return errno = ENOENT;
}
while (cmd) {
if (cmd[0] == '@') /* @username[:group] */
if (cmd[0] == '@') /* @username[:group] */
username = &cmd[1];
else if (cmd[0] == '[') /* [runlevels] */
runlevels = &cmd[0];
@@ -574,10 +674,12 @@ int service_register(int type, char *svcline, struct timeval *mtime)
else if (!strncasecmp(cmd, "wait", 4))
forking = 0;
#endif
else if (!strncasecmp(cmd, "log", 3))
log = cmd;
else if (!strncasecmp(cmd, "pid", 3))
pid = cmd;
else if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */
else if (!strncasecmp(cmd, "log", 3))
log = 1;
else
break;
@@ -632,11 +734,15 @@ int service_register(int type, char *svcline, struct timeval *mtime)
if (svc)
goto inetd_setup;
id = svc_next_id(cmd);
if (id <= 0)
id = svc_next_id(cmd);
}
recreate:
#endif
if (id <= 0)
id = 1;
svc = svc_find(cmd, id);
if (!svc) {
_d("Creating new svc for %s id #%d type %d", cmd, id, type);
@@ -658,9 +764,11 @@ recreate:
}
#endif
svc->log = log;
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
/* Always clear svc PID file, for now. See TODO */
svc->pidfile[0] = 0;
/* Decode any optional pid:/optional/path/to/file.pid */
if (pid && svc_is_daemon(svc) && pid_file_parse(svc, pid))
_e("Invalid 'pid' argument to service: %s", pid);
if (username) {
char *ptr = strchr(username, ':');
@@ -688,6 +796,11 @@ recreate:
conf_parse_cond(svc, cond);
if (log)
parse_log(svc, log);
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
char *iface, *name = service;
@@ -717,23 +830,61 @@ recreate:
}
}
#endif
/* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
/* New, recently modified or unchanged ... used on reload. */
svc_check_dirty(svc, mtime);
free(line);
if (file && conf_changed(file))
svc_mark_dirty(svc);
else
svc_mark_clean(svc);
if (!file)
svc->protected = 1;
/* Free duped line, from above */
free(line);
return 0;
}
/*
* This function is called when cleaning up lingering (stopped) services
* after a .conf reload, as well as when an inetd connection terminates.
*
* We need to ensure we properly stop the service before removing it,
* including stopping any pending restart or SIGKILL timers before we
* proceed to free() the svc itself.
*
* Remember to not try to stop inetd connections, they only get here
* when already stopped, if we do we end up in a recursive loop.
*/
void service_unregister(svc_t *svc)
{
if (!svc)
return;
/*
* Only try stopping @svc if it's *not* an inetd connection.
* Prevents infinite recursion when called from service_step()
*/
if (!svc_is_inetd_conn(svc))
service_stop(svc);
if (svc_is_inetd(svc)) {
if (svc_is_busy(svc->inetd.svc)) {
svc_unblock(svc->inetd.svc);
service_step(svc->inetd.svc);
}
inetd_del(&svc->inetd);
}
svc_del(svc);
}
void service_monitor(pid_t lost)
{
svc_t *svc;
char pidfile[MAX_ARG_LEN];
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return;
@@ -752,19 +903,22 @@ void service_monitor(pid_t lost)
_d("collected %s(%d)", svc->cmd, lost);
/* Try removing PID file (in case service does not clean up after itself) */
snprintf(pidfile, sizeof(pidfile), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
if (remove(pidfile)) {
if (errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), pidfile);
if (svc_is_daemon(svc)) {
char *fn;
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", basename(svc->cmd), fn);
}
/* No longer running, update books. */
svc->pid = 0;
service_step(svc);
svc->start_time = svc->pid = 0;
/* Clean out any bootstrap tasks, they've had their time in the sun. */
if (svc_clean_bootstrap(svc))
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
if (!service_step(svc)) {
/* Clean out any bootstrap tasks, they've had their time in the sun. */
if (svc_clean_bootstrap(svc))
_d("collected bootstrap task %s(%d), removing.", svc->cmd, lost);
}
sm_step(&sm);
}
@@ -772,33 +926,33 @@ void service_monitor(pid_t lost)
static void service_retry(svc_t *svc)
{
int timeout;
int *restart_counter = (int *)&svc->restart_counter;
char *restart_cnt = (char *)&svc->restart_cnt;
service_timeout_cancel(svc);
if (svc->state != SVC_HALTED_STATE ||
svc->block != SVC_BLOCK_RESTARTING) {
_d("%s not crashing anymore", svc->desc);
*restart_counter = 0;
_d("%s not crashing anymore", svc->cmd);
*restart_cnt = 0;
return;
}
if (*restart_counter >= RESPAWN_MAX) {
_e("%s keeps crashing, not restarting", svc->desc);
if (*restart_cnt >= RESPAWN_MAX) {
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->cmd);
svc_crashing(svc);
*restart_counter = 0;
*restart_cnt = 0;
service_step(svc);
return;
}
(*restart_counter)++;
(*restart_cnt)++;
_d("%s crashed, trying to start it again, attempt %d", svc->desc, *restart_counter);
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
svc_unblock(svc);
service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_counter) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
service_timeout_after(svc, timeout, service_retry);
}
@@ -815,17 +969,22 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
}
}
void service_step(svc_t *svc)
/*
* Transition inetd/task/run/service
*
* Returns: non-zero if the @svc is no longer valid (removed)
*/
int service_step(svc_t *svc)
{
int err;
int *restart_counter = (int *)&svc->restart_counter;
char *restart_cnt = (char *)&svc->restart_cnt;
svc_cmd_t enabled;
svc_state_t old_state;
cond_state_t cond;
restart:
old_state = svc->state;
enabled = service_enabled(svc);
enabled = svc_enabled(svc);
_d("%20s(%4d): %8s %3sabled/%-7s cond:%-4s", svc->cmd, svc->pid,
svc_status(svc), enabled ? "en" : "dis", svc_dirtystr(svc),
@@ -840,12 +999,8 @@ restart:
case SVC_DONE_STATE:
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) {
if (svc_is_busy(svc->inetd.svc)) {
svc_unblock(svc->inetd.svc);
service_step(svc->inetd.svc);
}
service_unregister(svc);
return;
return -1;
}
#endif
if (svc_is_changed(svc))
@@ -880,27 +1035,31 @@ restart:
if (!enabled) {
svc_set_state(svc, SVC_HALTED_STATE);
} else if (cond_get_agg(svc->cond) == COND_ON) {
/* wait until all processes has been stopped before continuing... */
/* wait until all processes have been stopped before continuing... */
if (sm_is_in_teardown(&sm))
break;
/*
* Make state transition *before* service_start(), because
* of HOOK_SVC_START, which may call service_step()
*/
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
err = service_start(svc);
if (err) {
(*restart_counter)++;
(*restart_cnt)++;
svc_set_state(svc, SVC_READY_STATE);
if (!svc_is_inetd_conn(svc))
break;
}
svc_mark_clean(svc);
svc_set_state(svc, SVC_RUNNING_STATE);
}
break;
case SVC_RUNNING_STATE:
if (!enabled) {
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
@@ -913,7 +1072,7 @@ restart:
* Restart directly after the first crash,
* then retry after 2 sec
*/
_d("delayed restart of %s", svc->desc);
_d("delayed restart of %s", svc->cmd);
service_timeout_after(svc, 1, service_retry);
break;
}
@@ -923,13 +1082,17 @@ restart:
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
if (svc_is_runtask(svc)) {
svc->once++;
break;
}
}
cond = cond_get_agg(svc->cond);
switch (cond) {
case COND_OFF:
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
case COND_FLUX:
@@ -946,7 +1109,6 @@ restart:
service_restart(svc);
} else {
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
}
svc_mark_clean(svc);
}
@@ -958,12 +1120,11 @@ restart:
if (!enabled) {
kill(svc->pid, SIGCONT);
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
if (!svc->pid) {
(*restart_counter)++;
(*restart_cnt)++;
svc_set_state(svc, SVC_READY_STATE);
break;
}
@@ -978,7 +1139,6 @@ restart:
case COND_OFF:
kill(svc->pid, SIGCONT);
service_stop(svc);
svc_set_state(svc, SVC_STOPPING_STATE);
break;
case COND_FLUX:
@@ -991,6 +1151,8 @@ restart:
_d("%20s(%4d): -> %8s", svc->cmd, svc->pid, svc_status(svc));
goto restart;
}
return 0;
}
void service_step_all(int types)
@@ -998,6 +1160,85 @@ void service_step_all(int types)
svc_foreach_type(types, service_step);
}
/**
* svc_clean_runtask - Clear once flag of runtasks
*
* XXX: runtasks should be stopped before calling this
*/
void service_runtask_clean(void)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_is_runtask(svc))
continue;
svc->once = 0;
if (svc->state == SVC_DONE_STATE)
svc_set_state(svc, SVC_HALTED_STATE);
}
}
/**
* service_runtask_completed - Have run/task completed in current runlevel
* @skip: Hardcoded list of conditions to skip
*
* This function checks if all run/task have run once in the current
* runlevel. E.g., at bootstrap we must wait for these scripts or
* programs to complete their run before switching to the configured
* runlevel.
*
* Setting @skip means skipping tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP
* set in their condition mask. These tasks cannot run until finalize()
*
* Returns:
* %TRUE(1) or %FALSE(0)
*/
int service_runtask_completed(int skip)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_is_runtask(svc))
continue;
if (!svc_enabled(svc))
continue;
if (strstr(svc->cond, plugin_hook_str(HOOK_SVC_UP)) ||
strstr(svc->cond, plugin_hook_str(HOOK_SYSTEM_UP))) {
_d("Skipping %s(%s), post-strap hook", svc->desc, svc->cmd);
continue;
}
if (!svc->once) {
_d("%s has not yet completed ...", svc->cmd);
return 0;
}
_d("%s has completed ...", svc->cmd);
}
return 1;
}
void service_bootstrap_cb(uev_t *w, void *arg, int events)
{
static int cnt = 10;
void (*finalize)(void) = arg;
_d("Checking completion of all run/task ... %d", cnt);
if (cnt-- > 0 && !service_runtask_completed(!runlevel))
return;
if (cnt > 0)
_d("All run/task have completed, resuming bootstrap.");
else
_d("Timeout, resuming bootstrap.");
cnt = 10;
uev_timer_stop(w);
finalize();
}
/**
* Local Variables:
* indent-tabs-mode: t
+5 -3
View File
@@ -28,15 +28,17 @@
#include "svc.h"
void service_runlevel (int newlevel);
int service_register (int type, char *line, struct timeval *mtime);
int service_register (int type, char *line, struct rlimit rlimit[], char *file);
void service_unregister (svc_t *svc);
int service_enabled (svc_t *svc);
void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_step (svc_t *svc);
int service_step (svc_t *svc);
void service_step_all (int types);
void service_bootstrap_cb (uev_t *w, void *arg, int events);
#endif /* FINIT_SERVICE_H_ */
/**
+57 -2
View File
@@ -27,6 +27,8 @@
*
* SIGHUP
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
* Also restarts API (initctl) socket, like SysV init and systemd
* does on USR1 with their FIFO/D-Bus.
*
* SIGUSR1
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
@@ -131,8 +133,12 @@ void do_kill(int signo)
continue;
if (fgets(file, sizeof(file), fp)) {
if (file[0] != '@')
if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file);
else if (file[0] != '@')
kill(pid, signo);
else
_d("Skipping %s ...", &file[1]);
}
fclose(fp);
}
@@ -179,6 +185,11 @@ void do_shutdown(shutop_t op)
return;
}
if (wdogpid) {
print(kill(wdogpid, SIGPWR) == 1, "Advising watchdog, system going down");
do_sleep(2);
}
/* Unmount any tmpfs before unmounting swap ... */
unmount_tmpfs();
run("swapoff -e -a");
@@ -201,7 +212,7 @@ void do_shutdown(shutop_t op)
int timeout = 10;
/* Wait here until the WDT reboots, or timeout with fallback */
print(kill(wdogpid, SIGPWR) == 1, "Pending watchdog reboot");
print(kill(wdogpid, SIGTERM) == 1, "Pending watchdog reboot");
while (timeout--)
do_sleep(1);
}
@@ -224,6 +235,15 @@ void do_shutdown(shutop_t op)
static void sighup_cb(uev_t *w, void *arg, int events)
{
_d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
/* Restart initctl API domain socket, similar to systemd/SysV init */
api_exit();
api_init(w->ctx);
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
service_reload_dynamic();
}
@@ -234,6 +254,11 @@ static void sighup_cb(uev_t *w, void *arg, int events)
static void sigint_cb(uev_t *w, void *arg, int events)
{
_d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_REBOOT;
service_runlevel(6);
}
@@ -244,6 +269,11 @@ static void sigint_cb(uev_t *w, void *arg, int events)
static void sigusr1_cb(uev_t *w, void *arg, int events)
{
_d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_HALT;
service_runlevel(0);
}
@@ -254,6 +284,11 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
static void sigusr2_cb(uev_t *w, void *arg, int events)
{
_d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_OFF;
service_runlevel(0);
}
@@ -264,6 +299,11 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
static void sigterm_cb(uev_t *w, void *arg, int events)
{
_d("...");
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
halt = SHUT_REBOOT;
service_runlevel(6);
}
@@ -275,6 +315,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
{
pid_t pid;
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
/* Reap all the children! */
do {
pid = waitpid(-1, NULL, WNOHANG);
@@ -290,6 +335,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
*/
static void sigstop_cb(uev_t *w, void *arg, int events)
{
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
touch(SYNC_STOPPED);
stopped++;
}
@@ -299,6 +349,11 @@ static void sigstop_cb(uev_t *w, void *arg, int events)
*/
static void sigcont_cb(uev_t *w, void *arg, int events)
{
if (UEV_ERROR == events) {
_e("Unrecoverable error in signal watcher");
return;
}
stopped = 0;
erase(SYNC_STOPPED);
}
+8 -7
View File
@@ -95,7 +95,8 @@ void sm_step(sm_t *sm)
restart:
old_state = sm->state;
_d("state: %s", sm_status(sm->state));
_d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d",
sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload);
switch (sm->state) {
case SM_BOOTSTRAP_STATE:
@@ -142,8 +143,11 @@ restart:
erase("/etc/nologin");
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (runlevel != 0 && runlevel != 6)
conf_reload_dynamic();
if (conf_any_change())
conf_reload();
/* Reset once flag of runtasks */
service_runtask_clean();
_d("Stopping services services not allowed in new runlevel ...");
sm->in_teardown = 1;
@@ -195,7 +199,7 @@ restart:
case SM_RELOAD_CHANGE_STATE:
/* First reload all *.conf in /etc/finit.d/ */
conf_reload_dynamic();
conf_reload();
/*
* Then, mark all affected service conditions as in-flux and
@@ -231,9 +235,6 @@ restart:
_d("Calling reconf hooks ...");
plugin_run_hooks(HOOK_SVC_RECONF);
/* Done, lift reconf condition */
cond_reassert(NULL);
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
_d("Reconfiguration done");
+125
View File
@@ -0,0 +1,125 @@
/* Reset terminal line settings (stty sane)
*
* Copyright (c) 2016-2017 Joachim Nilsson <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 <errno.h>
#include <stdlib.h>
#include <unistd.h> /* _POSIX_VDISABLE */
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
#include <termios.h>
speed_t stty_parse_speed(char *baud)
{
char *ptr;
size_t i;
unsigned long val;
struct { unsigned long val; speed_t speed; } v2s[] = {
{ 0, B0 },
{ 50, B50 },
{ 75, B75 },
{ 110, B110 },
{ 134, B134 },
{ 150, B150 },
{ 200, B200 },
{ 300, B300 },
{ 600, B600 },
{ 1200, B1200 },
{ 1800, B1800 },
{ 2400, B2400 },
{ 4800, B4800 },
{ 9600, B9600 },
{ 19200, B19200 },
{ 38400, B38400 },
{ 57600, B57600 },
{ 115200, B115200 },
{ 230400, B230400 },
{ 460800, B460800 },
{ 500000, B500000 },
{ 576000, B576000 },
{ 921600, B921600 },
{ 1000000, B1000000 },
{ 1152000, B1152000 },
{ 1500000, B1500000 },
{ 2000000, B2000000 },
{ 2500000, B2500000 },
{ 3000000, B3000000 },
{ 3500000, B3500000 },
{ 4000000, B4000000 },
};
errno = 0;
val = strtoul(baud, &ptr, 10);
if (errno || ptr == baud)
return B0;
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
if (v2s[i].val == val)
return v2s[i].speed;
}
return B0;
}
void stty(int fd, speed_t speed)
{
struct termios term;
tcdrain(fd);
if (tcgetattr(fd, &term))
return;
if (speed != B0) {
cfsetispeed(&term, speed);
cfsetospeed(&term, speed);
tcsetattr(fd, TCSAFLUSH, &term);
}
tcflush(fd, TCIOFLUSH);
/* Disable modem specific flags */
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
term.c_cflag &= ~CRTSCTS;
term.c_cflag |= CLOCAL;
/* Timeouts, minimum chars and default flags */
term.c_cc[VTIME] = 0;
term.c_cc[VMIN] = 1;
term.c_iflag = ICRNL|IXON|IXOFF;
term.c_oflag = OPOST|ONLCR;
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
/* Reset special characters to defaults */
term.c_cc[VINTR] = CTRL('C');
term.c_cc[VQUIT] = CTRL('\\');
term.c_cc[VEOF] = CTRL('D');
term.c_cc[VEOL] = '\n';
term.c_cc[VKILL] = CTRL('U');
term.c_cc[VERASE] = CERASE;
tcsetattr(fd, TCSANOW, &term);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+147 -245
View File
@@ -27,46 +27,17 @@
#include <stdlib.h>
#include <strings.h>
#include <sys/time.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include "finit.h"
#include "svc.h"
#include "helpers.h"
#include "pid.h"
#include "util.h"
/* Each svc_t needs a unique job# */
static int jobcounter = 1;
static svc_t *workaround = NULL;
static svc_t *__connect_shm(void)
{
svc_t *list;
list = finit_svc_connect();
if (!list) {
if (workaround)
return workaround;
/* Linux not built with CONFIG_SYSVIPC, or libc does not support shmat()/shmget() */
if (ENOSYS == errno)
warn("Kernel does support SYSV shmat() IPC, error %d", errno);
/* Try to prevent PID 1 from aborting, issue #81 */
if (getpid() == 1) {
warnx("Implementing PID 1 workaround, initctl tool will not work ...");
if (!workaround)
workaround = calloc(MAX_NUM_SVC, sizeof(svc_t));
list = workaround;
}
}
if (!list) {
warn("Failed connecting to shared memory, error %d", errno);
abort();
}
return list;
}
static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
/**
* svc_new - Create a new service
@@ -79,11 +50,12 @@ static svc_t *__connect_shm(void)
*/
svc_t *svc_new(char *cmd, int id, int type)
{
int i, job = -1;
svc_t *svc, *list = __connect_shm();
int job = -1;
char *desc;
svc_t *svc, *iter = NULL;
/* Find first job n:o if registering multiple instances */
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd)) {
job = svc->job;
break;
@@ -92,32 +64,26 @@ svc_t *svc_new(char *cmd, int id, int type)
if (job == -1)
job = jobcounter++;
for (i = 0; i < MAX_NUM_SVC; i++) {
svc_t *svc = &list[i];
svc = calloc(1, sizeof(*svc));
if (!svc)
return NULL;
if (svc->type == SVC_TYPE_FREE) {
char *desc;
svc->type = type;
svc->job = job;
svc->id = id;
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
memset(svc, 0, sizeof(*svc));
svc->type = type;
svc->job = job;
svc->id = id;
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
/* Default description, if missing */
desc = strrchr(cmd, '/');
if (desc)
desc++;
else
desc = cmd;
strlcpy(svc->desc, desc, sizeof(svc->desc));
/* Default description, if missing */
desc = rindex(cmd, '/');
if (desc)
desc++;
else
desc = cmd;
strlcpy(svc->desc, desc, sizeof(svc->desc));
TAILQ_INSERT_TAIL(&svc_list, svc, link);
return svc;
}
}
errno = ENOMEM;
return NULL;
return svc;
}
/**
@@ -129,50 +95,55 @@ svc_t *svc_new(char *cmd, int id, int type)
*/
int svc_del(svc_t *svc)
{
svc->type = SVC_TYPE_FREE;
TAILQ_REMOVE(&svc_list, svc, link);
memset(svc, 0, sizeof(*svc));
free(svc);
return 0;
}
/**
* svc_iterator - Naive iterator over all registered services.
* @first: Get first &svc_t object, or next until end.
* @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
*
* Returns:
* The first &svc_t when @first is set, otherwise the next &svc_t until
* the end when %NULL is returned.
* An &svc_t pointer, or %NULL when no more entries can be found.
*/
svc_t *svc_iterator(int first)
{
static int i = 0;
svc_t *list = __connect_shm();
if (first)
i = 0;
while (i < MAX_NUM_SVC) {
svc_t *svc = &list[i++];
if (svc->type != SVC_TYPE_FREE)
return svc;
}
return NULL;
}
/**
* svc_inetd_iterator - Naive iterator over all registered inetd services.
* @first: Get first &svc_t object, or next until end.
*
* Returns:
* The first inetd &svc_t when @first is set, otherwise the next
* inetd &svc_t until the end when %NULL is returned.
*/
svc_t *svc_inetd_iterator(int first)
svc_t *svc_iterator(svc_t **iter, int first)
{
svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
if (!iter) {
errno = EINVAL;
return NULL;
}
if (first)
svc = TAILQ_FIRST(&svc_list);
else
svc = *iter;
if (svc)
*iter = TAILQ_NEXT(svc, link);
return svc;
}
/**
* svc_inetd_iterator - Naive iterator over all registered inetd services.
* @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
*
* Returns:
* The first inetd &svc_t when %NULL is given as argument, otherwise the
* next inetd &svc_t until the end when %NULL is returned.
*/
svc_t *svc_inetd_iterator(svc_t **iter, int first)
{
svc_t *svc;
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
if (svc_is_inetd(svc))
return svc;
}
@@ -181,42 +152,22 @@ svc_t *svc_inetd_iterator(int first)
}
/**
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
* @first: Get first &svc_t object, or next until end.
*
* Returns:
* The first dynamically loaded &svc_t when @first is set, otherwise the
* next dynamically loaded &svc_t until the end when %NULL is returned.
*/
svc_t *svc_dynamic_iterator(int first)
{
svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
if (svc->mtime.tv_sec)
return svc;
}
return NULL;
}
/**
* svc_named_iterator - Iterates over all instances of a service.
* @first: Get first &svc_t object, or next until end.
* @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
* @cmd: Service name to look for.
*
* Returns:
* The first matching &svc_t when @first is set, otherwise the next
* &svc_t instance with the same @cmd until the end when %NULL is
* The first matching &svc_t when %NULL is given as argument, otherwise
* the next &svc_t with the same @cmd name until the end when %NULL is
* returned.
*/
svc_t *svc_named_iterator(int first, char *cmd)
svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
{
svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
char *name = basename(svc->cmd);
if (!strncmp(name, cmd, strlen(name)))
@@ -229,19 +180,20 @@ svc_t *svc_named_iterator(int first, char *cmd)
/**
* svc_job_iterator - Iterates over all instances of a service.
* @first: Get first &svc_t object, or next until end.
* @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
* @job: Job to look for.
*
* Returns:
* The first matching &svc_t when @first is set, otherwise the next
* &svc_t instance with the same @job until the end when %NULL is
* The first matching &svc_t when %NULL is given as argument, otherwise
* the next &svc_t with the same @job ID until the end when %NULL is
* returned.
*/
svc_t *svc_job_iterator(int first, int job)
svc_t *svc_job_iterator(svc_t **iter, int first, int job)
{
svc_t *svc;
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
if (svc->job == job)
return svc;
}
@@ -254,30 +206,14 @@ svc_t *svc_job_iterator(int first, int job)
* svc_foreach - Run a callback for each registered service
* @cb: Callback to run for each service
*/
void svc_foreach(void (*cb)(svc_t *))
void svc_foreach(int (*cb)(svc_t *))
{
svc_t *svc;
svc_t *svc, *iter = NULL;
if (!cb)
return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
cb(svc);
}
/**
* svc_foreach_dynamic - Run a callback for each registered dynamic service
* @cb: Callback to run for each dynamic service
*/
void svc_foreach_dynamic(void (*cb)(svc_t *))
{
svc_t *svc;
if (!cb)
return;
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
cb(svc);
}
@@ -287,14 +223,14 @@ void svc_foreach_dynamic(void (*cb)(svc_t *))
* @types: Mask of service types
* @cb: Callback to run for each matching type
*/
void svc_foreach_type(int types, void (*cb)(svc_t *))
void svc_foreach_type(int types, int (*cb)(svc_t *))
{
svc_t *svc;
svc_t *svc, *iter = NULL;
if (!cb)
return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!(svc->type & types))
continue;
@@ -312,9 +248,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *))
*/
svc_t *svc_stop_completed(void)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->state == SVC_STOPPING_STATE)
return svc;
}
@@ -331,9 +267,9 @@ svc_t *svc_stop_completed(void)
*/
svc_t *svc_find(char *cmd, int id)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
return svc;
}
@@ -350,9 +286,9 @@ svc_t *svc_find(char *cmd, int id)
*/
svc_t *svc_find_by_pid(pid_t pid)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid == pid)
return svc;
}
@@ -370,9 +306,9 @@ svc_t *svc_find_by_pid(pid_t pid)
*/
svc_t *svc_find_by_jobid(int job, int id)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->job == job && svc->id == id)
return svc;
}
@@ -391,9 +327,9 @@ svc_t *svc_find_by_jobid(int job, int id)
svc_t *svc_find_by_nameid(char *name, int id)
{
char *ptr;
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
ptr = strrchr(svc->cmd, '/');
if (ptr)
ptr++;
@@ -407,6 +343,27 @@ svc_t *svc_find_by_nameid(char *name, int id)
return NULL;
}
/**
* svc_find_by_plidfile - Find an service object by its PID file
* @fn: PID file, can be absolute path or relative to /run
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_pidfile(char *fn)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char path[MAX_ARG_LEN];
if (string_compare(pid_runpath(fn, path, sizeof(path)), pid_file(svc)))
return svc;
}
return NULL;
}
/**
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
*
@@ -419,11 +376,13 @@ svc_t *svc_find_by_nameid(char *name, int id)
*/
void svc_mark_dynamic(void)
{
svc_t *svc = svc_dynamic_iterator(1);
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protected)
continue;
while (svc) {
*((int *)&svc->dirty) = -1;
svc = svc_dynamic_iterator(0);
}
}
@@ -437,17 +396,6 @@ void svc_mark_clean(svc_t *svc)
*((int *)&svc->dirty) = 0;
}
void svc_check_dirty(svc_t *svc, struct timeval *mtime)
{
if (mtime && timercmp(&svc->mtime, mtime, !=))
svc_mark_dirty(svc);
else
svc_mark_clean(svc);
svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
}
/**
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
* @cb: Callback to run for each stale service
@@ -457,14 +405,11 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime)
*/
void svc_clean_dynamic(void (*cb)(svc_t *))
{
svc_t *svc = svc_dynamic_iterator(1);
svc_t *svc, *iter = NULL;
while (svc) {
if (svc->dirty == -1 && cb) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->dirty == -1 && cb)
cb(svc);
svc_mark_clean(svc);
}
svc = svc_dynamic_iterator(0);
}
}
@@ -495,85 +440,44 @@ int svc_clean_bootstrap(svc_t *svc)
*/
void svc_prune_bootstrap(void)
{
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc->pid)
svc_clean_bootstrap(svc);
}
}
char *svc_status(svc_t *svc)
/**
* svc_enabled - Should the service run?
* @svc: Pointer to &svc_t object
*
* Returns:
* 1, if the service is allowed to run in the current runlevel and the
* user has not manually requested that this service should not run. 0
* otherwise.
*/
int svc_enabled(svc_t *svc)
{
switch (svc->state) {
case SVC_HALTED_STATE:
switch (svc->block) {
case SVC_BLOCK_NONE:
return "halted";
if (!svc)
return 0;
case SVC_BLOCK_MISSING:
return "missing";
if (!svc_in_runlevel(svc, runlevel))
return 0;
case SVC_BLOCK_CRASHING:
return "crashed";
if (svc_is_removed(svc) || svc_is_blocked(svc))
return 0;
case SVC_BLOCK_USER:
return "stopped";
case SVC_BLOCK_BUSY:
return "busy";
case SVC_BLOCK_RESTARTING:
return "restart";
}
case SVC_DONE_STATE:
return "done";
case SVC_STOPPING_STATE:
switch (svc->type) {
case SVC_TYPE_INETD_CONN:
case SVC_TYPE_RUN:
case SVC_TYPE_TASK:
return "active";
default:
return "stopping";
}
case SVC_WAITING_STATE:
return "waiting";
case SVC_READY_STATE:
return "ready";
case SVC_RUNNING_STATE:
return "running";
default:
return "UNKNOWN";
}
}
const char *svc_dirtystr(svc_t *svc)
{
if (svc_is_removed(svc))
return "removed";
if (svc_is_updated(svc))
return "updated";
if (svc_is_changed(svc))
return "UNKNOWN";
return "clean";
return 1;
}
/* Same base service, return unique ID */
int svc_next_id(char *cmd)
{
int id = 0;
svc_t *svc;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!strcmp(svc->cmd, cmd) && id < svc->id)
id = svc->id;
}
@@ -583,12 +487,10 @@ int svc_next_id(char *cmd)
int svc_is_unique(svc_t *svc)
{
svc_t *list = __connect_shm();
int i, unique = 1;
for (i = 0; i < MAX_NUM_SVC; i++) {
svc_t *s = &list[i];
svc_t *s, *iter = NULL;
int unique = 1;
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
if (svc->type == SVC_TYPE_FREE)
continue;
@@ -619,21 +521,21 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
token = strtok_r(input, " ", &pos);
while (token) {
int id = 1;
svc_t *svc;
svc_t *svc, *iter = NULL;
char *ptr = strchr(token, ':');
if (isdigit(token[0])) {
int job = atonum(token);
if (!ptr) {
svc = svc_job_iterator(1, job);
svc = svc_job_iterator(&iter, 1, job);
if (!svc && not_found)
result += not_found(NULL, job);
while (svc) {
if (found)
result += found(svc);
svc = svc_job_iterator(0, job);
svc = svc_job_iterator(&iter, 0, job);
}
} else {
*ptr++ = 0;
@@ -648,14 +550,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
}
} else {
if (!ptr) {
svc = svc_named_iterator(1, token);
svc = svc_named_iterator(&iter, 1, token);
if (!svc && not_found)
result += not_found(token, id);
while (svc) {
if (found)
result += found(svc);
svc = svc_named_iterator(0, token);
svc = svc_named_iterator(&iter, 0, token);
}
} else {
*ptr++ = 0;
+100 -43
View File
@@ -26,9 +26,10 @@
#define FINIT_SVC_H_
#include <sys/ipc.h> /* IPC_CREAT */
#include <sys/shm.h> /* shmat() */
#include <sys/resource.h>
#include <sys/types.h> /* pid_t */
#include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include "inetd.h"
#include "helpers.h"
@@ -65,13 +66,11 @@ typedef enum {
SVC_BLOCK_RESTARTING,
} svc_block_t;
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
#define MAX_ARG_LEN 64
#define MAX_STR_LEN 64
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
#define MAX_USER_LEN 16
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC 64 /* Enough? */
#define MAX_NUM_SVC_ARGS 32
/*
@@ -81,31 +80,44 @@ typedef enum {
* initctl <stop|start|restart> service
*/
typedef struct svc {
TAILQ_ENTRY(svc) link;
/* Instance specifics */
int job, id; /* JOB:ID */
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
/* Service details */
pid_t pid;
char pidfile[MAX_ARG_LEN];
long start_time; /* Start time, as seconds since boot, from sysinfo() */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type;
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
const int dirty; /* Set if old mtime != new mtime => reloaded,
* or -1 when marked for removal */
svc_type_t type; /* Service, run, task, inetd, ... */
int protected; /* Services like dbus-daemon & udev by Finit */
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
int starting; /* ... waiting for pidfile to be re-asserted */
int runlevels;
int sighup; /* This service supports SIGHUP :) */
svc_block_t block; /* Reason that this service is currently stopped */
char cond[MAX_COND_LEN];
/* Incremented for each restart by service monitor. */
const unsigned int restart_counter;
/* Counters */
char once; /* run/task, (at least) once per runlevel */
const char restart_cnt; /* Incremented for each restart by service monitor. */
/* For inetd services */
inetd_t inetd;
int stdin_fd;
/* Set for services we need to redirect stdout/stderr to syslog */
int log;
struct {
char enabled;
char null;
char file[64];
char prio[20];
char ident[20];
} log;
/* Identity */
char username[MAX_USER_LEN];
@@ -124,23 +136,6 @@ typedef struct svc {
void (*timer_cb)(struct svc *svc);
} svc_t;
typedef struct svc_map svc_map_t;
/* Put services array in shm */
static inline svc_t *finit_svc_connect(void)
{
static void *ptr = (void *)-1;
if ((void *)-1 == ptr) {
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
0600 | IPC_CREAT), NULL, 0);
if ((void *)-1 == ptr)
return NULL;
}
return (svc_t *)ptr;
}
svc_t *svc_new (char *cmd, int id, int type);
int svc_del (svc_t *svc);
@@ -148,50 +143,48 @@ svc_t *svc_find (char *cmd, int id);
svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_by_jobid (int job, int id);
svc_t *svc_find_by_nameid (char *name, int id);
svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (int first);
svc_t *svc_inetd_iterator (int first);
svc_t *svc_dynamic_iterator (int first);
svc_t *svc_named_iterator (int first, char *cmd);
svc_t *svc_job_iterator (int first, int job);
svc_t *svc_iterator (svc_t **iter, int first);
svc_t *svc_inetd_iterator (svc_t **iter, int first);
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
void svc_foreach (void (*cb)(svc_t *));
void svc_foreach_dynamic (void (*cb)(svc_t *));
void svc_foreach_type (int types, void (*cb)(svc_t *));
void svc_foreach (int (*cb)(svc_t *));
void svc_foreach_type (int types, int (*cb)(svc_t *));
svc_t *svc_stop_completed (void);
void svc_mark_dynamic (void);
void svc_check_dirty (svc_t *svc, struct timeval *mtime);
void svc_mark_dirty (svc_t *svc);
void svc_mark_clean (svc_t *svc);
void svc_clean_dynamic (void (*cb)(svc_t *));
int svc_clean_bootstrap (svc_t *svc);
void svc_prune_bootstrap (void);
char *svc_status (svc_t *svc);
const char *svc_dirtystr (svc_t *svc);
int svc_enabled (svc_t *svc);
int svc_next_id (char *cmd);
int svc_is_unique (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; }
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_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
@@ -202,6 +195,70 @@ static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
static inline char *svc_status(svc_t *svc)
{
switch (svc->state) {
case SVC_HALTED_STATE:
switch (svc->block) {
case SVC_BLOCK_NONE:
return "halted";
case SVC_BLOCK_MISSING:
return "missing";
case SVC_BLOCK_CRASHING:
return "crashed";
case SVC_BLOCK_USER:
return "stopped";
case SVC_BLOCK_BUSY:
return "busy";
case SVC_BLOCK_RESTARTING:
return "restart";
}
case SVC_DONE_STATE:
return "done";
case SVC_STOPPING_STATE:
switch (svc->type) {
case SVC_TYPE_INETD_CONN:
case SVC_TYPE_RUN:
case SVC_TYPE_TASK:
return "active";
default:
return "stopping";
}
case SVC_WAITING_STATE:
return "waiting";
case SVC_READY_STATE:
return "ready";
case SVC_RUNNING_STATE:
return "running";
default:
return "UNKNOWN";
}
}
static inline const char *svc_dirtystr(svc_t *svc)
{
if (svc_is_removed(svc))
return "removed";
if (svc_is_updated(svc))
return "updated";
if (svc_is_changed(svc))
return "UNKNOWN";
return "clean";
}
#endif /* FINIT_SVC_H_ */
/**
+159
View File
@@ -0,0 +1,159 @@
/* Simple telinit client
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2015 Joachim Nilsson <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" /* Generated by configure script */
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <lite/lite.h>
#include "finit.h"
#include "util.h"
static int do_send(struct init_request *rq)
{
static int fd = -1;
int result = 255;
ssize_t len;
if (fd == -1) {
if (!fexist(FINIT_FIFO)) {
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
return 1;
}
fd = open(FINIT_FIFO, O_RDWR);
if (-1 == fd) {
perror("Failed opening " FINIT_FIFO);
return 1;
}
}
len = write(fd, rq, sizeof(*rq));
if (sizeof(*rq) == len)
result = 0;
return result;
}
static int do_reload(void)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RELOAD,
};
return do_send(&rq);
}
static int set_env(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_SETENV,
};
if (runlevel == 0) {
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
return do_send(&rq);
}
return 0;
}
static int set_runlevel(int runlevel)
{
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = runlevel,
};
return set_env(runlevel) || do_send(&rq);
}
/* telinit q | telinit <0-9> */
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
"Options:\n"
" -h, --help This help text\n"
" -V, --version Show Finit version\n\n"
"Commands:\n"
" 0 Power-off the system, same as initctl poweroff\n"
" 6 Reboot the system, same as initctl reboot\n"
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
" services in prev. runlevel that are not allowed in new.\n"
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
" sending SIGHUP to PID 1\n"
" 1, s, S Enter system rescue mode, runlevel 1\n"
"\n", prognm);
return rc;
}
int client(int argc, char *argv[])
{
int c;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0}
};
progname(argv[0]);
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
switch(c) {
case 'v':
return puts("v" VERSION) == EOF;
case 'h':
case '?':
return usage(0);
}
}
if (optind < argc) {
int req = (int)argv[optind][0];
if (isdigit(req))
return set_runlevel(req);
if (req == 'q' || req == 'Q')
return do_reload();
if (req == 's' || req == 'S')
return set_runlevel(1);
}
return usage(1);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+57 -38
View File
@@ -45,12 +45,28 @@ static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
static char *canonicalize(char *tty)
{
char buf[42];
struct stat st;
static char path[80];
if (!tty)
return NULL;
/* Auto-detect serial console, for embedded devices mostly */
if (!strcmp(tty, "@console")) {
FILE *fp;
fp = fopen("/sys/class/tty/console/active", "r");
if (!fp) {
_e("Cannot find system console, is sysfs not mounted?");
return NULL;
}
if (fgets(buf, sizeof(buf), fp))
tty = chomp(buf);
fclose(fp);
}
strlcpy(path, tty, sizeof(path));
if (stat(path, &st)) {
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
@@ -74,22 +90,8 @@ void tty_mark(void)
{
tty_node_t *tty;
LIST_FOREACH(tty, &tty_list, link) {
if (tty->mtime.tv_sec)
tty->dirty = -1;
}
}
void tty_check(tty_node_t *tty, struct timeval *mtime)
{
if (mtime && timercmp(&tty->mtime, mtime, !=))
tty->dirty = 1; /* Modified, restart */
else
tty->dirty = 0; /* Not modified */
/* Update mtime, if given */
tty->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
tty->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
LIST_FOREACH(tty, &tty_list, link)
tty->dirty = -1;
}
void tty_sweep(void)
@@ -97,23 +99,24 @@ void tty_sweep(void)
tty_node_t *tty, *tmp;
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
if (tty->mtime.tv_sec && tty->dirty) {
_d("TTY %s dirty, stopping ...", tty->data.name);
tty_stop(&tty->data);
if (!tty->dirty)
continue;
if (tty->dirty == -1) {
_d("TTY %s removed, cleaning up.", tty->data.name);
LIST_REMOVE(tty, link);
tty_unregister(tty);
}
_d("TTY %s dirty, stopping ...", tty->data.name);
tty_stop(&tty->data);
if (tty->dirty == -1) {
_d("TTY %s removed, cleaning up.", tty->data.name);
tty_unregister(tty);
}
}
}
/**
* tty_register - Register a getty on a device
* @line: Configuration, text after initial "tty"
* @mtime: Modification time, to propagate to lower layers
* @line: Configuration, text after initial "tty"
* @rlimit: Limits for this service/task/run/inetd, may be global limits
* @file: The file name TTY was loaded from
*
* A Finit tty line can use the internal getty implementation or an
* external one, like the BusyBox getty for instance. This function
@@ -131,10 +134,10 @@ void tty_sweep(void)
* Different getty implementations prefer the TTY device argument in
* different order, so take care to investigate this first.
*/
int tty_register(char *line, struct timeval *mtime)
int tty_register(char *line, struct rlimit rlimit[], char *file)
{
tty_node_t *entry;
int insert = 0, noclear = 0, nowait = 0;
int insert = 0, noclear = 0, nowait = 0, nologin = 0;
size_t i, num = 0;
char *tok, *cmd = NULL, *args[TTY_MAX_ARGS];
char *dev = NULL, *baud = NULL;
@@ -156,6 +159,8 @@ int tty_register(char *line, struct timeval *mtime)
noclear = 1;
else if (!strcmp(tok, "nowait"))
nowait = 1;
else if (!strcmp(tok, "nologin"))
nologin = 1;
else
args[num++] = tok;
@@ -168,6 +173,8 @@ int tty_register(char *line, struct timeval *mtime)
if (!dev && !cmd) {
if (args[i][0] == '[')
runlevels = line;
if (!strcmp(args[i], "@console"))
dev = args[i];
if (!strncmp(args[i], "/dev", 4))
dev = args[i];
if (!strncmp(args[i], "tty", 3))
@@ -236,6 +243,7 @@ int tty_register(char *line, struct timeval *mtime)
entry->data.term = term ? strdup(term) : NULL;
entry->data.noclear = noclear;
entry->data.nowait = nowait;
entry->data.nologin = nologin;
entry->data.runlevels = conf_parse_runlevels(runlevels);
/* External getty */
@@ -261,7 +269,13 @@ int tty_register(char *line, struct timeval *mtime)
if (insert)
LIST_INSERT_HEAD(&tty_list, entry, link);
tty_check(entry, mtime);
/* Register configured limits */
memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit));
if (file && conf_changed(file))
entry->dirty = 1; /* Modified, restart */
else
entry->dirty = 0; /* Not modified */
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
return 0;
@@ -274,6 +288,8 @@ int tty_unregister(tty_node_t *tty)
return errno = EINVAL;
}
LIST_REMOVE(tty, link);
if (tty->data.name)
free(tty->data.name);
if (tty->data.baud)
@@ -361,7 +377,6 @@ static int tty_exist(char *dev)
void tty_start(finit_tty_t *tty)
{
int is_console = 0;
char *dev;
if (tty->pid) {
@@ -375,18 +390,20 @@ void tty_start(finit_tty_t *tty)
return;
}
if (console && !strcmp(dev, console))
is_console = 1;
if (tty_exist(dev)) {
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
return;
}
if (tty->nologin) {
tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit);
return;
}
if (!tty->cmd)
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, is_console);
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
else
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, is_console);
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
}
void tty_stop(finit_tty_t *tty)
@@ -491,11 +508,13 @@ void tty_reload(char *dev)
if (dev) {
tty = tty_find(dev);
if (!tty)
if (!tty) {
logit(LOG_WARNING, "No TTY registered for %s", dev);
else
tty_action(tty);
return;
}
tty_action(tty);
tty->dirty = 0;
return;
}
+6 -3
View File
@@ -26,6 +26,7 @@
#define FINIT_TTY_H_
#include <limits.h>
#include <sys/resource.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#define TTY_MAX_ARGS 16
@@ -37,12 +38,16 @@ typedef struct {
char *term;
int noclear;
int nowait;
int nologin;
int runlevels;
char *cmd; /* NULL when running built-in getty */
char *args[TTY_MAX_ARGS];
int pid;
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
} finit_tty_t;
typedef struct tty_node {
@@ -50,7 +55,6 @@ typedef struct tty_node {
finit_tty_t data;
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
int dirty; /* Set if old mtime != new mtime => reloaded,
* or -1 when marked for removal */
} tty_node_t;
@@ -58,10 +62,9 @@ typedef struct tty_node {
//extern LIST_HEAD(, tty_node) tty_list;
void tty_mark (void);
void tty_check (tty_node_t *tty, struct timeval *mtime);
void tty_sweep (void);
int tty_register (char *line, struct timeval *mtime);
int tty_register (char *line, struct rlimit rlimit[], char *file);
int tty_unregister (tty_node_t *tty);
tty_node_t *tty_find (char *dev);
+102 -1
View File
@@ -23,8 +23,12 @@
#include "config.h"
#include <ctype.h> /* isprint() */
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */
#include "util.h"
int screen_rows = 24;
@@ -42,12 +46,92 @@ char *progname(char *arg0)
return prognm;
}
int strtobytes(char *arg)
{
int mod = 0, bytes;
size_t pos;
if (!arg)
return -1;
pos = strspn(arg, "0123456789");
if (arg[pos] != 0) {
if (arg[pos] == 'G')
mod = 3;
else if (arg[pos] == 'M')
mod = 2;
else if (arg[pos] == 'k')
mod = 1;
else
return -1;
arg[pos] = 0;
}
bytes = atoi(arg);
while (mod--)
bytes *= 1000;
return bytes;
}
void do_sleep(unsigned int sec)
{
while ((sec = sleep(sec)))
;
}
/* Seconds since boot, from sysinfo() */
long jiffies(void)
{
struct sysinfo si;
if (!sysinfo(&si))
return si.uptime;
return 0;
}
char *uptime(long secs, char *buf, size_t len)
{
long mins, hours, days, years;
char y[20] = "", d[20] = "", h[20] = "", m[20] = "", s[20] = "";
if (!buf) {
errno = EINVAL;
return NULL;
}
years = secs / 31556926;
secs = secs % 31556926;
days = secs / 86400;
secs = secs % 86400;
hours = secs / 3600;
secs = secs % 3600;
mins = secs / 60;
secs = secs % 60;
if (years)
snprintf(y, sizeof(y), "%ld year", years);
if (days)
snprintf(d, sizeof(d), "%ld day", days);
if (hours)
snprintf(h, sizeof(h), "%ld hour", hours);
if (mins)
snprintf(m, sizeof(m), "%ld min", mins);
if (secs)
snprintf(s, sizeof(s), "%ld sec", secs);
snprintf(buf, len, "%s%s%s%s%s%s%s%s%s",
y, years ? " " : "",
d, days ? " " : "",
h, hours ? " " : "",
m, mins ? " " : "",
s);
return buf;
}
/* Allowed characters in job/id/name */
static int isallowed(int ch)
{
@@ -67,17 +151,34 @@ char *sanitize(char *arg, size_t len)
return arg;
}
if (i > 1 && arg[i] == 0)
if (i > 0 && arg[i] == 0)
return arg;
return NULL;
}
/*
* Called at boot, and shutdown, to (re)initialize the
* screen size for print() et al.
*/
void screen_init(void)
{
if (!isatty(STDOUT_FILENO))
return;
initscr(&screen_rows, &screen_cols);
}
/*
* Called when debug mode is enabled to revert back
* to old-school safe defaults.
*/
void screen_exit(void)
{
screen_rows = 24;
screen_cols = 80;
}
/**
* Local Variables:
* indent-tabs-mode: t
+6
View File
@@ -39,10 +39,16 @@ extern char *prognm;
#include <lite/conio.h>
char *progname (char *arg0);
int strtobytes (char *arg);
void do_sleep (unsigned int sec);
long jiffies (void);
char *uptime (long secs, char *buf, size_t len);
char *sanitize (char *arg, size_t len);
void screen_init (void);
void screen_exit (void);
int screen_width (void);
#endif /* FINIT_UTIL_H_ */
+39 -53
View File
@@ -21,62 +21,29 @@
* THE SOFTWARE.
*/
#include <paths.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/watchdog.h>
#include "finit.h"
#include "helpers.h"
#include "util.h"
#include "log.h"
#include "watchdog.h"
int running = 1;
int handover = 0;
static int notify(void)
{
int sd, result = 255;
ssize_t len;
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
.sun_path = INIT_SOCKET,
};
struct init_request rq;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
return -1;
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
goto exit;
rq.cmd = INIT_CMD_WDOG_HELLO;
rq.magic = INIT_MAGIC;
rq.runlevel = getpid();
len = sizeof(rq);
if (write(sd, &rq, len) != len)
goto exit;
if (read(sd, &rq, len) != len)
goto exit;
result = 0;
exit:
close(sd);
return result;
}
int shutdown = 0;
static void sighandler(int signo)
{
if (signo == SIGTERM)
handover = 1;
if (signo == SIGPWR)
shutdown = 1;
running = 0;
}
static int init(char *progname, char *devnode, int timeout)
static int init(char *progname, char *devnode)
{
int fd;
@@ -88,45 +55,64 @@ static int init(char *progname, char *devnode, int timeout)
if (fd == -1)
return -1;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
return fd;
}
static int loop(int fd)
static int loop(int fd, int timeout)
{
int done = 0;
int dummy = 0;
int period = timeout / 2;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
while (running) {
do_sleep(1);
ioctl(fd, WDIOC_KEEPALIVE, &done);
if (!done && !notify())
done = 1;
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
sleep(period);
}
if (handover)
/* System is going down, prepare to reboot system on TERM */
if (shutdown) {
timeout /= 3;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
}
/* External watchdogd wants to take over ... */
if (handover) {
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
return !write(fd, "V", 1);
}
return 0;
}
void watchdog(char *progname)
int watchdog(char *progname)
{
if (fork() == 0) {
int pid;
pid = fork();
if (pid == 0) {
int fd, ret;
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT);
fd = init(progname, WDT_DEVNODE);
if (fd == -1) {
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
_exit(1);
}
ret = loop(fd);
ret = loop(fd, WDT_TIMEOUT);
while (!handover) {
/* Waiting for SIGTERM ... */
sleep(1);
/* Set lowest possible timeout on SIGTERM */
ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
}
close(fd);
_exit(ret);
}
return pid;
}
/**
+3 -3
View File
@@ -24,12 +24,12 @@
#include "config.h" /* Generated by configure script */
#define WDT_DEVNODE "/dev/watchdog"
#define WDT_TIMEOUT 3
#define WDT_TIMEOUT 30
#ifdef BUILTIN_WATCHDOG
void watchdog(char *progname);
int watchdog(char *progname);
#else
#define watchdog(progname)
#define watchdog(progname) 0
#endif /* BUILTIN_WATCHDOG */
/**