Compare commits

..
113 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
Joachim Nilsson 9b361864fa Fix make dist target for v1.11 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 16:16:13 +01:00
Joachim Nilsson 41b0dc41a6 Update ChangeLog for v1.11 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 16:13:46 +01:00
Joachim Nilsson cab339c857 Simplify build example.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 16:07:55 +01:00
Joachim Nilsson dcb42602c9 Cleanup README for release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 16:04:54 +01:00
Joachim Nilsson 9e446557b4 Change Coverity Scan branch to use 'dev' instead of master.
We don't want to run out of scan tokens for every README update.  This
also means the introduction of a new workflow.  All new development must
be staged to the dev branch before being merged to master.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 15:29:16 +01:00
Joachim Nilsson a3d76c58a4 Expand on how plugins can interact with system bootstrap.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-21 19:34:01 +01:00
Joachim Nilsson e0e0581d10 Trim slogan ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-21 13:29:35 +01:00
Joachim Nilsson ef75d5ec31 Cleanup Introduction and add Bootstrap section.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-21 13:25:24 +01:00
Joachim Nilsson dfeabd8bae Improve inetd syntax proposal
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-21 13:15:16 +01:00
Joachim Nilsson 7aa687bad9 Reindent to more 'pure' Markdown and remove fixed event lib TODO.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-19 21:24:06 +01:00
Joachim Nilsson c2ea2c1193 Reindent to more 'pure' Markdown.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-19 21:12:14 +01:00
Joachim Nilsson 291b22e987 Give up and add actual link to GIT commit.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 23:46:21 +01:00
Joachim Nilsson a091b69dd6 Another try ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 23:45:16 +01:00
Joachim Nilsson 887c84a0d1 Attempt to fix GIT commit auto linking.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 23:44:27 +01:00
Joachim Nilsson 5009d54ab8 Reindent to more 'pure' Markdown.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 23:43:04 +01:00
Joachim Nilsson 840ade6da7 Bump version for next release, fix make dist and rename NEWS --> CHANGELOG
- Bump version for coming 1.11 release
- Update `make dist` to use Kentzo's `git-archive-all` from
  https://github.com/Kentzo/git-archive-all
- Rename NEWS --> CHANGELOG and update for coming release
- Update CHANGELOG with info on the libuEv migration

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 23:22:39 +01:00
Joachim Nilsson 030aa70842 Simplify.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 22:29:34 +01:00
Joachim Nilsson dea3ae857b Steal controlling TTY on fallback console to enable job control.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 22:25:04 +01:00
Joachim Nilsson 9f5172b0d1 Try removing delayed start of TTYs, should not be needed anymore.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 21:40:09 +01:00
Joachim Nilsson e0529a5597 Unblock all signals blocked by finit when starting children
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 21:40:05 +01:00
Joachim Nilsson 425cd17e57 Initial conversion to use libuEv for all event handling.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 00:53:14 +01:00
Joachim Nilsson 0669a9cb81 Fix missing #include for copyfile()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-18 00:46:06 +01:00
Joachim Nilsson 5e040449c4 Add libuEv as submodule for event handling.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 02:59:21 +01:00
Joachim Nilsson 074686b520 Use vfork() instead of fork() before exec()
If running on older Linux systems, or any BSD UNIX, vfork()
is more efficient when we are about to call exec() anyway.

For more info on vfork() vs fork(), see Stack Exchange:
http://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-and-vfork

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-06 23:38:47 +01:00
Joachim Nilsson d40f52357e Restructure and add section on Watchdog process supervision
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-06 21:45:14 +01:00
Joachim Nilsson 076f77a31b Update TODO: more on inetd and at support.
Also a minor fix to the README.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-06 20:54:52 +01:00
Joachim Nilsson 8f1c16a8a5 Update TODO with section on Cron.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-01 02:04:50 +01:00
Joachim Nilsson b149dbc73e Update TODO
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-01 00:11:44 +01:00
Joachim Nilsson aa8d6a7294 Bump version for 1.10 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-27 22:33:24 +01:00
Joachim Nilsson a94dde403f Update NEWS to Markdown syntax and add info on next release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-27 09:42:49 +01:00
Joachim Nilsson 9bb7e7b014 Change NEWS documentation from reST to Markdown
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-27 09:19:33 +01:00
Joachim Nilsson 02a97e8d7d Fix various problems with unchecked return values from syscalls.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-27 00:12:07 +01:00
Joachim Nilsson def759fe41 run_interactive(): Fix a potential file descriptor leak.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 23:45:20 +01:00
Joachim Nilsson 939cfc2476 pidfile_read(): Reindent.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 23:44:00 +01:00
Joachim Nilsson 7f2c73468f libite/rsync.c: Fix build regression from previous commit.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 23:37:04 +01:00
Joachim Nilsson 81a368f710 Fix problem with unchecked return value of mkdir(), wrap in makedir() macro.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 23:27:15 +01:00
Joachim Nilsson 4e975e4974 plugin.c:load_one() - Comment on resource leak, we never unload plugins atm.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 23:24:49 +01:00
Joachim Nilsson a296c2dd00 pidfile_read(): Fix potential integer overflow when reading PID
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 23:02:54 +01:00
Joachim Nilsson 5ff7994852 touch(): Check return value of mknod(), report error if errno != EEXIST.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 22:57:23 +01:00
Joachim Nilsson 1bdcef9a2c run_interactive(): Fix file descriptor leak when debug is enabled.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 22:49:31 +01:00
Joachim Nilsson f621117eb9 README: Update section on plugins with info on hooks and I/O plugins.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 18:48:25 +01:00
Joachim Nilsson 83cec7129a run_interactive(): Fix copy-paste bug introduced in 06b33c5.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 17:58:50 +01:00
Joachim Nilsson 06b33c58fd run_interactive(): Fix descriptor leak.
Make sure to close dup()'ed descriptors when we're done with them.
Thanks to Coverity Scan for finding this nasty one!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 17:30:58 +01:00
Joachim Nilsson 8ce39f592c procname_kill(): Fix possible resource leak and let's use opendir() errno
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 17:22:55 +01:00
Joachim Nilsson d37c451e81 svc_start(): Fix out of bounds write to args[] array.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 17:20:27 +01:00
Joachim Nilsson a7ff1a61d5 svc_start(): Fix swapped arguments to dup2() and add close(fd), descriptor leak.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 17:17:47 +01:00
Joachim Nilsson 8349909f60 helpers.c:run_interactive() - Check arguments and return values better.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 17:08:00 +01:00
Joachim Nilsson 3a2cf30f47 README: Minor touch up.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 16:41:04 +01:00
Joachim Nilsson 9a3cab3645 TODO: Remove finished items now documented in README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 16:40:43 +01:00
Joachim Nilsson 7dbc02f879 Standardize on two-space indented * for bullet lists in Markdown
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 16:13:53 +01:00
Joachim Nilsson a39f75744c Update TODO to Markdown syntax.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 16:08:59 +01:00
Joachim Nilsson 5742c6591a Change TODO documentation from reST to Markdown
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 15:59:34 +01:00
Joachim Nilsson 588901ee4c Update README to Markdown syntax.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 15:56:50 +01:00
Joachim Nilsson e30d2aae19 Change documentation from reST to Markdown
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-26 15:55:33 +01:00
Joachim Nilsson 463e4f12f8 Update Coverity URL and add section on hooks, plugins & callbacks.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-24 18:21:47 +01:00
Joachim Nilsson 01c2817005 Add support for Coverity Scan
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-24 13:49:18 +01:00
Joachim Nilsson 1a16aab970 queue.h: Upgrade to a newer slightly bugfiex version of OpenBSD queue.h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-11-03 20:56:11 +01:00
Joachim Nilsson 8247344f0d helpers.c:set_hostname() - Fix resource and memory leaks.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-10-31 15:26:49 +01:00
Joachim Nilsson 2fdd64a2d8 helpers.c:run() - Fix resource and memory leaks.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-10-31 15:16:00 +01:00
Joachim Nilsson 22ed4e7f73 svc.c - Break out nested function.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-10-31 14:58:13 +01:00
Joachim Nilsson 327ac9f931 libite:rsync.c - Break out nested function.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-10-31 14:56:51 +01:00
Joachim Nilsson 5ad0c38fe8 Sanity check plugpath as well, no point in searching for plugins otherwise.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-10-30 22:11:23 +01:00
43 changed files with 2752 additions and 975 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "libuev"]
path = libuev
url = https://github.com/troglobit/libuev.git
+16 -1
View File
@@ -1,6 +1,21 @@
# Travis CI integration
# Travis CI integration -- https://travis-ci.org/troglobit/finit
language: c
compiler:
- gcc
env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "eOiuHs02lG2Uv146K9dMqucGTWUJFEhpz1oAtwe+kPrRXsXWc20hYMO+DSpMgD05vSvNeQA6p7h7LdRFQ+/g/Obj27TsOE+RKkUvRAtdwzxnKVyii9C/nmML8cFw7rIA0VS8O9mAkVYeyDxNDupBp3e9DPCibhKPvqCvioTnyy4="
addons:
coverity_scan:
project:
name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com
build_command: "make"
branch_pattern: dev
script: make
Symlink
+1
View File
@@ -0,0 +1 @@
CHANGELOG.md
+320
View File
@@ -0,0 +1,320 @@
Change Log
==========
All notable changes to the project are documented in this file.
[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.
* Rename NEWS.md --> CHANGELOG.md, with symlinks for `make install`
* Attempt to align with http://keepachangelog.com/ for this file.
* Travis CI now only invokes Coverity Scan from the 'dev' branch. This
means that all development, except documentation updates, must go into
that branch.
### Fixes
* Fix bug with finit dying when no `tty` is defined in `finit.conf`, now
even the fallback shell has control over its TTY, see fix in GIT
commit [dea3ae8] for this.
[1.10] - 2014-11-27
-------------------
Major bug fix release.
### Changes
* Project now relies on static code analysis from Coverity, so this
release contains many serious bug fixes.
* Convert to use Markdown for README, NEWS and TODO.
* Serious update to README and slight pruning of finished TODO items.
### Fixes
* Fix serious file descriptor and memory leaks in the following
functions. In particular the leaks in `run_interactive()` are very
serious since that function is called every time a service is started
and/or restarted! For details, see the GIT log:
* `helpers.c:run()`
* `helpers.c:run_interactive()`
* `helpers.c:set_hostname()`
* `helpers.c:procname_kill()`
* `svc.c:svc_start()`: Fix swapped arguments to dup2() and add close(fd)
to prevent descriptor leak.
* `svc.c:svc_start()`: Fix out of bounds write to local stack variable,
wrote off-by-one outside array.
* Several added checks for return values to `mknod()`, `mkdir()`,
`remove()`, etc.
[1.9] - 2014-04-21
------------------
### Changes
* Add support for an include directive to `.conf` files
* Fallback to `/bin/sh` if user forgets tty setting
* Initial support for restarting lost services during `norespwan`
### Fixes
* Bug fixes, code cleanup
* Handle `SIGHUP` from service callback properly when switching runlevel
* Misc. major (memleak) and minor fixes and additions to `libite/lite.h`
[1.8] - 2013-06-07
------------------
### Changes
* Support for runlevels, with a bootstrap runlevel 'S'
* Support for saving previous and current runlevel to UTMP
* Support for new `finit.conf` commands: run, task, and runlevel
* Support for tty and console commands in `finit.conf`, like services
but for launching multiple getty logins
* New tty plugin to monitor TTYs coming and going, like USB TTYs
### Fixes
* Bugfixes to libite
[1.7] - 2012-10-08
------------------
### Changes
* Show `__FILE__` in `_d()` debug messages, useful for plugins with
similarily named callbacks. Also, only in debug mode anyway
* Make sure to cleanup recorded PID when a service is lost. Required by
service plugins for their callbacks to work.
* Only clear screen when in verbose mode. Maybe this should be removed
altogether?
### Fixes
* Bugfix: Do not `free()` static string in `finit.conf` parser
[1.6] - 2012-10-06
------------------
### Changes
* Skip `.` and `..` in plugin loader and display error when failing to
load plugins
* Support for overriding `/etc/finit.d` with `runparts DIR` in
`finit.conf`
* Revoke support for starting services not starting with a slash.
* Prevent endless restart of non-existing services in `finit.conf`
* Support for sysvinit style startstop scripts in `/etc/finit.d`
### Fixes
* Minor fix to alsa-utils plugin to silence on non-existing cards
[1.5] - 2012-10-03
------------------
### Changes
* Use bootmisc plugin to setup standard FHS 2.3 structure in `/var`
* Added `FLOG_WARN()` syslog macro, for plugins
* Add plugin dependency resolver. Checks `plugin_t` for `.depends`
[1.4] - 2012-10-02
------------------
### Changes
* Start refactoring helpers.c into a libite.so (-lite). This means
other user space applications/daemons can make use of the neat toolbox
available in finit
* Use short-form -s/-w -u to work with BusyBox hwclock as well
* Use RTLD_GLOBAL flag to tell dynamic loader to load dependent .so
files as well. Lets other plugins use global symbols.
* Greatly simplify svc hook for external plugins and cleanup plugin API.
* And more ... see the GIT log for more details.
### Fixes
* Fix I/O plugin watcher and load plugins earlier for a new hook
[1.3] - 2012-09-28
------------------
### Changes
* Cleanup public plugin API a bit and add new pid/pidfile funcs
* Add plugin hook to end of service startup
* Remove finit.h from svc.h, plugins should not need this.
* Move utility macros etc. to helpers.h
* Make `finit.h` daemon internal, only
* Move defines of FIFO, conf and rcS.d to Makefile => correct paths
* Add support for installing required headers in system include dir
* Better support for distributions and packagers with install-exec,
install-data, and install-dev targets in Makefile. Useful if you want
to call targets with different `$DESTDIR`!
* Makefile fixes for installation, paths encoded wrong
* Strip binaries + .so files, support for `$(CROSS)` toolchain strip
* Default install is now to `/sbin/finit` and `/usr/`
* Note change in `$PLUGIN_DIR` environemnt variable to `$plugindir`
[1.2] - 2012-09-27
------------------
### Changes
* Update README with section on building and enviroment variables
### Fixes
* Fix installation paths encoded in finit binary
[1.1] - 2012-09-27
------------------
### Changes
* Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
### Fixes
* Build fixes for ARM eabi/uClibc
[1.0] - 2012-09-26
------------------
### Changes
* New plugin based system for all odd extensions
* New service monitor that restarts services if they die
* New maintainer at GitHub http://github.com/troglobit/finit
* Add standard LICENSE and AUTHORS files
* New focus: embedded systems and small headless servers
[0.6] - 2010-06-14
------------------
* Don't start consolekit manually, dbus starts it (rtp)
* Unmount all filesystems before rebooting
* Disable `USE_VAR_RUN_RESOLVCONF` for Mandriva
* Unset terminal type in Mandriva before running X
* Remove extra sleep in finit-alt before calling services.sh (caio)
[0.5] - 2008-08-21
------------------
* Add option to start dbus and consolekit before the X server
* finit-alt listens to `/dev/initctl` to work with `reboot(8)` (smurfy)
* Write runlevel to utmp, needed by Printerdrake (Pascal Terjan)
* Fix ownership of `/var/run/utmp` (reported by Pascal Terjan)
* Remove obsolete code to load AGP drivers
* Conditional build of `/etc/resolveconf/run` support
* Add support to `/var/run/resolvconf` in Mandriva (blino)
[0.4] - 2008-05-16
------------------
* Default username for finit-alt configurable in Makefile
* Create loopback device node in finit-alt (for squashfs)
* Add option to use built-in run-parts instead of `/bin/run-parts`
* Ignore signal instead of setting to an empty handler (Metalshark)
* Handle pam_console permissions in finit-alt for Mandriva
* Add services.sh example and nash-hotplug patch for Mandriva
* Mount `/proc/bus/usb` in Mandriva
* Add runtime debug to finit-alt if finit_debug parameter is specified
* Read configuration from `/etc/finit.conf`
* Run getty with openvt on the virtual terminal
[0.3] - 2008-02-23
------------------
* Change poweroff method to `reboot(RB_POWER_OFF)` (Metalshark)
* Remove duplicate `unionctl()` reimplementation error
* Fix string termination in path creation
* Mount `/var/lock` and `/var/run` as tmpfs
[0.2] - 2008-02-19
------------------
* Replace `system("touch")` with `touch()` in finit-mod (Metalshark)
* Disable `NO_HCTOSYS` by default to match stock Eeepc kernel
* Drop `system("rm -f")` to clean `/tmp`, its a fresh mounted tmpfs
* Write ACPI sleep state to `/sys/power/state` instead of
`/proc/acpi/sleep` (Metalshark)
* Use direct calls to set loopback instead of `system("ifconfig")`
* Replace `system("cat")` and `system("dd")` with C implementation
* Moved finit-mod and finit-alt helpers to `helpers.c`
* Replace `system("echo;cat")` to draw shutdown splash with C calls
0.1 - 2008-02-14
----------------
* Initial release
[libuEv]: https://github.com/troglobit/libuev
[dea3ae8]: https://github.com/troglobit/finit/commit/dea3ae8
[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
[1.7]: https://github.com/troglobit/finit/compare/1.6...1.7
[1.6]: https://github.com/troglobit/finit/compare/1.5...1.6
[1.5]: https://github.com/troglobit/finit/compare/1.4...1.5
[1.4]: https://github.com/troglobit/finit/compare/1.3...1.4
[1.3]: https://github.com/troglobit/finit/compare/1.2...1.3
[1.2]: https://github.com/troglobit/finit/compare/1.1...1.2
[1.1]: https://github.com/troglobit/finit/compare/1.0...1.1
[1.0]: https://github.com/troglobit/finit/compare/0.9...1.0
[0.9]: https://github.com/troglobit/finit/compare/0.8...0.9
[0.8]: https://github.com/troglobit/finit/compare/0.7...0.8
[0.7]: https://github.com/troglobit/finit/compare/0.6...0.7
[0.6]: https://github.com/troglobit/finit/compare/0.5...0.6
[0.5]: https://github.com/troglobit/finit/compare/0.4...0.5
[0.4]: https://github.com/troglobit/finit/compare/0.3...0.4
[0.3]: https://github.com/troglobit/finit/compare/0.2...0.3
[0.2]: https://github.com/troglobit/finit/compare/0.1...0.2
+29 -14
View File
@@ -26,15 +26,17 @@
uninstall-exec uninstall-data uninstall-dev
#VERSION ?= $(shell git tag -l | tail -1)
VERSION ?= 1.9
VERSION ?= 1.12
NAME = finit
PKG = $(NAME)-$(VERSION)
DEV = $(NAME)-dev
ARCHIVE = $(PKG).tar.xz
ARCHTOOL = `which git-archive-all`
ARCHIVE = $(PKG).tar
ARCHIVEZ = ../$(ARCHIVE).xz
EXEC = finit reboot
HEADERS = plugin.h svc.h helpers.h queue.h
DISTFILES = LICENSE README NEWS finit.conf services
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o
DISTFILES = LICENSE README CHANGELOG finit.conf services
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))
@@ -57,22 +59,24 @@ FINIT_RCSD ?= $(sysconfdir)/finit.d
CFLAGS += -W -Wall -Werror -Os
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
CPPFLAGS += -U_FORTIFY_SOURCE
CPPFLAGS += -Ilibite -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE
CPPFLAGS += -Ilibite -Ilibuev
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
CPPFLAGS += -DFINIT_FIFO=\"$(FINIT_FIFO)\" -DFINIT_CONF=\"$(FINIT_CONF)\"
CPPFLAGS += -DFINIT_RCSD=\"$(FINIT_RCSD)\" -DPLUGIN_PATH=\"$(plugindir)\"
LDFLAGS += -rdynamic -L$(TOPDIR)/libite
DEPLIBS = libite/libite.so
DEPLIBS = libite/libite.so libuev/libuev.a
LDLIBS += -ldl -lite
include common.mk
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS
all: $(DEPLIBS) $(EXEC)
$(MAKE) -C plugins $@
+$(MAKE) -C plugins $@
$(DEPLIBS): Makefile
$(MAKE) -C libite all
+$(MAKE) -C libite all
+$(MAKE) -C libuev all
$(OBJS): Makefile
@@ -133,21 +137,32 @@ uninstall: uninstall-exec uninstall-data uninstall-dev
clean:
-@$(RM) $(OBJS) $(DEPS) $(EXEC)
$(MAKE) -C libite $@
$(MAKE) -C plugins $@
+$(MAKE) -C plugins $@
+$(MAKE) -C libite $@
+$(MAKE) -C libuev $@
distclean: clean
-@$(RM) $(JUNK) unittest *.o .*.d
$(MAKE) -C libite $@
$(MAKE) -C plugins $@
+$(MAKE) -C plugins $@
+$(MAKE) -C libite $@
+$(MAKE) -C libuev $@
check:
$(CHECK) *.c plugins/*.c libite/*.c
dist:
@if [ x"$(ARCHTOOL)" = x"" ]; then \
echo "Missing git-archive-all from https://github.com/Kentzo/git-archive-all"; \
exit 1; \
fi
@if [ -e $(ARCHIVEZ) ]; then \
echo "Distribution already exists."; \
exit 1; \
fi
@echo "Building xz tarball of $(PKG) in parent dir..."
git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE)
@(cd ..; md5sum $(ARCHIVE) | tee $(ARCHIVE).md5)
@$(ARCHTOOL) ../$(ARCHIVE)
@xz ../$(ARCHIVE)
@md5sum $(ARCHIVEZ) | tee $(ARCHIVEZ).md5
dev: distclean
@echo "Building unstable xz $(DEV) in parent dir..."
-1
View File
@@ -1 +0,0 @@
NEWS.rst
-169
View File
@@ -1,169 +0,0 @@
==============================================================================
NEWS
==============================================================================
1.9 (2014-04-21)
----------------
* Bug fixes, code cleanup
* Add support for an include directive to .conf files
* Fallback to ``/bin/sh`` if user forgets tty setting
* Handle ``SIGHUP`` from service callback properly when switching runlevel
* Initial support for restarting lost services during ``norespwan``
* Misc. major (memleak) and minor fixes and additions to libite/lite.h
1.8 (2013-06-07)
----------------
* Support for runlevels, with a bootstrap runlevel 'S'
* Support for saving previous and current runlevel to UTMP
* Support for new finit.conf commands: run, task, and runlevel
* Support for tty and console commands in finit.conf, like services but
for launching multiple getty logins
* New tty plugin to monitor TTYs coming and going, like USB TTYs
* Bugfixes to libite
1.7 (2012-10-08)
----------------
* Show __FILE__ in _d() debug messages, useful for plugins with
similarily named callbacks. Also, only in debug mode anyway
* Make sure to cleanup recorded PID when a service is lost. Required by
service plugins for their callbacks to work.
* Bugfix: Do not free() static string in finit.conf parser
* Only clear screen when in verbose mode. Maybe this should be removed
altogether?
1.6 (2012-10-06)
----------------
* Skip . and .. in plugin loader and display error on failed plugins
* Support for overriding /etc/finit.d with "runparts DIR" in finit.conf
* Revoke support for starting services not starting with a slash.
* Prevent endless restart of non-existing services in finit.conf
* Support for sysvinit style startstop scripts in /etc/finit.d
* Minor fix to alsa-utils plugin to silence on non-existing cards
1.5 (2012-10-03)
----------------
* Use bootmisc plugin to setup standard FHS 2.3 structure in /var
* Added FLOG_WARN() syslog macro, for plugins
* Add plugin dependency resolver. Checks plugin_t for .depends
1.4 (2012-10-02)
----------------
* Fix I/O plugin watcher and load plugins earlier for a new hook
* Start refactoring helpers.c into a libite.so (-lite). This means
other user space applications/daemons can make use of the neat toolbox
available in finit
* Use short-form -s/-w -u to work with BusyBox hwclock as well
* Use RTLD_GLOBAL flag to tell dynamic loader to load dependent .so
files as well. Lets other plugins use global symbols.
* Greatly simplify svc hook for external plugins and cleanup plugin API.
* And more... see the GIT log for more details.
1.3 (2012-09-28)
----------------
* Cleanup public plugin API a bit and add new pid/pidfile funcs
* Add plugin hook to end of service startup
* Remove finit.h from svc.h, plugins should not need this.
* Move utility macros etc. to helpers.h
* Make finit.h daemon internal, only
* Move defines of FIFO, conf and rcS.d to Makefile => correct paths
* Add support for installing required headers in system include dir
* Better support for distributions and packagers with install-exec,
install-data, and install-dev targets in Makefile. Useful if you want
to call targets with different $DESTDIR!
* Makefile fixes for installation, paths encoded wrong
* Strip binaries + .so files, support for $(CROSS) toolchain strip
* Default install is now to /sbin/finit and /usr/
* Note change in $PLUGIN_DIR environemnt variable to $plugindir
1.2 (2012-09-27)
----------------
* Fix installation paths encoded in finit binary
* Update README with section on building and enviroment variables
1.1 (2012-09-27)
----------------
* Build fixes for ARM eabi/uClibc
* Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
1.0 (2012-09-26)
----------------
* New plugin based system for all odd extensions
* New service monitor that restarts services if they die
* New maintainer at GitHub http://github.com/troglobit/finit
* Add standard LICENSE and AUTHORS files
* New focus: embedded systems and small headless servers
0.6 (2010-06-14)
----------------
* don't start consolekit manually, dbus starts it (rtp)
* unmount all filesystems before rebooting
* disable USE_VAR_RUN_RESOLVCONF for Mandriva
* unset terminal type in Mandriva before running X
* remove extra sleep in finit-alt before calling services.sh (caio)
0.5 (2008-08-21)
----------------
* add option to start dbus and consolekit before the X server
* finit-alt listens to /dev/initctl to work with reboot(8) (smurfy)
* write runlevel to utmp, needed by Printerdrake (Pascal Terjan)
* fix ownership of /var/run/utmp (reported by Pascal Terjan)
* remove obsolete code to load AGP drivers
* conditional build of /etc/resolveconf/run support
* add support to /var/run/resolvconf in Mandriva (blino)
O.4 (2008-05-16)
----------------
* default username for finit-alt configurable in Makefile
* create loopback device node in finit-alt (for squashfs)
* add option to use built-in run-parts instead of /bin/run-parts
* ignore signal instead of setting to an empty handler (Metalshark)
* handle pam_console permissions in finit-alt for Mandriva
* add services.sh example and nash-hotplug patch for Mandriva
* mount /proc/bus/usb in Mandriva
* add runtime debug to finit-alt if finit_debug parameter is specified
* read configuration from /etc/finit.conf
* run getty with openvt on the virtual terminal
0.3 (2008-02-23)
----------------
* Change poweroff method to reboot(RB_POWER_OFF) (Metalshark)
* Remove duplicate unionctl() reimplementation error
* Fix string termination in path creation
* Mount /var/lock and /var/run as tmpfs
0.2 (2008-02-19)
----------------
* replace system("touch") with touch() in finit-mod (Metalshark)
* disable NO_HCTOSYS by default to match stock Eeepc kernel
* drop system("rm -f") to clean /tmp, its a fresh mounted tmpfs
* write ACPI sleep state to /sys/power/state instead of /proc/acpi/sleep
(Metalshark)
* use direct calls to set loopback instead of system("ifconfig")
* replace system("cat") and system("dd") with C implementation
* moved finit-mod and finit-alt helpers to helpers.c
* replace system("echo;cat") to draw shutdown splash with C calls
0.1 (2008-02-14)
----------------
* initial release
.. Local Variables:
.. mode: rst
.. version-control: t
.. End:
+1 -1
View File
@@ -1 +1 @@
README.rst
README.md
+585
View File
@@ -0,0 +1,585 @@
Finit | Fast & Extensible init for Linux
========================================
[![Travis Status]][Travis] [![Coverity Status]][Coverity Scan]
![Original Finit homepage image](images/finit.jpg "Finit in action!")
Table of Contents
-----------------
* [Introduction](#introduction)
* [Features](#features)
* [/etc/finit.conf](#etcfinitconf)
* [/etc/finit.d](#etcfinitd)
* [Bootstrap](#bootstrap)
* [Runlevels](#runlevels)
* [Inetd](#inetd)
* [Hooks, Callbacks & Plugins](#hooks-callbacks--plugins)
* [Rebooting & Halting](#rebooting--halting)
* [Building](#building)
* [Running](#running)
* [Debugging](#debugging)
* [Origin & References](#origin--references)
Introduction
------------
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.
> 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.
Features
--------
**Process Supervision**
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 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
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, *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:
* Service callbacks -- modify service arguments, run/restart/stop
* Task/Run callbacks -- a one-shot commands, executed in sequence
* Hooks -- hook into the boot at predefined points to extend finit
* I/O -- listen to external events and control finit behavior/services
Extensions and functionality not purely related to what an `/sbin/init`
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
/etc/finit.conf
---------------
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>`
Run fsck on a file system before mounting it
* `module <MODULE>`
Load a kernel module, with optional arguments
* `network <PATH>`
Script or program to bring up networking, with optional arguments
* `runlevel <N>`
N is the runlevel number 1-9, where 6 is reserved for reboot.
Default is 2.
* `run [LVLS] /path/to/cmd ARGS -- Optional description`
One-shot command to run in sequence when entering a runlevel, with
optional arguments and description. This command is guaranteed to be
completed before running the next command.
* `task [LVLS] /path/to/cmd ARGS -- Optional description`
One-shot like 'run', but starts in parallel with the next command
* `service [LVLS] /path/to/daemon ARGS -- Optional description`
Service, or daemon, to be monitored and automatically restarted if it
exits prematurely. Please note that you often need to provide a
--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``
* `include <CONF>`
Include another configuration file, ``/etc/finit.d``, or runparts path
is prepended to file if the file is not found or an absolute path is
not given
* `tty [LVLS] <DEV | /path/to/cmd [args]>`
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.
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 | /path/to/cmd [args]>`
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
earlier support for a `/usr/sbin/services.sh` script in the original
finit.
Bootstrap
---------
1. Setup `/dev`
2. Parse `/etc/finit.conf`
3. Load all `.so` plugins
4. Remount/Pivot `/` to get R+W
5. Call 1st level hooks, `HOOK_ROOTFS_UP`
6. Mount `/etc/fstab` and swap, if available
7. Cleanup stale files from `/tmp/*` et al
8. Enable SysV init signals
9. Call 2nd level hooks, `HOOK_BASEFS_UP`
10. Start all 'S' runlevel tasks and services
11. Setup `/etc/sysctl.conf`
12. Setup hostname and loopback
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 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`
19. Start TTYs defined in `/etc/finit.conf`, or rescue on `/dev/console`
20. Enter main monitor loop
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` 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
they please.
For instance, at `HOOK_BASEFS_UP` a plugin could read an XML file from a
USB stick, convert/copy its contents to the system's `/etc/` directory,
well before all 'S' runlevel tasks are started. This could be used with
system images that are created read-only and all configuration is stored
on external media.
Runlevels
---------
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:
service [S12345] /sbin/syslogd -n -x -- System log daemon
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
task [S] /etc/init.d/kbd start -- Preparing console
service [S12345] /sbin/klogd -n -x -- Kernel log daemon
tty [12345] /dev/tty1
tty [2] /dev/tty2
tty [2] /dev/tty3
tty [2] /dev/tty4
tty [2] /dev/tty5
tty [2] /dev/tty6
In this example syslogd is first started, in parallel, and then acpid is
called using a conventional SysV init script. It is called with the run
command, meaning the following task command to start the kbd script is
not called until the acpid init script has fully completed. Then the
keyboard setup script is called in parallel with klogd as a monitored
service.
Again, tasks and services are started in parallel, while run commands
are called in the order listed and subsequent commands are not started
until a run command has completed.
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
--------------------------
Finit provides only the bare necessities for starting and supervising
processes, with an emphasis on *bare* -- for your convenience it does
however come with support for hooks, service callbacks and plugins that
can used to extend finit with. For your convenience a set of *optional*
plugins are available:
* *alsa-utils.so*: Restore and save ALSA sound settings on
startup/shutdown.
* *bootmisc.so*: Setup necessary files for UTMP, tracks logins at boot.
* *dbus.so*: Setup and start system message bus, D-Bus, at boot.
* *hwclock.so*: Restore and save system clock from/to RTC on
startup/shutdown.
* *initctl.so*: Extends finit with a traditional initctl functionality.
* *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. 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
mechanisms, like generating configuration files, restoring HW device
state, etc. Available hook points are:
* `HOOK_ROOTFS_UP`: When `finit.conf` has been read and `/` has is
mounted -- very early
* `HOOK_BASEFS_UP`: All of `/etc/fstab` is mounted, swap is available
and default init signals are setup
* `HOOK_NETWORK_UP`: System bootstrap, runlevel S, has completed and
networking is up (`lo` is up and the `network` script has run)
* `HOOK_SVC_UP`: All services in the active runlevel has been launched
* `HOOK_SYSTEM_UP`: All services *and* everything in `/etc/finit.d`
has been launched
* `HOOK_SHUTDOWN`: Called at shutdown/reboot, right before all
services are sent `SIGTERM`
Plugins like `initctl.so` and `tty.so` extend finit by acting on events,
they are called I/O plugins and are called from the finit main loop when
`poll()` detects an event. See the source code for `plugins/*.c` for
more help and ideas.
Callback plugins are called by finit them right before a process is
started, or restarted if it exits. The callback receive a pointer to
the `svc_t` (defined in `svc.h`) of the service, with all command line
parameters free to modify if needed. All the callback needs to do is
respond with one of: `SVC_STOP (0)` tells finit to *not* start the
service, `SVC_START (1)` to start the service, or `SVC_RELOAD (2)` to
have finit signal the process with `SIGHUP`.
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.
This latter functionality is implemented in the optional `initctl.so`
plugin.
Building
--------
The finit build system does not employ the GNU Configure and Build System,
instead standard makefiles are used. The user is encouraged to make source
code changes, using defines and conditionally building plugins instead to
alter the behavior of finit.
The following environment variables are checked by the makefiles and control
what is built and where resulting binaries are installed.
* `ROOTDIR=`: Top directory for building complete system, used in pretty
printing.
* `VERSION=`: Defaults to the currently released version of finit,
e.g., 1.3 but can be overridden by packagers using this variable to
add a suffix or completely alter the version.
* `CFLAGS=`: Default `CFLAGS` are inherited from the environment.
* `CPPFLAGS=`: Default `CPPFLAGS` are inherited from the environment.
* `LDFLAGS=`: Default `LDFLAGS` are inherited from the environment.
* `LDLIBS=`: Default `LIBLIBS` are inherited from the environment.
* `prefix=`: Base prefix path for all files, except `sbindir` and
`sysconfdir`. Used in concert with the `DESTDIR` variable.
Defaults to `/usr`.
* `sbindir=`: Path to where resulting binaries should install to. Used
in concert with the `DESTDIR` variable.
Defaults to `/sbin`.
* `sysconfdir=`: Path to where finit configuration files should install
to. Used in concert with the `DESTDIR` variable.
Defaults to `/etc`, but is currently unused.
* `PLUGINS=`: List of stock finit plugins to build and install.
* `plugindir=`: Absolute path to where finit should look for dynamically
loadable plugins at runtime. At installation prepended by `DESTDIR`
and `prefix`.
Defaults to `/lib/finit/plugins`.
* `DESTDIR=`: Used by packagers and distributions when building a
relocatable bundle of files. Always prepended to the `prefix`
destination directory.
**Example**
First, unpack the archive:
<kbd>$ tar xfJ finit-1.3.tar.xz; cd finit-1.3/</kbd>
Then build and install:
<kbd>$ PLUGINS="initctl.so hwclock.so" DESTDIR=/tmp/finit make install</kbd>
CC finit.o
CC conf.o
CC helpers.o
CC sig.o
CC svc.o
CC plugin.o
CC strlcpy.o
LINK finit
CC plugins/initctl.o
PLUGIN plugins/initctl.so
CC plugins/hwclock.o
PLUGIN plugins/hwclock.so
INSTALL /tmp/finit/sbin/finit
INSTALL /tmp/finit/lib/finit/plugins/initctl.so
INSTALL /tmp/finit/lib/finit/plugins/hwclock.so
In this example the [finit-1.3.tar.xz][10] archive is unpacked to the
user's home directory, built and installed to a temporary staging
directory. The environment variables `DESTDIR` and `PLUGINS` are
changed to suit this particular build.
Running
-------
The default install does not setup finit as the system default
`/sbin/init`, neither does it setup an initial `/etc/finit.conf`.
It is assumed that users of finit are competent enough to either setup
finit as their default `/sbin/init` or alter their respective Grub,
LOADLIN, LILO, U-Boot/Barebox or RedBoot boot loader configuration to
give the kernel the following extra command line:
init=/sbin/finit
![Finit starting Debian 6.0](images/finit-screenshot.jpg "Finit screenshot")
Debugging
---------
Add `finit_debug`, or `--debug`, to the kernel command line to
enable trace messages. A console getty is always started, see the file
`finit.h` for more useful compile-time tweaks:
init=/sbin/finit --debug
Origin & References
-------------------
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]
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
[3]: http://smarden.org/runit/
[4]: http://en.wikipedia.org/wiki/Init
[5]: http://en.wikipedia.org/wiki/Runlevel
[6]: http://upstart.ubuntu.com/
[7]: http://www.freedesktop.org/wiki/Software/systemd/
[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:
-->
-295
View File
@@ -1,295 +0,0 @@
==============================================================================
Finit | Fast Init Replacement
==============================================================================
|cistatus|
Finit is a small `SysV init`_ replacement with `process supervision`_
similar to that of `daemontools`_ and `runit`_. Its focus is on small
and embedded GNU/Linux systems, although fully functional on standard
server and desktop installations. It should also work on other UNIX
systems, but this has yet to be proven.
Finit is fast because it starts services in parallel, it then supervises
and automatically restarts them if they fail. This can be extended upon
with custom callbacks for all services, hooks into the boot process, or
plugins to extend the functionality and adapt Finit to your needs.
Finit is not only fast, it's arguably one of the easiest to get started
with. A complete system can be booted with one simple configuration
file, ``/etc/finit.conf``, see below for syntax.
Features
--------
**Process Supervision**
Start, monitor and restarting services if they fail.
**Runlevels**
Finit supports standard runlevels if you want, but you don't need them
for simple installations.
**Plugins**
Extend and modify finit behavior. See examples in plugins/ directory.
Plugin capabilities:
* Service callbacks — modify service arguments, run/restart/stop
* Task/Run callbacks — a one-shot commands, executed in sequence
* Hooks — hook into the boot at predefined points to extend finit
* I/O — listen to external events and control finit behavior/services
Extensions and functionality not purely related to what an /sbin/init
needs to start a system are available as a set of plugins that either
hook into the boot process or respond to various I/O.
/etc/finit.conf
---------------
Contrary to most script based init alternatives (`SysV init`_, upstart_,
systemd_, OpenRC_ and the likes) finit instead reads its configuration
from ``/etc/finit.conf``. Below is a brief list, see the source code
for the full list:
check <DEV>
Run fsck on a file system before mounting it
module <MODULE>
Load a kernel module, with optional arguments
network <PATH>
Script or program to bring up networking, with optional arguments
runlevel <N>
N is the runlevel number 1-9, where 6 is reserved for reboot
run [RUN_LVLS] /path/to/cmd ARGS -- Optional description
One-shot command to run in sequence when entering a runlevel, with
optional arguments and description. This command is guaranteed to
be completed before running the next command.
task [RUN_LVLS] /path/to/cmd ARGS -- Optional description
One-shot like 'run', but starts in parallel with the next command
service [RUN_LVLS] /path/to/daemon ARGS -- Optional description
Service, or daemon, to be monitored and automatically restarted if
it exits prematurely. Please note that you often need to provide
a --foreground or --no-background argument to most daemons to
prevent them from forking off to the background.
runparts <PATH>
Call run-parts(8) on a directory other than default ``/etc/finit.d``
include <CONF>
Include another configuration file, ``/etc/finit.d``, or runparts
path is prepended to file if the file is not found or an absolute
path is not given
tty [RUN_LVLS] <DEV | /bin/sh>
Start a getty on the given TTY device, in the given runlevels. When
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
console <DEV>
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.
When running ``make install`` no default ``/etc/finit.conf`` will be
provided since the 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!
/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
earlier support for a ``/usr/sbin/services.sh`` script in the original
finit.
Runlevels
---------
Support for runlevels_ 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 finit.conf, like this::
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
task [S] /etc/init.d/kbd start -- Preparing console
service [S12345] /sbin/klogd -n -x -- Kernel log daemon
tty [12345] /dev/tty1
tty [2] /dev/tty2
tty [2] /dev/tty3
tty [2] /dev/tty4
tty [2] /dev/tty5
tty [2] /dev/tty6
In this example acpid is started once at bootstrap using a conventional
SysV init script. Here the run command was used, meaning the following
task command is not run until the init script has fully completed.
Tasks and services are started in parallel, while run commands are run
in the order listed and subsequent commands are not started until a run
command has completed.
Existing finit.conf files that lack runlevel setting will get a default
runlevel assigned, [234].
Switching between runlevels can be done by calling init with a single
argument, e.g., 'init 5' switches to runlevel 5.
Rebooting and Halting
---------------------
Finit handles SIGUSR1 and SIGUSR2 for reboot and halt, and listens to
``/dev/initctl`` so standard Linux reboot and halt commands should also
work.
Building
--------
The finit build system does not employ the GNU Configure and Build System,
instead standard makefiles are used. The user is encouraged to make source
code changes, using defines and conditionally building plugins instead to
alter the behavior of finit.
The following environment variables are checked by the makefiles and control
what is built and where resulting binaries are installed.
**ROOTDIR=**
Top directory for building complete system, used in pretty printing
**VERSION=**
Defaults to the currently released version of finit, e.g., 1.3 but can
be overridden by packages to add a suffix or completely alter the version.
**CFLAGS=**
Standard CFLAGS are inherited from the build environment.
**CPPFLAGS=**
Standard CPPFLAGS are inherited from the build environment.
**LDFLAGS=**
Standard LDFLAGS are inherited from the build environment.
**LDLIBS=**
Standard LIBLIBS are inherited from the build environment.
**prefix=**
Base prefix path for all files, except ``sbinbdir`` and ``sysconfdir``.
Used in concert with the ``DESTDIR`` variable. Defaults to ``/usr``
**sbindir=**
Path to where resulting binaries should install to. Used in concert
with the ``DESTDIR`` variable. Defaults to ``/sbin``
**sysconfdir=**
Path to where finit configuration files should install to. Used in
concert with the ``DESTDIR`` variable. Defaults to ``/etc``, but is
currently unused.
**PLUGINS=**
List of stock finit plugins to build and install.
**plugindir=**
Absolute path to where finit should look for dynamically loadable plugins
at runtime. At installation prepended by ``DESTDIR`` and ``prefix``.
Defaults to ``/lib/finit/plugins``
**DESTDIR=**
Used by packagers and distributions when building a relocatable
bundle of files. Always prepended to the ``prefix`` destination
directory.
**Example**::
$ tar xfJ finit-1.3.tar.xz
$ PLUGINS="initctl.so hwclock.so" DESTDIR=/tmp/finit/dst \
make -C finit-1.3/ clean install
make: Entering directory `/home/troglobit/finit-1.3'
CC finit.o
CC conf.o
CC helpers.o
CC sig.o
CC svc.o
CC plugin.o
CC strlcpy.o
LINK finit
CC plugins/initctl.o
PLUGIN plugins/initctl.so
CC plugins/hwclock.o
PLUGIN plugins/hwclock.so
INSTALL /tmp/finit/dst/sbin/finit
INSTALL /tmp/finit/dst/lib/finit/plugins/initctl.so
INSTALL /tmp/finit/dst/lib/finit/plugins/hwclock.so
make: Leaving directory `/home/troglobit/finit-1.3'
In this example the `finit-1.3.tar.xz`_ archive is unpacked to the
user's home directory, built and installed to a temporary staging
directory. The environment variables ``DESTDIR`` and ``PLUGINS`` are
changed to suit this particular build.
Running
-------
The default install does not setup finit as the system default
``/sbin/init``, neither does it setup an initial ``/etc/finit.conf``.
It is assumed that users of finit are competent enough to either setup
finit as their default ``/sbin/init`` or alter their respective Grub,
LOADLIN, LILO, U-Boot/Barebox or RedBoot boot loader configuration to
give the kernel the following extra command line::
init=/sbin/finit
Debugging
---------
Add ``finit_debug``, or ``--debug``, to the kernel command line to
enable trace messages. A console getty is always started, see the file
``finit.h`` for more useful compile-time tweaks::
init=/sbin/finit --debug
Contact
-------
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 ...". It 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.
.. _`SysV init`: https://en.wikipedia.org/wiki/Init
.. _`Joachim Nilsson`: http://troglobit.com
.. _GitHub: http://github.com/troglobit/finit
.. _`process supervision`: https://en.wikipedia.org/wiki/Process_supervision
.. _`daemontools`: http://cr.yp.to/daemontools.html
.. _`runit`: http://smarden.org/runit/
.. _`original finit`: http://helllabs.org/finit/
.. _`EeePC fastinit`: http://wiki.eeeuser.com/boot_process:the_boot_process
.. _upstart: http://upstart.ubuntu.com/
.. _runlevels: http://en.wikipedia.org/wiki/Runlevel
.. _systemd: http://www.freedesktop.org/wiki/Software/systemd/
.. _openrc: http://www.gentoo.org/proj/en/base/openrc/
.. _`finit-1.3.tar.xz`: ftp://troglobit.com/finit/finit-1.3.tar.xz
.. |cistatus| image:: https://travis-ci.org/troglobit/finit.png?branch=master
:target: https://travis-ci.org/troglobit/finit
+1 -1
View File
@@ -1 +1 @@
TODO.rst
TODO.md
+288
View File
@@ -0,0 +1,288 @@
Goals of Finit
==============
The intent of finit is to monitor not just processes, but to supervise
an entire system and the behavior of certain processes that declare
themselves to be finit compliant. Similar to launchd, the goal of
finit is to be a replacement for:
* init
* inetd
* crond
* watchdogd
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`
* Implement `initctl stop|start|restart|reload|status <SVC>` and
`service <SVC> stop|start|restart|reload|status` on top
* Add PRE and POST hooks for when switching between runlevels
* Add support for `init -v,--version | version`
* Cleanup move sources from top-level directory to `src/` and `include/`
* Make sure to install `queue.h` to `$(PREFIX)/include/finit/queue.h`
* Move `finit.conf` "check" command to plugin which checks `/etc/fstab`
instead. This is the de-facto practice. But keep the check command
for really low-end systems w/o `/etc/fstab`.
Init
----
Finit in itself is not SysV Init compatible, it doesn't use `/etc/rc.d`
and `/etc/inet.d` scripts to boot the system. However, a plausible way
to achieve compatibility would be to add a plugin to finit which reads
`/etc/inittab` to be able to start standard Linux systems.
Inetd
-----
Simple way of running Internet services on demand, like the old inetd:
inetd PORT/PROTO <wait|nowait> [@USER[:GROUP]] /path/to/daemon args -- desc
Examples:
# Inetd services, launched on demand
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 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
# Allow SSH connections originating from eth0
allow ssh eth0
Another option, to further extend the Inetd capabilities, is a separate
`finetd.conf` to list services. This would be beneficial when running
an SSH service on different ports on different interfaces:
# Run SSH on port 22 on all interfaces except upstream
tcp ssh {
listen = *, !wan0
exec = dropbear -i
}
# Run SSH on non-standard port 222 for upstream interface
tcp ssh {
port = 222
listen = wan0
exec = dropbear -i
wait = yes
}
# Run TFTP service on all interfaces except upstream interface
udp tftp {
listen = *, !wan0
exec = uftpd -i -t
user = root
}
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
-----
Requirements are quite rudimentary, basic cron functionality, mainly
system-level timed periodic tasks:
* Periodically run a task, in a matching runlevel
* Optionally run as non-root (`crontab -e` as operator)
Proposed syntax:
# Crontab
cron @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
# One-shot 'at' command
at @YY-mm-ddTHH:MM [LVLS] /path/to/cmd [ARGS] -- Optional descr
Notice the ISO date in notation. The runlevels is extra filtering
sugar. E.g., if cron service is only allowed in runlevels two or three
it will not start if system currently is in runlevel four.
To run a command as another user the `.conf` file must have a that
owner. E.g., `/etc/finit.d/extra.conf` may be owned by operator and
only hold `cron ...` lines.
What would be extremely useful is if `initctl`, or a homegrown `at`,
could support the basic functionality of the `at` command directly from
the shell -- that way setting up one-time jobs would not entail
re-reading `/etc/finit.conf`
Watchdog
--------
Support for monitoring the health of the system and its processes.
* 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 a more generic `/etc/finit.d/PLUGIN.conf`?
* Support enable/disable watchdog features:
- Supervise processes,
- CPU loadavg,
- Watch for file descriptor leaks,
- etc.
Documentation
-------------
* Write man pages for finit and `finit.conf`, steal from the excellent
`pimd` man pages ...
* Update Debian `finit.conf` example with runlevels, `tty/console` and
dependency handling for Debian 8 (the dreaded systemd release).
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:
-->
-118
View File
@@ -1,118 +0,0 @@
==============================================================================
TODO
==============================================================================
Unsorted TODO list of features and points of investigation that could be
useful for improving finit. But what is an improvement to finit? Well,
similiar to launchd, the goal of finit is to be a FAST replacement for:
* init
* inetd
* crond
* watchdogd
The main goal is to be FAST. Secondary goals are to be: small, simple
and with a very low dependency on external packages.
Inetd
-----
Simple way of running Internet services on demand, like the old inetd::
inetd SVC [RUNLVL] /path/to/daemon args -- description
Example::
# Inetd services, launched on demand
inetd ssh [2345] /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.
Priority
--------
The intent of finit is to monitor not just processes, but also to
be able to supervise the entire system and the behavior of certain
processes that declare themselves to be finit compliant.
* Integrate the ingenious libev to handle all events: signals, polling
fd's, etc. Because "Event driven software improves concurrency" --
Dave Zarzycki, Apple. See the `launchd video`_ for more info!
* Add support for a /dev/watchdog plugin to replace watchdogd
* Integrate watchdog plugin with process/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.
Configuration
-------------
* Add support for "init <q | reload-configuration>" and SIGHUP to
reload finit.conf, but also
* Add support for inotify to automatically reload finit.conf
Runlevels
---------
* Add support for "runlevel N" to finit.conf
* Improve support for runlevels: startup, running (services from
finit.conf), and shutdown as well as hooks when switching RL, startup
at RL 1 and a default run level.
* Implement initctl stop|start|restart|reload|status <SVC> and service
<SVC> stop|start|restart|reload|status on top
* Add PRE and POST hooks for when changing runlevels
* SysV init in Debian uses an rcS.d/ for scripts that should run once at
boot. These scripts are run before the actual runlevel, which is set
in inittab. We could use "task [S]" and "run [S]" (below) for this.
* Add support for a new runlevel 'S' that is started before all other
services in the selected runlevel.
Services and Tasks
------------------
Both the "task" and "boot" stanzas below are like "service", share the
same mechanism for registering callbacks. These callbacks can be used
to check a configuration db and return 1 (RUN), 2 (STOP), or 3 (RELOAD).
* Add support for "task [RUNLVL] ..." similar to services, but one-shot.
* Add support for "run [RUNLVL] ..." similar to task, but waits for
completion before continuing with the next task/service
* Add support for service/task/run dependencies
* Add support for "init list" to show running services
It is tempting to say that a task called at runlevel S would be a
blocking call, like run, but there are likely use-cases when you want to
call on-shot tasks in parallell with something else, even at boot. Hence
the differentiation between "task" and "run", which are essentially the
same, except for the "pause and wait for completion" that "run" has.
Miscellaneous
-------------
* Add support for "init -v,--version | version"
* Cleanup move sources from top-level directory to src/ and include/
* Make sure to install queue.h to $(PREFIX)/include/finit/queue.h
* Move finit.conf "check" command to plugin which checks /etc/fstab
instead. This is the de-facto practise. But keep the check command
for really low-end systems w/o /etc/fstab.
Documentation
-------------
* Write man pages for finit and finit.conf, steal from pimd man pages...
* Update Debian finit.conf example with runlevels, tty/console and
dependency handling
Investigation
-------------
* `FHS changes`_ affecting runtime status, plugins, etc.
.. _`launchd video`: http://www.youtube.com/watch?v=cD_s6Fjdri8
.. _`FHS changes`: http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run
..
.. Local Variables:
.. mode: rst
.. version-control: t
.. 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;
}
+26 -35
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;
@@ -131,27 +134,6 @@ static int client(int argc, char *argv[])
return 0;
}
static int run_loop(void)
{
int delay = DELAY_TTY ?: 1;
_d("Entering main loop ...");
while (1) {
svc_monitor();
plugin_monitor();
/* Delayed start of TTYs to let the system stabilize
* after switching runlevels. */
if (delay) {
if (--delay == 0)
tty_runlevel(runlevel);
continue;
}
}
return 0;
}
static void banner(void)
{
delline();
@@ -160,6 +142,8 @@ static void banner(void)
int main(int argc, char* argv[])
{
uev_ctx_t loop;
if (getpid() != 1)
return client(argc, argv);
@@ -173,6 +157,12 @@ int main(int argc, char* argv[])
*/
sig_init();
/*
* Initalize event context.
*/
uev_init(&loop);
ctx = &loop;
/*
* Mount base file system, kernel is assumed to run devtmpfs for /dev
*/
@@ -181,8 +171,8 @@ int main(int argc, char* argv[])
mount("none", "/proc", "proc", 0, NULL);
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
mount("none", "/sys", "sysfs", 0, NULL);
mkdir("/dev/pts", 0755);
mkdir("/dev/shm", 0755);
makedir("/dev/pts", 0755);
makedir("/dev/shm", 0755);
mount("none", "/dev/pts", "devpts", 0, "gid=5,mode=620");
mount("none", "/dev/shm", "tmpfs", 0, NULL);
umask(022);
@@ -197,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(PLUGIN_PATH));
/* Set hostname as soon as possible, for syslog et al. */
set_hostname(&hostname);
/*
* Mount filesystems
@@ -232,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();
sig_setup(&loop);
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
@@ -249,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);
@@ -279,10 +267,13 @@ int main(int argc, char* argv[])
/* Hooks that should run at the very end */
plugin_run_hooks(HOOK_SYSTEM_UP);
/* Start TTYs */
tty_runlevel(runlevel);
/*
* Enter main loop to monior /dev/initctl and services
*/
return run_loop();
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 -5
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
@@ -65,9 +67,6 @@
# define DEFHOST "noname"
#endif
/* Delay start of GETTY at boot. Must be > 0 */
#define DELAY_TTY 2
#define CMD_SIZE 256
#define LINE_SIZE 1024
#define BUF_SIZE 4096
+117 -59
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.
@@ -131,31 +151,31 @@ void ifconfig(char *ifname, char *addr, char *mask, int up)
*/
pid_t pidfile_read(char *pidfile)
{
pid_t pid = 0;
char buf[16];
FILE *fp;
pid_t pid = 0;
char buf[16];
FILE *fp;
if (!pidfile) {
errno = EINVAL;
return -1;
}
if (!pidfile) {
errno = EINVAL;
return -1;
}
if (!fexist(pidfile))
return -1;
if (!fexist(pidfile))
return -1;
fp = fopen(pidfile, "r");
if (!fp)
return -1;
fp = fopen(pidfile, "r");
if (!fp)
return -1;
if (fgets(buf, sizeof(buf), fp)) {
errno = 0;
pid = strtoul(buf, NULL, 0);
if (errno)
pid = 0; /* Failed conversion. */
}
fclose(fp);
if (fgets(buf, sizeof(buf), fp)) {
errno = 0;
pid = (pid_t)strtol(buf, NULL, 10);
if (errno)
pid = 0; /* Failed conversion. */
}
fclose(fp);
return pid;
return pid;
}
/**
@@ -270,12 +290,15 @@ int procname_kill(char *name, int signo)
DIR *dir;
struct dirent *entry;
dir = opendir("/proc");
if (!dir || !name) {
if (!name) {
errno = EINVAL;
return 0;
}
dir = opendir("/proc");
if (!dir)
return 0;
while ((entry = readdir(dir)) != NULL) {
/* Skip non-process entries in /proc */
if (!isdigit(*entry->d_name))
@@ -291,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. */
}
}
}
@@ -404,7 +431,6 @@ int complete(char *cmd, int pid)
int run(char *cmd)
{
int status, result, i = 0;
FILE *fp;
char *args[NUM_ARGS + 1], *arg, *backup;
pid_t pid;
@@ -444,17 +470,18 @@ int run(char *cmd)
return 1;
}
fp = fopen("/dev/null", "w");
pid = fork();
if (0 == pid) {
int i;
FILE *fp;
struct sigaction sa;
/* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
/* Redirect stdio if the caller requested so. */
/* Always redirect stdio for run() */
fp = fopen("/dev/null", "w");
if (fp) {
int fd = fileno(fp);
@@ -463,18 +490,20 @@ int run(char *cmd)
dup2(fd, STDERR_FILENO);
}
sig_unblock();
execvp(args[0], args);
_exit(1); /* Only if execv() fails. */
} else if (-1 == pid) {
_pe("%s", args[0]);
free(backup);
return -1;
}
status = complete(args[0], pid);
if (-1 == status) {
if (fp) fclose(fp);
free(backup);
return 1;
}
@@ -489,7 +518,6 @@ int run(char *cmd)
* change their return code accordingly. --Jocke */
}
if (fp) fclose(fp);
free(backup);
return result;
@@ -500,27 +528,49 @@ int run_interactive(char *cmd, char *fmt, ...)
int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
va_list ap;
FILE *fp = tmpfile();
FILE *fp;
va_start(ap, fmt);
vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
if (!cmd) {
errno = EINVAL;
return 1;
}
if (fmt) {
va_start(ap, fmt);
vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
if (fmt)
print_desc("", line);
}
/* Redirect output from cmd to a tempfile */
fp = tmpfile();
if (fp && !debug) {
oldout = dup(STDOUT_FILENO);
olderr = dup(STDERR_FILENO);
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
}
/* Run cmd ... */
status = run(cmd);
/* Restore stderr/stdout */
if (fp && !debug) {
dup2(oldout, STDOUT_FILENO);
dup2(olderr, STDERR_FILENO);
if (oldout >= 0) {
dup2(oldout, STDOUT_FILENO);
close(oldout);
}
if (olderr >= 0) {
dup2(olderr, STDERR_FILENO);
close(olderr);
}
}
if (fmt)
print_result(status);
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
if (fp && !debug) {
size_t len, written;
@@ -529,9 +579,11 @@ int run_interactive(char *cmd, char *fmt, ...)
len = fread(line, 1, sizeof(line), fp);
written = fwrite(line, len, sizeof(char), stderr);
} while (len > 0 && written == len);
fclose(fp);
}
if (fp)
fclose(fp);
return status;
}
@@ -540,10 +592,8 @@ pid_t run_getty(char *cmd, char *args[], int console)
pid_t pid = fork();
if (!pid) {
int i, fd;
int fd;
char c;
sigset_t nmask;
struct sigaction sa;
if (console) {
/* Detach from initial controlling TTY */
@@ -564,13 +614,13 @@ pid_t run_getty(char *cmd, char *args[], int console)
prctl(PR_SET_NAME, "console", 0, 0, 0);
}
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
/* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
sig_unblock();
setsid();
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
_pe("Failed TIOCSCTTY");
while (!fexist(SYNC_SHUTDOWN)) {
static const char msg[] = "\nPlease press Enter to activate this console.";
@@ -581,7 +631,7 @@ pid_t run_getty(char *cmd, char *args[], int console)
}
if (console) {
i = write(STDERR_FILENO, msg, sizeof(msg));
(void)write(STDERR_FILENO, msg, sizeof(msg));
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
continue;
}
@@ -646,6 +696,7 @@ int run_parts(char *dir, char *cmd)
pid = fork();
if (!pid) {
_d("Calling %s ...", path);
sig_unblock();
execv(path, args);
exit(0);
}
@@ -698,26 +749,33 @@ void do_sleep(unsigned int sec)
;
}
void set_hostname(char *hostname)
void set_hostname(char **hostname)
{
FILE *fp;
_d("Set hostname: %s", hostname);
if ((fp = fopen("/etc/hostname", "r")) != NULL) {
_d("Set hostname: %s", *hostname);
fp = fopen("/etc/hostname", "r");
if (fp) {
struct stat st;
fstat(fileno(fp), &st);
hostname = realloc(hostname, st.st_size);
if (!hostname)
if (fstat(fileno(fp), &st)) {
fclose(fp);
return;
}
fgets(hostname, st.st_size, fp);
chomp(hostname);
*hostname = realloc(*hostname, st.st_size);
if (!*hostname) {
fclose(fp);
return;
}
fgets(*hostname, st.st_size, fp);
chomp(*hostname);
fclose(fp);
}
if (hostname)
sethostname(hostname, strlen(hostname));
if (*hostname)
sethostname(*hostname, strlen(*hostname));
}
/**
+16 -4
View File
@@ -29,20 +29,30 @@
#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))
#ifndef touch
# define touch(x) mknod((x), S_IFREG|0644, 0)
# define touch(x) do { if (mknod((x), S_IFREG|0644, 0) && errno != EEXIST) _pe("Failed creating %s", x); } while (0)
#endif
#ifndef makedir
# define makedir(x, p) do { if (mkdir(x, p) && errno != EEXIST) _pe("Failed creating directory %s", x); } while (0)
#endif
#ifndef makefifo
# define makefifo(x, p) do { if (mkfifo(x, p) && errno != EEXIST) _pe("Failed creating FIFO %s", x); } while (0)
#endif
#ifndef erase
# define erase(x) do { if (remove(x) && errno != ENOENT) _pe("Failed removing %s", x); } while (0)
#endif
#ifndef chardev
# define chardev(x,m,maj,min) mknod((x), S_IFCHR|(m), makedev((maj),(min)))
@@ -84,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);
@@ -101,7 +113,7 @@ void chomp (char *str);
void do_sleep (unsigned int sec);
int getuser (char *username);
int getgroup (char *group);
void set_hostname (char *hostname);
void set_hostname (char **hostname);
int complete (char *cmd, int pid);
int run (char *cmd);
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+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:
*/
+2 -2
View File
@@ -344,7 +344,7 @@ int main(void)
if (fexist(files[i + 2]))
fprintf(stderr, "OK => %s\n", files[i + 2]);
remove(files[i + 2]);
erase(files[i + 2]);
i += 3;
}
@@ -358,7 +358,7 @@ int main(void)
if (fexist(files[i + 2]))
fprintf(stderr, "OK => %s\n", files[i + 2]);
remove(files[i + 2]);
erase(files[i + 2]);
i += 3;
}
+2 -2
View File
@@ -142,13 +142,13 @@ int simulate_files(int creat)
if (creat)
touch(files[i]);
else
remove(files[i]);
erase(files[i]);
}
if (creat)
symlink("config2.cfg", STARTUP_CONFIG);
else
remove(STARTUP_CONFIG);
erase(STARTUP_CONFIG);
return 0;
}
+27 -19
View File
@@ -27,7 +27,8 @@
#include "lite.h"
#include "../helpers.h"
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
static int copy(char *src, char *dst);
static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
static int prune(char *dst, char **new_files, int new_num);
@@ -58,26 +59,16 @@ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
int i = 0, num = 0, result = 0;
char **files; /* Array of file names. */
void copy(char *src, char *dst)
{
errno = 0;
copyfile(src, dst, 0, 1);
if (errno) {
if (errno == EEXIST)
errno = 0;
else
result++;
}
}
if (!fisdir(dst))
mkdir(dst, 0755);
makedir(dst, 0755);
if (!fisdir(src)) {
if (!fexist(src))
return 1;
copy(src, dst);
if (copy(src, dst))
result++;
return errno;
}
@@ -90,7 +81,7 @@ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
else
ptr++;
if (makedir(dest, sizeof(dest), dst, ptr, fmode(src)))
if (mdir(dest, sizeof(dest), dst, ptr, fmode(src)))
return 1;
dst = dest;
}
@@ -103,7 +94,7 @@ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
char dst2[256];
strcat(source, "/");
if (makedir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
if (mdir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
result++;
continue;
}
@@ -111,7 +102,9 @@ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
rsync(source, dst2, delete, filter);
continue; /* Next file/dir in @src to copy... */
}
copy(source, dst);
if (copy(source, dst))
result++;
}
/* We ignore any errors from the pruning, that phase albeit useful is only
@@ -128,8 +121,23 @@ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
return result;
}
static int copy(char *src, char *dst)
{
errno = 0;
copyfile(src, dst, 0, 1);
if (errno) {
if (errno != EEXIST)
return 1;
errno = 0;
}
return 0;
}
/* Creates dir/name @mode ... skipping / if dir already ends so. */
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode)
static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode)
{
snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name);
if (mkdir(buf, mode)) {
Submodule
+1
Submodule libuev added at ce3930f0cf
+47 -90
View File
@@ -36,22 +36,19 @@
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0)
static char *plugpath = NULL; /* Set by first load. */
static size_t num_fds = 0;
static struct pollfd fds[MAX_NUM_FDS];
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;
@@ -67,44 +64,15 @@ int plugin_register(plugin_t *plugin)
return 0;
}
/* Resolve plugin dependencies */
check_plugin_depends(plugin);
if (is_io_plugin(plugin)) {
if (num_fds + 1 >= MAX_NUM_FDS) {
num_fds = MAX_NUM_SVC;
errno = ENOMEM;
return 1;
}
num_fds++;
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);
@@ -113,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;
}
@@ -159,7 +132,7 @@ plugin_t *plugin_find(char *name)
SEARCH_PLUGIN(name);
if (name[0] != '/') {
if (plugpath && name[0] != '/') {
int noext;
char path[CMD_SIZE];
@@ -189,62 +162,31 @@ void plugin_run_hooks(hook_point_t no)
}
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
static void generic_io_cb(struct pollfd *io)
static void generic_io_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int events)
{
plugin_t *p, *tmp;
plugin_t *p = (plugin_t *)arg;
/* Find matching plugin, pick first matching fd */
PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p) && p->io.fd == io->fd) {
_d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, io->fd, io->events);
if (is_io_plugin(p) && p->io.fd == w->fd) {
/* Stop watcher, callback may close descriptor on us ... */
uev_io_stop(w);
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
io->fd = p->io.fd;
io->events = p->io.flags;
io->revents = 0;
break;
}
}
}
_d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, w->fd, events);
void plugin_monitor(void)
{
int ret;
size_t i;
while ((ret = poll(fds, num_fds, 500))) {
if (-1 == ret) {
if (EINTR == errno)
continue;
_e("Failed polling I/O plugin descriptors, error %d: %s",
errno, strerror(errno));
break;
}
/* Traverse all I/O fds and run callbacks */
for (i = 0; i < num_fds; i++) {
if (fds[i].revents)
generic_io_cb(&fds[i]);
}
break;
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
uev_io_set(w, p->io.fd, p->io.flags);
}
}
/* Setup any I/O callbacks for plugins that use them */
static void init_plugins(void)
static void init_plugins(uev_ctx_t *ctx)
{
int i = 0;
plugin_t *p, *tmp;
PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p)) {
_d("Initializing plugin %s for I/O", basename(p->name));
fds[i].revents = 0;
fds[i].events = p->io.flags;
fds[i++].fd = p->io.fd;
uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags);
}
}
}
@@ -267,8 +209,9 @@ static void init_plugins(void)
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;
@@ -277,15 +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));
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
_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(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;
}
@@ -315,12 +271,13 @@ static void check_plugin_depends(plugin_t *plugin)
}
}
int plugin_load_all(char *path)
int plugin_load_all(uev_ctx_t *ctx, char *path)
{
int fail = 0;
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;
@@ -338,7 +295,7 @@ int plugin_load_all(char *path)
}
closedir(dp);
init_plugins();
init_plugins(ctx);
return fail;
}
+16 -4
View File
@@ -24,13 +24,13 @@
#ifndef FINIT_PLUGIN_H_
#define FINIT_PLUGIN_H_
#include <poll.h>
#include "queue.h" /* BSD sys/queue.h API */
#include "svc.h"
#include "libuev/uev.h"
#define PLUGIN_DEP_MAX 10
#define PLUGIN_IO_READ POLLIN
#define PLUGIN_IO_WRITE POLLOUT
#define PLUGIN_IO_READ UEV_READ
#define PLUGIN_IO_WRITE UEV_WRITE
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
@@ -79,8 +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. */
@@ -103,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)
+21 -22
View File
@@ -39,25 +39,24 @@ static void setup(void *UNUSED(arg))
#endif
_d("Setting up FHS structure in /var ...");
mkdir("/var/cache", 0755);
mkdir("/var/games", 0755);
mkdir("/var/lib", 0755);
mkdir("/var/lib/misc", 0755);
mkdir("/var/lib/alarm", 0755);
mkdir("/var/lock", 0755);
mkdir("/var/log", 0755);
mkdir("/var/mail", 0755);
mkdir("/var/opt", 0755);
mkdir("/var/run", 0755);
mkdir("/var/spool", 0755);
mkdir("/var/spool/cron", 0755);
mkdir("/var/tmp", 0755);
mkdir("/var/empty", 0755);
makedir("/var/cache", 0755);
makedir("/var/games", 0755);
makedir("/var/lib", 0755);
makedir("/var/lib/misc", 0755);
makedir("/var/lib/alarm", 0755);
makedir("/var/lock", 0755);
makedir("/var/log", 0755);
makedir("/var/mail", 0755);
makedir("/var/opt", 0755);
makedir("/var/run", 0755);
makedir("/var/spool", 0755);
makedir("/var/spool/cron", 0755);
makedir("/var/tmp", 0755);
makedir("/var/empty", 0755);
_d("Setting up necessary UTMP files ...");
touch("/var/run/utmp");
chown("/var/run/utmp", 0, getgroup("utmp"));
chmod("/var/run/utmp", 0664);
#ifdef RUNLEVEL
memset(&entry, 0, sizeof(struct utmp));
@@ -71,16 +70,16 @@ static void setup(void *UNUSED(arg))
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
touch("/etc/network/run/ifstate");
#else
remove("/etc/network/run/ifstate");
erase("/etc/network/run/ifstate");
#endif
_d("Setting up misc files ...");
mkdir("/var/run/lldpd", 0755); /* Needed by lldpd */
mkdir("/var/run/pluto", 0755); /* Needed by Openswan */
mkdir("/var/run/quagga", 0755); /* Needed by Quagga */
mkdir("/var/log/quagga", 0755); /* Needed by Quagga */
mkdir("/var/run/sshd", 01755); /* OpenSSH */
mkfifo("/dev/xconsole", 0640); /* sysklogd */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
makedir("/var/run/sshd", 01755); /* OpenSSH */
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, getgroup("tty"));
}
+3 -3
View File
@@ -29,10 +29,10 @@ static void setup(void *UNUSED(arg))
{
#ifdef HAVE_DBUS
_d("Starting D-Bus ...");
mkdir("/var/run/dbus", 0755);
mkdir("/var/lock/subsys/messagebus", 0755);
makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
run("dbus-uuidgen --ensure");
remove("/var/run/dbus/pid");
erase("/var/run/dbus/pid");
run_interactive("dbus-daemon --system", "Starting D-Bus");
#endif
}
+7 -4
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':
@@ -124,7 +127,7 @@ static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
PLUGIN_INIT(plugin_init)
{
_d("Setting up %s", FINIT_FIFO);
mkfifo(FINIT_FIFO, 0600);
makefifo(FINIT_FIFO, 0600);
fifo_open();
plugin_register(&plugin);
+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:
*/
+3 -2
View File
@@ -26,16 +26,17 @@
#include <sys/stat.h>
#include "finit.h"
#include "libite/lite.h"
#include "helpers.h"
#include "plugin.h"
static void setup(void *UNUSED(arg))
{
#ifdef RANDOMSEED
copyfile(RANDOMSEED, "/dev/urandom", 0);
copyfile(RANDOMSEED, "/dev/urandom", 0, 0);
unlink(RANDOMSEED);
umask(077);
copyfile("/dev/urandom", RANDOMSEED, 4096);
copyfile("/dev/urandom", RANDOMSEED, 4096, 0);
umask(0);
#endif
}
+3 -3
View File
@@ -36,7 +36,7 @@ static void setup(void *UNUSED(arg))
#endif
char line[LINE_SIZE];
makepath("/var/run/console");
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
touch(line);
@@ -48,8 +48,8 @@ static void setup(void *UNUSED(arg))
}
#endif
mkdir("/tmp/.X11-unix", 01777);
mkdir("/tmp/.ICE-unix", 01777);
makedir("/tmp/.X11-unix", 01777);
makedir("/tmp/.ICE-unix", 01777);
}
static plugin_t plugin = {
+4 -3
View File
@@ -28,12 +28,13 @@
#include "svc.h"
#include "plugin.h"
uev_ctx_t *ctx; /* Main loop context */
void svc_bootstrap (void);
void svc_monitor (void);
void svc_monitor (pid_t lost);
void plugin_run_hooks (hook_point_t no);
int plugin_load_all (char *path);
void plugin_monitor (void);
int plugin_load_all (uev_ctx_t *ctx, char *path);
#endif /* FINIT_PRIVATE_H_ */
+83 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */
/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
@@ -316,6 +316,86 @@ struct { \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
/*
* XOR Simple queue definitions.
*/
#define XSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqx_first; /* first element */ \
struct type **sqx_last; /* addr of last next element */ \
unsigned long sqx_cookie; \
}
#define XSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqx_next; /* next element */ \
}
/*
* XOR Simple queue access methods.
*/
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
(unsigned long)(ptr)))
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
#define XSIMPLEQ_END(head) NULL
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
#define XSIMPLEQ_FOREACH(var, head, field) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) != XSIMPLEQ_END(head); \
(var) = XSIMPLEQ_NEXT(head, var, field))
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
(var) = (tvar))
/*
* XOR Simple queue functions.
*/
#define XSIMPLEQ_INIT(head) do { \
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqx_next = (head)->sqx_first) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
(elm)->field.sqx_next)->field.sqx_next) \
== XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = \
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
/*
* Tail queue definitions.
*/
@@ -553,12 +633,12 @@ struct { \
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head).cqh_last = (elm2); \
(head)->cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head).cqh_first = (elm2); \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
+72 -28
View File
@@ -47,9 +47,13 @@
#include "private.h"
#include "sig.h"
static int stopped = 0;
static int stopped = 0;
static uev_t sigint_watcher, sigpwr_watcher;
static uev_t sigchld_watcher, sigsegv_watcher;
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
void do_shutdown (int sig)
void do_shutdown(int sig)
{
touch(SYNC_SHUTDOWN);
@@ -84,40 +88,46 @@ void do_shutdown (int sig)
}
/*
* Shut down on INT USR1 USR2
* Shut down on INT PWR
*/
static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
static void sigint_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
_d("Rebooting on signal %d from %s (PID %d)",
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
do_shutdown(sig);
do_shutdown(w->signo);
}
/*
* SIGCHLD: one of our children has died
*/
static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(ctx))
static void sigchld_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
/* Do nothing, the svc_monitor() is the designated child reaper. */
svc_monitor(waitpid(-1, NULL, WNOHANG));
}
/*
* SIGSTOP: Paused by user or netflash
* SIGSEGV: mostly if service callbacks segfault
*/
static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
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
*/
static void sigstop_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
_d("Received SIGSTOP(%d) from %s (PID %d)",
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
touch(SYNC_STOPPED);
stopped++;
}
static void sigcont_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
/*
* SIGCONT: Restart service monitor
*/
static void sigcont_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
_d("Received SIGCONT(%d) from %s (PID %d)",
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
stopped = 0;
remove(SYNC_STOPPED);
erase(SYNC_STOPPED);
}
/*
@@ -131,6 +141,11 @@ int sig_stopped(void)
/*
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding
*/
static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(ctx))
{
/* NOP */
}
void sig_init(void)
{
int i;
@@ -142,26 +157,49 @@ void sig_init(void)
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
}
/*
* Unblock all signals blocked by finit when starting children
*/
void sig_unblock(void)
{
int i;
sigset_t nmask;
struct sigaction sa;
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigaddset(&nmask, SIGINT);
sigaddset(&nmask, SIGPWR);
sigaddset(&nmask, SIGSTOP);
sigaddset(&nmask, SIGTSTP);
sigaddset(&nmask, SIGCONT);
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
/* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
}
/*
* Setup limited set of SysV compatible signals to respond to
*/
void sig_setup(void)
void sig_setup(uev_ctx_t *ctx)
{
struct sigaction sa;
_d("Setup signals");
/* Cleanup any stale finit control files */
remove(SYNC_SHUTDOWN);
remove(SYNC_STOPPED);
erase(SYNC_SHUTDOWN);
erase(SYNC_STOPPED);
/* Standard SysV init calls ctrl-alt-delete handler */
SETSIG(sa, SIGINT, shutdown_handler, 0);
SETSIG(sa, SIGPWR, shutdown_handler, 0);
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
uev_signal_init(ctx, &sigpwr_watcher, sigint_cb, NULL, SIGPWR);
/* Ignore SIGUSR1/2 for now, only BusyBox init implements them as reboot+halt. */
// SETSIG2(sa, SIGUSR1, reopen_initctl, 0);
// SETSIG2(sa, SIGUSR2, pwrdwn_handler, 0);
// uev_signal_init(&ctx, &sigusr1_watcher, reopen_initctl_cb, NULL, SIGUSR1);
// uev_signal_init(&ctx, &sigusr2_watcher, pwrdwn_cb, NULL, SIGUSR2);
/* Init must ignore SIGTERM. May otherwise get false SIGTERM in forked children! */
IGNSIG(sa, SIGTERM, 0);
@@ -172,10 +210,16 @@ void sig_setup(void)
/* We don't have any /etc/inittab yet, reread finit.conf? */
IGNSIG(sa, SIGHUP, 0);
/* 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. */
SETSIG(sa, SIGSTOP, sigstop_handler, 0);
SETSIG(sa, SIGTSTP, sigstop_handler, 0);
SETSIG(sa, SIGCONT, sigcont_handler, 0);
uev_signal_init(ctx, &sigstop_watcher, sigstop_cb, NULL, SIGSTOP);
uev_signal_init(ctx, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP);
uev_signal_init(ctx, &sigcont_watcher, sigcont_cb, NULL, SIGCONT);
/* Disable CTRL-ALT-DELETE from kernel, we handle shutdown gracefully with SIGINT, above */
reboot(RB_DISABLE_CAD);
+3 -1
View File
@@ -26,6 +26,7 @@
#define FINIT_SIG_H_
#include <signal.h>
#include "libuev/uev.h"
#define SYNC_SHUTDOWN "/var/lock/finit.shutdown"
#define SYNC_STOPPED "/var/lock/finit.stopped"
@@ -57,7 +58,8 @@
void do_shutdown (int sig);
int sig_stopped (void);
void sig_init (void);
void sig_setup (void);
void sig_unblock (void);
void sig_setup (uev_ctx_t *ctx);
#endif /* FINIT_SIG_H_ */
+311 -72
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,30 +194,37 @@ 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);
}
}
static int encode(int lvl)
{
if (!lvl)
return 0;
return lvl + '0';
}
static void utmp_save(int pre, int now)
{
struct utmp utent;
struct utmp utent;
int encode(int lvl)
{
if (!lvl) return 0;
return lvl + '0';
}
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();
}
/**
@@ -183,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);
}
}
@@ -209,7 +288,7 @@ void svc_runlevel(int newlevel)
if (runlevel == 1)
touch("/etc/nologin"); /* Disable login in single-user mode */
else
remove("/etc/nologin");
erase("/etc/nologin");
if (0 != prevlevel)
tty_runlevel(runlevel);
@@ -233,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
@@ -247,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.");
@@ -268,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];
@@ -281,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);
@@ -292,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);
@@ -334,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;
@@ -356,13 +538,18 @@ 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);
}
}
void svc_monitor(void)
void svc_monitor(pid_t lost)
{
pid_t lost;
svc_t *svc;
if (was_stopped && !is_norespawn()) {
@@ -371,10 +558,6 @@ void svc_monitor(void)
return;
}
lost = waitpid(-1, NULL, WNOHANG);
if (lost < 1)
return;
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return;
@@ -387,6 +570,9 @@ void svc_monitor(void)
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;
@@ -423,7 +609,8 @@ void svc_monitor(void)
/* 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;
@@ -432,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);
@@ -446,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);
@@ -460,6 +678,7 @@ int svc_start(svc_t *svc)
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];
@@ -477,18 +696,26 @@ int svc_start(svc_t *svc)
setuid(uid);
/* Serve copy of args to process in case it modifies them. */
for (i = 0; i < MAX_NUM_SVC_ARGS && svc->args[i][0] != 0; i++)
for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
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] = "";
fd = open(CONSOLE, O_WRONLY | O_APPEND);
if (-1 != fd) {
dup2(STDOUT_FILENO, fd);
dup2(STDERR_FILENO, fd);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
}
for (i = 0; i < MAX_NUM_SVC_ARGS && args[i]; i++) {
@@ -501,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);
@@ -581,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);
+4 -2
View File
@@ -144,10 +144,12 @@ void tty_start(finit_tty_t *tty)
#elif defined(GETTY_BUSYBOX)
snprintf(line, sizeof(line), "%s %d %s %s", GETTY, tty->baud, tty->name, tty->term ?: "");
#else
strlcpy(line, FALLBACK_CONSOLE, sizeof(line));
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);
@@ -179,7 +181,7 @@ int tty_enabled(finit_tty_t *tty, int runlevel)
if (!ISSET(tty->runlevels, runlevel))
return 0;
else if (fexist(tty->name))
if (fexist(tty->name))
return 1;
return 0;