Compare commits

..
51 Commits
Author SHA1 Message Date
Joachim Nilsson cf651df744 Bump version for v1.12 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 17:00:37 +01:00
Joachim Nilsson 807df44769 finit.conf: Update sample config with some example inetd services.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 16:59:53 +01:00
Joachim Nilsson f97bc83ac1 Update libuev to v1.1.0 +1, with .gitignore update.
This commit prevents false warnings about untracked files in libuev.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 16:14:18 +01:00
Joachim Nilsson daa299721d inetd.c: Check for SO_REUSEPORT capability in system first.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 15:54:13 +01:00
Joachim Nilsson ec90e3dac3 Update ChangeLog before release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 15:32:49 +01:00
Joachim Nilsson 5ddd5c15a5 inetd.c:spawn_socket() - Set socket options for reusing address and port
Without this patch binding to sockets when restarting them fails.  This
was found when quickly changing between runlevels where a service runs,
not runs, then back to a runlevel where is shall run.

Also, refactor previous setsockopt() call to use new common macro.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 15:21:43 +01:00
Joachim Nilsson 957586db09 inetd.c:inetd_dgram_peek() - Fix reading inbound UDP interface.
There seems to have been a regression from earlier when this worked.
I'm sure it worked ... or am I?  This patch makes the whole thing look
a lot more sane, so I wonder ... might have worked in my unit tests on
x86, but not when I cross-compile to ARM.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 15:15:03 +01:00
Joachim Nilsson e0c8a7df66 inetd.c: Shutdown sockets properly before closing them.
When changing runlevels netstat still listed closed sockets as being
open and listening.  With this patch all sockets are properly shut down
before we close them.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 14:45:21 +01:00
Joachim Nilsson c769fbda0f svc.c: Add debug to svc_runlevel().
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 14:45:04 +01:00
Joachim Nilsson 1f860164f4 initctl: Make sure to open fifo with CLOEXEC
With O_CLOEXEC we prevent forked+exec'ed children from accessing the
initctl fifo via that socket.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 14:43:06 +01:00
Joachim Nilsson e29c5c069b TODO: Minor update.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 11:49:28 +01:00
Joachim Nilsson 872d0d24e7 inetd.c: Only ever allow respawn if correct runlevel.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 11:48:16 +01:00
Joachim Nilsson b644f6f635 plugins/initctl.c: Minor debug updates.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 11:47:14 +01:00
Joachim Nilsson 510d15195c plugins.c:generic_io_cb() -- Stop watcher before running callback
Stop event watcher before running callback, the plugin may manipulate
the descriptor and cause the kernel into a EPOLLHUP frenzy.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 11:46:32 +01:00
Joachim Nilsson 7ad4bce487 Upgrade to libuEv v1.1.0 for error handling fixes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 11:02:01 +01:00
Joachim Nilsson 703b7537cf Refactor inetd support to add support for switching runlevels.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-01 02:35:30 +01:00
Joachim Nilsson 9cb6c88f34 Spellchecks, cleanup and simplify intro.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-27 10:02:08 +01:00
Joachim Nilsson 97068223ff CHANGELOG: Add user-friendly info on the 1.12 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-27 00:14:49 +01:00
Joachim Nilsson 1de5777f7e README: Update with new inetd support.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-27 00:14:14 +01:00
Joachim Nilsson aff11d83d8 tty.c: Let fallback shells be consoles, improved job control etc.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 18:57:17 +01:00
Joachim Nilsson 335f09726f conf.c: Slight update in TTY fallback behavior.
If no tty is configured in finit.conf, only use FALLBACK_SHELL (usually
/bin/sh on UNIX) when no console is configured either.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 18:57:10 +01:00
Joachim Nilsson 12c470667f Only restart daemons when getting SIGCONT after SIGSTOP
Fix `restart_any_lost_procs()`, called when finit recovers from being
stopped for a while.  This function should not restart task/run/inetd
services when called -- must check for type `SVC_CMD_SERVICE` when
iterating over all registered `svc_t`, just like a regular lost pid.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 16:55:15 +01:00
Joachim Nilsson 71ac8bb802 Set hostname early, so boostrap processes, e.g., syslog can use it.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 16:47:55 +01:00
Joachim Nilsson fc9758da8e Fix Coverity CID #87811 Double close
Fix double close of TCP socket introduced in GIT ded4b39,
replaced with closing all open stdio descriptors, which
shouldn't really be needed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 16:39:30 +01:00
Joachim Nilsson 216701ce3c Attempt to fix Coverity CID #87810 Dereference null return value
Fixes, or rather works around the Coverity warning of possible NULL
pointer dereference.  (Not an error.)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 16:34:38 +01:00
Joachim Nilsson 5d5075954e TODO: Add some ideas on pmon to watchdog chapter.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 16:14:29 +01:00
Joachim Nilsson ded4b3926e Refactor inetd support, now with support for custom ports and iface filtering
This patch adds support for custom port and interface filtering to inetd
services, in a very condensed syntax:

    inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
    inetd ssh@eth1:22/tcp  nowait [2345] /usr/sbin/sshd -i

In this example eth0 and eth1 are allowed inbound interfaces for SSH
connections, on port 222 and 22, respectively.  Attempting to connect
from any other interface is denied.  Also, if the system is not in
runlevel 2, 3, 4, or 5, ports 222 and 22 will not even be opened.

If eth0 is your upstream interface you may want to avoid using the
default port.  To run ssh on port 222, and all others on port 22:

    inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
    inetd ssh/tcp          nowait [2345] /usr/sbin/sshd -i

This actually adds a deny rule for eth0 on ssh/tcp, implicitly.  You can
even list the services in the reverse order with the same result:

    inetd ssh/tcp          nowait [2345] /usr/sbin/sshd -i
    inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i

There is no specific deny syntax available yet, see the TODO file for
more details on how this can be implemented.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-26 01:37:11 +01:00
Joachim Nilsson 365932510f Update dreamworld.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-25 10:13:44 +01:00
Joachim Nilsson 888128e56b README: Slight cleanup to make it more readable w/o Markdown.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-25 10:13:12 +01:00
Joachim Nilsson 2adec264af Replace atoi() with atonum() --> better error handling.
Friends don't let friends use atoi().

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-25 10:12:15 +01:00
Joachim Nilsson ccdab464c3 Fix Coverity CID 87570: Unchecked return value from setsockopt()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 23:10:30 +01:00
Joachim Nilsson a7353678a3 Actually fix Coverity CID 56281: dlopen() resource leak
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 22:59:32 +01:00
Joachim Nilsson ad8ae4af91 Fix Coverity CID CID 56281: Resource leak (RESOURCE_LEAK)
Initial support for unloading plugins.  Currently we only save dlopen()
handle.  The plugin_unregister() function itself is never called atm.
This should at the very least make Coverity happy.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 22:39:54 +01:00
Joachim Nilsson dd7f0ce090 Fix Coverity CID 87571: Uninitialized scalar variable (UNINIT)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 22:18:02 +01:00
Joachim Nilsson 8c955836dd Load plugins before finit.conf is parsed, needed by internal inetd services
This patch is slightly sub-optimal, since it will causes us to load all
availble plugins, not just those referenced by finit.conf.  However, in
the short term perspective we need to reference internal inted services
provided by plugins from finit.conf, so the dependency order is clear.

In the midterm perspective we want to add support for SIGHUP to reload
finit.conf, but not plugins.  So again, this is an OK patch.

One fix would be to allow loading of all plugins, parse finit.conf and
then unload all unused plugins before continuing.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 22:04:36 +01:00
Joachim Nilsson 183022ebe4 Initial support for RFC 868 (rdate), internal inetd service.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 22:04:31 +01:00
Joachim Nilsson 33be077f92 TODO: Remember to support non-getty logins.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 16:53:00 +01:00
Joachim Nilsson 6a137fa73c TODO: Update with info on internal inetd services, like time, echo etc.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-18 15:17:00 +01:00
Joachim Nilsson 25c85dfc88 Add markdown hinting for Emacs.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-16 10:27:16 +01:00
Joachim Nilsson 58cc96115c Initial support for figuring out inbound interface for inetd connections
This patch adds initial support (only SOCK_STREAM verified!) for
figuring out the inbound interface for inetd service connections.

The intention is to use this later on for a very simple tcpwrappers
replacement, e.g: `deny telnet eth0 eth3`

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-16 10:26:54 +01:00
Joachim Nilsson b4b63d3722 Trap segfaults caused by service callbacks in separate process context
This patch traps segfaults caused by 3rd party service callbacks in a
separate process context.  Effectively preventing a single programming
mistake from taking down the entire system.  Previously it was up to the
callback coder to write error free code so that PID 1 did not crash.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-16 10:21:03 +01:00
Joachim Nilsson d6efe85a57 Silence launch of inetd services.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-09 00:57:21 +01:00
Joachim Nilsson 51b27e8372 Revert "Use vfork() instead of fork() before exec()"
This reverts commit 074686b520.  Which
turned out to not work so well after all.  For instance, launching TTYs
in a background process completely blocked inetd services from even
starting up listening sockets ... proper fork seems to work fine though.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-09 00:45:09 +01:00
Joachim Nilsson cac612c485 Remember to fix these ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-09 00:43:33 +01:00
Joachim Nilsson a5b9f566b3 Initial support for inetd/on-demand services \o/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-09 00:43:15 +01:00
Joachim Nilsson 0d098616e8 Whitespace changes only
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-01 17:28:36 +01:00
Joachim Nilsson 63722a20ac Allow mixed case configuration commands in finit.conf
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-01 17:27:18 +01:00
Joachim Nilsson 6f6ab6bc33 Minor whitespace fixes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-29 12:34:30 +01:00
Joachim Nilsson 7f0a85c106 Updated inetd syntax and add possible bug, needs investigating.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-29 12:33:35 +01:00
Joachim Nilsson 46ddf4bcc6 Commence new -dev phase.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 23:56:52 +01:00
Joachim Nilsson f5ab075f9d Upgrade libuEv to v1.0.4
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 23:54:53 +01:00
23 changed files with 1518 additions and 216 deletions
+44 -2
View File
@@ -4,11 +4,51 @@ Change Log
All notable changes to the project are documented in this file.
[1.11] - 2015-01-24
[1.12] - 2015-03-04
-------------------
The inetd release.
### Changes
* Add support for built-in inetd super server -- launch services on
demand. Supports filtering per interface and custom Inet ports.
* Upgrade to [libuEv] v1.1.0 to better handle error conditions.
* Allow mixed case config directives in `finit.conf`
* Add support for RFC 868 (rdate) time plugin, start as inetd service.
* Load plugins before parsing `finit.conf`, this makes it possible to
extend finit even with configuration commands. E.g., the `time.so`
plugin must be loaded for the `inetd time/tcp internal` service to be
accepted when parsing `finit.conf`.
* Slight change in TTY fallback behavior, if no TTY is listed in the
system `finit.conf` first inspect the `console` setting and only if
that too is unset fall back to `/bin/sh`
* When falling back to the `console` TTY or `/bin/sh`, finit now marks
this fallback as console. Should improve usability in some use cases.
### Fixes
* Revert "Use vfork() instead of fork() before exec()" from v1.11. It
turned out to not work so well after all. For instance, launching
TTYs in a background process completely blocked inetd services from
even starting up listening sockets ... proper fork seems to work fine
though. This is the casue for *yanking* the [1.11] release, below.
* Trap segfaults caused by external plugins/callbacks in a sub-process.
This prevents a single programming mistake in by a 3rd party developer
from taking down the entire system.
* Fix Coverity CID 56281: dlopen() resource leak by storing the pointer.
For the time being we do not support unloading plugins.
* Set hostname early, so bootstrap processes like syslog can use it.
* Only restart *lost daemons* when recovering from a SIGSTOP/norespawn.
[1.11] - 2015-01-24 [YANKED]
----------------------------
The [libuEv] release.
**Note:** This release has been *yanked* from distribution due to a
regression in launching background processes and TTY's. Fixed in
Finit v[1.12].
### Changes
* Now using the asynchronous libuEv library for handling all events:
signals, timers and listening to sockets or file descriptors.
@@ -255,7 +295,9 @@ Major bug fix release.
[libuEv]: https://github.com/troglobit/libuev
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
[1.11]: https://github.com/troglobit/finit/compare/1.10...HEAD
[UNRELEASED]: https://github.com/troglobit/finit/compare/1.12...HEAD
[1.12]: https://github.com/troglobit/finit/compare/1.11...1.12
[1.11]: https://github.com/troglobit/finit/compare/1.10...1.11
[1.10]: https://github.com/troglobit/finit/compare/1.9...1.10
[1.9]: https://github.com/troglobit/finit/compare/1.8...1.9
[1.8]: https://github.com/troglobit/finit/compare/1.7...1.8
+2 -2
View File
@@ -26,7 +26,7 @@
uninstall-exec uninstall-data uninstall-dev
#VERSION ?= $(shell git tag -l | tail -1)
VERSION ?= 1.11
VERSION ?= 1.12
NAME = finit
PKG = $(NAME)-$(VERSION)
DEV = $(NAME)-dev
@@ -36,7 +36,7 @@ ARCHIVEZ = ../$(ARCHIVE).xz
EXEC = finit reboot
HEADERS = plugin.h svc.h helpers.h queue.h
DISTFILES = LICENSE README CHANGELOG finit.conf services
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o inetd.o
SRCS = $(OBJS:.o=.c)
DEPS = $(addprefix .,$(SRCS:.c=.d))
+175 -58
View File
@@ -1,10 +1,10 @@
Finit | Fast & Extensible init for Linux
========================================
[![Build Status](https://travis-ci.org/troglobit/finit.png?branch=master)](https://travis-ci.org/troglobit/finit)
[![Coverity Scan Status](https://scan.coverity.com/projects/3545/badge.svg)](https://scan.coverity.com/projects/3545)
[![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
![Original Finit homepage image](images/finit.jpg "Finit in action!")
Table of Contents
-----------------
@@ -14,8 +14,9 @@ Table of Contents
* [/etc/finit.d](#etcfinitd)
* [Bootstrap](#bootstrap)
* [Runlevels](#runlevels)
* [Inetd](#inetd)
* [Hooks, Callbacks & Plugins](#hooks-callbacks--plugins)
* [Rebooting and Halting](#rebooting-and-halting)
* [Rebooting & Halting](#rebooting--halting)
* [Building](#building)
* [Running](#running)
* [Debugging](#debugging)
@@ -25,30 +26,26 @@ Table of Contents
Introduction
------------
Init is the first process to run once a UNIX kernel has booted, it
always has PID 1 and is responsible for starting up the rest of the
system. Finit is plugin-based with built in [process supervision][1]
similar to that of D.J. Bernstein's [daemontools][2] and Gerrit Pape's
[runit][3]. The main focus of Finit is on small and embedded GNU/Linux
systems, yet fully functional on standard server and desktop
installations as well.
Init is the first userland process started by the UNIX kernel, therefore
it always has PID 1 and is responsible for starting up the rest of the
system.
Finit is a plugin-based init with [process supervision][1] similar to
that of D.J. Bernstein's [daemontools][2] and Gerrit Pape's [runit][3].
The main focus of Finit is on small and embedded GNU/Linux systems, yet
fully functional on standard server and desktop installations as well.
Traditional [SysV init][4] style systems are scripted. For low-resource
embedded systems this can be quite resource intensive and cause longer
boot times. Finit is optimized to reduce context switches and forking
of processes to provide a very basic bootstrap written entirely in C.
Hence, there is no `/etc/init.d/rcS` script, or similar, but instead a
human-readable `/etc/finit.conf`. This file details what kernel modules
to load, programs to run and daemons to supervise.
The command line arguments given in `/etc/finit.conf` to each service
provide a default. Each service can register callbacks, using plugins,
to override and modify the behavior to suit the current runlevel and
system configuration. For instance, before starting a heavily resource
intensive service like IPsec or OpenVPN, a callback can check if the
outbound interface is up and has an IP address, or just check if the
service is disabled -- much like what a SysV init start script usually
does.
> Finit basically does everything you need from its `main()` function!
Hence, there is no `/etc/init.d/rcS` script, or similar, instead Finit
reads its configuration from [/etc/finit.conf](#etcfinitconf). This
file details what kernel modules to load, programs to run, daemons to
supervise, and services to launch on demand.
See [TroglOS][9] for an example of how to boot a small embedded system
with Finit.
@@ -59,11 +56,16 @@ Features
**Process Supervision**
Start, monitor and restart services if they fail.
Start, monitor and restart processes (daemons) if they fail.
**Inetd**
Finit comes with an `inetd` server built-in. No need to maintain a
separate config file for services that you want to start on demand.
**Runlevels**
Runlevels is optional in Finit, but support for [SysV runlevels][5] is
Runlevels are optional in Finit, but support for [SysV runlevels][5] is
available if needed. All services in runlevel S(1) are started first,
followed by the desired run-time runlevel. Runlevel S can be started in
sequence by using `run [S] cmd`. Changing runlevels at runtime is done
@@ -72,9 +74,10 @@ like any other init, e.g. <kbd>init 4</kbd>.
**Plugins**
Finit plugins can be either *boot hooks* into different stages of the
boot process or *service callbacks*, or both. A basic set of plugins
that extend and modify the basic behavior are bundled. See examples in
the `plugins/` directory.
boot process, *service callbacks*, or *pure extensions*. A plugin is a
C file that compiles into a dynamic library that is loaded automatically
by finit at boot. A basic set of plugins that extend and modify the
basic behavior are bundled. See examples in the `plugins/` directory.
Plugin capabilities:
@@ -95,6 +98,16 @@ Contrary to most other script based init alternatives ([SysV init][4],
[upstart][6], [systemd][7], [OpenRC][8], etc.) Finit reads its entire
configuration from `/etc/finit.conf`.
The command line arguments given in `/etc/finit.conf` to each service
provide a default. A plugin can be used to register a callback to a
service and then modify the behavior to suit the current runlevel and
system configuration.
For instance, before starting a heavily resource intensive service like
IPsec or OpenVPN, a callback can check if the outbound interface is up
and has an IP address, or just check if the service is disabled -- much
like what a SysV init start script usually does.
Syntax:
* `check <DEV>`
@@ -132,6 +145,20 @@ Syntax:
--foreground or --no-background argument to most daemons to prevent
them from forking off to the background.
* `inetd SVC[@iface[:port]]/PROTO <wait|nowait> [LVLS] /path/to/daemon args`
Launch daemon on demand when a client initiates a connection to `SVC`.
Services (SVC) are specified in the standard UNIX `/etc/services`
file. With optional filtering for `iface` and possible custom `port`.
The following example opens port 2323 and only allows inbound telnet
connections from `eth0`:
inetd telnet@eth0:2323/tcp nowait [2345] /sbin/telnetd -i -F
The `inetd` directive can also have ` -- Optional Description`, only
Finit does not output this text on the console when launching inetd
services. Instead this text is sent to syslog.
* `runparts <PATH>`
Call run-parts(8) on a directory other than default ``/etc/finit.d``
@@ -142,36 +169,50 @@ Syntax:
is prepended to file if the file is not found or an absolute path is
not given
* `tty [LVLS] <DEV | /bin/sh>`
* `tty [LVLS] <DEV | /path/to/cmd [args]>`
Start a getty on the given TTY device, in the given runlevels. When
Start a getty on the given TTY device DEV, in the given runlevels. If
no tty setting is given in `finit.conf`, or if `/bin/sh` is given as
argument instead of a device path, a single shell is started on the
default console. Useful for really bare-bones systems
default console. Useful for really bare-bones systems.
It is also possible to supply the full command line, with arguments,
to your getty. In this case finit will simply use that command.
See `finit.h` for the `#define GETTY` that is called, along with the
default baud rate.
* `console <DEV>`
* `console <DEV | /path/to/cmd [args]>`
Some embedded systems have a dedicated console port. This command
tells finit to not start getty, but instead print a friendly message
and wait for the user to activate the console with a key press before
starting getty.
Some embedded systems have a dedicated serial console/service port.
This command tells finit to not start getty directly, since there may
not be anyone there. To save RAM and CPU finit instead displays a
friendly message and waits for the user to activate the console with a
key press before starting getty. Finit also does some other magic and
changes the process name to "console".
Like the `tty` command, the `console` command can also be used to
simply start a UNIX shell, e.g. `/bin/sh`.
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!
Every `run`, `task`, `service`, or `inetd` can also list the privileges
the `/path/to/cmd` should be executed with. Simply prefix the path with
`[@USR[:GRP]]` like this:
run [2345] @joe:users /usr/bin/logger "Hello world"
/etc/finit.d
------------
At the end of the boot, when networking and all services are up, finit
calls its built-in run-parts(8) on the `/etc/finit.d/` directory, if it
exists. Similar to how the `/ec/rc.local` file works in most other init
daemons, only finit runs a directory of scripts. This replaces the
calls its built-in [run-parts(8)] on the `/etc/finit.d/` directory, if
it exists. Similar to how the `/ec/rc.local` file works in most other
init daemons, only finit runs a directory of scripts. This replaces the
earlier support for a `/usr/sbin/services.sh` script in the original
finit.
@@ -194,7 +235,7 @@ Bootstrap
13. Call `network` script, if set in `/etc/finit.conf`
14. Call 3rd level hooks, `HOOK_NETWORK_UP`
15. Switch to active runlevel, as set in `/etc/finit.conf`, default 2.
Here is where the rest of all tasks and services are started.
Here is where the rest of all tasks and inetd services are started.
16. Call 4th level hooks, `HOOK_SVC_UP`
17. Run-parts in `/etc/finit.d`, if any
18. Call 5th level (last) hooks, `HOOK_SYSTEM_UP`
@@ -205,7 +246,7 @@ In (10) and (15) tasks and services defined in `/etc/finit.conf` are
started. Remember, all `service` and `task` stanzas are started in
parallel and `run` in sequence, and in the order listed. Hence, to
emulate a SysV `/etc/init.d/rcS` one could write a long file with only
`run` statments.
`run` statements.
Notice the five hook points that are called at various point in the
bootstrap process. This is where plugins can extend the boot in any way
@@ -221,10 +262,10 @@ on external media.
Runlevels
---------
Basic support for [runlevels](http://en.wikipedia.org/wiki/Runlevel) is
included in Finit from v1.8. By default all services, tasks, run
commands and TTYs listed without a set of runlevels get a default set
`[234]` assigned. The default runlevel after boot is 2.
Basic support for [runlevels][5] is included in Finit from v1.8. By
default all services, tasks, run commands and TTYs listed without a set
of runlevels get a default set `[234]` assigned. The default runlevel
after boot is 2.
To specify an allowed set of runlevels for a `service`, `run` command, `task`,
or `tty`, add `[NNN]` to it in your `/etc/finit.conf`, like this:
@@ -255,6 +296,65 @@ Switching between runlevels can be done by calling init with a single
argument, e.g. <kbd>init 5</kbd> switches to runlevel 5.
Inetd
-----
A built-in *Internet Super Server* support was added in Finit v1.12,
along with an internal `time` inetd service, RFC 868 (rdate). The
latter is supplied as a plugin to illustrate how simple it is to extend
finit with more internal inetd services.
> Please note, not all UNIX daemons are prepared to run as inetd services.
> In the example below `sshd` also need the command line argument `-i`.
The inetd support in finit is quite advanced. Not only does it launch
services on demand, it can do so on custom ports and also filter inbound
traffic using a poor man's tcpwrappers. The syntax is very similar to
traditional `/etc/inetd.conf`, yet keeping with the style of Finit:
# Launch SSH on demand, in runlevels 2-5 as root
inetd ssh/tcp nowait [2345] @root:root /usr/sbin/sshd -i
A more advanced example:
# Start sshd if inbound connection on eth0, port 222, or
# inbound on eth1, port 22. Ignore on other interfaces.
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
inetd ssh@eth1:22/tcp nowait [2345] /usr/sbin/sshd -i
If eth0 is your Internet interface you may want to avoid using the
default port. To run ssh on port 222, and all others on port 22:
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
This actually adds a deny rule for eth0 on ssh/tcp, implicitly. You can
even list the services in the reverse order with the same result:
inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
There is no specific deny syntax available yet, see the TODO file for
more details on how this can be implemented.
The `time.so` inetd plugin setup as follows. Notice the keyword
`internal` which applies to all built-in inetd services:
inetd time/tcp nowait [2345] internal
Then call `rdate` from a remote machine (or use localhost):
rdate -p <IP>
If you use `time/udp` you must use the standard rdate implementation and
then call it with `rdate -up` to connect using UDP. Without the `-p`
argument rdate will try to set the system clock. Please note that rdate
has been deprecated by the NTP protocol and this plugin should only be
used for testing or environments where NTP for some reason is blocked.
Also, remember the UNIX year 2038 bug, or in the case of RFC 868 (and
some NTP implementations), year 2036!
Hooks, Callbacks & Plugins
--------------------------
@@ -278,13 +378,17 @@ plugins are available:
* *resolvconf.so*: Setup necessary files for resolvconf at startup.
* *tty.so*: Watches /dev, using inotify, for new device nodes (TTY's) to
start/stop getty consoles on them on demand.
* *tty.so*: Watches `/dev`, using inotify, for new device nodes (TTY's)
to start/stop getty consoles on them on demand. Useful when plugging
in a usb2serial converter to login to your embedded device.
* *urandom.so*: Setup random seed at startup.
* *x11-common.so*: Setup necessary files for X-Window.
* *time.so*: RFC 868 (rdate) plugin. Start as inetd service. Useful
for testing inetd filtering -- BusyBox has an rdate (TCP) client.
Usually you want to hook into the boot process once, simple hook plugins
like `bootmisc.so` are great for that purpose. They are called at each
hook point in the boot process, useful to insert some pre-bootstrap
@@ -322,8 +426,8 @@ service, `SVC_START (1)` to start the service, or `SVC_RELOAD (2)` to
have finit signal the process with `SIGHUP`.
Rebooting and Halting
---------------------
Rebooting & Halting
-------------------
Finit handles `SIGUSR1` and `SIGUSR2` for reboot and halt, and listens
to `/dev/initctl` so system reboot and halt commands should also work.
@@ -357,7 +461,7 @@ what is built and where resulting binaries are installed.
* `LDLIBS=`: Default `LIBLIBS` are inherited from the environment.
* `prefix=`: Base prefix path for all files, except `sbinbdir` and
* `prefix=`: Base prefix path for all files, except `sbindir` and
`sysconfdir`. Used in concert with the `DESTDIR` variable.
Defaults to `/usr`.
@@ -445,16 +549,13 @@ enable trace messages. A console getty is always started, see the file
Origin & References
-------------------
This is the continuation of the [original finit](http://helllabs.org/finit/)
by [Claudio Matsuoka](https://github.com/cmatsuoka), which in turn was
reverse engineered from syscalls of the
[EeePC fastinit](http://wiki.eeeuser.com/boot_process:the_boot_process)
-- "gaps filled with frog DNA ...".
This is the continuation of the [original finit] by [Claudio Matsuoka],
which in turn was reverse engineered from syscalls of the
[EeePC fastinit] -- "gaps filled with frog DNA ..."
Finit is currently being developed and maintained by
[Joachim Nilsson](http://troglobit.com) at
[GitHub](http://github.com/troglobit/finit). Please file bug reports, clone it,
or send pull requests for bug fixes and proposed extensions.
Finit is currently being developed and maintained by [Joachim Nilsson]
at [GitHub]. Please file bug reports, clone it, or send pull requests
for bug fixes and proposed extensions.
[1]: https://en.wikipedia.org/wiki/Process_supervision
[2]: http://cr.yp.to/daemontools.html
@@ -466,3 +567,19 @@ or send pull requests for bug fixes and proposed extensions.
[8]: http://www.gentoo.org/proj/en/base/openrc/
[9]: https://github.com/troglobit/troglos
[10]: ftp://troglobit.com/finit/finit-1.3.tar.xz
[run-parts(8)]: http://manpages.debian.org/cgi-bin/man.cgi?query=run-parts
[original finit]: http://helllabs.org/finit/
[EeePC fastinit]: http://wiki.eeeuser.com/boot_process:the_boot_process
[Claudio Matsuoka]: https://github.com/cmatsuoka
[Joachim Nilsson]: http://troglobit.com
[GitHub]: https://github.com/troglobit/finit
[Travis]: https://travis-ci.org/troglobit/finit
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+139 -16
View File
@@ -14,9 +14,11 @@ finit is to be a replacement for:
A small and simple replacement, primarily for embedded systems, with a
very low dependency on external packages.
General
-------
* Add compile-time supoprt for running `/bin/sh` instead of `getty`
* Add support for `init <q | reload-configuration>` and `SIGHUP` to
have finit reload its `finit.conf`, but also
* Add support for inotify to automatically reload `finit.conf`
@@ -45,23 +47,24 @@ Inetd
Simple way of running Internet services on demand, like the old inetd:
inetd PROTO/SERVICE <wait|nowait> [@USER[:GROUP]] /path/to/daemon args
inetd PORT/PROTO <wait|nowait> [@USER[:GROUP]] /path/to/daemon args -- desc
Examples:
# Inetd services, launched on demand
inetd tcp/ssh nowait [2345] @root:root /usr/sbin/sshd -i
inetd ssh/tcp nowait [2345] @root:root /usr/sbin/sshd -i -- SSH daemon
In keeping with the finit tradition, an optional callback can be setup
to each inetd service. When a client connects the finit will call the
callback with a `struct in_pktinfo` argument which the callback then can
allowed or deny.
Alternatively, borrowing from TCP wrappers by Wietse Venema, a set of
`allow` and `deny` directives can be added to `finit.conf` to control
inetd services. It could be what IP addreses, networks or interfaces
are allowed to connect to the given inetd services. To start with the
easiest (and what we need for WeOS at Westermo) is allow/deny per iface.
Alternatively, borrowing slightly from TCP wrappers by Wietse Venema, a
set of `allow` and `deny` directives can be added to `finit.conf` to
control inetd services. It could be what IP addreses, networks or
interfaces are allowed to connect to the given inetd services. To start
with the easiest (and what we need for WeOS at Westermo) is allow/deny
per iface.
# Disable access to SSH
deny ssh all
@@ -79,7 +82,8 @@ an SSH service on different ports on different interfaces:
}
# Run SSH on non-standard port 222 for upstream interface
tcp 222 {
tcp ssh {
port = 222
listen = wan0
exec = dropbear -i
wait = yes
@@ -92,7 +96,102 @@ an SSH service on different ports on different interfaces:
user = root
}
Syntax very similar to [xinetd](http://en.wikipedia.org/wiki/Xinetd)
or, more concise
# System has four interfaces: wan0, eth0, eth1, eth2
# This runs SSH on only three, with various ports
tcp ssh {
listen = wan0:222 eth0:22 eth1:22
exec = /usr/sbin/dropbear -i
wait = yes
}
this would be equivalent
tcp ssh {
listen = wan0:222 !eth2
exec = /usr/sbin/dropbear -i
wait = yes
}
Syntax similar to [xinetd](http://en.wikipedia.org/wiki/Xinetd)
### Custom Inet Port for Common Services
- How to do this, without an obtrusive syntax?
Maybe like this? Then we can still refer to it as the time protocol,
and have multiple entries.
inetd time:8037/udp wait [2345] @root:root internal
inetd time:3737/udp wait [2345] @root:root internal
But what if we want to have different allow/deny rules per service?
Here's a proposal, similar to user/pass encoding in URI's. This variant
could potentially be a lot easier to implement and also to maintain for
users, compared to the above allow/deny/xinetd variants.
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
inetd ssh@eth1:22/tcp nowait [2345] /usr/sbin/sshd -i
or
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
inetd ssh@*:22/tcp nowait [2345] /usr/sbin/sshd -i
or
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
All three versions would be equivalent, except for the first one when
there are more interfaces than just eth0 and eth1. The latter two would
however be equivalent.
Multiple `inetd ssh` entries with the same port will not be unique, they
will reuse all settings of the first listed entry. However, an entry
with a unique port and interface will be registered as a unique service.
**Note:** Also, there is currently no verification that the same port is
used more than once. So a standard http service will clash with an
ssh entry ssh@*:80/tcp ... why you would ever want that?
Inetd callbacks are no different from other callbacks. They match to an
inetd line using the command. So multiple `/usr/bin/sshd` inetd lines
in `finit.conf` will all use the same callback. However, the callback
will be called with the Internet port and interface as arguments. Like
this:
svc_cmd_t sshd_callback(svc_t *svc, int port, char *ifname)
{
if (!cfg_is_ssh_enabled(ifname, port))
return SVC_STOP;
return SVC_START;
}
As usual, the callback is run in a separate process context and it is up
to the implementer to return `SVC_STOP` or `SVC_START` (`SVC_RELOAD` is
not applicable for inetd services).
### Internal inetd services
There are a few standard services that inetd usually had built-in:
- time
- echo
- chargen
- discard
At least 'time' will be implemented in finit, as a plugin, to serve as a
simple means of testing inetd functionality stand-alone, but also as a
very rudimentary time server for rdate clients.
Example:
# Built-in inetd time service, launched on demand
inetd time/udp wait [2345] @root:root internal
Crond
@@ -131,16 +230,32 @@ Watchdog
Support for monitoring the health of the system and its processes.
* Add support for a `/dev/watchdog` plugin to replace `watchdogd`
* Integrate system supervisor to optionally reboot the system when a
process stops responding, or when a respawn limit has been reached,
as well as when system load gets too high.
* API for processes to register with the watchdog.
- Deeper monitoring of a process main loop
* Add support for a `/dev/watchdog` built-in to replace both the BusyBox
watchdogd and my own refreshed uClinux-dist adaptation,
[watchdogd](https://github.com/troglobit/watchdogd)
* Add a system supervisor (pmon) to optionally reboot the system when a
process stops responding, or when a respawn limit has been reached, as
well as when system load gets too high. For details on UNIX loadavg,
see http://stackoverflow.com/questions/11987495/linux-proc-loadavg
- Default to 0.7 as MAX recommended load before warning and 0.9 reboot
* API for processes to register with the watchdog, libwdt
- Deeper monitoring of a process' main loop by instrumenting
- If a process is not heard from within its subscribed period time
reboot system or restart process.
- Add client libwdt library, inspired by old Westermo API and the
[libwdt] API published in the [Fritz!Box source dump], take for
instance the `fritzbox7170-source-files-04.87-tar.gz` drop and see
the `GPL-release_kernel.tgz` in `drivers/char/avm_new/` for the
kernel driver, and the `LGPL-GPL-release_target_tools.tgz` in `wdt/`
for the userland API.
- When a client process (a standard app/daemon instrumented with
libwdt calls in its main loop) registers with the watchdog, we raise
the RT priority to 98 (just below the kernel watchdog in prio).
This to ensure that system monitoring goes before anything else in
the system.
- Use UNIX domain sockets for communication between daemon and clients
* Separate `/etc/watchdog.conf` configuration file, or a perhaps
support for `/etc/finit.d/PLUGIN.conf`?
support for a more generic `/etc/finit.d/PLUGIN.conf`?
* Support enable/disable watchdog features:
- Supervise processes,
- CPU loadavg,
@@ -163,3 +278,11 @@ Investigation
* [FHS changes](http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run)
affecting runtime status, plugins, etc.
[libwdt]: http://www.wehavemorefun.de/fritzbox/Libwdt.so
[Fritz!Box source dump]: ftp://ftp.avm.de/fritz.box/fritzbox.fon_wlan_7170/x_misc/opensrc/
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+15 -3
View File
@@ -31,7 +31,7 @@
#include "helpers.h"
#define MATCH_CMD(l, c, x) \
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
static char *strip_line(char *line)
{
@@ -244,6 +244,12 @@ static int parse_conf(char *file)
continue;
}
/* Classic inetd service */
if (MATCH_CMD(line, "inetd ", x)) {
svc_register(SVC_CMD_INETD, x, NULL);
continue;
}
if (MATCH_CMD(line, "console ", x)) {
if (console) free(console);
console = strdup(strip_line(x));
@@ -269,8 +275,14 @@ int parse_finit_conf(char *file)
rcsd = strdup(FINIT_RCSD);
result = parse_conf(file);
if (!tty_num())
tty_register(FALLBACK_SHELL);
if (!tty_num()) {
char *fallback = FALLBACK_SHELL;
if (console)
fallback = console;
tty_register(fallback);
}
return result;
}
+16 -14
View File
@@ -36,6 +36,7 @@
#include "sig.h"
#include "tty.h"
#include "lite.h"
#include "inetd.h"
int debug = 0;
int verbose = 1;
@@ -49,6 +50,8 @@ char *hostname = NULL;
char *rcsd = NULL;
char *console = NULL;
uev_ctx_t *ctx = NULL; /* Main loop context */
static void parse_kernel_cmdline(void)
{
FILE *fp;
@@ -139,7 +142,7 @@ static void banner(void)
int main(int argc, char* argv[])
{
uev_ctx_t ctx;
uev_ctx_t loop;
if (getpid() != 1)
return client(argc, argv);
@@ -157,7 +160,8 @@ int main(int argc, char* argv[])
/*
* Initalize event context.
*/
uev_init(&ctx);
uev_init(&loop);
ctx = &loop;
/*
* Mount base file system, kernel is assumed to run devtmpfs for /dev
@@ -183,18 +187,19 @@ int main(int argc, char* argv[])
*/
run_interactive(SETUP_DEVFS, "Populating device tree");
/*
* Load plugins first, finit.conf may contain references to
* features implemented by plugins.
*/
print_result(plugin_load_all(&loop, PLUGIN_PATH));
/*
* Parse configuration file
*/
parse_finit_conf(FINIT_CONF);
/*
* Load plugins. Must run after finit.conf has registered
* all services, or service plugins won't have anything to
* hook on to.
*/
print_desc("Loading plugins", NULL);
print_result(plugin_load_all(&ctx, PLUGIN_PATH));
/* Set hostname as soon as possible, for syslog et al. */
set_hostname(&hostname);
/*
* Mount filesystems
@@ -218,7 +223,7 @@ int main(int argc, char* argv[])
run_interactive("rm -rf /tmp/* /var/run/* /var/lock/*", "Cleanup temporary directories");
/* Base FS up, enable standard SysV init signals */
sig_setup(&ctx);
sig_setup(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
@@ -235,9 +240,6 @@ int main(int argc, char* argv[])
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
run("/sbin/sysctl -e -p /etc/sysctl.conf >/dev/null");
/* Set initial hostname. */
set_hostname(&hostname);
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
if (network)
run_interactive(network, "Starting networking: %s", network);
@@ -271,7 +273,7 @@ int main(int argc, char* argv[])
/*
* Enter main loop to monior /dev/initctl and services
*/
return uev_run(&ctx, 0);
return uev_run(&loop, 0);
}
/**
+6
View File
@@ -35,3 +35,9 @@ service [3] /usr/sbin/gdm -- GNOME Display Manager
# By default Finit looks for /etc/finit.d
# runparts /etc/finit.d
# Inetd services
inetd time/udp wait [2345] internal -- UNIX rdate service
inetd time/tcp nowait [2345] internal -- UNIX rdate service
inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i -- SSH service
inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i -- SSH service
+4 -2
View File
@@ -39,14 +39,14 @@
# define CONSOLE _PATH_CONSOLE
# define SETUP_DEVFS "/sbin/mdev -s"
# define GETTY "/sbin/getty -L"
# define GETTY_BUSYBOX
# define GETTY_BUSYBOX /* Tell tty.c about the getty argument order */
# define BAUDRATE 115200
# define RUNLEVEL 2
#else /* Debian/Ubuntu based distributions */
# define CONSOLE "/dev/tty1"
# define SETUP_DEVFS "/sbin/udevd --daemon"
# define GETTY "/sbin/getty -8"
# define GETTY_AGETTY
# define GETTY_AGETTY /* Tell tty.c about the getty argument order */
# define BAUDRATE 38400
# define RUNLEVEL 2
# define RANDOMSEED "/var/lib/urandom/random-seed"
@@ -54,6 +54,8 @@
# define HAVE_DBUS
#endif
/* If the user configures us to not know about any getty use the system
* defined /bin/sh (or similar) as fallback for TTYs. */
#ifndef FALLBACK_SHELL
# define FALLBACK_SHELL _PATH_BSHELL
#endif
+31 -7
View File
@@ -116,6 +116,26 @@ void ifconfig(char *ifname, char *addr, char *mask, int up)
close(sock);
}
/**
* atonum - Convert string to integer
* @str: String value to convert.
*
* Returns:
* The converted (positive) number, or -1 on error, with @errno set.
*/
int atonum(char *str)
{
int val;
char *end;
errno = 0;
val = strtol(str, &end, 10);
if (errno || end == str)
return -1;
return val;
}
/**
* pidfile_read - Reads a PID value from a pidfile.
@@ -294,11 +314,15 @@ int procname_kill(char *name, int signo)
if (strncmp(pname, name, strlen(pname) - 1) == 0) {
int error = errno;
int pid = atonum(entry->d_name);
if (kill(atoi(entry->d_name), signo)) {
FLOG_ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
if (-1 == pid) {
FLOG_ERROR("Failed converting %s to a PID: %s", entry->d_name, strerror(error));
} else {
result++; /* Track number of processes we deliver the signal to. */
if (kill(pid, signo))
FLOG_ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
else
result++; /* Track number of processes we deliver the signal to. */
}
}
}
@@ -446,7 +470,7 @@ int run(char *cmd)
return 1;
}
pid = vfork();
pid = fork();
if (0 == pid) {
int i;
FILE *fp;
@@ -565,7 +589,7 @@ int run_interactive(char *cmd, char *fmt, ...)
pid_t run_getty(char *cmd, char *args[], int console)
{
pid_t pid = vfork();
pid_t pid = fork();
if (!pid) {
int fd;
@@ -629,7 +653,7 @@ pid_t run_getty(char *cmd, char *args[], int console)
int run_parts(char *dir, char *cmd)
{
struct dirent **e;
volatile int i, num;
int i, num;
num = scandir(dir, &e, NULL, alphasort);
if (num < 0) {
@@ -669,7 +693,7 @@ int run_parts(char *dir, char *cmd)
}
args[j++] = NULL;
pid = vfork();
pid = fork();
if (!pid) {
_d("Calling %s ...", path);
sig_unblock();
+5 -2
View File
@@ -29,13 +29,14 @@
#include <syslog.h>
#define DO_LOG(level, fmt, args...) \
{ \
do { \
openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \
syslog(level, fmt, ##args); \
closelog(); \
}
} while (0)
#define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args)
#define FLOG_INFO(fmt, args...) DO_LOG(LOG_INFO, fmt, ##args)
#define FLOG_WARN(fmt, args...) DO_LOG(LOG_WARNING, fmt, ##args)
#define FLOG_ERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt, ##args)
#define FLOG_PERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt ". Error %d: %s", ##args, errno, strerror(errno))
@@ -93,6 +94,8 @@ extern int verbose;
int makepath (char *path);
void ifconfig (char *ifname, char *addr, char *mask, int up);
int atonum (char *str);
pid_t pidfile_read (char *pidfile);
pid_t pidfile_poll (char *cmd, char *path);
+521
View File
@@ -0,0 +1,521 @@
/* Classic inetd services launcher for Finit
*
* Copyright (c) 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 "inetd.h" /* XXX: Move back down after upgrading to libuEv >1.0.4 */
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "libuev/uev.h"
#include "libite/lite.h"
#include "finit.h"
#include "helpers.h"
#include "private.h"
#include "svc.h"
#define ENABLE_SOCKOPT(sd, level, opt) \
do { \
int val = 1; \
if (setsockopt(sd, level, opt, &val, sizeof(val)) < 0) \
FLOG_PERROR("Failed setting %s on %s service.", \
#opt, inetd->name); \
} while (0);
/* Socket callback, looks up correct svc and starts it as an inetd service */
static void socket_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int UNUSED(events))
{
svc_t *svc = (svc_t *)arg;
if (SVC_START != svc_enabled(svc, -1, NULL))
return;
if (!svc->inetd.forking)
uev_io_stop(w);
svc_start(svc);
}
/* Launch Inet socket for service.
* TODO: Add filtering ALLOW/DENY per interface.
*/
static void spawn_socket(inetd_t *inetd)
{
int sd;
socklen_t len = sizeof(struct sockaddr);
struct sockaddr_in s;
if (!inetd->type) {
FLOG_ERROR("Skipping invalid inetd service %s", inetd->name);
return;
}
_d("Spawning server socket for inetd %s ...", inetd->name);
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
if (-1 == sd) {
FLOG_PERROR("Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto);
return;
}
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEADDR);
#ifdef SO_REUSEPORT
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEPORT);
#endif
memset(&s, 0, sizeof(s));
s.sin_family = AF_INET;
s.sin_addr.s_addr = INADDR_ANY;
s.sin_port = htons(inetd->port);
if (bind(sd, (struct sockaddr *)&s, len) < 0) {
FLOG_PERROR("Failed binding to port %d, maybe another %s server is already running",
inetd->port, inetd->name);
close(sd);
return;
}
if (inetd->port) {
if (inetd->type == SOCK_STREAM) {
if (-1 == listen(sd, 20)) {
FLOG_PERROR("Failed listening to inetd service %s", inetd->name);
close(sd);
return;
}
} else { /* SOCK_DGRAM */
/* Set extra sockopt to get ifindex from inbound packets */
ENABLE_SOCKOPT(sd, SOL_IP, IP_PKTINFO);
}
}
uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->arg, sd, UEV_READ);
}
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
int inetd_dgram_peek(int sd, char *ifname)
{
char cmbuf[0x100];
struct msghdr msgh;
struct cmsghdr *cmsg;
memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = cmbuf;
msgh.msg_controllen = sizeof(cmbuf);
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
return -1;
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
continue;
if_indextoname(ipi->ipi_ifindex, ifname);
return 0;
}
return -1;
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
int inetd_stream_peek(int sd, char *ifname)
{
struct ifaddrs *ifaddr, *ifa;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
return -1;
if (-1 == getifaddrs(&ifaddr))
return -1;
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
size_t len = sizeof(struct in_addr);
struct sockaddr_in *iin;
if (!ifa->ifa_addr)
continue;
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
iin = (struct sockaddr_in *)ifa->ifa_addr;
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
break;
}
}
freeifaddrs(ifaddr);
return 0;
}
/* Inetd monitor, called by svc_monitor() */
int inetd_respawn(pid_t pid)
{
svc_t *svc = svc_find_by_pid(pid);
if (svc && SVC_CMD_INETD == svc->type) {
inetd_t *inetd = &svc->inetd;
svc->pid = 0;
if (ISSET(svc->runlevels, runlevel) && !inetd->forking)
uev_io_set(&inetd->watcher, inetd->watcher.fd, UEV_READ);
return 1; /* It was us! */
}
return 0; /* Not an inetd service */
}
void inetd_start(inetd_t *inetd)
{
if (inetd->watcher.fd == -1)
spawn_socket(inetd);
}
void inetd_stop(inetd_t *inetd)
{
svc_t *svc = (svc_t *)inetd->arg;
if (inetd->watcher.fd != -1) {
uev_io_stop(&inetd->watcher);
shutdown(inetd->watcher.fd, SHUT_RDWR);
close(inetd->watcher.fd);
inetd->watcher.fd = -1;
}
/* Stop any running service, not allowed anymore. */
if (svc->pid)
svc_stop(svc);
}
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
{
*sv = getservbyname(service, proto);
if (!*sv) {
_pe("Invalid inetd %s/%s (service/proto), skipping", service, proto);
return errno = EINVAL;
}
if (pv) {
*pv = getprotobyname((*sv)->s_proto);
if (!*pv) {
_pe("Cannot find proto %s, skipping.", (*sv)->s_proto);
return errno = EINVAL;
}
}
return 0;
}
/*
* Find exact match.
*/
static inetd_filter_t *find_filter(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!ifname)
ifname = "";
LIST_FOREACH(filter, &inetd->filters, link) {
_d("Checking filters for %s: '%s' vs '%s' (exact match) ...",
inetd->name, filter->ifname, ifname);
if (!strcmp(filter->ifname, ifname))
return filter;
}
return NULL;
}
/*
* First try exact match, then fall back to any match.
*/
inetd_filter_t *inetd_filter_match(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter = find_filter(inetd, ifname);
if (filter)
return filter;
if (!ifname)
ifname = "";
LIST_FOREACH(filter, &inetd->filters, link) {
_d("Checking filters for %s: '%s' vs '%s' (any match) ...",
inetd->name, filter->ifname, ifname);
if (!strlen(filter->ifname))
return filter;
}
return NULL;
}
int inetd_allow(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!inetd)
return errno = EINVAL;
filter = inetd_filter_match(inetd, ifname);
if (filter) {
_d("Filter %s for inetd %s already exists, skipping.", ifname ?: "*", inetd->name);
return 0;
}
_d("Allow iface %s for service %s (port %d)", ifname ?: "*", inetd->name, inetd->port);
filter = calloc(1, sizeof(*filter));
if (!filter) {
_e("Out of memory, cannot add filter to service %s", inetd->name);
return errno = ENOMEM;
}
if (!ifname)
ifname = "";
filter->deny = 0;
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
LIST_INSERT_HEAD(&inetd->filters, filter, link);
return 0;
}
int inetd_deny(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!inetd)
return errno = EINVAL;
/* Reset to NULL for debug output below */
if (!ifname[0])
ifname = NULL;
filter = find_filter(inetd, ifname);
if (filter) {
_d("%s filter %s for inetd %s already exists, cannot set deny filter for same, skipping.",
filter->deny ? "Deny" : "Allow", ifname ?: "*", inetd->name);
return 1;
}
_d("Deny iface %s for service %s (port %d)", ifname ?: "*", inetd->name, inetd->port);
filter = calloc(1, sizeof(*filter));
if (!filter) {
_e("Out of memory, cannot add filter to service %s", inetd->name);
return errno = ENOMEM;
}
if (!ifname)
ifname = "";
filter->deny = 1;
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
LIST_INSERT_HEAD(&inetd->filters, filter, link);
return 0;
}
int inetd_is_allowed(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!inetd) {
errno = EINVAL;
return 0;
}
filter = inetd_filter_match(inetd, ifname);
if (filter) {
_d("Found matching filter for %s, deny: %d ... ", inetd->name, filter->deny);
return !filter->deny;
}
_d("No matching filter for %s ... ", inetd->name);
return 0;
}
int inetd_match(inetd_t *inetd, char *service, char *proto, char *port)
{
int cport = port ? atonum(port) : -1;
if (!inetd || !service || !proto)
return errno = EINVAL;
if (strncmp(inetd->name, service, sizeof(inetd->name)))
return 0;
if (cport != -1) {
if (inetd->port == cport)
return 1;
} else {
struct servent *sv = NULL;
if (getent(service, proto, &sv, NULL))
return 0;
if (inetd->port == ntohs(sv->s_port))
return 1;
}
return 0;
}
/*
* This function is called to add a new, unique, inetd service. When an
* ifname is given as argument this means *only* run service on this
* interface. If a similar service runs on another port, this function
* must add this ifname as "deny" to that other service.
*
* Example:
* inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
* inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
*
* In this example eth0:222 is very specific, so when ssh/tcp (default)
* is added we must find the previous 'ssh' service and add its eth0 as
* deny, so we don't accept port 22 session on eth0.
*
* In the reverse case, where the default (ssh/tcp) entry is listed
* before the specific (eth0:222), the new inetd service must find the
* (any!) previous rule and add its ifname to their deny list.
*
* If equivalent service exists already svc_register() will instead call
* inetd_allow().
*/
int inetd_new(inetd_t *inetd, char *service, char *proto, int forking)
{
int result;
struct servent *sv = NULL;
struct protoent *pv = NULL;
if (!inetd || !service || !proto)
return errno = EINVAL;
result = getent(service, proto, &sv, &pv);
if (result)
return result;
/* Setup defaults */
inetd->std = 1;
inetd->port = ntohs(sv->s_port);
inetd->proto = pv->p_proto;
inetd->forking = !!forking;
strlcpy(inetd->name, service, sizeof(inetd->name));
/* Naïve mapping tcp->stream, udp->dgram, other->dgram */
if (!strcasecmp(sv->s_proto, "tcp"))
inetd->type = SOCK_STREAM;
else
inetd->type = SOCK_DGRAM;
/* Reset descriptor, used internally */
inetd->watcher.fd = -1;
_d("New service %s (default port %d proto %s:%d)", service, inetd->port, sv->s_proto, pv->p_proto);
return 0;
}
int inetd_del(inetd_t *inetd)
{
inetd_filter_t *filter, *tmp;
LIST_FOREACH_SAFE(filter, &inetd->filters, link, tmp)
free(filter);
return 0;
}
int inetd_init(inetd_t *inetd, void *arg, char *ifname, char *port)
{
int result, cport = -1;
svc_t *svc;
if (!inetd || !arg)
return errno = EINVAL;
if (port)
cport = atonum(port);
if (cport != -1 && cport != inetd->port) {
inetd->std = 0;
inetd->port = cport;
}
/* Setup socket callback argument */
inetd->arg = arg;
/* Poor man's tcpwrappers filtering */
result = inetd_allow(inetd, ifname);
/* For each similar service, on other port, add their ifnames as deny to ours. */
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
inetd_filter_t *filter;
/* Skip non-inetd services */
if (svc->type != SVC_CMD_INETD)
continue;
/* Skip ourselves */
if (&svc->inetd == inetd)
continue;
/* Skip different service types (telnet != ssh) */
if (strcmp(svc->inetd.name, inetd->name))
continue;
/* Deny all their interfaces from using my service */
LIST_FOREACH(filter, &svc->inetd.filters, link) {
if (filter->deny)
continue;
inetd_deny(inetd, filter->ifname);
}
/* Deny my interfaces from using their service ... */
LIST_FOREACH(filter, &inetd->filters, link) {
if (filter->deny)
continue;
inetd_deny(&svc->inetd, filter->ifname);
}
}
return result;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+81
View File
@@ -0,0 +1,81 @@
/* Classic inetd services launcher for Finit
*
* Copyright (c) 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.
*/
#ifndef FINIT_INETD_H_
#define FINIT_INETD_H_
#include <netdb.h>
#include <net/if.h>
#include "queue.h"
#include "libuev/uev.h"
typedef struct inetd_filter {
LIST_ENTRY(inetd_filter) link;
int deny; /* 0:allow, 1:deny */
char ifname[IFNAMSIZ]; /* E.g., eth0 */
} inetd_filter_t;
typedef struct {
uev_t watcher;
void *arg; /* svc_t pointer for the socket callback */
int type; /* Socket type: SOCK_STREAM/SOCK_DGRAM */
int std; /* Standard proto/port from /etc/services */
int proto;
int port;
int forking;
char name[10];
int (*cmd)(int type); /* internal inetd service, like 'time' */
LIST_HEAD(, inetd_filter) filters;
} inetd_t;
int inetd_dgram_peek (int sd, char *ifname);
int inetd_stream_peek (int sd, char *ifname);
void inetd_start (inetd_t *inetd);
void inetd_stop (inetd_t *inetd);
int inetd_respawn (pid_t pid);
int inetd_new (inetd_t *inetd, char *service, char *proto, int forking);
int inetd_del (inetd_t *inetd);
int inetd_init (inetd_t *inetd, void *arg, char *ifname, char *port);
int inetd_match (inetd_t *inetd, char *service, char *proto, char *port);
int inetd_allow (inetd_t *inetd, char *ifname);
int inetd_deny (inetd_t *inetd, char *ifname);
int inetd_is_allowed (inetd_t *inetd, char *ifname);
#endif /* FINIT_INETD_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
Submodule libuev updated: 5c7a6f1163...ce3930f0cf
+34 -38
View File
@@ -36,20 +36,19 @@
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0)
static char *plugpath = NULL; /* Set by first load. */
static TAILQ_HEAD(, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
static char *plugpath = NULL; /* Set by first load. */
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
static void check_plugin_depends(plugin_t *plugin);
int plugin_register(plugin_t *plugin)
{
int i, inuse = 0;
if (!plugin) {
errno = EINVAL;
return 1;
}
/* Setup default name if none is provided */
if (!plugin->name) {
Dl_info info;
@@ -65,37 +64,15 @@ int plugin_register(plugin_t *plugin)
return 0;
}
/* Resolve plugin dependencies */
check_plugin_depends(plugin);
if (is_io_plugin(plugin))
inuse++;
if (plugin->svc.cb) {
svc_t *svc = svc_find(plugin->name);
if (svc) {
inuse++;
svc->cb = plugin->svc.cb;
svc->dynamic = plugin->svc.dynamic;
svc->dynamic_stop = plugin->svc.dynamic_stop;
}
}
for (i = 0; i < HOOK_MAX_NUM; i++) {
if (plugin->hook[i].cb)
inuse++;
}
if (!inuse) {
_d("No service \"%s\" loaded, and no I/O or finit hooks, skipping plugin.", basename(plugin->name));
return 1;
}
TAILQ_INSERT_TAIL(&plugins, plugin, link);
return 0;
}
/* Not called, at the moment plugins cannot be unloaded. */
int plugin_unregister(plugin_t *plugin)
{
TAILQ_REMOVE(&plugins, plugin, link);
@@ -104,12 +81,17 @@ int plugin_unregister(plugin_t *plugin)
svc_t *svc = svc_find(plugin->name);
if (svc) {
svc->cb = NULL;
svc->dynamic = 0;
svc->cb = NULL;
svc->dynamic = 0;
svc->dynamic_stop = 0;
}
}
/* XXX: Unfinished, add cleanup code here! */
/* Unload plugin */
if (dlclose(plugin->handle)) {
_e("Failed unloading %s ...", plugin->name);
return 1;
}
return 0;
}
@@ -185,12 +167,14 @@ static void generic_io_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int event
plugin_t *p = (plugin_t *)arg;
if (is_io_plugin(p) && p->io.fd == w->fd) {
/* Stop watcher, callback may close descriptor on us ... */
uev_io_stop(w);
_d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, w->fd, events);
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
if (p->io.fd != w->fd)
uev_io_set (w, p->io.fd, p->io.flags);
uev_io_set(w, p->io.fd, p->io.flags);
}
}
@@ -225,8 +209,9 @@ static void init_plugins(uev_ctx_t *ctx)
static int load_one(char *path, char *name)
{
int noext;
char plugin[CMD_SIZE];
char sofile[CMD_SIZE];
void *handle;
plugin_t *plugin;
if (!path || !fisdir(path) || !name) {
errno = EINVAL;
@@ -235,18 +220,28 @@ static int load_one(char *path, char *name)
/* Compose full path, with optional .so extension, to plugin */
noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(plugin, sizeof(plugin), "%s/%s%s", path, name, noext ? ".so" : "");
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
_d("Loading plugin %s ...", basename(plugin));
_d("Loading plugin %s ...", basename(sofile));
/* Ignore leak, we do not support unloading of plugins atm.
* TODO: Add handle to list of loaded plugins, dlclose() on
* plugin_unregister() */
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
handle = dlopen(sofile, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
_e("Failed loading plugin %s: %s", plugin, dlerror());
_e("Failed loading plugin %s: %s", sofile, dlerror());
return 1;
}
plugin = TAILQ_LAST(&plugins, plugin_head);
if (!plugin) {
_e("Plugin %s failed to register, unloading from memory.", sofile);
dlclose(handle);
return 1;
}
/* Remember handle from dlopen() for plugin_unregister() */
plugin->handle = handle;
return 0;
}
@@ -282,6 +277,7 @@ int plugin_load_all(uev_ctx_t *ctx, char *path)
DIR *dp = opendir(path);
struct dirent *entry;
print_desc("Loading plugins", NULL);
if (!dp) {
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1;
+10 -1
View File
@@ -79,11 +79,14 @@ typedef struct plugin {
/* BSD sys/queue.h linked list node. */
TAILQ_ENTRY(plugin) link;
/* Handle to be sent to dlclose() when plugins is unloaded. */
void *handle;
/* Event loop handler, used internally by Finit */
uev_t watcher;
/* Plugin name, defaults to basename of plugin path if unset.
* NOTE: Must be same as @cmd for service plugins! */
* NOTE: Must match cmd for services or inetd plugins! */
char *name;
/* Service callback to be called once per lap of runloop. */
@@ -106,6 +109,12 @@ typedef struct plugin {
void (*cb)(void *arg, int fd, int events);
} io;
/* Inetd Plugin, stdio used as client socket.
* @type argument will be either SOCK_DGRAM or SOCK_STREAM */
struct {
int (*cmd)(int type);
} inetd;
char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */
} plugin_t;
+1 -1
View File
@@ -4,7 +4,7 @@ CPPFLAGS += -I..
LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
LDFLAGS += -shared
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
resolvconf.so urandom.so x11-common.so tty.so
resolvconf.so urandom.so x11-common.so tty.so time.so
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
TOPDIR ?= $(shell pwd)
+6 -3
View File
@@ -45,7 +45,7 @@ static plugin_t plugin = {
static void fifo_open(void)
{
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (-1 == plugin.io.fd) {
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
return;
@@ -59,7 +59,7 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
{
struct init_request rq;
_d("Receiving request on %s...", FINIT_FIFO);
_d("Receiving request on descriptor %d, from %s ...", fd, FINIT_FIFO);
while (1) {
ssize_t len = read(fd, &rq, sizeof(rq));
@@ -73,6 +73,9 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
}
_d("Nothing to do, bailing out.");
break;
}
@@ -81,7 +84,7 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
break;
}
_d("Magic OK...");
_d("Magic OK ...");
if (rq.cmd == INIT_CMD_RUNLVL) {
switch (rq.runlevel) {
case '0':
+87
View File
@@ -0,0 +1,87 @@
/* Optional inetd plugin for time (rdate) protocol RFC 868
*
* Copyright (c) 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 <arpa/inet.h>
#include <time.h>
#include <unistd.h> /* STDIN_FILENO */
#include <sys/socket.h>
#include "plugin.h"
/* UNIX epoch starts midnight, 1st Jan, 1970 */
#define EPOCH_OFFSET 2208988800ULL
/* Return number of seconds since midnight, 1st Jan, 1900 */
static int rfc868(int type)
{
int sd = STDIN_FILENO;
time_t now;
struct sockaddr_storage sa;
socklen_t sa_len = sizeof(sa);
if (SOCK_DGRAM == type) {
int dummy;
/* Read empty datagram in UDP mode before sending reply. */
if (-1 == recvfrom(sd, &dummy, sizeof(dummy), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len))
return -1; /* On error, close connection. */
}
now = time(NULL);
if ((time_t)-1 == now)
return -1; /* On error, close connection. */
/* Account for UNIX epoch offset */
now += EPOCH_OFFSET;
/* Convert to network byte order, not explicitly stated in RFC */
now = htonl(now);
/* Send reply, ignore error, simply close connection. */
return sendto(sd, &now, sizeof(now), MSG_DONTWAIT, (struct sockaddr *)&sa, sa_len);
}
static plugin_t plugin = {
.name = "time",
.inetd = {
.cmd = rfc868
}
};
PLUGIN_INIT(plugin_init)
{
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+2
View File
@@ -28,6 +28,8 @@
#include "svc.h"
#include "plugin.h"
uev_ctx_t *ctx; /* Main loop context */
void svc_bootstrap (void);
void svc_monitor (pid_t lost);
+13 -1
View File
@@ -49,7 +49,7 @@
static int stopped = 0;
static uev_t sigint_watcher, sigpwr_watcher;
static uev_t sigchld_watcher;
static uev_t sigchld_watcher, sigsegv_watcher;
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
@@ -103,6 +103,15 @@ static void sigchld_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(ar
svc_monitor(waitpid(-1, NULL, WNOHANG));
}
/*
* SIGSEGV: mostly if service callbacks segfault
*/
static void sigsegv_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
_e("PID %d caused a segfault!\n", getpid());
exit(-1);
}
/*
* SIGSTOP/SIGTSTP: Paused by user or netflash
*/
@@ -204,6 +213,9 @@ void sig_setup(uev_ctx_t *ctx)
/* After initial bootstrap of Finit we call the service monitor to reap children */
uev_signal_init(ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD);
/* Trap SIGSEGV in case service callbacks crash */
uev_signal_init(ctx, &sigsegv_watcher, sigsegv_cb, NULL, SIGSEGV);
/* Stopping init is a bit tricky. */
uev_signal_init(ctx, &sigstop_watcher, sigstop_cb, NULL, SIGSTOP);
uev_signal_init(ctx, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP);
+298 -57
View File
@@ -1,7 +1,7 @@
/* Finit service monitor, task starter and generic API for managing svc_t
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2013 Joachim Nilsson <troglobit@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
@@ -25,6 +25,7 @@
#include <string.h>
#include <sys/wait.h>
#include <utmp.h>
#include <net/if.h>
#include "finit.h"
#include "helpers.h"
@@ -33,6 +34,7 @@
#include "tty.h"
#include "lite.h"
#include "svc.h"
#include "inetd.h"
/* The registered services are kept in shared memory for easy read-only access
* by 3rd party APIs. Mostly because of the ability to, from the outside, query
@@ -67,7 +69,7 @@ svc_t *svc_new(void)
svc_t *svc = NULL;
__connect_shm();
if (svc_counter < MAX_NUM_SVC) {
if (svc_counter >= 0 && svc_counter < MAX_NUM_SVC) {
svc = &services[svc_counter++];
memset(svc, 0, sizeof(*svc));
} else {
@@ -78,23 +80,62 @@ svc_t *svc_new(void)
return svc;
}
/**
* svc_del - Delete a service object
* @svc: Pointer to an &svc_t object
*
* Note, this function is not safe for public use. Should only be used
* by this module until the storage of svc_t's has been refactored!
*
* Returns:
* Always succeeds, returning POSIX OK(0).
*/
static int svc_del(svc_t *svc)
{
if (svc_counter >= 0 && svc_counter < MAX_NUM_SVC) {
svc = &services[--svc_counter];
memset(svc, 0, sizeof(*svc));
}
return 0;
}
/**
* svc_iterator - Iterates over all registered services.
* @first: Get first &svc_t object, or next until end.
*
* Returns:
* The first &svc_t when @first is set, otherwise the next &svc_t until
* the end when %NULL is returned.
*/
svc_t *svc_iterator(int first)
{
static int i = 0;
__connect_shm();
if (first)
i = 0;
if (i >= svc_counter)
return NULL;
return &services[i++];
}
/**
* svc_find - Find a service object by its full path name
* @name: Full path name, e.g., /sbin/syslogd
* @path: Full path name, e.g., /sbin/syslogd
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find(char *name)
svc_t *svc_find(char *path)
{
int i;
svc_t *svc;
__connect_shm();
for (i = 0; i < svc_counter; i++) {
svc_t *svc = &services[i];
if (!strncmp(name, svc->cmd, strlen(svc->cmd))) {
_d("Found a matching svc for %s", name);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
if (!strncmp(path, svc->cmd, strlen(svc->cmd))) {
_d("Found a matching svc for %s", path);
return svc;
}
}
@@ -103,25 +144,42 @@ svc_t *svc_find(char *name)
}
/**
* svc_iterator - Iterates over all registered services.
* @restart: Get first &svc_t object by setting @restart
* svc_find_by_pid - Find an service object by its PID
* @pid: Process ID to match
*
* Returns:
* The first &svc_t when @restart is set, otherwise the next &svc_t until
* the end when %NULL is returned.
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_iterator(int restart)
svc_t *svc_find_by_pid(pid_t pid)
{
static int i = 0;
svc_t *svc;
__connect_shm();
if (restart)
i = 0;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
if (svc->pid == pid)
return svc;
}
if (i >= svc_counter)
return NULL;
return NULL;
}
return &services[i++];
svc_t *svc_find_inetd(char *path, char *service, char *proto, char *port)
{
svc_t *svc;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
if (svc->type != SVC_CMD_INETD)
continue;
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
continue;
if (inetd_match(&svc->inetd, service, proto, port)) {
_d("Found a matching svc for %s", path);
return svc;
}
}
return NULL;
}
/**
@@ -136,8 +194,13 @@ void svc_bootstrap(void)
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
svc_cmd_t cmd = svc_enabled(svc, 0, NULL);
svc_cmd_t cmd;
/* Inetd services cannot be part of bootstrap currently. */
if (svc->type == SVC_CMD_INETD)
continue;
cmd = svc_enabled(svc, 0, NULL);
if (SVC_START == cmd || (SVC_RELOAD == cmd))
svc_start(svc);
}
@@ -153,15 +216,15 @@ static int encode(int lvl)
static void utmp_save(int pre, int now)
{
struct utmp utent;
struct utmp utent;
utent.ut_type = RUN_LVL;
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
utent.ut_type = RUN_LVL;
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
setutent();
pututline(&utent);
endutent();
setutent();
pututline(&utent);
endutent();
}
/**
@@ -185,16 +248,30 @@ void svc_runlevel(int newlevel)
runlevel = newlevel;
utmp_save(prevlevel, newlevel);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
int run = svc_enabled(svc, 0, NULL);
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
svc_cmd_t cmd;
/* Inetd services have slightly different semantics */
if (svc->type == SVC_CMD_INETD) {
if (!ISSET(svc->runlevels, runlevel))
inetd_stop(&svc->inetd);
else
inetd_start(&svc->inetd);
continue;
}
/* All other services consult their callback here */
cmd = svc_enabled(svc, 0, NULL);
if (svc->pid) {
if (run == SVC_STOP)
if (cmd == SVC_STOP)
svc_stop(svc);
else if (run == SVC_RELOAD)
else if (cmd == SVC_RELOAD)
svc_reload(svc);
} else {
if (run == SVC_START)
if (cmd == SVC_START)
svc_start(svc);
}
}
@@ -235,9 +312,10 @@ void svc_runlevel(int newlevel)
* "service @username [!0-6,S] /path/to/daemon arg -- Description text"
* "task @username [!0-6,S] /path/to/task arg -- Description text"
* "run @username [!0-6,S] /path/to/cmd arg -- Description text"
* "inetd tcp/ssh nowait [2345] @root:root /usr/sbin/sshd -i -- Description"
*
* If the username is left out the command is started as root.
* Inetd services, launched on demand (SVC_CMD_INETD)
* The [] brackets are there to denote the allowed runlevels. Allowed
* runlevels mimic that of SysV init with the addition of the 'S'
* runlevel, which is only run once at startup. It can be seen as the
@@ -249,9 +327,11 @@ void svc_runlevel(int newlevel)
*/
int svc_register(int type, char *line, char *username)
{
int i = 0;
int i = 0, forking = 0;
char *service = NULL, *proto = NULL, *iface = NULL, *port = NULL;
char *cmd, *desc, *runlevels = NULL;
svc_t *svc;
plugin_t *plugin = NULL;
if (!line) {
_e("Invalid input argument.");
@@ -270,7 +350,13 @@ int svc_register(int type, char *line, char *username)
}
while (cmd) {
if (cmd[0] == '@') /* @username */
if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */
else if (!strncasecmp(cmd, "nowait", 6))
forking = 1;
else if (!strncasecmp(cmd, "wait", 4))
forking = 0;
else if (cmd[0] == '@') /* @username[:group] */
username = &cmd[1];
else if (cmd[0] == '[') /* [runlevels] */
runlevels = &cmd[0];
@@ -283,6 +369,36 @@ int svc_register(int type, char *line, char *username)
goto incomplete;
}
/* Example: inetd ssh@eth0:222/tcp */
if (service) {
proto = strchr(service, '/');
if (!proto)
goto incomplete;
*proto++ = 0;
port = strchr(service, ':');
if (port)
*port++ = 0;
iface = strchr(service, '@');
if (iface)
*iface++ = 0;
}
/* Find plugin that provides a callback for this inetd service */
if (type == SVC_CMD_INETD && !strncasecmp(cmd, "internal", 8)) {
plugin = plugin_find(service);
if (!plugin || !plugin->inetd.cmd) {
_e("Inetd service %s has no internal plugin, skipping.", service);
return errno = ENOENT;
}
}
/* Check if known inetd, then add ifname for filtering only. */
svc = svc_find_inetd(cmd, service, proto, port);
if (svc)
return inetd_allow(&svc->inetd, iface);
svc = svc_new();
if (!svc) {
_e("Out of memory, cannot register service %s", cmd);
@@ -294,15 +410,48 @@ int svc_register(int type, char *line, char *username)
if (desc)
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
if (username)
if (username) {
char *ptr = strchr(username, ':');
if (ptr) {
*ptr++ = 0;
strlcpy(svc->group, ptr, sizeof(svc->group));
}
strlcpy(svc->username, username, sizeof(svc->username));
}
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
if (svc->type == SVC_CMD_INETD) {
int result;
result = inetd_new(&svc->inetd, service, proto, forking);
result += inetd_init(&svc->inetd, svc, iface, port);
if (result) {
_e("Failed registering new inetd service %s.", service);
inetd_del(&svc->inetd);
return svc_del(svc);
}
}
if (plugin) {
/* Internal plugin provides this service */
svc->inetd.cmd = plugin->inetd.cmd;
} else {
/* External program to call */
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while ((cmd = strtok(NULL, " ")))
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
svc->args[i][0] = 0;
while ((cmd = strtok(NULL, " ")))
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
svc->args[i][0] = 0;
plugin = plugin_find(svc->cmd);
if (plugin && plugin->svc.cb) {
svc->cb = plugin->svc.cb;
svc->dynamic = plugin->svc.dynamic;
svc->dynamic_stop = plugin->svc.dynamic_stop;
}
}
svc->runlevels = parse_runlevels(runlevels);
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
@@ -336,8 +485,39 @@ svc_cmd_t svc_enabled(svc_t *svc, int event, void *arg)
return SVC_STOP;
/* Is there a service plugin registered? */
if (svc->cb)
return svc->cb(svc, event, arg);
if (svc->cb) {
int status;
pid_t pid;
/* Let callback run in separate process so it doesn't crash PID 1 */
pid = fork();
if (-1 == pid) {
_pe("Failed in %s callback", svc->cmd);
return SVC_STOP;
}
if (!pid) {
status = svc->cb(svc, event, arg);
exit(status);
}
if (waitpid(pid, &status, 0) == -1) {
_pe("Failed reading status from %s callback", svc->cmd);
return SVC_STOP;
}
/* Callback normally exits here. */
if (WIFEXITED(status))
return WEXITSTATUS(status);
/* Check for SEGFAULT or other error ... */
if (WCOREDUMP(status))
_e("Callback to %s crashed!\n", svc->cmd);
else
_e("Callback to %s did not exit normally!\n", svc->cmd);
return SVC_STOP;
}
/* No service plugin, default to start, since listed in finit.conf */
return SVC_START;
@@ -358,6 +538,12 @@ static void restart_any_lost_procs(void)
if (svc->pid > 0 && pid_alive(svc->pid))
continue;
/* Only restart lost daemons, not task/run/inetd services */
if (SVC_CMD_SERVICE != svc->type) {
svc->pid = 0;
continue;
}
svc_start(svc);
}
}
@@ -384,6 +570,9 @@ void svc_monitor(pid_t lost)
if (tty_respawn(lost))
return;
if (inetd_respawn(lost))
return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
if (lost != svc->pid)
continue;
@@ -420,7 +609,8 @@ void svc_monitor(pid_t lost)
/* Remember: svc_enabled() must be called before calling svc_start() */
int svc_start(svc_t *svc)
{
int respawn;
int respawn, sd = 0;
char ifname[IF_NAMESIZE] = "UNKNOWN";
pid_t pid;
sigset_t nmask, omask;
@@ -429,7 +619,7 @@ int svc_start(svc_t *svc)
respawn = svc->pid != 0;
/* Don't try and start service if it doesn't exist. */
if (!fexist(svc->cmd)) {
if (!fexist(svc->cmd) && !svc->inetd.cmd) {
char msg[80];
snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd);
@@ -443,7 +633,38 @@ int svc_start(svc_t *svc)
if (is_norespawn())
return 0;
if (SVC_CMD_SERVICE != svc->type)
if (SVC_CMD_INETD == svc->type) {
sd = svc->inetd.watcher.fd;
if (svc->inetd.type == SOCK_STREAM) {
/* Open new client socket from server socket */
sd = accept(sd, NULL, NULL);
if (sd < 0) {
FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port);
return 0;
}
_d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port);
/* Find ifname by means of getsockname() and getifaddrs() */
inetd_stream_peek(sd, ifname);
} else { /* SOCK_DGRAM */
/* Find ifname by means of IP_PKTINFO sockopt --> ifindex + if_indextoname() */
inetd_dgram_peek(sd, ifname);
}
if (!inetd_is_allowed(&svc->inetd, ifname)) {
FLOG_INFO("Service %s on port %d not allowed from interface %s.",
svc->inetd.name, svc->inetd.port, ifname);
if (svc->inetd.type == SOCK_STREAM)
close(sd);
return 0;
}
FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname);
}
else if (SVC_CMD_SERVICE != svc->type)
print_desc("", svc->desc);
else if (!respawn)
print_desc("Starting ", svc->desc);
@@ -453,10 +674,11 @@ int svc_start(svc_t *svc)
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = vfork();
pid = fork();
sigprocmask(SIG_SETMASK, &omask, NULL);
if (pid == 0) {
int i = 0;
int status;
int uid = getuser(svc->username);
struct sigaction sa;
char *args[MAX_NUM_SVC_ARGS];
@@ -478,7 +700,14 @@ int svc_start(svc_t *svc)
args[i] = svc->args[i];
args[i] = NULL;
if (debug) {
/* Redirect inetd socket to stdin for service */
if (SVC_CMD_INETD == svc->type) {
/* sd set previously */
dup2(sd, STDIN_FILENO);
close(sd);
dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO);
} else if (debug) {
int fd;
char buf[CMD_SIZE] = "";
@@ -499,15 +728,28 @@ int svc_start(svc_t *svc)
_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
}
/* XXX: Maybe change to use execve() to be able to launch scripts? */
execv(svc->cmd, args);
if (svc->inetd.cmd)
status = svc->inetd.cmd(svc->inetd.type);
else
status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */
/* Only reach this point if exec() fails. */
exit(0);
if (SVC_CMD_INETD == svc->type) {
if (svc->inetd.type == SOCK_STREAM) {
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
}
exit(status);
}
svc->pid = pid;
if (SVC_CMD_RUN == svc->type)
if (SVC_CMD_INETD == svc->type) {
if (svc->inetd.type == SOCK_STREAM)
close(sd);
}
else if (SVC_CMD_RUN == svc->type)
print_result(WEXITSTATUS(complete(svc->cmd, pid)));
else if (!respawn)
print_result(svc->pid > 1 ? 0 : 1);
@@ -579,7 +821,6 @@ int svc_reload(svc_t *svc)
/**
* Local Variables:
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
+25 -8
View File
@@ -1,7 +1,7 @@
/* Finit service monitor and generic API for managing svc_t structures
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2014 Joachim Nilsson <troglobit@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
@@ -31,6 +31,10 @@
#include <sys/shm.h> /* shmat() */
#include <sys/types.h> /* pid_t */
#include "inetd.h"
#include "queue.h" /* BSD sys/queue.h API */
#include "libuev/uev.h"
typedef enum {
SVC_STOP = 0, /* Disabled */
SVC_START, /* Enabled */
@@ -40,7 +44,8 @@ typedef enum {
typedef enum {
SVC_CMD_SERVICE = 0, /* Monitored, will be respawned */
SVC_CMD_TASK, /* One-shot, runs in parallell */
SVC_CMD_RUN /* Like task, but wait for completion */
SVC_CMD_RUN, /* Like task, but wait for completion */
SVC_CMD_INETD /* Classic inetd service */
} svc_type_t;
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
@@ -59,11 +64,21 @@ typedef struct svc {
svc_type_t type;
int reload;
int runlevels;
unsigned int restart_counter; /* Incremented for each restart by service monitor. */
/* Incremented for each restart by service monitor. */
unsigned int restart_counter;
/* For inetd services */
inetd_t inetd;
/* Identity */
char username[MAX_USER_LEN];
char group[MAX_USER_LEN];
/* Command, arguments and service description */
char cmd[MAX_ARG_LEN];
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
char desc[MAX_STR_LEN];
char username[MAX_USER_LEN];
/* For external plugins. If @cb is set a plugin is loaded.
* @dynamic: Set by plugins that want dynamic events.
@@ -84,8 +99,8 @@ 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);
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
0600 | IPC_CREAT), NULL, 0);
if ((void *)-1 == ptr)
return NULL;
}
@@ -94,8 +109,10 @@ static inline svc_t *finit_svc_connect(void)
}
svc_t *svc_new (void);
svc_t *svc_find (char *name);
svc_t *svc_iterator (int restart);
svc_t *svc_find (char *path);
svc_t *svc_find_by_pid (pid_t pid);
svc_t *svc_find_inetd (char *path, char *service, char *proto, char *port);
svc_t *svc_iterator (int first);
void svc_runlevel (int newlevel);
int svc_register (int type, char *line, char *username);
+2
View File
@@ -145,9 +145,11 @@ void tty_start(finit_tty_t *tty)
snprintf(line, sizeof(line), "%s %d %s %s", GETTY, tty->baud, tty->name, tty->term ?: "");
#else
strlcpy(line, FALLBACK_SHELL, sizeof(line));
is_console = 1;
#endif
} else {
strlcpy(line, tty->name, sizeof(line));
is_console = 1;
}
cmd = strtok(line, " ");
args[i++] = basename(cmd);