Compare commits

...
113 Commits
Author SHA1 Message Date
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
46 changed files with 2082 additions and 1176 deletions
+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:
+69
View File
@@ -3,6 +3,75 @@ Change Log
All relevant changes are documented in this file.
[3.1][] - 2017-12-xx
--------------------
Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
### Changes
* Support for `rlimit` per service/run/task/inetd/tty, issue #45
* 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.
* 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
* 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.
### 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
* 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`
[3.0][] - 2017-10-19
--------------------
+32 -8
View File
@@ -111,7 +111,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](doc/conditions.md). One of
the most common conditions is to wait for basic networking to become
available:
@@ -124,6 +124,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:
@@ -179,6 +183,15 @@ tty [12345] /dev/ttyAMA0 115200 noclear vt100
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
```
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
```
For more information, see [doc/config.md](doc/config.md#syntax).
@@ -346,28 +359,39 @@ 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/]
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 | 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
+3 -3
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 3.0, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
AC_INIT(Finit, 3.1-rc1, 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])
@@ -33,8 +33,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,
+3 -8
View File
@@ -21,9 +21,9 @@ 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 `rescue` on the kernel command line, start rescue mode
if enabled with `configure` script. Could mean skip `/etc/finit.conf`
et al and jump straight to a single getty login on console.
* 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.
@@ -33,11 +33,6 @@ General
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.
+6
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
---------
+29 -23
View File
@@ -13,7 +13,7 @@ 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
@@ -51,15 +51,15 @@ Syntax
* `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. `RESOURCE` is a lower-case
string matching the `RLIMIT_` constants from `setrlimit(2)`, without
the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
The limit is an integer that depends on the resource being modified,
see the man page, or the kernel `/proc/PID/limits` file, for more
information. Finit versions before v3.1 used `infinity` for
`unlimited`, which is still a supported keyword.
```shell
# No process is allowed more than 8MB of address space
@@ -162,9 +162,9 @@ Syntax
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.
@@ -185,7 +185,20 @@ Syntax
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 +210,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
@@ -272,9 +279,8 @@ the above `finit.conf` settings described above, only the following:
- `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.
+2 -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")) {
+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);
+5 -6
View File
@@ -92,17 +92,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,7 +113,7 @@ 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);
}
@@ -148,7 +147,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);
+4 -3
View File
@@ -11,10 +11,10 @@ endif
sbin_PROGRAMS = finit initctl reboot
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 \
@@ -43,7 +43,8 @@ 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)
+112 -14
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,41 @@ 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);
}
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;
@@ -292,8 +380,17 @@ static void cb(uev_t *w, void *arg, int events)
_d("Failed sending ACK/NACK back to client");
}
if (UEV_ERROR == events)
goto error;
leave:
close(sd);
return;
error:
api_exit();
if (api_init(w->ctx))
_e("Unrecoverable error on API socket");
}
int api_init(uev_ctx_t *ctx)
@@ -305,6 +402,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 +418,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:
+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;
}
/**
+42
View File
@@ -0,0 +1,42 @@
/* 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"
typedef struct {
char *cmd;
int (*cb)(char *arg);
} command_t;
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_ */
+54 -14
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,16 +25,53 @@
#include <libgen.h>
#include <lite/lite.h>
#include <stdio.h>
#include <sys/stat.h>
#include "finit.h"
#include "cond.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);
}
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);
@@ -50,8 +87,7 @@ int cond_set_path(const char *path, enum cond_state new)
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 0;
}
touch(path);
utimensat(0, path, NULL, 0);
cond_set_gen(path, rgen);
break;
case COND_OFF:
@@ -88,14 +124,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);
}
}
@@ -121,8 +157,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);
}
@@ -149,15 +185,19 @@ 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)
{
if (makepath(COND_PATH) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", COND_PATH);
return;
}
cond_bump_reconf();
}
/**
* Local Variables:
+24 -15
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
@@ -28,14 +28,6 @@
#include "cond.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)
{
@@ -57,18 +49,35 @@ const char *cond_path(const char *name)
return 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)
+2
View File
@@ -15,6 +15,7 @@ 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);
@@ -25,6 +26,7 @@ void cond_set (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_ */
+314 -110
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"
@@ -35,11 +37,22 @@
#include "tty.h"
#include "helpers.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);
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)
{
@@ -163,59 +176,74 @@ 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;
const struct rlimit_name *rn;
for (rn = rlimit_names; rn->name; rn++) {
if (!strcmp(str, rn->name))
return rn->val;
}
return -1;
}
char *rlim2str(int rlim)
{
const struct rlimit_name *rn;
for (rn = rlimit_names; rn->name; rn++) {
if (rn->val == rlim)
return rn->name;
}
return "unknown";
}
void conf_parse_rlimit(char *line, struct rlimit arr[])
{
char *level, *limit, *val;
int resource = -1;
rlim_t cfg, *set;
char *tok = strtok(line, " \t");
level = strtok(line, " \t");
if (!level)
goto error;
if (tok && !strcmp(tok, "soft"))
set = &rlim.rlim_cur;
else if (tok && !strcmp(tok, "hard"))
set = &rlim.rlim_max;
limit = strtok(NULL, " \t");
if (!limit)
goto error;
resource = str2rlim(limit);
if (resource < 0 || resource > RLIMIT_NLIMITS)
goto error;
val = strtok(NULL, " \t");
if (!val)
goto error;
if (!strcmp(level, "soft"))
set = &arr[resource].rlim_cur;
else if (!strcmp(level, "hard"))
set = &arr[resource].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;
}
if (resource < 0)
goto fail;
tok = strtok(NULL, " \t");
if (!tok)
goto fail;
if (!strcmp(tok, "infinity")) {
new = RLIM_INFINITY;
/* 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;
*set = new;
if (setrlimit(resource, &rlim))
goto fail;
return;
fail:
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
*set = cfg;
return;
error:
logit(LOG_WARNING, "rlimit: parse error");
@@ -226,13 +254,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)) {
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 +270,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;
@@ -273,10 +301,12 @@ static void parse_static(char *line)
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,7 +319,7 @@ 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];
@@ -315,26 +345,26 @@ static void parse_dynamic(char *line, struct timeval *mtime)
/* 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 +373,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 +395,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 +406,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 +420,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 +432,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,43 +455,56 @@ 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", rlim2str(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);
/* 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);
return 0;
}
/* Update global limits in case operator used prlimit(1) on us */
for (i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
for (i = 0; i < num; i++) {
char *name = e[i]->d_name;
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 +521,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 +535,210 @@ 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);
/* Drop record of all .conf changes */
drop_changes();
/*
* 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_changed(char *file)
{
char *ptr;
if (file && (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;
/*
* 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);
}
/**
+10 -2
View File
@@ -26,11 +26,19 @@
#include "svc.h"
extern struct rlimit global_rlimit[];
int str2rlim(char *str);
char *rlim2str(int rlim);
int conf_init (void);
void conf_reload (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_ */
+107 -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");
@@ -351,14 +437,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;
}
+99 -65
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"
@@ -57,7 +58,6 @@ char *network = NULL;
char *hostname = NULL;
char *rcsd = FINIT_RCSD;
char *runparts = NULL;
char *console = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
@@ -108,7 +108,7 @@ static int fismnt(char *dir)
*/
static int fsck(int pass)
{
int save;
// int save;
struct fstab *fs;
if (!setfsent()) {
@@ -116,8 +116,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,8 +143,8 @@ 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;
@@ -241,11 +241,80 @@ 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)
{
/*
* Network stuff
*/
_d("Setting up networking ...");
networking();
umask(022);
/* Hooks that rely on loopback, or basic networking being up. */
_d("Calling all network up hooks ...");
plugin_run_hooks(HOOK_NETWORK_UP);
/*
* 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);
/*
* 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 */
_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;
/*
@@ -298,16 +367,13 @@ int main(int argc, char* argv[])
* Load plugins early, finit.conf may contain references to
* features implemented by plugins.
*/
ret = plugin_init(&loop, PLUGIN_PATH);
plugin_init(&loop, PLUGIN_PATH);
/*
* Hello world.
*/
banner();
/* Show results after banner() */
print(ret, "Loading plugins");
/*
* Check file filesystems in /etc/fstab
*/
@@ -329,9 +395,8 @@ int main(int argc, char* argv[])
makedir("/dev/pts", 0755);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
/* Some systems rely on /dev/shm being there, for `mount -a` below */
makedir("/dev/shm", 0755);
if (!fismnt("/dev/shm"))
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
/*
* New tmpfs based /run for volatile runtime data
@@ -361,7 +426,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;
}
@@ -386,18 +451,14 @@ 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.
* Initialize .conf system and load static /etc/finit.conf
*/
conf_parse_config();
conf_init();
/*
* 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
@@ -413,6 +474,9 @@ int main(int argc, char* argv[])
if (fexist("/lib/udev/udev-finish"))
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
/* Bootstrap conditions, needed for hooks */
cond_init();
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
@@ -426,6 +490,12 @@ int main(int argc, char* argv[])
/* Base FS up, enable standard SysV init signals */
sig_setup(&loop);
/*
* Set up inotify watcher for /etc/finit.d and read all .conf
* files to figure out how to bootstrap the system.
*/
conf_monitor(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
@@ -436,56 +506,20 @@ 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);
/*
* 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);
}
+3 -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
@@ -98,7 +101,6 @@ extern char *sdown;
extern char *network;
extern char *hostname;
extern char *runparts;
extern char *console;
extern uev_ctx_t *ctx;
#endif /* FINIT_H_ */
+9 -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,29 +226,20 @@ 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);
}
+10 -3
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,6 +51,11 @@ struct fstab *getfsent (void);
char *strip_line (char *line);
int getty (char *tty, speed_t speed, char *term, char *user);
int stty (int fd, speed_t speed);
speed_t stty_parse_speed(char *baud);
int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
@@ -56,7 +64,6 @@ 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);
@@ -65,8 +72,8 @@ 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[]);
int run_parts (char *dir, char *cmd);
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
+28 -5
View File
@@ -70,6 +70,22 @@ 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];
do {
inetd_dgram_peek(sd, pkt_interface);
if(string_compare(pkt_interface, ifname)) {
recv(sd, buf, sizeof(buf), 0);
continue;
}
} while (0);
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
static int inetd_stream_peek(int sd, char *ifname)
{
@@ -129,6 +145,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;
}
@@ -142,7 +160,12 @@ static void socket_cb(uev_t *w, void *arg, int events)
svc_t *svc = (svc_t *)arg, *task;
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);
@@ -202,10 +225,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)
@@ -642,9 +665,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;
+86 -148
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,25 @@
#define _PATH_COND _PATH_VARRUN "finit/cond/"
typedef struct {
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 +72,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 +91,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 +102,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 +132,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 +145,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 +201,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 +220,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 +248,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;
@@ -334,9 +273,9 @@ static int do_cond(char *cmd)
int c;
char *arg;
command_t 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 +285,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 +370,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,50 +398,42 @@ 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);
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("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);
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));
@@ -539,7 +470,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 +479,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 +510,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 +524,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 +537,7 @@ 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 | 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,6 +558,7 @@ static int do_help(char *arg)
int main(int argc, char *argv[])
{
int c;
char *cmd, arg[120];
command_t command[] = {
{ "debug", toggle_debug },
{ "help", do_help },
@@ -633,6 +567,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 +608,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);
+26 -28
View File
@@ -63,7 +63,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));
@@ -105,45 +105,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);
}
+38 -3
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) \
@@ -163,6 +165,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 +189,34 @@ 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) {
_d("End plugin recursion in our lifetime!");
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(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 +225,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;
+35 -21
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
@@ -34,8 +34,16 @@
#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 +51,34 @@
#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
*/
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_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, "hook/svc/reconf"), \
CHOOSE(HOOK_SVC_LOST, "hook/svc/lost"), \
CHOOSE(HOOK_SVC_START, "hook/svc/start"), \
CHOOSE(HOOK_RUNLEVEL_CHANGE, "hook/sys/runlevel"), \
\
/* 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
+1
View File
@@ -40,6 +40,7 @@ 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);
+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_ */
+173 -72
View File
@@ -26,6 +26,7 @@
#include <ctype.h> /* isblank() */
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <net/if.h>
#include <lite/lite.h>
@@ -46,55 +47,33 @@
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;
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
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 +86,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)
{
@@ -160,16 +140,16 @@ static int service_start(svc_t *svc)
return 1;
}
if (svc_is_daemon(svc) || svc_is_inetd(svc))
#ifdef INETD_ENABLED
if (svc_is_inetd(svc))
return inetd_start(&svc->inetd);
#endif
if (svc_is_daemon(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));
#endif
/* Declare we're waiting for svc to create its pidfile */
svc_starting(svc);
@@ -191,6 +171,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);
@@ -286,7 +274,7 @@ static int service_start(svc_t *svc)
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);
@@ -323,8 +311,6 @@ static int service_start(svc_t *svc)
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)) {
@@ -334,8 +320,17 @@ static int service_start(svc_t *svc)
}
sigprocmask(SIG_SETMASK, &omask, NULL);
print_result(result);
return 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 +343,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;
}
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));
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
kill(svc->pid, SIGKILL);
/* Let SIGKILLs stand out, show result as [WARN] */
@@ -470,9 +471,10 @@ void service_runlevel(int newlevel)
/**
* 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
@@ -519,10 +521,10 @@ 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
@@ -533,12 +535,12 @@ int service_register(int type, char *svcline, struct timeval *mtime)
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;
@@ -554,7 +556,7 @@ int service_register(int type, char *svcline, struct timeval *mtime)
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;
}
@@ -632,11 +634,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);
@@ -717,11 +723,17 @@ 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);
/* Free duped line, from above */
free(line);
return 0;
}
@@ -772,33 +784,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;
*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->desc);
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->desc, *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);
}
@@ -818,14 +830,14 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
void 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),
@@ -880,20 +892,25 @@ 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;
@@ -923,6 +940,11 @@ restart:
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
if (svc_is_runtask(svc)) {
svc->once++;
break;
}
}
cond = cond_get_agg(svc->cond);
@@ -963,7 +985,7 @@ restart:
}
if (!svc->pid) {
(*restart_counter)++;
(*restart_cnt)++;
svc_set_state(svc, SVC_READY_STATE);
break;
}
@@ -998,6 +1020,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
+4 -2
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);
void service_step_all (int types);
void service_bootstrap_cb (uev_t *w, void *arg, int events);
#endif /* FINIT_SERVICE_H_ */
/**
+40
View File
@@ -224,6 +224,11 @@ 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;
}
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
service_reload_dynamic();
}
@@ -234,6 +239,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 +254,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 +269,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 +284,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 +300,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 +320,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 +334,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);
}
+7 -6
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:
@@ -143,7 +144,10 @@ restart:
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (runlevel != 0 && runlevel != 6)
conf_reload_dynamic();
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:
*/
+119 -241
View File
@@ -27,6 +27,7 @@
#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"
@@ -35,38 +36,7 @@
/* 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 +49,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 +63,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 = rindex(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 +94,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 +151,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 +179,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;
}
@@ -256,28 +207,12 @@ svc_t *svc_job_iterator(int first, int job)
*/
void svc_foreach(void (*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);
}
@@ -289,12 +224,12 @@ void svc_foreach_dynamic(void (*cb)(svc_t *))
*/
void svc_foreach_type(int types, void (*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 +247,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 +266,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 +285,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 +305,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 +326,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++;
@@ -419,12 +354,10 @@ 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;
while (svc) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
*((int *)&svc->dirty) = -1;
svc = svc_dynamic_iterator(0);
}
}
void svc_mark_dirty(svc_t *svc)
@@ -437,17 +370,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 +379,13 @@ 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) {
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 +416,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 +463,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 +497,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 +526,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;
+82 -36
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,24 +80,28 @@ 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;
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, ... */
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;
@@ -124,23 +127,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);
@@ -149,28 +135,24 @@ 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_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 *));
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);
@@ -183,7 +165,6 @@ 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; }
@@ -191,6 +172,7 @@ 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_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & 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; }
@@ -202,6 +184,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:
*/
+48 -37
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,7 +134,7 @@ 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;
@@ -168,6 +171,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))
@@ -261,7 +266,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 +285,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 +374,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 +387,15 @@ 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->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 +500,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;
}
+5 -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
@@ -43,6 +44,9 @@ typedef struct {
char *args[TTY_MAX_ARGS];
int pid;
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
} finit_tty_t;
typedef struct tty_node {
@@ -50,7 +54,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 +61,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);
+4 -1
View File
@@ -67,7 +67,7 @@ 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;
@@ -75,6 +75,9 @@ char *sanitize(char *arg, size_t len)
void screen_init(void)
{
if (!isatty(STDOUT_FILENO))
return;
initscr(&screen_rows, &screen_cols);
}
+16 -49
View File
@@ -21,54 +21,17 @@
* 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;
}
static void sighandler(int signo)
{
if (signo == SIGTERM)
@@ -93,27 +56,29 @@ static int init(char *progname, char *devnode, int timeout)
return fd;
}
static int loop(int fd)
static int loop(int fd, int period)
{
int done = 0;
int dummy = 0;
while (running) {
do_sleep(1);
ioctl(fd, WDIOC_KEEPALIVE, &done);
if (!done && !notify())
done = 1;
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
sleep(period);
}
if (handover)
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);
@@ -122,11 +87,13 @@ void watchdog(char *progname)
_exit(1);
}
ret = loop(fd);
ret = loop(fd, WDT_TIMEOUT / 2);
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 */
/**