Compare commits

...
54 Commits
Author SHA1 Message Date
Joachim Nilsson 685618691d Update ChangeLog and bump version for release. 2012-10-06 18:07:39 +02:00
Joachim Nilsson f29d2733c7 Fix incorrect use of variable arguments in run_parts() and simplify code
The run_parts() implementation used variable arguments in a bad way,
resulting in it not working properly.  Replaced va_*() code with a
simple cmd argument that's passed to each script called.

Also, the opendir()/readdir() code has been replaced with scandir()
using regular alphasort(). This simplifies the code somewhat as well.

Otherwise, minor cleanups.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-06 15:45:42 +02:00
Joachim Nilsson af59174e49 Prevent endless restart of non-existing services and support start-stop scripts
Sometimes people type in the wrong path to a service directive in
finit.conf, or the executable isn't installed.  This commit makes
sure to check if the command exists before attempting to start it.
This also means that we now require service commands to start with
/ to indicate an absolute path.  We used to search the PATH for a
suitable executable, using execvp(), but that's rather insecure as
well, so we now use only execv().

Also, added support for adding "start" or "stop" to entries found
in /etc/finit.d starting with SNNname, or KNNname, where NN is a
number.  This makes it possible to simply import sysvinit style
start scripts from /etc/rcS.d and /etc/rcN.d/.

The alsa-utils plugin also had some minor fixes, added -g option
to ignore non-existing soundcards and fixed cut-paste bug on the
_d() debug messages.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-06 13:56:26 +02:00
Joachim Nilsson 785c21d27b Support for overriding built-in default FINIT_RCSD in finit.conf
This commit adds support for the "runparts <DIR>" directive to
finit.conf.  Enabling the user to override the built-in default,
which by default is /etc/finit.d.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-05 18:50:21 +02:00
Joachim Nilsson 40c7a44d4f Skip . and .. in plugin loader and trigger error on failed plugin load.
Fix from field testing.  Need to skip "." and ".." directory entries
when loading plugins from /lib/finit/plugins.  Also, trigger an error
when a required plugin fails to load.  We need to print to console
since syslogd has not started yet.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-04 17:22:40 +02:00
Joachim Nilsson cbb50937cd Update ChangeLog and bump version.
New release, 1.5! :)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-03 23:30:23 +02:00
Joachim Nilsson 83a4bf7947 Add dependency resolver for plugins.
This has been something I wanted to avoid, but then I stumbled so hard
on it myself I simply had to add it.  With this commit finit actually
comes of age -- it's now possible to do anything a modern init can --
only faster! :-)

Included in this commit is the OpenBSD version of sys/queue.h, which
unline all versions of the same file on Linux has the _FOREACH_SAFE
macros for iterating over a linked list.

The plugins are now in a TAILQ, loaded in alphabetic order and, unless
the dependency resolver says otherwise, added last in the queue.  So,
naming your plugins according to standard sysvinit rules: S01, S02,
etc. takes care of the most basic dependency resolution.  When that is
not sufficient, simply add a line ".depends = { "other_plugin",
"another_plugin" }," to your plugin_t definition.  Be careful though
for circular dependencies, we don't check for those.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-03 23:20:34 +02:00
Joachim Nilsson 0d7d5a9820 Setup standard FHS 2.3 structure in /var
This was missing from the 1.4 release due to it being hidden in one
of my private plugins. Sorry!

Also, add another log macro. Warnings are quite useful things to have.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-03 11:44:33 +02:00
Joachim Nilsson 65c9e7ebc6 Fix I/O plugin watcher, load plugins earlier to enable a new early hook!
Restructure start by calling the plugin loader earlier. This makes it
possible to run hooks even earlier.  This also adds a new hook:
HOOK_ROOTFS_UP which runs as soon as the system root (/) is available.

Another very notable change is the slow refactor of helpers.c into a finit
helper library called libite.so (-lite). This is a light-weight

Also, lots of misc. fixes all over the place. Signal handling still really
needs to be looked into once more!

Makefile:
        Add dependency handling for parallell build
        Add libite library

ChangeLog:
        Update with new 1.4 release. There is a lot of fixes!

libite/:
        Collection of utility functions and C library extensions for
        finit & its plugins

reboot.c:
        New reboot tool. Simply sends SIGINT to PID 1

finit.h:
        Cleanup old LISTEN_INITCTL define, code is in an optional plugin now

finit.c:
        Let mdev/udev run *after* cls()
        Add useful debug statement for /proc/cmdline
        Earlier plugin load => new early hook point
        Show OK/FAIL for total plugin load, not for each plugin => speedup

helpers.h:
        Rename syslog macros to prevent common name clashes (DEBUG/ERROR)
        Add __FILE__ to _e(), _d() and _pe() macros

helpers.c:
        In ifconfig() the addr and mask arguments are now optional
        The copyfile() has been moved to libite.so
        Temporarily remove const directive from some functions to build
        Use DFLSIG() macro in run_getty() to simplify code

plugin.h:
        Add new HOOK_ROOTFS_UP hook

plugin.c:
        Fix init_plugins() to actually register the plugin's I/O fd
        Skip verbose listing of all plugins in plugin_load_all()

svc.c:
        svc_start(): Dito

plugins/Makefile:
        Inherit CFLAGS and LDFLAGS, but make sure to filter out -rdynamic
        from LDFLAGS, it's used by the daemon, only

plugins/initctl.c:
        New includes after helpers.h refactor

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-02 08:25:50 +02:00
Joachim Nilsson c002b5301f plugins/hwclock: Use short-form -s/-w -u to work with BusyBox hwclock as well.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-02 06:39:26 +02:00
Joachim Nilsson 3f1c2f6211 Plugin loader and registration fixes
- Use RTLD_GLOBAL flag to tell dynamic loader to load dependent .so
  files as well.  Also lets other plugins use globally scoped symbols.

- Check for plugin hooks as well before dismissing plugin as unused.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-02 06:39:26 +02:00
Joachim Nilsson 5116cd66ef Greatly simplify svc hook for external plugins
This commit removes a lot of unnecessary overhead for handling external
service plugins.  Instead of traversing the plugin array every time a
service callback should run we set the plugin callback in the svc_t when
the plugin is registered.

Also, make sure to check for norespawn/SIGSTOP in svc_reload() as well.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-02 06:39:26 +02:00
Joachim Nilsson f55f68d032 Cleanup and refactor of public plugin API
Improve upon API names, both internal and external, to make it clearer
where methods are located and clarify responsibility.

Add new Makefile target "dev" to create untagged development snapshots.

Bump version for upcoming release.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-02 06:39:19 +02:00
Joachim Nilsson 5e99b5e735 Minor API change.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-29 15:13:13 +02:00
Joachim Nilsson d26852e07a README: Further cleanup
Remove prefix= from list of overridden environment variables in example.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-28 18:00:49 +02:00
Joachim Nilsson a90a7c04e4 README: Further updates, clarification and additions on, e.g., running finit.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-28 17:56:26 +02:00
Joachim Nilsson 14a1fb9cde Create /etc/finit.d at install and update README with v1.3 changes.
Updates to README include changed environment variables and a
clarification of why there is no default /etc/finit.conf after make
install.

Also, the /etc/finit.d directory is now created, as expected, by
make install.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-28 17:26:31 +02:00
Joachim Nilsson b373e199a8 Install fixes, $prefix supported better, strip binaries and .so files
- 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/
- Use the 'plugindir' environemnt variable instead of 'PLUGIN_DIR'

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-28 16:46:17 +02:00
Joachim Nilsson 149ed0e81c Minor header comment updates and bump version to 1.3
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-28 15:59:15 +02:00
Joachim Nilsson e32ca1fc83 Keep separate install/uninstall targets for exec, data and dev files.
Break up install/uninstall targets in top Makefile into separate
install-exec, install-data, and install-dev for packagers and
distributions wishing to call Makefile with different DESTDIR's
depending on building -bin or -devel package.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-28 14:23:29 +02:00
Joachim Nilsson 9aa90763b0 Support for installing plugin headers to system include/finit/ directory.
Minor cleanup of Makefile and added support for installing necessary
header files, for developing external plugins, to system include path.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-27 23:25:41 +02:00
Joachim Nilsson 06759d3544 Streamline plugin include files plugin.h, svc.h, and helpers.h
Move utility macros from finit.h to helpers.h and continue refactoring
and streamlining of public plugin API.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-27 22:10:07 +02:00
Joachim Nilsson ff73dab447 Slight cleanup of public API in helpers.c
Function renames:
  - set_procname() --> procname_set()
  - kill_procname() --> procname_kill()
  - get_pidname() --> pid_get_name()

Also added three new helper functions, for external plugins:
  - pid_alive()
  - pidfile_poll()
  - pidfile_read()

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-27 20:56:46 +02:00
Joachim Nilsson 0d70fa4cf0 Let plugins hook into service startup to notify 3rd party daemon.
This commit adds support for calling plugin hooks when all services
have been started properly. Useful for, e.g., a 3rd party service
monitor to trigger further events on.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-27 15:25:10 +02:00
Joachim Nilsson 103bf33b3c Remove finit.h from includes in svc.h, not needed.
Also, when 3rd party sources include svc.h they would get finit
internals included. Not good.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-27 13:14:00 +02:00
Joachim Nilsson 2b2fcb740c Fix installation paths and update README on building, bump version
The installation path for plugins was encoded wrong in the finit binary
if *not* unset, i.e., "prefix=".

Updated the README with information on all build related environment
variables.  Much of it is perhaps only needed for packagers and
distributions.

New version bump, v1.2 already.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-27 09:33:55 +02:00
Joachim Nilsson 7542f4e199 Update ChangeLog and bump version for release. 2012-09-26 23:17:18 +02:00
Joachim Nilsson 1fece1f9b5 Work around compiler error for breaking strict-aliasing rules
This commit fixes the following warning on an ARM eabi/uClibc system.
helpers.c:86: error: dereferencing pointer 'a' does break strict-aliasing rules

The fix is trivial. Simply set IPv4 addressing scheme in the
struct ifreq instead of the struct sockaddr_in, whic is just
a pointer into the same ifreq anyway.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 23:10:53 +02:00
Joachim Nilsson 814ff1f77e Fix build problem on ARM eabi/uClibc by removing unused Linux header file.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 23:00:46 +02:00
Joachim Nilsson fbfffbc9a4 Eliminate name clash with system signal.h header file.
Tried to solve the signal.h name clash by using #include_next, but
seeing as this is not recommended[1] and breaks builds on some cross
compilers, the files were simply renamed: signal.[ch] --> sig.[ch]

[1] - http://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 22:57:25 +02:00
Joachim Nilsson e496913f5c README: Redo feature table into a definition list.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 13:53:25 +02:00
Joachim Nilsson 5bc9ff0e20 README: Slight syntax change in feature table
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 13:49:32 +02:00
Joachim Nilsson dd0220fd2c Update and clarify README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 13:47:57 +02:00
Joachim Nilsson bb0a378f1c Rename README --> README.rst and symlink README to it.
This change is purely for people interacting with the GitHub interface.
The README itself is written in the plain text reST format.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 13:30:05 +02:00
Joachim Nilsson 2a025356d4 Bump version, update README and add TODO and dist: target to Makefile
Preparing for 1.0 release.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 13:20:21 +02:00
Joachim Nilsson c49d89dfe0 Revert new experimental libev backend due to time constraints
Due to time constraints I had to revert the new libev backend to the
service, I/O and signals monitor.  Employed instead is the tried and
tested signals based service monitor, with a small extension for an
additional I/O monitor using poll() for the I/O plugins to use.

Tried restoring the startx finit.conf directive on top of the service
implementation. However, it's currently not usable to run /usr/bin/startx
for any other user than root. I recommend using the examples/finit.conf
and run /usr/sbin/gdm instead.

finit.c:
        New run_loop() function, replaces libev ev_run().

helpers.c:
        Refactor start_process() into svc_start().

plugin.c:
        Add fds[] array for use in the io_monitor() called by the
        new run_loop().

signal.c:
        Ignore SIGCHLD. Handled by waitpid() in svc_monitor() now,
        which is called by the new run_loop().

svc.c:
        svc_register(): Optional @username parsing for the command line.
        svc_monitor(): Replaced libev callback with waitpid()
        svc_start(): Refactored to use most of helpers.c:start_process()

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-26 12:18:54 +02:00
Joachim Nilsson 75eb5cc043 Move fork() of console to run_getty() and remove startx conf option
New function helpers.c:run_getty() refactored from main(). Used to
start a default console getty.

Removed startx finit.conf option, superseded by service statement,
see examples/finit.conf for a working use case.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-25 15:20:55 +02:00
Joachim Nilsson dfa0fe6428 Refactor conf-file parser into a file of its own.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-25 14:49:33 +02:00
Joachim Nilsson 0e02429e75 Convert finit to use libev and fix so plugin ctors runs on dlopen().
Lots of cleanup and simplification of the main code in this commit.
Most notably libev has been added to take care of the main loop and
monitor services. Still TODO is migrating to use the libev signal
handling.

Convert initctl.c:listen_initctl() to a plugin instead.  This also
meant introducing a new form of I/O plugin to finit, also handled
by libev.

Load plugins *after* we've setup the base filesystem (BASEFS) in case
plugins reside on a filesystem not reachable before "mount -a" has run.
This also has the side effect of lining up nicely with the first level
plugin hooks.

If a plugin wants to hook up with a loded service we connect them by
their respective numeric IDs in plugin_register().  I.e., the service
monitor checks if a service has a registered callback, which in turn
decides if a service should be started, stopped or reloaded.

Rip out old EeePC distribution defines from finit.h, none of it really
applies anymore.  If you need distribution specific settings this is
the place to put them.

Make hookpoint names (#defines) clearer: after BASEFS, after networking,
after all setup, etc.

Use #inlude_next <signal.h> in signal.h to include system header file!

Simplify service monitor: move essential code to svc.c and rip out
the TIPC dependency. Write your own service.so plugin, or wait for
a more complete example of how to extend service monitoring using
the primitives in svc.c

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-25 14:23:23 +02:00
Joachim Nilsson 5dda9051ac Refactor main() into more functions and convert #ifdef hell into plugins.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-21 03:32:07 +02:00
Joachim Nilsson 239772d667 Add basic support for dynamically loadable plugins
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-21 00:33:46 +02:00
Joachim Nilsson a0fa877520 Move example configuration files to examples subdirectory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-21 00:31:13 +02:00
Joachim Nilsson 23ef745985 Remove deprecated finit implementations, see the 0.6-pre tag for reference.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-20 17:54:29 +02:00
Joachim Nilsson 077a7e82e0 Debian now boots to X!
finit.conf:
        Use this as /etc/finit.conf on a Debian 6.0 based system with X
        installed and sysklogd instead of rsyslogd.  Works.

services:
        Alternative method for extending the finit boot procedure. Simply
        place this script file in an /etc/finit.d/ directory.  The script
        uses the standard run-parts tool to chain load other start scripts.

helpers.c:
        o New functions:
          - run(): home grown system() replacement
          - run_interactive(): display progress and status when called

        o Bugfix start_process(), make sure to use same signal setup for
          children as in run() and run_interactive(). Also, use execvp()
          instead of execv() to allow searching $PATH for executables.

finit.h:
        o Move signal specific code to new signal.h.
        o Bypass verbosity setting to let echo() always print to screen.

finit.c:
        o Make run_parts() the default, remove external deps, it works.

        o Use run() and run_interactive() to speed up execution further
          on embedded systems where forking is expensive.

        o Kernel cmdline "quiet" should not close STDIO since this makes
          it impossible to run some daemons and start scripts.  Instead,
          we let "quiet" toggle the finit verbosity setting.

        o Remove kernel cmdline "--verbose" (Upstart compat), to be used
          to enable verbose finit when kernel should be quiet.

        o Setup kernel specific settings in /etc/sysctl.conf

        o Fix D-Bus startup problems. Probably what caused X from not
          working initially. Silly rabbit.

        o Use system() workaround to start X, cannot get it to start
          at all using run() or run_interactive() at the moment.

signal.h:
        New file, header file for signal.c.  Was about time.

signal.c:
        Remove blocking of SIGCHLD from sig_setup(), instead we setup
        SIGCHLD early in sig_init() to handle fork() in run() & C:o.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-20 17:50:53 +02:00
Joachim Nilsson d21dc2b477 Westermo service starter and supervisor, inspired by daemontools
finit.c:
        o New service and shell_service stanzas for finit.conf
        o Run service_startup() before /etc/finit.d/ hooks
        o Run service_monitor() at end of startup

helpers.c:
        o Several new functions:
          - print_desc() displays a service starting up
          - print_result() shows if service started OK or not
          - start_process() is used by the services framework

svc.c:
        Generic API for managing svc_t structures

ipc.o:
        TIPC based framework for communicating with the service monitor.
        E.g., to relay (re)configuration signals from a Web/CLI
        front-end via finit to one or many setup daemons, and back.

services.c:
        Service monitor framework

strlcpy.c:
        Secure strncpy() replacement from OpenBSD under the free ISC License.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-20 01:16:00 +02:00
Joachim Nilsson c0356b5c7a Massive cleanup to be able to boot Debian 6.0
Update license and copyright information details in all files.
Much cleanup by moving hard coded strings/data to #defines and using more
common default values for each such #define.

finit.c:
        o Make sdown[], startx[] allocated strings by default NULL
        o This fixes the issues where the shutdown handler segfaulted
        o Add ability to handle NULL input to build_cmd() by allocating space
        o Ignore kernel cmdline "quiet" if in debug mode
        o Add support for "network" stanza in finit.conf
        o Mount -n to skip removing /etc/mtab
        o Call swapon to enable swap if enabled in /etc/fstab
        o Cleanup /tmp, /var/run and /var/lock
        o Move sig_setup() *after* mount, sig_init() otoh runs early
        o Start initctl monitor before forking off startx/getty process
        o The startx process is now the console process. Only a getty is
          started when in debug mode. Otherwise only X is started if it
          is properly setup in /etc/finit.conf
        o NOTE: Currently X evdev does not work!
        o Support for /usr/sbin/services.sh has been replaced with finit
          now instead supporting builtin run-parts for /etc/finit.d/

helpers.c:
        o Bug fixes in run_parts() and also added chdir(oldpwd) at bottom.
        o New functions:
          - getuser() similar to getgroup()
          - cls() clear screen, only runs when not in debug mode
          - set_procname() used to set name "console" on console process
          - set_hostname() encapsulates /etc/hostname and sethostname()

signal.c:
        o Split sig_setup() in two: sig_init() and sig_setup()
          The former to be called as soon as possible, while latter is
          supposed to run when the base filesystem is properly up.
        o Remove hard coded distribution specific progress bar write()
        o Add debug messages to do_shutdown()
        o Remove any lingering sync files in sig_setup()

initctl.c:
        o Add debug messages and a new default: case to catch stray runlevels

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-19 18:06:12 +02:00
Joachim Nilsson 855c7d8251 Assume kernel has devtmpfs enabled --> cleanup device creation & mount
Cleanup device creation and initial mount by assuming that the kernel
has devtmpfs enabled.  This is the default for most modern GNU/Linux
distributions.  See http://lwn.net/Articles/331818/ for more info.

Also, since finit is targeted at deeply embedded systems hooks for the
BusyBox udev replacement, mdev has been added.  If MDEV is defined in
the build it will take care of both hotplugging of devices and initial
setup of /dev

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-17 16:05:41 +02:00
Joachim Nilsson 2f527215d0 Compat kernel command line debug option --verbose
Check for "--verbose", as well as "finit_debug", on kernel command
line to enable debugging of finit.  The former is a compatibility
option with upstart, see http://upstart.ubuntu.com/wiki/Debugging

Add the following to the kernel command line to enable:

    "init=/sbin/finit --verbose"

Without the quote characters.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-17 11:09:35 +02:00
Joachim Nilsson 5da4f4cfc3 Add support for standard UNIX comment chars in finit.conf
finit.c:
        Skip standard UNIX comment character '#' if it appears at the
        beginning of a row in finit.conf

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-17 10:43:06 +02:00
Joachim Nilsson b0aec188bc Further cleanup, move signal setup to a file of its own.
finit.c:
        o Perform signal setup as soon as possible, no need to do any
          mounting of /proc or /sys beforehand
        o Move signal setup and callbacks to a file of its own
        o Use more #defines for hard coded strings
        o Use a more neutral default hostname

signal.c:
        o New file, for setup and signal handlers
        o Add support for SIGSTOP/SIGSTP and SIGCONT.  This pauses finit
          respawning of xinit/shell.  Useful for, e.g., debugging
        o Document each ignored and handled signal better
        o Disable SIGUSR1 and SIGUSR2 for now, will be reenabled later

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-17 10:41:18 +02:00
Joachim Nilsson 4657d9713a Minor cleanup. Moved code around a bit, to functions, away from main().
This commit is in preparation for importing the Westermo code changes.
Slowly refactoring the finit core into a more modularized unit to be a
bit more maintainable.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-17 01:51:37 +02:00
Joachim Nilsson a654d99b92 Archive previous versions for reference.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-14 13:23:17 +02:00
Joachim Nilsson e2c4a6ceaf Remove optional --directisa call to hwclock, this is the default anyway.
This is a followup to Olivier Blin's patch from 88ffb93 in that it
applies the same patch to finit-mod.c that is already in finit.c, or
finit-alt.c as it was called then.

Also, the optional --directisa argument to hwclock is not necessary.
The hwclock utility has a built-in fallback to use excplicit I/O over
the /dev/rtc in case the latter does not exist.  See the hwclock(8) man
page for more information on this.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-14 13:11:31 +02:00
Joachim Nilsson 5816c9cab9 Embedded finit for small embedded Linux systems and servers.
o Make finit-alt.c the new finit.c, save old finit.c as finit-org.c
o Keep original finit-alt.c for reference.
o Update README and ChangeLog with direction of this fork
o Correct ChangeLog date for v0.1 and set release date for
  the 0.6-pre release to 20100614, which is the last commit
  by the original author.
o Add standard LICENSE file for MIT License
o Add standard AUTHORS file to document history of project
o Bump version to 1.0-pre to distance fork from original

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-09-14 13:10:39 +02:00
53 changed files with 5444 additions and 1655 deletions
+4
View File
@@ -1,2 +1,6 @@
*.o
.*.d
*.so
*.gz
*.map
finit
+6
View File
@@ -0,0 +1,6 @@
Current Maintainer(s):
* Joachim Nilsson <troglobit@gmail.com>
Original Author(s):
* Claudio Matsuoka <cmatsuoka@gmail.com>
+103
View File
@@ -0,0 +1,103 @@
1.6 (20121006)
- 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 (20121003)
- 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 (20121002)
- 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
the plugin API.
- And more... see the GIT log for more details.
1.3 (20120928)
- 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 (20120927)
- Fix installation paths encoded in finit binary
- Update README with section on building and enviroment variables
1.1 (20120927)
- Build fixes for ARM eabi/uClibc
- Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
1.0 (20120926)
- 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 (20100614)
- 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 (20080821)
- 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 (20080516)
- 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 (20080223)
- 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 (20080219)
- 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 (20080214)
- initial release
-47
View File
@@ -1,47 +0,0 @@
0.6 ()
- 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 (20080821)
- 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 (20080516)
- 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 (20080223)
- 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 (20080219)
- 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 (20081214)
- initial release
+20
View File
@@ -0,0 +1,20 @@
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
Copyright (c) 2008-2012 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.
+142 -65
View File
@@ -1,91 +1,168 @@
CC = gcc
CFLAGS = -Os -Wall -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
LD = gcc
LDFLAGS =
LIBS =
VERSION = 0.6-pre
PKG = finit-$(VERSION)
DFILES = Makefile README finit.c finit-mod.c finit-alt.c helpers.c helpers.h \
patches/*patch contrib/services.sh* contrib/Makefile.*
BINS = finit-mod finit-alt finit-mdv #finit-exb
# Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
#
# Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
# Copyright (c) 2008-2012 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.
.PHONY: all install clean distclean dist \
install-exec install-data install-dev \
uninstall-exec uninstall-data uninstall-dev
# Top directory for building complete system, fall back to this directory
ROOTDIR = $(shell pwd)
#### Configurable parameters
#VERSION ?= $(shell git tag -l | tail -1)
VERSION ?= 1.6
NAME = finit
PKG = $(NAME)-$(VERSION)
DEV = $(NAME)-dev
ARCHIVE = $(PKG).tar.xz
EXEC = finit reboot
HEADERS = plugin.h svc.h helpers.h queue.h
DISTFILES = LICENSE README ChangeLog finit.conf services
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o
OBJS += strlcpy.o
SRCS = $(OBJS:.o=.c)
DEPS = $(addprefix .,$(SRCS:.c=.d))
# Default user for finit-alt if not "user"
#DEFUSER = fred
# Installation paths, always prepended with DESTDIR if set
prefix ?= /usr
sysconfdir ?= /etc
sbindir ?= /sbin
libdir ?= /lib
plugindir ?= $(libdir)/finit/plugins
incdir = $(prefix)/include/finit
datadir = $(prefix)/share/doc/finit
mandir = $(prefix)/share/man/man8
# Disable annoying gcc warning
CFLAGS += -U_FORTIFY_SOURCE
# The initctl FIFO, should probably be in /run, not /dev
FINIT_FIFO ?= /dev/initctl
FINIT_CONF ?= $(sysconfdir)/finit.conf
FINIT_RCSD ?= $(sysconfdir)/finit.d
# Use -march=pentium-m to build for Eeepc
#CFLAGS += -march=pentium-m
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 += -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$(ROOTDIR)/libite
DEPLIBS = libite/libite.so
LDLIBS += -ldl -lite
# Omit the first /sbin/hwclock if CONFIG_RTC_HCTOSYS is enabled in the kernel
#CFLAGS += -DNO_HCTOSYS
include common.mk
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS
# Append -directisa to /sbin/hwclock lines if the user has disabled
# CONFIG_GENRTC and enabled CONFIG_RTC but not enabled CONFIG_HPET_TIMER
# and CONFIG_HPET_RTC_IRQ.
#CFLAGS += -DDIRECTISA
all: $(DEPLIBS) $(EXEC)
$(MAKE) -C plugins $@
# Use built-in run-parts instead of /bin/run-parts
CFLAGS += -DBUILTIN_RUNPARTS
$(DEPLIBS): Makefile
$(MAKE) -C libite all
#### End of configurable parameters
$(OBJS): Makefile
finit: $(OBJS) $(DEPLIBS)
reboot: reboot.o $(DEPLIBS)
ifneq ($(DEFUSER),)
CFLAGS += -DDEFUSER=\"$(DEFUSER)\"
endif
install-exec: all
@$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD)
@$(INSTALL) -d $(DESTDIR)$(sbindir)
@for file in $(EXEC); do \
printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
$(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
done
$(MAKE) -C libite install-exec
$(MAKE) -C plugins install
.c.o:
$(CC) -c $(CFLAGS) -o $*.o $<
install-data:
@$(INSTALL) -d $(DESTDIR)$(datadir)
@$(INSTALL) -d $(DESTDIR)$(mandir)
@for file in $(DISTFILES); do \
printf " INSTALL $(DESTDIR)$(datadir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(datadir)/$$file; \
done
all: $(BINS)
install-dev:
@$(INSTALL) -d $(DESTDIR)$(incdir)
@for file in $(HEADERS); do \
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
done
$(MAKE) -C libite install-dev install-exec
finit: finit.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
install: install-exec install-data install-dev
finit-mod: finit-mod.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
uninstall-exec:
-@for file in $(EXEC); do \
printf " REMOVE $(DESTDIR)$(sbindir)/$$file\n"; \
rm $(DESTDIR)$(sbindir)/$$file 2>/dev/null; \
done
-@rmdir $(DESTDIR)$(sbindir) 2>/dev/null
-@rmdir $(DESTDIR)$(FINIT_RCSD) 2>/dev/null
$(MAKE) -C libite uninstall
$(MAKE) -C plugins uninstall
finit-alt: finit-alt.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
uninstall-data:
@for file in $(DISTFILES); do \
printf " REMOVE $(DESTDIR)$(datadir)/$$file\n"; \
rm $(DESTDIR)$(datadir)/$$file 2>/dev/null; \
done
-@rmdir $(DESTDIR)$(mandir)
-@rmdir $(DESTDIR)$(datadir)
finit-mdv: finit-mdv.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
uninstall-dev:
-@$(RM) -rf $(DESTDIR)$(incdir)
finit-mdv.o: finit-alt.c Makefile
$(CC) -c $(CFLAGS) -DDIST_MDV -o $@ finit-alt.c
finit-exb: finit-exb.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
finit-exb.o: finit-alt.c Makefile
$(CC) -c $(CFLAGS) -DDIST_EEEXUBUNTU -o $@ finit-alt.c
uninstall: uninstall-exec uninstall-data uninstall-dev
clean:
rm -f *.o core *~ $(BINS)
-@$(RM) $(OBJS) $(DEPS) $(EXEC)
$(MAKE) -C libite $@
$(MAKE) -C plugins $@
finit.o: finit.c Makefile
finit-mod.o: finit-mod.c Makefile
helpers.o: helpers.c Makefile
distclean: clean
-@$(RM) $(JUNK) unittest *.o .*.d
$(MAKE) -C libite $@
$(MAKE) -C plugins $@
dist:
rm -Rf $(PKG)
mkdir -p $(PKG)/contrib $(PKG)/patches
for i in $(DFILES); do cp $$i $(PKG)/`dirname $$i`; done
tar cf - $(PKG) | gzip -c > $(PKG).tar.gz
rm -Rf $(PKG)
ls -l $(PKG).tar.gz
@echo "Building xz tarball of $(PKG) in parent dir..."
git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE)
@(cd ..; md5sum $(ARCHIVE) | tee $(ARCHIVE).md5)
dev: distclean
@echo "Building unstable xz $(DEV) in parent dir..."
-@$(RM) -f ../$(DEV).tar.xz*
@(dir=`mktemp -d`; mkdir $$dir/$(DEV); cp -a . $$dir/$(DEV); \
cd $$dir; tar --exclude=.git --exclude=contrib \
-c -J -f $(DEV).tar.xz $(DEV); \
cd - >/dev/null; mv $$dir/$(DEV).tar.xz ../; cd ..; \
rm -rf $$dir; md5sum $(DEV).tar.xz | tee $(DEV).tar.xz.md5)
# Include automatically generated rules, such as:
# uncgi.o: .../some/dir/uncgi.c /usr/include/stdio.h
# but don't bother during clean!
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),distclean)
-include $(DEPS)
endif
endif
-48
View File
@@ -1,48 +0,0 @@
Finit is a reimplementation of the Eeepc "fastinit" program based on its
system calls with gaps filled with frog DNA.
Finit's purpose is to build upon the general workings of fastinit and
improve/optimize it for Xandros or alternative distributions running on
the Eeepc. Finit comes in three flavors: finit is a replica of the Asus
fastinit, finit-mod is an improved drop-in replacement for fastinit,
and finit-alt is a generic replacement for other systems. Finit-mod
and finit-alt were much improved with patches from Metalshark.
See more information at http://helllabs.org/finit/
Usage scenarios
---------------
1. Replace the original Eeepc 702 fastinit (finit-mod)
2. Allow fast booting in alternative Linux distributions running on the
Eeepc and other netbooks (finit-alt)
Finit-alt is distribution-specific. Currently it generates a finit-mdv
executable for Mandriva 2008/2008.1 systems. Smurfy ported it to
eeeXubuntu, and parts of his port have been merged to finit-alt.
Finit-alt features
------------------
Finit-alt reads configuration information from /etc/finit.conf. See
the source code for available options.
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.
Debugging
---------
Add finit_debug to the kernel parameter line to enable trace messages
and to run a shell after the X server.
Symlink
+1
View File
@@ -0,0 +1 @@
README.rst
-4
View File
@@ -1,4 +0,0 @@
Experimental eeeXubuntu support merged from smurfy's port, see
http://forum.eeeuser.com/viewtopic.php?id=15685
-33
View File
@@ -1,33 +0,0 @@
Mandriva 2008
-------------
1 - Add init=/sbin/finit-mdv to the kernel boot line
2 - Provide a suitable /usr/sbin/services.sh, this is the one I use in my
test system running e17:
mkdir /var/lock/subsys
chmod 1777 /var/lock/subsys
/etc/init.d/network start
dhclient eth0
/sbin/start_udev&
/etc/init.d/portmap start
/etc/init.d/nfs-common start
If you use KDE or need more services, see examples in contrib/
3 - Create /etc/finit.conf with user, module, filesystems to check and
hostname parameters. See the included finit.conf for more information.
If no user is specified, user "user" will be used as default.
4 - Set the pointer device in the X server configuration to /dev/input/mice
Mandriva 2008.1
---------------
If you have a stuck nash-hotplug consuming 100% of CPU after the boot, you
may want to patch mkinitrd using patches/mkinitrd-6.0.34-nash-hotplug.patch
+195
View File
@@ -0,0 +1,195 @@
==============================================================================
Finit — A fast plugin-based /sbin/init with service monitoring
==============================================================================
Welcome to finit!
This is an extremely fast /sbin/init replacement with focus on small
embedded GNU/Linux systems. Based on Claudio Matsuoka's original finit_
with I/O, service and hook plugin extensions. The original was a
reimplementation of the EeePC fastinit_ daemon based on its system
calls with gaps filled with frog DNA.
In the world of finit we write C plugins that hook into and extend the
``main()`` loop of ``/sbin/init``, this is what gives the system its raw
performance and the user full control.
If you chose finit you know what you want, a fast no hassles boot!
Features
--------
**Service monitoring**
Restarting a service when it exits
**Plugins**
Extend and modify finit behavior. See examples in plugins/ directory.
Plugin capabilities:
* Service callbacks — should the service run/restart/stop?
* Hooks — hook into the boot at predefined points to extend finit
* I/O — listen to external events and control finit behaviour/services
Most extensions and functionality not purely related to what an /sbin/init
needs to start a system from the original finit_ has been reactored into
plugins with either hooks or I/O demands.
/etc/finit.conf
---------------
Contrary to most script based init alternatives (`SysV init`_, upstart_,
OpenRC_ and the likes) finit instead reads its configuration from
``/etc/finit.conf``, see the source code for available options.
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, akin
to how the ``/ec/rc.local`` file works in most other inits, only finit
runs a directory of scripts. This replaces the earlier
``/usr/sbin/services.sh`` support of the original finit_.
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 enviornmant.
**CPPFLAGS=**
Standard CPPFLAGS are inherited from the build enviornmant.
**LDFLAGS=**
Standard LDFLAGS are inherited from the build enviornmant.
**LDLIBS=**
Standard LIBLIBS are inherited from the build enviornmant.
**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. Alwawys 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 enviroment 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 comile-time tweaks::
init=/sbin/finit --debug
Contact
-------
Finit is maintained collaborativly at http://github.com/troglobit/finit —
please file a bug report, clone it, or send pull requests for bug fixes and
proposed extensions, or become a co-maintainer by contacting the main author.
Regards
/Joachim Nilsson <troglobit@gmail.com>
.. _finit: http://helllabs.org/finit/
.. _fastinit: http://wiki.eeeuser.com/boot_process:the_boot_process
.. _`SysV init`: http://savannah.nongnu.org/projects/sysvinit
.. _upstart: http://upstart.ubuntu.com/
.. _openrc: http://www.gentoo.org/proj/en/base/openrc/
.. _`finit-1.3.tar.xz`: http://github.com/downloads/troglobit/finit/finit-1.3.tar.xz
+6
View File
@@ -0,0 +1,6 @@
* FHS changes, see http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run
* Look at Erik Troan's nash linuxrc parser for more inspiration
* Look at how to support a standard SysV init runlevel mechanism
* Arguments to finit, like to SysV init "init q" to reload finit.conf at runtime, etc.
* Implement initctl stop|start|reload <SVC> and service <SVC> stop|start|reload on top
* Write man pages for finit and finit.conf, steal from pimd man pages...
+25
View File
@@ -0,0 +1,25 @@
# Some junk files we always want to be removed when doing a make clean.
JUNK = *~ *.bak *.aux *.dvi *.idx *.ind *.log *.ps *.map .*.d DEADJOE semantic.cache *.gdb *.elf core core.*
MAKE := @$(MAKE)
MAKEFLAGS = --no-print-directory --silent
INSTALL := install --backup=off
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755
# Smart autodependecy generation via GCC -M.
.%.d: %.c
@$(SHELL) -ec "$(CC) -MM $(CFLAGS) $(CPPFLAGS) $< \
| sed 's,.*: ,$*.o $@ : ,g' > $@; \
[ -s $@ ] || rm -f $@"
# Override default implicit rules
%.o: %.c
@printf " CC $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
%: %.o
@printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@)))
%.so: %.o
@printf " PLUGIN $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+150
View File
@@ -0,0 +1,150 @@
/* Parser for finit.conf
*
* Copyright (c) 2012 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 <string.h>
#include "finit.h"
#include "svc.h"
#include "helpers.h"
/* Match one command. */
#define MATCH_CMD(l, c, x) \
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
static char *build_cmd(char *cmd, char *line, int len)
{
int l;
char *c;
/* Trim leading whitespace */
while (*line && (*line == ' ' || *line == '\t'))
line++;
if (!cmd) {
cmd = malloc (strlen(line) + 1);
if (!cmd) {
_e("No memory left for '%s'", line);
return NULL;
}
*cmd = 0;
}
c = cmd + strlen(cmd);
for (l = 0; *line && *line != '#' && *line != '\t' && l < len; l++)
*c++ = *line++;
*c = 0;
_d("cmd = %s", cmd);
return cmd;
}
void parse_finit_conf(char *file)
{
FILE *fp;
char line[LINE_SIZE];
char cmd[CMD_SIZE];
/* Default username and hostname */
username = strdup(DEFUSER);
hostname = strdup(DEFHOST);
if ((fp = fopen(file, "r")) != NULL) {
char *x;
_d("Parse %s ...", file);
while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp))
continue;
chomp(line);
_d("conf: %s", line);
/* Skip comments. */
if (MATCH_CMD(line, "#", x)) {
continue;
}
/* Do this before mounting / read-write */
if (MATCH_CMD(line, "check ", x)) {
strcpy(cmd, "/sbin/fsck -C -a ");
build_cmd(cmd, x, CMD_SIZE);
run_interactive(cmd, "Checking file system integrity on %s", x);
continue;
}
if (MATCH_CMD(line, "user ", x)) {
if (username) free(username);
username = build_cmd(NULL, x, USERNAME_SIZE);
continue;
}
if (MATCH_CMD(line, "host ", x)) {
if (hostname) free(hostname);
hostname = build_cmd(NULL, x, HOSTNAME_SIZE);
continue;
}
if (MATCH_CMD(line, "shutdown ", x)) {
if (sdown) free(sdown);
sdown = build_cmd(NULL, x, CMD_SIZE);
continue;
}
if (MATCH_CMD(line, "module ", x)) {
strcpy(cmd, "/sbin/modprobe ");
build_cmd(cmd, x, CMD_SIZE);
run_interactive(cmd, " Loading module %s", x);
continue;
}
if (MATCH_CMD(line, "mknod ", x)) {
strcpy(cmd, "/bin/mknod ");
build_cmd(cmd, x, CMD_SIZE);
run_interactive(cmd, " Creating device node %s", x);
continue;
}
if (MATCH_CMD(line, "network ", x)) {
if (network) free(network);
network = build_cmd(NULL, x, CMD_SIZE);
continue;
}
if (MATCH_CMD(line, "runparts ", x)) {
if (rcsd) free(rcsd);
hostname = build_cmd(NULL, x, CMD_SIZE);
continue;
}
if (MATCH_CMD(line, "startx ", x)) {
svc_register(x, username);
continue;
}
if (MATCH_CMD(line, "service ", x)) {
svc_register(x, NULL);
continue;
}
}
fclose(fp);
}
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-88
View File
@@ -1,88 +0,0 @@
CC = gcc
CFLAGS = -Os -Wall -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
LD = gcc
LDFLAGS =
LIBS =
VERSION = 0.6-pre
PKG = finit-$(VERSION)
DFILES = Makefile README finit.c finit-mod.c finit-alt.c helpers.c helpers.h \
patches/*patch contrib/services.sh*
BINS = finit-mod finit-alt finit-mdv #finit-exb
#### Configurable parameters
# Default user for finit-alt if not "user"
#DEFUSER = fred
# Use -march=pentium-m to build for Eeepc
#CFLAGS += -march=pentium-m
# Omit the first /sbin/hwclock if CONFIG_RTC_HCTOSYS is enabled in the kernel
CFLAGS += -DNO_HCTOSYS
# Append -directisa to /sbin/hwclock lines if the user has disabled
# CONFIG_GENRTC and enabled CONFIG_RTC but not enabled CONFIG_HPET_TIMER
# and CONFIG_HPET_RTC_IRQ.
#CFLAGS += -DDIRECTISA
# Use built-in run-parts instead of /bin/run-parts
CFLAGS += -DBUILTIN_RUNPARTS
#### End of configurable parameters
ifneq ($(DEFUSER),)
CFLAGS += -DDEFUSER=\"$(DEFUSER)\"
endif
.c.o:
$(CC) -c $(CFLAGS) -o $*.o $<
all: $(BINS)
finit: finit.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
finit-mod: finit-mod.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
finit-alt: finit-alt.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
finit-mdv: finit-mdv.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
finit-mdv.o: finit-alt.c Makefile
$(CC) -c $(CFLAGS) -DDIST_MDV -o $@ finit-alt.c
finit-exb: finit-exb.o helpers.o
$(LD) $(LDFLAGS) -o $@ $+ $(LIBS)
strip $@
finit-exb.o: finit-alt.c Makefile
$(CC) -c $(CFLAGS) -DDIST_EEEXUBUNTU -o $@ finit-alt.c
clean:
rm -f *.o core *~ $(BINS)
finit.o: finit.c Makefile
finit-mod.o: finit-mod.c Makefile
helpers.o: helpers.c Makefile
dist:
rm -Rf $(PKG)
mkdir -p $(PKG)/contrib $(PKG)/patches
for i in $(DFILES); do cp $$i $(PKG)/`dirname $$i`; done
tar cf - $(PKG) | gzip -c > $(PKG).tar.gz
rm -Rf $(PKG)
ls -l $(PKG).tar.gz
-20
View File
@@ -1,20 +0,0 @@
#!/bin/sh
#
# Example /usr/sbin/services.sh that can be used in a Mandriva 2008 system
# running KDE
mkdir /var/lock/subsys
chmod 1777 /var/lock/subsys
/sbin/start_udev
mkdir /var/run/dbus
/etc/init.d/messagebus start
/etc/init.d/haldaemon start
/etc/init.d/alsa start
/etc/init.d/sound start
/etc/init.d/crond start
/etc/init.d/network-up start
/etc/init.d/network start
#/etc/init.d/sshd start
/etc/init.d/syslog start
/sbin/pam_console_apply
#kbuildsycoca --noincremental
-28
View File
@@ -1,28 +0,0 @@
#!/bin/sh
# Else oocalc will crash
mknod /dev/random c 1 8
mkdir /var/lock/subsys
chmod 1777 /var/lock/subsys
mkdir /var/run/dbus
mkdir /var/run/netreport
/etc/init.d/messagebus start
/sbin/start_udev.simple
for module in `/bin/grep -hv '#' /etc/modprobe.preload /etc/modprobe.preload.d/*`; do
/sbin/modprobe $module
done
# Need to find a way to apply udev config
chmod 0666 /dev/ptmx
/etc/init.d/network-up start
/etc/init.d/network start
/etc/init.d/acpid start
/etc/init.d/haldaemon start
sleep 10
for i in /etc/rc5.d/S*; do
$i status > /dev/null 2>&1 || $i start
done
-44
View File
@@ -1,44 +0,0 @@
#!/bin/sh
# precaution, if the hotplug tag exists in mkinitrd
killall nash-hotplug &
#give some priority to the user processes
renice -5 -u gdium
# Else oocalc will crash
mknod /dev/random c 1 8 &
mkdir /var/lock/subsys &
chmod 1777 /var/lock/subsys
mkdir /var/run/dbus &
mkdir /var/run/netreport &
mkdir /var/run/ConsoleKit &
mkdir /var/log &
/etc/init.d/haldaemon start &
sleep 25
# postpone udev and others startup, they are too slow
# limit the amount of udev childs, should speed up a little
export UDEV_MAX_CHILDS=2
export UDEV_MAX_CHILDS_RUNNING=2
/sbin/start_udev
/sbin/swapon -a &
chmod 0666 /dev/null
chmod 0666 /dev/ptmx
/etc/init.d/alsa start
/etc/init.d/network start
for module in `/bin/grep -hv '#' /etc/modprobe.preload /etc/modprobe.preload.d/*`; do
/sbin/modprobe $module
done
# restore (hopefully) the priority to the user processes
renice 0 -u gdium
# fuse status always say 0
/sbin/service fuse start
for i in /etc/rc5.d/S*; do
$i status > /dev/null 2>&1 || $i start
done
# enable this to stop bootchart when testing
(sleep 30; /sbin/bootchartd stop)&
-621
View File
@@ -1,621 +0,0 @@
/*
Improved fast init
Copyright (c) 2008 Claudio Matsuoka
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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/reboot.h>
#include <sys/wait.h>
#include <linux/fs.h>
#include <utmp.h>
#include "helpers.h"
#ifndef DEFUSER
#define DEFUSER "user"
#endif
/* Distribution configuration */
#if defined DIST_MDV /* Mandriva */
#define RANDOMSEED "/var/lib/random-seed"
#define SYSROOT "/sysroot"
#define GETTY "/usr/bin/openvt /sbin/mingetty tty2"
#define RUNPARTS "/usr/bin/run-parts"
#define REMOUNT_ROOTFS_RW
#define MAKE_DEVICES
#define MAKE_DEV_VCS
#define PAM_CONSOLE
#define LISTEN_INITCTL
#define RUNLEVEL 5
#define USE_MESSAGE_BUS
#elif defined DIST_EEEXUBUNTU /* eeeXubuntu */
#define RANDOMSEED "/var/lib/urandom/random-seed"
#define SYSROOT "/sysroot"
#define GETTY "/usr/bin/openvt /sbin/getty 38400 tty2"
#define RUNPARTS "/bin/run-parts"
#define REMOUNT_ROOTFS_RW
#define MAKE_DEVICES
#define TOUCH_ETC_NETWORK_RUN_IFSTATE
#define LISTEN_INITCTL
#else /* original Eeepc distribution */
#define RANDOMSEED "/var/lib/urandom/random-seed"
#define SYSROOT "/mnt"
#define GETTY "/usr/bin/openvt /sbin/getty 38400 tty2"
#define RUNPARTS "/bin/run-parts"
#define TOUCH_ETC_NETWORK_RUN_IFSTATE
#define USE_ETC_RESOLVCONF_RUN
#endif
#ifdef DIRECTISA
#define HWCLOCK_DIRECTISA " --directisa"
#else
#define HWCLOCK_DIRECTISA
#endif
/* From sysvinit */
/* Set a signal handler. */
#define SETSIG(sa, sig, fun, flags) \
do { \
sa.sa_handler = fun; \
sa.sa_flags = flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while (0)
#ifdef LISTEN_INITCTL
#define INIT_MAGIC 0x03091969
#define INIT_CMD_RUNLVL 1
struct init_request {
int magic; /* Magic number */
int cmd; /* What kind of request */
int runlevel; /* Runlevel to change to */
int sleeptime; /* Time between TERM and KILL */
char data[368];
};
#endif
#define touch(x) mknod((x), S_IFREG|0644, 0)
#define chardev(x,m,maj,min) mknod((x), S_IFCHR|(m), makedev((maj),(min)))
#define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min)))
#define MATCH_CMD(l,c,x) \
((!strncmp((l), (c), strlen((c)))) && ((x) = (l) + strlen((c))))
#define _d(x...) do { if (debug) { printf(x); printf("\n"); } } while (0)
#define LINE_SIZE 1024
#define CMD_SIZE 256
#define USERNAME_SIZE 16
#define HOSTNAME_SIZE 32
void shutdown(int);
void signal_handler(int);
void chld_handler(int);
static int debug = 0;
static char sdown[CMD_SIZE];
static void build_cmd(char *cmd, char *x, int len)
{
int l;
char *c;
c = cmd + strlen(cmd);
/* skip spaces */
for (; *x && (*x == ' ' || *x == '\t'); x++);
/* copy next arg */
for (l = 0; *x && *x != '#' && *x != '\t' && l < len; l++)
*c++ = *x++;
*c = 0;
_d("cmd = %s", cmd);
}
#ifdef LISTEN_INITCTL
/* Standard reboot/shutdown utilities talk to init using /dev/initctl.
* We should check if the fifo was recreated and reopen it.
*/
static void listen_initctl()
{
if (!fork()) {
int ctl;
fd_set fds;
struct init_request request;
mkfifo("/dev/initctl", 0600);
ctl = open("/dev/initctl", O_RDONLY);
while (1) {
FD_ZERO(&fds);
FD_SET(ctl, &fds);
if (select(ctl + 1, &fds, NULL, NULL, NULL) <= 0)
continue;
read(ctl, &request, sizeof(request));
if (request.magic != INIT_MAGIC)
continue;
if (request.cmd == INIT_CMD_RUNLVL) {
switch (request.runlevel) {
case '0':
shutdown(SIGUSR2);
break;
case '6':
shutdown(SIGUSR1);
}
}
}
}
}
#endif
int main()
{
int i;
FILE *f;
char line[LINE_SIZE];
struct sigaction sa, act;
sigset_t nmask, nmask2;
char username[USERNAME_SIZE] = DEFUSER;
char hostname[HOSTNAME_SIZE] = "eviltwin";
char cmd[CMD_SIZE];
char startx[CMD_SIZE] = "xinit";
#ifdef USE_ETC_RESOLVCONF_RUN
DIR *dir;
struct dirent *d;
#endif
#ifdef RUNLEVEL
struct utmp entry;
#endif
#ifdef PAM_CONSOLE
int fd;
#endif
chdir("/");
umask(022);
/*
* Signal management
*/
for (i = 1; i < NSIG; i++)
SETSIG(sa, i, SIG_IGN, SA_RESTART);
SETSIG(sa, SIGINT, shutdown, 0);
SETSIG(sa, SIGPWR, SIG_IGN, 0);
SETSIG(sa, SIGUSR1, shutdown, 0);
SETSIG(sa, SIGUSR2, shutdown, 0);
SETSIG(sa, SIGTERM, SIG_IGN, 0);
SETSIG(sa, SIGALRM, SIG_IGN, 0);
SETSIG(sa, SIGHUP, SIG_IGN, 0);
SETSIG(sa, SIGCONT, SIG_IGN, SA_RESTART);
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
/* Block sigchild while forking */
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, NULL);
reboot(RB_DISABLE_CAD);
mount("none", "/proc", "proc", 0, NULL);
mount("none", "/sys", "sysfs", 0, NULL);
/*
* Parse kernel parameters
*/
if ((f = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, LINE_SIZE, f);
if ((strstr(line, "finit_debug"))) {
debug = 1;
}
if (strstr(line, "quiet")) {
close(0);
close(1);
close(2);
}
fclose(f);
}
_d("finit-alt " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
setsid();
/*
* Parse configuration file
*/
if ((f = fopen("/etc/finit.conf", "r")) != NULL) {
char *x;
while (!feof(f)) {
fgets(line, LINE_SIZE, f);
chomp(line);
_d("conf: %s", line);
/* Do this before mounting / read-write */
if (MATCH_CMD(line, "check ", x)) {
strcpy(cmd, "/sbin/fsck -C -a ");
build_cmd(cmd, x, CMD_SIZE);
system(cmd);
continue;
}
if (MATCH_CMD(line, "user ", x)) {
*username = 0;
build_cmd(username, x, USERNAME_SIZE);
continue;
}
if (MATCH_CMD(line, "startx ", x)) {
*startx = 0;
build_cmd(startx, x, CMD_SIZE);
continue;
}
if (MATCH_CMD(line, "host ", x)) {
*hostname = 0;
build_cmd(hostname, x, HOSTNAME_SIZE);
continue;
}
if (MATCH_CMD(line, "shutdown ", x)) {
*sdown = 0;
build_cmd(sdown, x, CMD_SIZE);
continue;
}
if (MATCH_CMD(line, "module ", x)) {
strcpy(cmd, "/sbin/modprobe ");
build_cmd(cmd, x, CMD_SIZE);
system(cmd);
continue;
}
#ifdef MAKE_DEVICES
/* This is used if the system uses udev and doesn't
* mount /dev as tmpfs in the initrd, or if it doesn't
* use an initrd. Mount /dev as tmpfs and create basic
* device nodes.
*/
if (MATCH_CMD(line, "mountdev", x)) {
mount("none", "/dev", "tmpfs", 0, "mode=0755");
chardev("/dev/null", 0666, 1, 3);
blkdev("/dev/sda1", 0660, 8, 1);
blkdev("/dev/sda2", 0660, 8, 2);
blkdev("/dev/sda3", 0660, 8, 3);
blkdev("/dev/sda4", 0660, 8, 4);
blkdev("/dev/sda5", 0660, 8, 5);
blkdev("/dev/sda6", 0660, 8, 6);
#ifdef MAKE_DEV_VCS
/* Caio reports that newer X.org needs these */
blkdev("/dev/vcs1", 0660, 7, 1);
blkdev("/dev/vcs2", 0660, 7, 2);
blkdev("/dev/vcs3", 0660, 7, 3);
blkdev("/dev/vcs4", 0660, 7, 4);
blkdev("/dev/vcsa1", 0660, 7, 129);
blkdev("/dev/vcsa2", 0660, 7, 130);
blkdev("/dev/vcsa3", 0660, 7, 131);
blkdev("/dev/vcsa4", 0660, 7, 132);
#endif
continue;
}
/* This works only if /dev is tmpfs! If not, create
* devices statically on the filesystem
*/
if (MATCH_CMD(line, "mknod ", x)) {
strcpy(cmd, "/bin/mknod ");
build_cmd(cmd, x, CMD_SIZE);
system(cmd);
continue;
}
#endif
}
fclose(f);
}
/*
* Mount filesystems
*/
_d("mount filesystems");
#ifdef REMOUNT_ROOTFS_RW
system("/bin/mount -n -o remount,rw /");
#endif
umask(0);
#ifdef MAKE_DEVICES
mkdir("/dev/shm", 0755);
mkdir("/dev/pts", 0755);
#endif
mount("none", "/dev/pts", "devpts", 0, "gid=5,mode=620");
mount("none", "/dev/shm", "tmpfs", 0, NULL);
mount("none", "/tmp", "tmpfs", 0, "mode=1777,size=128m");
mount("none", "/var/run", "tmpfs", 0, "mode=0755");
mount("none", "/var/lock", "tmpfs", 0, "mode=1777");
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
_d("make devices");
#ifdef MAKE_DEVICES
mkdir("/dev/input", 0755);
chardev("/dev/urandom", 0666, 1, 9);
chardev("/dev/ptmx", 0666, 5, 2);
chardev("/dev/null", 0666, 1, 3);
chmod("/dev/null", 0666);
chardev("/dev/mem", 0640, 1, 1);
chardev("/dev/tty0", 0660, 4, 0);
chardev("/dev/tty1", 0660, 4, 1);
chardev("/dev/tty2", 0660, 4, 2);
chardev("/dev/tty3", 0660, 4, 3);
chardev("/dev/input/mice", 0660, 13, 63);
chardev("/dev/input/event0", 0660, 13, 64);
chardev("/dev/agpgart", 0660, 10, 175);
blkdev("/dev/loop0", 0600, 7, 0);
#endif
unlink("/etc/mtab");
system("mount -a");
umask(0022);
/*
* Time adjustments
*/
_d("adjust clock");
#ifndef NO_HCTOSYS
system("/sbin/hwclock --hctosys" HWCLOCK_DIRECTISA);
#endif
/*
* Network stuff
*/
#ifdef USE_ETC_RESOLVCONF_RUN
makepath("/dev/shm/network");
makepath("/dev/shm/resolvconf/interface");
if ((dir = opendir("/etc/resolvconf/run/interface")) != NULL) {
while ((d = readdir(dir)) != NULL) {
if (isalnum(d->d_name[0]))
continue;
snprintf(line, LINE_SIZE,
"/etc/resolvconf/run/interface/%s", d->d_name);
unlink(line);
}
closedir(dir);
}
#endif
#ifdef USE_VAR_RUN_RESOLVCONF
makepath("/var/run/resolvconf/interface");
symlink("../../../etc/resolv.conf", "/var/run/resolvconf/resolv.conf");
#endif
touch("/var/run/utmp");
chown("/var/run/utmp", 0, getgroup("utmp"));
chmod("/var/run/utmp", 0664);
#ifdef RUNLEVEL
memset(&entry, 0, sizeof(struct utmp));
entry.ut_type = RUN_LVL;
entry.ut_pid = '0' + RUNLEVEL;
setutent();
pututline(&entry);
endutent();
#endif
#ifdef USE_ETC_RESOLVCONF_RUN
touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface");
#ifdef BUILTIN_RUNPARTS
run_parts("/etc/resolvconf/update.d", "-i", NULL);
#else
system(RUNPARTS " --arg=i /etc/resolvconf/update.d");
#endif
chdir("/");
#endif
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
touch("/etc/network/run/ifstate");
#endif
if ((f = fopen("/etc/hostname", "r")) != NULL) {
fgets(hostname, HOSTNAME_SIZE, f);
chomp(hostname);
fclose(f);
}
sethostname(hostname, strlen(hostname));
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
/*
* Set random seed
*/
copyfile(RANDOMSEED, "/dev/urandom", 0);
unlink(RANDOMSEED);
umask(077);
copyfile("/dev/urandom", RANDOMSEED, 4096);
umask(0);
/*
* Console setup (for X)
*/
makepath("/var/run/console");
snprintf(line, LINE_SIZE, "/var/run/console/%s", username);
touch(line);
#ifdef PAM_CONSOLE
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username));
close(fd);
system("/sbin/pam_console_apply");
}
#endif
/*
* Misc setup
*/
mkdir("/tmp/.X11-unix", 01777);
mkdir("/tmp/.ICE-unix", 01777);
umask(022);
system(GETTY "&");
_d("forking");
if (!fork()) {
/* child process */
vhangup();
close(2);
close(1);
close(0);
if (open("/dev/tty1", O_RDWR) != 0)
exit(1);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
sigemptyset(&nmask2);
sigaddset(&nmask2, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask2, NULL);
for (i = 1; i < NSIG; i++)
sigaction(i, &sa, NULL);
dup2(0, 0);
dup2(0, 1);
dup2(0, 2);
#ifdef USE_MESSAGE_BUS
_d("dbus");
mkdir("/var/run/dbus", 0755);
mkdir("/var/lock/subsys/messagebus", 0755);
system("dbus-uuidgen --ensure;dbus-daemon --system");
#endif
#ifdef LISTEN_INITCTL
listen_initctl();
#endif
/* Prevents bash from running loadkeys */
unsetenv("TERM");
/* ConsoleKit needs this */
setenv("DISPLAY", ":0", 1);
while (access("/tmp/shutdown", F_OK) < 0) {
_d("start X as %s\n", username);
if (debug) {
snprintf(line, LINE_SIZE,
"su -l %s -c \"%s\"", username, startx);
system(line);
system("/bin/sh");
} else {
snprintf(line, LINE_SIZE,
"su -l %s -c \"%s\" &> /dev/null",
username, startx);
system(line);
}
}
exit(0);
}
/* parent process */
/* sleep(1); */
system("/usr/sbin/services.sh &>/dev/null&");
while (1) {
sigemptyset(&nmask);
pselect(0, NULL, NULL, NULL, NULL, &nmask);
}
}
/*
* Shut down on INT USR1 USR2
*/
void shutdown(int sig)
{
touch("/tmp/shutdown");
if (sdown) {
system(sdown);
}
kill(-1, SIGTERM);
write(1, "\033[?25l\033[30;40m", 14);
copyfile("/boot/shutdown.fb", "/dev/fb/0", 0);
sleep(2);
system("/usr/sbin/alsactl store > /dev/null 2>&1");
system("/sbin/hwclock --systohc" HWCLOCK_DIRECTISA);
kill(-1, SIGKILL);
sync();
sync();
system("/bin/umount -a;/bin/mount -n -o remount,ro /");
//system("/sbin/unionctl.static / --remove / > /dev/null 2>&1");
if (sig == SIGINT || sig == SIGUSR1)
reboot(RB_AUTOBOOT);
reboot(RB_POWER_OFF);
}
/*
* SIGCHLD: one of our children has died
*/
void chld_handler(int sig)
{
int status;
while (waitpid(-1, &status, WNOHANG) != 0) {
if (errno == ECHILD)
break;
}
}
-322
View File
@@ -1,322 +0,0 @@
/*
Improved fast init
Copyright (c) 2008 Claudio Matsuoka
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.
*/
/*
Changelog from the original Eeepc fastinit:
- Use mknod() instead of close(open()) call to create empty file
- Fix random seed initialization
- Don't try to handle signal 0, SIGKILL or SIGSTOP
- Implement makepath() to avoid system("mkdir -p")
- Omit first hwclock if user has CONFIG_RTC_HCTOSYS in kernel (by Metalshark)
- Add –directisa to hwclock if user has disabled CONFIG_GENRTC and enabled
CONFIG_RTC but not CONFIG_HPET_TIMER and CONFIG_HPET_RTC_IRQ (by Metalshark)
- Drop system() call to clean /tmp, it's a fresh mounted tmpfs
- Set loopback interface using direct calls instead of system("ifconfig")
- Copy 4096 data block in C instead of system("cat") or system("dd")
- Draw shutdown splash screen using C calls instead of system("echo;cat")
- Change poweroff method to reboot(RB_POWER_OFF) (by Metalshark)
- Mount /var/run and /var/lock as tmpfs
- Use SIG_IGN instead of empty signal handler (by Metalshark)
- Add built-in run-parts option to be used instead of system("run-parts")
- Reuse cmdline buffer as hostname and filename buffers
- Open terminal in vt2 using openvt
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/reboot.h>
#include <sys/wait.h>
#include <linux/fs.h>
#include "helpers.h"
#ifdef DIRECTISA
#define HWCLOCK_DIRECTISA " --directisa"
#else
#define HWCLOCK_DIRECTISA
#endif
/* From sysvinit */
/* Set a signal handler. */
#define SETSIG(sa, sig, fun, flags) \
do { \
sa.sa_handler = fun; \
sa.sa_flags = flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while (0)
#define touch(x) mknod((x), S_IFREG|0644, 0)
#define LINE_SIZE 1024
void shutdown(int);
void chld_handler(int);
int main()
{
int i;
FILE *f;
char line[LINE_SIZE];
int fd;
DIR *dir;
struct dirent *d;
struct sigaction sa, act;
char *x;
sigset_t nmask, nmask2;
puts("finit-mod " VERSION " (built " __DATE__ " " __TIME__
" by " WHOAMI ")");
chdir("/");
umask(022);
/*
* Signal management
*/
for (i = 1; i < NSIG; i++)
SETSIG(sa, i, SIG_IGN, SA_RESTART);
SETSIG(sa, SIGINT, shutdown, 0);
SETSIG(sa, SIGPWR, SIG_IGN, 0);
SETSIG(sa, SIGUSR1, shutdown, 0);
SETSIG(sa, SIGUSR2, shutdown, 0);
SETSIG(sa, SIGTERM, SIG_IGN, 0);
SETSIG(sa, SIGALRM, SIG_IGN, 0);
SETSIG(sa, SIGHUP, SIG_IGN, 0);
SETSIG(sa, SIGCONT, SIG_IGN, SA_RESTART);
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
/* Block sigchild while forking */
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, NULL);
reboot(RB_DISABLE_CAD);
/*
* Parse kernel parameters
*/
if ((f = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, LINE_SIZE, f);
if (strstr(line, "quiet")) {
close(0);
close(1);
close(2);
}
fclose(f);
}
setsid();
/*
* Mount filesystems
*/
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
mount("tmpfs", "/dev/shm", "tmpfs", 0, NULL);
mount("tmpfs", "/tmp", "tmpfs", 0, "mode=1777,size=128m");
mount("tmpfs", "/var/run", "tmpfs", 0, "mode=0755");
mount("tmpfs", "/var/lock", "tmpfs", 0, "mode=1777");
mount("/mnt", "/", NULL, MS_MOVE, NULL);
/*
* Time adjustments
*/
if ((fd = open("/etc/adjtime", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, "0.0 0 0.0\n", 10);
close(fd);
}
#ifndef NO_HCTOSYS
system("/sbin/hwclock --hctosys --localtime" HWCLOCK_DIRECTISA);
#endif
/*
* Network stuff
*/
makepath("/dev/shm/network");
makepath("/dev/shm/resolvconf/interface");
if ((dir = opendir("/etc/resolvconf/run/interface")) != NULL) {
while ((d = readdir(dir)) != NULL) {
if (isalnum(d->d_name[0]))
continue;
snprintf(line, LINE_SIZE,
"/etc/resolvconf/run/interface/%s", d->d_name);
unlink(line);
}
closedir(dir);
}
touch("/var/run/utmp");
touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface");
#ifdef BUILTIN_RUNPARTS
run_parts("/etc/resolvconf/update.d", "-i", NULL);
#else
system("/bin/run-parts --arg=-i /etc/resolvconf/update.d");
#endif
chdir("/");
touch("/etc/network/run/ifstate");
if ((f = fopen("/etc/hostname", "r")) != NULL) {
fgets(line, LINE_SIZE, f);
if ((x = strchr(line, 0x0a)) != NULL)
*x = 0;
sethostname(line, strlen(line));
fclose(f);
}
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
/*
* Set random seed
*/
copyfile("/var/lib/urandom/random-seed", "/dev/urandom", 0);
unlink("/var/lib/urandom/random-seed");
umask(077);
copyfile("/dev/urandom", "/var/lib/urandom/random-seed", 4096);
/*
* Misc setup
*/
unlink("/var/run/.clean");
unlink("/var/lock/.clean");
umask(0);
mkdir("/tmp/.X11-unix", 01777);
mkdir("/tmp/.ICE-unix", 01777);
umask(022);
system("/usr/bin/openvt /sbin/getty 38400 tty2&");
if (!fork()) {
/* child process */
vhangup();
close(2);
close(1);
close(0);
if (open("/dev/tty1", O_RDWR) != 0)
exit(1);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
sigemptyset(&nmask2);
sigaddset(&nmask2, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask2, NULL);
for (i = 1; i < NSIG; i++)
sigaction(i, &sa, NULL);
dup2(0, 0);
dup2(0, 1);
dup2(0, 2);
touch("/tmp/nologin");
while (access("/tmp/shutdown", F_OK) < 0)
system("su -c startx -l user &> /dev/null");
exit(0);
}
/* parent process */
sleep(1);
system("/usr/sbin/services.sh &>/dev/null&");
while (1) {
sigemptyset(&nmask);
pselect(0, NULL, NULL, NULL, NULL, &nmask);
}
}
/*
* Shut down on INT USR1 USR2
*/
void shutdown(int sig)
{
touch("/tmp/shutdown");
kill(-1, SIGTERM);
write(1, "\033[?25l\033[30;40m", 14);
copyfile("/boot/shutdown.fb", "/dev/fb/0", 0);
sleep(2);
system("/usr/sbin/alsactl store > /dev/null 2>&1");
system("/sbin/hwclock --systohc --localtime" HWCLOCK_DIRECTISA);
kill(-1, SIGKILL);
sync();
sync();
system("/bin/mount -n -o remount,ro /");
system("/sbin/unionctl.static / --remove / > /dev/null 2>&1");
if (sig == SIGINT || sig == SIGUSR1)
reboot(RB_AUTOBOOT);
reboot(RB_POWER_OFF);
}
/*
* SIGCHLD: one of our children has died
*/
void chld_handler(int sig)
{
int status;
while (waitpid(-1, &status, WNOHANG) != 0) {
if (errno == ECHILD)
break;
}
}
+174 -243
View File
@@ -1,282 +1,213 @@
/* Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/reboot.h>
#include <sys/wait.h>
#include <linux/fs.h>
#include <sys/stat.h> /* umask(), mkdir() */
#include "finit.h"
#include "helpers.h"
#include "private.h"
#include "plugin.h"
#include "svc.h"
#include "sig.h"
#include "lite.h"
int debug = 0;
int verbose = 1;
char *sdown = NULL;
char *network = NULL;
char *username = NULL;
char *hostname = NULL;
char *rcsd = FINIT_RCSD;
/* From sysvinit */
/* Set a signal handler. */
#define SETSIG(sa, sig, fun, flags) \
do { \
sa.sa_handler = fun; \
sa.sa_flags = flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while(0)
#define touch(x) close(open((x), O_CREAT|O_WRONLY|O_TRUNC, 0644))
void shutdown(int);
void signal_handler(int);
void chld_handler(int);
int main()
static void parse_kernel_cmdline(void)
{
int i;
FILE *f;
char line[2096];
int fd;
DIR *dir;
struct dirent *d;
char filename[1024];
struct sigaction sa, act;
char hline[1024];
char *x;
sigset_t nmask, nmask2;
FILE *fp;
char line[LINE_SIZE];
chdir("/");
umask(022);
if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, sizeof(line), fp);
_d("Kernel command line: %s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
debug = 1;
if (!debug && strstr(line, "quiet"))
verbose = 0;
fclose(fp);
}
}
static int run_loop(void)
{
while (1) {
svc_monitor();
plugin_monitor();
}
return 0;
}
int main(int UNUSED(args), char *argv[])
{
/*
* Signal management
* Initial setup of signals, ignore all until we're up.
*/
for (i = 1; i < NSIG; i++)
SETSIG(sa, i, SIG_IGN, SA_RESTART);
sig_init();
SETSIG(sa, SIGINT, shutdown, 0);
SETSIG(sa, SIGPWR, signal_handler, 0);
SETSIG(sa, SIGUSR1, shutdown, 0);
SETSIG(sa, SIGUSR2, shutdown, 0);
SETSIG(sa, SIGTERM, signal_handler, 0);
SETSIG(sa, SIGKILL, signal_handler, 0);
SETSIG(sa, SIGALRM, signal_handler, 0);
SETSIG(sa, SIGHUP, signal_handler, 0);
SETSIG(sa, SIGSTOP, signal_handler, SA_RESTART);
SETSIG(sa, SIGCONT, signal_handler, SA_RESTART);
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
/* Block sigchild while forking */
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, NULL);
reboot(RB_DISABLE_CAD);
/*
* Mount base file system, kernel is assumed to run devtmpfs for /dev
*/
chdir("/");
umask(0);
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);
mount("none", "/dev/pts", "devpts", 0, "gid=5,mode=620");
mount("none", "/dev/shm", "tmpfs", 0, NULL);
umask(022);
/*
* Parse kernel parameters
*/
if ((f = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, 2095, f);
if (strstr(line, "quiet")) {
close(0);
close(1);
close(2);
}
fclose(f);
}
parse_kernel_cmdline();
setsid();
cls();
echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
/*
* Populate /dev and prepare for runtime events from kernel.
*/
#if defined(USE_UDEV)
run_interactive("udevd --daemon", "Populating device tree");
#elif defined (MDEV)
run_interactive(MDEV " -s", "Populating device tree");
#endif
/*
* 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");
print_result(plugin_load_all(PLUGIN_PATH));
/*
* Mount filesystems
*/
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
mount("tmpfs", "/dev/shm", "tmpfs", 0, NULL);
mount("tmpfs", "/tmp", "tmpfs", 0, "mode=1777,size=128m");
mount("/mnt", "/", NULL, MS_MOVE, NULL);
_d("Mount filesystems in /etc/fstab ...");
#ifdef REMOUNT_ROOTFS_RW
run("/bin/mount -n -o remount,rw /");
#endif
#ifdef SYSROOT
run(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
umask(0);
run("/bin/mount -na");
run("/sbin/swapon -ea");
umask(0022);
/* Cleanup stale files, if any still linger on. */
run_interactive("rm -rf /tmp/* /var/run/* /var/lock/*", "Cleanup temporary directories");
/*
* Time adjustments
* Base FS up, enable standard SysV init signals
*/
if ((fd = open("/etc/adjtime", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, "0.0 0 0.0\n", 10);
close(fd);
}
system("/sbin/hwclock --hctosys --localtime");
sig_setup();
_d("Base FS up, calling hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP);
/*
* Network stuff
*/
system("mkdir -p /dev/shm/network");
system("mkdir -p /dev/shm/resolvconf/interface");
if ((dir = opendir("/etc/resolvconf/run/interface")) != NULL) {
while ((d = readdir(dir)) != NULL) {
if (isalnum(d->d_name[0]))
continue;
sprintf(filename, "/etc/resolvconf/run/interface/%s",
d->d_name);
unlink(filename);
}
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
run("/sbin/sysctl -e -p /etc/sysctl.conf >/dev/null");
closedir(dir);
}
/* Set initial hostname. */
set_hostname(hostname);
touch("/var/run/utmp");
touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface");
system("/bin/run-parts --arg=-i /etc/resolvconf/update.d");
chdir("/");
touch("/etc/network/run/ifstate");
if ((f = fopen("/etc/hostname", "r")) != NULL) {
fgets(hline, 1023, f);
if ((x = strchr(hline, 0x0a)) != NULL)
*x = 0;
sethostname(hline, strlen(hline));
fclose(f);
}
system("/sbin/ifconfig lo 127.0.0.1 netmask 255.0.0.0 up > /dev/null");
/*
* Set random seed
*/
/* Bug: the eeepc never sets its random seed from file */
system("/bin/cat /var/lib/urandom/random-seed >/dev/urandom "
"> /dev/null 2>&1");
unlink("/var/lib/urandom/random-seed");
umask(077);
system("/bin/dd if=/dev/urandom of=/var/lib/urandom/random-seed "
"bs=4096 count=1 >/dev/null 2>&1");
/*
* Misc setup
*/
/* not really needed, /tmp is a fresh mounted tmpfs */
system("/bin/rm -rf /tmp/* /tmp/.* 2>/dev/null");
unlink("/var/run/.clean");
unlink("/var/lock/.clean");
umask(0);
mkdir("/tmp/.X11-unix", 01777);
mkdir("/tmp/.ICE-unix", 01777);
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
if (network)
run_interactive(network, "Starting networking: %s", network);
umask(022);
if (!fork()) {
/* child process */
/*
* Hooks that rely on loopback, or basic networking being up.
*/
plugin_run_hooks(HOOK_NETWORK_UP);
vhangup();
/*
* Start service monitor framework
*/
_d("Starting all static services from %s", FINIT_CONF);
svc_start_all();
close(2);
close(1);
close(0);
if (open("/dev/tty1", O_RDWR, 0))
exit(1);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
sigemptyset(&nmask2);
sigaddset(&nmask2, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask2, NULL);
/* Bug: no signal 0 */
for (i = 0; i < NSIG; i++)
sigaction(i, &sa, NULL);
dup2(0, 0);
dup2(0, 1);
dup2(0, 2);
touch("/tmp/nologin");
while (access("/tmp/shutdown", F_OK) < 0)
system("su -c startx -l user &> /dev/null");
exit(0);
/*
* Run startup scripts in /etc/finit.d/, if any.
*/
if (rcsd && fisdir(rcsd)) {
_d("Running startup scripts in %s ...", rcsd);
run_parts(rcsd, NULL);
}
/* parent process */
system("/sbin/getty 38400 tty3 &");
sleep(1);
system("/usr/sbin/services.sh &> /dev/null &");
/*
* Hooks that should run at the very end
*/
plugin_run_hooks(HOOK_SYSTEM_UP);
while (1) {
sigemptyset(&nmask);
pselect(0, NULL, NULL, NULL, NULL, &nmask);
}
/* Start GETTY on console */
_d("Starting getty on console ...");
run_getty(GETTY, argv);
/*
* Enter main loop to monior /dev/initctl and services
*/
_d("Entering main loop ...");
return run_loop();
}
/*
* Shut down on INT USR1 USR2
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
void shutdown(int sig)
{
int fd;
system("/usr/bin/touch /tmp/shutdown");
kill(-1, SIGTERM);
system("/bin/echo -e \"\033[?25l\033[30;40m\"; "
"/bin/cp /boot/shutdown.fb /dev/fb/0");
sleep(1);
sleep(1);
system("/usr/sbin/alsactl store > /dev/null 2>&1");
system("/sbin/hwclock --systohc --localtime");
kill(-1, SIGKILL);
sync();
sync();
system("/bin/mount -n -o remount,ro /");
system("/sbin/unionctl.static / --remove / > /dev/null 2>&1");
if (sig == SIGINT || sig == SIGUSR1)
reboot(RB_AUTOBOOT);
if ((fd = open("/proc/acpi/sleep", O_WRONLY)) >= 0) {
write(fd, "5", 1);
close(fd);
}
}
/*
* SIGCHLD: one of our children has died
*/
void chld_handler(int sig)
{
int status;
while (waitpid(-1, &status, WNOHANG) != 0) {
if (errno == ECHILD)
break;
}
}
/*
* We got a signal (PWR TERM ALRM HUP STOP CONT)
*/
void signal_handler(int sig)
{
/* do nothing */
}
+12 -6
View File
@@ -1,6 +1,12 @@
user fred
module intel-agp
module p4-clockmod
check /dev/sda3
host eviltwin
startx /usr/bin/startx
# Simple Debian finit.conf
check /dev/vda1
module evdev
module loop
module psmouse
user admin
host testbed
network service networking start
service /sbin/klogd -n -- Kernel logging server
service /sbin/syslogd -n -- Syslog server
service /usr/sbin/sshd -D -- OpenSSH server
service /usr/sbin/gdm -- GNOME Display Manager
+82
View File
@@ -0,0 +1,82 @@
/* Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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_H_
#define FINIT_H_
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* Distribution configuration */
#if defined EMBEDDED_SYSTEM
# define CONSOLE "/dev/console"
# define MDEV "/sbin/mdev"
# define GETTY "/sbin/getty -L 115200 ttyS0 vt100"
#else /* Debian/Ubuntu based distributions */
# define RANDOMSEED "/var/lib/urandom/random-seed"
# define CONSOLE "/dev/tty1"
# define GETTY "/sbin/getty -8 38400 tty1"
# define REMOUNT_ROOTFS_RW
# define RUNLEVEL 2
# define USE_UDEV
# define USE_MESSAGE_BUS
#endif
#ifndef DEFUSER
# define DEFUSER "root"
#endif
#ifndef DEFHOST
# define DEFHOST "noname"
#endif
#define CMD_SIZE 256
#define LINE_SIZE 1024
#define BUF_SIZE 4096
#define USERNAME_SIZE 16
#define HOSTNAME_SIZE 32
extern char *sdown;
extern char *network;
extern char *hostname;
extern char *username;
extern char *rcsd;
/* conf.c */
void parse_finit_conf(char *file);
#endif /* FINIT_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+579 -76
View File
@@ -1,4 +1,28 @@
/* Misc. utility functions and C-library extensions for finit and its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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 <ctype.h> /* isdigit() */
#include <string.h>
#include <limits.h>
#include <stdlib.h>
@@ -7,42 +31,56 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef BUILTIN_RUNPARTS
#include <dirent.h>
#include <stdarg.h>
#include <sys/wait.h>
#endif
#include <grp.h>
#include <pwd.h>
#include "finit.h"
#include "helpers.h"
#include "private.h"
#include "sig.h"
#include "lite.h"
#define NUM_ARGS 16
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */
/*
* Helpers to replace system() calls
*/
int makepath(char *p)
int makepath(char *path)
{
char *x, path[PATH_MAX];
char buf[PATH_MAX];
char *x = buf;
int ret;
x = path;
if (!path) {
errno = EINVAL;
return -1;
}
do {
do { *x++ = *p++; } while (*p && *p != '/');
do {
*x++ = *path++;
} while (*path && *path != '/');
*x = 0;
ret = mkdir(path, 0777);
} while (*p && (*p != '/' || *(p + 1))); /* ignore trailing slash */
ret = mkdir(buf, 0777);
} while (*path && (*path != '/' || *(path + 1))); /* ignore trailing slash */
return ret;
}
void ifconfig(char *name, char *inet, char *mask, int up)
void ifconfig(char *ifname, char *addr, char *mask, int up)
{
struct ifreq ifr;
struct sockaddr_in *a = (struct sockaddr_in *)&ifr.ifr_addr;
@@ -52,14 +90,18 @@ void ifconfig(char *name, char *inet, char *mask, int up)
return;
memset(&ifr, 0, sizeof (ifr));
strncpy(ifr.ifr_name, name, IFNAMSIZ);
a->sin_family = AF_INET;
strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
if (up) {
inet_aton(inet, &a->sin_addr);
ioctl(sock, SIOCSIFADDR, &ifr);
inet_aton(mask, &a->sin_addr);
ioctl(sock, SIOCSIFNETMASK, &ifr);
if (addr) {
inet_aton(addr, &a->sin_addr);
ioctl(sock, SIOCSIFADDR, &ifr);
}
if (mask) {
inet_aton(mask, &a->sin_addr);
ioctl(sock, SIOCSIFNETMASK, &ifr);
}
}
ioctl(sock, SIOCGIFFLAGS, &ifr);
@@ -74,97 +116,530 @@ void ifconfig(char *name, char *inet, char *mask, int up)
close(sock);
}
#define BUF_SIZE 4096
void copyfile(char *src, char *dst, int size)
/**
* pidfile_read - Reads a PID value from a pidfile.
* @pidfile: File containing PID, usually in /var/run/<PROC>.pid
*
* This function takes a @pidfile and returns the PID found therein.
*
* Returns:
* On invalid @pidfile -1 and @errno set to %EINVAL, when @pidfile does not exist -1
* and errno set to %ENOENT. When the pidfile is empty or when its contents cannot
* be translated this function returns zero (0), on success this function returns
* a PID value greater than one. PID 1 is reserved for the system init process.
*/
pid_t pidfile_read(char *pidfile)
{
char buffer[BUF_SIZE];
int s, d, n;
pid_t pid = 0;
char buf[16];
FILE *fp;
/* Size == 0 means copy entire file */
if (size == 0)
size = INT_MAX;
if (!pidfile) {
errno = EINVAL;
return -1;
}
if ((s = open(src, O_RDONLY)) >= 0) {
if ((d = open(dst, O_WRONLY | O_CREAT, 0644)) >= 0) {
do {
int csize = size > BUF_SIZE ? BUF_SIZE : size;
if ((n = read(s, buffer, csize)) > 0)
write(d, buffer, n);
size -= csize;
} while (size > 0 && n == BUF_SIZE);
close(d);
}
close(s);
if (!fexist(pidfile))
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);
return pid;
}
/**
* pidfile_poll - Poll for the existence of a pidfile and return PID
* @cmd: Process name, or command, called to expect a pidfile
* @path: Path to pidfile to poll for
*
* This function polls for the pidfile at @path for at most 5 seconds
* before timing out. If the file is created within that time span the
* file is read and its PID contents returned.
*
* Returns:
* The PID read from @path, or zero on timeout.
*/
pid_t pidfile_poll(char *cmd, char *path)
{
pid_t pid = 0;
int tries = 0;
/* Timeout = 100 * 50ms = 5s */
while (!fexist(path) && tries++ < 100) {
/* Wait 50ms between retries */
usleep(50000);
}
if (!fexist(path)) {
_e("Timeout! No PID found for %s, pidfile %s does not exist?", cmd, path);
pid = 0;
} else {
pid = pidfile_read(path);
}
return pid;
}
#ifdef BUILTIN_RUNPARTS
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */
#define NUM_ARGS 16
static int cmp(const void *s1, const void *s2)
/**
* pid_alive - Check if a given process ID is running
* @pid: Process ID to check for.
*
* Returns:
* %TRUE(1) if pid is alive (/proc/pid exists), otherwise %FALSE(0)
*/
int pid_alive(pid_t pid)
{
return strcmp(*(char **)s1, *(char **)s2);
char name[24]; /* Enough for max pid_t */
snprintf(name, sizeof(name), "/proc/%d", pid);
return fexist(name);
}
int run_parts(char *dir, ...)
/**
* pid_get_name - Find name of a process
* @pid: PID of process to find.
* @name: Pointer to buffer where to return process name, may be %NULL
* @len: Length in bytes of @name buffer.
*
* If @name is %NULL, or @len is zero the function will return
* a static string. This may be useful to one-off calls.
*
* Returns:
* %NULL on error, otherwise a va
*/
char *pid_get_name(pid_t pid, char *name, size_t len)
{
DIR *d;
struct dirent *e;
struct stat st;
char *ent[NUM_SCRIPTS];
int i, num = 0, argnum = 1;
char *args[NUM_ARGS];
va_list ap;
int ret = 1;
FILE *fp;
char path[32];
static char line[64];
if (chdir(dir))
return -1;
if (!name || len == 0)
name = line;
if ((d = opendir(dir)) == NULL)
return -1;
snprintf(path, sizeof(path), "/proc/%d/status", pid);
if ((fp = fopen(path, "r")) != NULL) {
if (fgets(line, sizeof (line), fp)) {
char *pname = line + 6; /* Skip first part of line --> "Name:\t" */
va_start(ap, dir);
while (argnum < NUM_ARGS && (args[argnum++] = va_arg(ap, char *)));
va_end(ap);
chomp(pname);
strlcpy(name, pname, len);
ret = 0; /* Found it! */
}
while ((e = readdir(d))) {
if (e->d_type == DT_REG && stat(e->d_name, &st) == 0) {
if (st.st_mode & S_IXUSR) {
ent[num++] = strdup(e->d_name);
if (num >= NUM_SCRIPTS)
break;
fclose(fp);
}
if (ret)
return NULL;
return name;
}
/**
* procname_set - Change process name, as seen in process listnings
* @name: New name of process
* @args: The process' argv[] vector.
*/
void procname_set(char *name, char *args[])
{
size_t len = strlen(args[0]) + 1; /* Include terminating '\0' */
prctl(PR_SET_NAME, name, 0, 0, 0);
memset(args[0], 0, len);
strlcpy(args[0], name, len);
}
/**
* procname_kill - Send a signal to a process group by name.
* @name: Name of process to send signal to.
* @signo: Signal to send.
*
* Send a signal to a running process (group). This function searches
* for a given process @name and sends a signal, @signo, to each
* process ID matching that @name.
*
* Returns:
* Number of signals sent, i.e. number of processes who have received the signal.
*/
int procname_kill (char *name, int signo)
{
int result = 0;
char path[32], line[64];
FILE *fp;
DIR *dir;
struct dirent *entry;
dir = opendir("/proc");
if (!dir || !name) {
errno = EINVAL;
return 0;
}
while ((entry = readdir (dir)) != NULL) {
/* Skip non-process entries in /proc */
if (!isdigit (*entry->d_name))
continue;
snprintf (path, sizeof(path), "/proc/%s/status", entry->d_name);
/* Skip non-readable files (protected?) */
if ((fp = fopen (path, "r")) == NULL)
continue;
if (fgets (line, sizeof (line), fp)) {
char *pname = line + 6; /* Skip first part of line --> "Name:\t" */
if (strncmp(pname, name, strlen(pname) - 1) == 0) {
int error = errno;
if (kill(atoi(entry->d_name), signo)) {
FLOG_ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
} else {
result++; /* Track number of processes we deliver the signal to. */
}
}
}
fclose(fp);
}
closedir(d);
closedir(dir);
if (num == 0)
return 0;
return result;
}
qsort(ent, num, sizeof(char *), cmp);
static int print_uptime(void)
{
#if defined(CONFIG_PRINTK_TIME)
FILE * uptimefile;
float num1, num2;
char uptime_str[30];
if((uptimefile = fopen("/proc/uptime", "r")) == NULL)
return 1;
fgets(uptime_str, 20, uptimefile);
fclose(uptimefile);
sscanf(uptime_str, "%f %f", &num1, &num2);
sprintf(uptime_str, "[ %.6f ]", num1);
write(STDERR_FILENO, uptime_str, strlen(uptime_str));
#endif
return 0;
}
void print_desc(char *action, char *desc)
{
const char home[] = "\r\e[K";
const char dots[] = " .....................................................................";
write(STDERR_FILENO, home, strlen(home));
print_uptime();
write(STDERR_FILENO, action, strlen(action));
write(STDERR_FILENO, desc, strlen(desc));
write(STDERR_FILENO, dots, 60 - strlen(desc) - strlen(action)); /* pad with dots. */
}
int print_result(int fail)
{
if (fail)
fprintf(stderr, " \e[7m[FAIL]\e[0m\n");
else
fprintf(stderr, " \e[1m[ OK ]\e[0m\n");
return fail;
}
int run(char *cmd)
{
int status, result, i = 0;
FILE *fp;
char *args[NUM_ARGS + 1], *arg, *backup;
pid_t pid;
/* We must create a copy that is possible to modify. */
backup = arg = strdup(cmd);
if (!arg)
return 1; /* Failed allocating a string to be modified. */
/* Split command line into tokens of an argv[] array. */
args[i++] = strsep(&arg, "\t ");
do {
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
if (*arg == '\'' || *arg == '"') {
char *p, delim[2] = " ";
delim[0] = arg[0];
args[i++] = arg++;
strsep(&arg, delim);
p = arg - 1;
*p = *delim;
*arg++ = 0;
} else {
args[i++] = strsep(&arg, "\t ");
}
} while (arg && i < NUM_ARGS);
args[i] = NULL;
#if 0
_e("Splitting: '%s' =>", cmd);
for (i = 0; args[i]; i++)
_e("\t%s", args[i]);
#endif
if (i == NUM_ARGS && args[i]) {
_e("Command too long: %s", cmd);
free(backup);
errno = EOVERFLOW;
return 1;
}
fp = fopen("/dev/null", "w");
pid = fork();
if (0 == pid) {
int i;
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. */
if (fp) {
int fd = fileno(fp);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
execvp(args[0], args);
_exit(1); /* Only if execv() fails. */
} else if (-1 == pid) {
_pe("%s", args[0]);
return -1;
}
if (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR)
_e("Caught unblocked signal waiting for %s, aborting.", args[0]);
else if (errno == ECHILD)
_e("Caught SIGCHLD waiting for %s, aborting.", args[0]);
else
_e("Failed starting %s, error %d: %s", args[0], errno, strerror (errno));
if (fp) fclose(fp);
free(backup);
return 1;
}
result = WEXITSTATUS(status);
if (WIFEXITED(status)) {
_d("Started %s and ended OK: %d", args[0], result);
} else if (WIFSIGNALED(status)) {
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
if (!result)
result = 1; /* Must alert callee that the command did complete successfully.
* This is necessary since not all programs trap signals and
* change their return code accordingly. --Jocke */
}
if (fp) fclose(fp);
free(backup);
return result;
}
int run_interactive(char *cmd, char *fmt, ...)
{
int status, oldout = 1, olderr = 2;
char line[LINE_SIZE];
va_list ap;
FILE *fp = tmpfile();
va_start(ap, fmt);
vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
print_desc("", line);
if (fp && !debug) {
oldout = dup(STDOUT_FILENO);
olderr = dup(STDERR_FILENO);
dup2(fileno(fp), STDOUT_FILENO);
dup2(fileno(fp), STDERR_FILENO);
}
status = run(cmd);
if (fp && !debug) {
dup2(oldout, STDOUT_FILENO);
dup2(olderr, STDERR_FILENO);
}
print_result(status);
if (fp && !debug) {
size_t len, written;
rewind(fp);
do {
len = fread(line, 1, sizeof(line), fp);
written = fwrite(line, len, sizeof(char), stderr);
} while (len > 0 && written == len);
fclose(fp);
}
return status;
}
pid_t run_getty(char *cmd, char *argv[])
{
pid_t pid = fork();
if (!pid) {
int i;
char c;
sigset_t nmask;
struct sigaction sa;
/* Detach from initial controlling TTY */
vhangup();
close(2);
close(1);
close(0);
/* Attach TTY to console */
if (open(CONSOLE, O_RDWR) != 0)
exit(1);
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);
dup2(0, STDIN_FILENO);
dup2(0, STDOUT_FILENO);
dup2(0, STDERR_FILENO);
procname_set("console", argv);
while (!fexist(SYNC_SHUTDOWN)) {
static const char msg[] = "\nPlease press Enter to activate this console. ";
if (fexist(SYNC_STOPPED)) {
sleep(1);
continue;
}
i = write(STDERR_FILENO, msg, sizeof(msg) - 1);
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
continue;
if (fexist(SYNC_STOPPED))
continue;
run(cmd);
}
exit(0);
}
return pid;
}
int run_parts(char *dir, char *cmd)
{
struct dirent **e;
int i, num;
num = scandir(dir, &e, NULL, alphasort);
if (num < 0) {
_d("No files found in %s, skipping ...", dir);
return -1;
}
for (i = 0; i < num; i++) {
args[0] = ent[i];
if (!fork()) {
execv(ent[i], args);
int j = 0, status;
pid_t pid = 0;
mode_t mode;
char *args[NUM_ARGS];
char *name = e[i]->d_name;
char path[CMD_SIZE];
snprintf(path, sizeof(path), "%s/%s", dir, name);
mode = fmode(path);
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
_d("Skipping %s ...", path);
continue;
}
/* Fill in args[], starting with full path to executable */
args[j++] = path;
/* If the callee didn't supply a run_parts() argument */
if (!cmd) {
/* Check if S<NUM>service or K<NUM>service notation is used */
_d("Checking if %s is a sysvinit startstop script ...", name);
if (name[0] == 'S' && isdigit(name[1])) {
args[j++] = "start";
} else if (name[0] == 'K' && isdigit(name[1])) {
args[j++] = "stop";
}
} else {
args[j++] = cmd;
}
args[j++] = NULL;
pid = fork();
if (!pid) {
_d("Calling %s ...", path);
execv(path, args);
exit(0);
}
wait(NULL);
free(ent[i]);
waitpid(pid, &status, 0);
}
while (num--)
free(e[num]);
free(e);
return 0;
}
int getgroup(char *s)
int getuser(char *username)
{
struct passwd *usr;
if (!username || (usr = getpwnam(username)) == NULL)
return -1;
return usr->pw_uid;
}
int getgroup(char *group)
{
struct group *grp;
if ((grp = getgrnam(s)) == NULL)
if ((grp = getgrnam(group)) == NULL)
return -1;
return grp->gr_gid;
@@ -174,12 +649,40 @@ int getgroup(char *s)
* Other convenience functions
*/
void chomp(char *s)
void cls(void)
{
static const char cls[] = "\e[2J\e[1;1H";
if (!debug)
fprintf (stderr, "%s", cls);
}
void chomp(char *str)
{
char *x;
if ((x = strchr((s), 0x0a)) != NULL)
if ((x = strchr((str), 0x0a)) != NULL)
*x = 0;
}
#endif
void set_hostname(char *hostname)
{
FILE *fp;
_d("Set hostname: %s", hostname);
if ((fp = fopen("/etc/hostname", "r")) != NULL) {
fgets(hostname, HOSTNAME_SIZE, fp);
chomp(hostname);
fclose(fp);
}
sethostname(hostname, strlen(hostname));
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+101 -10
View File
@@ -1,14 +1,105 @@
#ifndef __FINIT_HELPERS_H
#define __FINIT_HELPERS_H
/* Misc. utility functions and C-library extensions for finit and its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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.
*/
int makepath (char *);
void ifconfig (char *, char *, char *, int);
void copyfile (char *, char *, int);
void chomp (char *);
int getgroup (char *);
#ifndef FINIT_HELPERS_H_
#define FINIT_HELPERS_H_
#ifdef BUILTIN_RUNPARTS
int run_parts (char *, ...);
#include <stdio.h>
#include <syslog.h>
#define DO_LOG(level, fmt, args...) \
{ \
openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \
syslog(level, fmt, ##args); \
closelog(); \
}
#define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, 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)
#endif
#ifndef chardev
# define chardev(x,m,maj,min) mknod((x), S_IFCHR|(m), makedev((maj),(min)))
#endif
#ifndef blkdev
# define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min)))
#endif
#ifndef S_ISEXEC
# define S_ISEXEC(m) (((m) & S_IXUSR) == S_IXUSR)
#endif
#ifndef UNUSED
#define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
#endif
#endif
#define echo(fmt, args...) do { if (verbose) { fprintf(stderr, fmt "\n", ##args); } } while (0)
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0)
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __func__, ##args); } while (0)
#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt ". Error %d: %s\n", __FILE__, __func__, ##args, errno, strerror(errno)); } while (0)
extern int debug;
extern int verbose;
int makepath (char *path);
void ifconfig (char *ifname, char *addr, char *mask, int up);
pid_t pidfile_read (char *pidfile);
pid_t pidfile_poll (char *cmd, char *path);
int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
void procname_set (char *name, char *args[]);
int procname_kill (char *name, int signo);
void print_desc (char *action, char *desc);
int print_result (int fail);
int start_process (char *cmd, char *args[], int console);
void cls (void);
void chomp (char *str);
int getuser (char *username);
int getgroup (char *group);
void set_hostname (char *hostname);
int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...);
pid_t run_getty (char *cmd, char *argv[]);
int run_parts (char *dir, char *cmd);
/* strlcpy.c */
size_t strlcpy(char *dst, const char *src, size_t siz);
#endif /* FINIT_HELPERS_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+79
View File
@@ -0,0 +1,79 @@
# Finit libite -- Light-weight utility functions and C-library extensions
#
# Copyright (c) 2008-2012 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.
.EXPORT_ALL_VARIABLES:
.PHONY: all install clean
include ../common.mk
CFLAGS += -fPIC
HEADERS = lite.h
OBJS := copyfile.o dir.o fexist.o fisdir.o fmode.o rsync.o
DEPS := $(addprefix ., $(OBJS:.o=.d))
VER = 0
LIBNAME = libite
TARGET = $(LIBNAME).so
VERLIB = $(TARGET).$(VER)
STATICLIB = $(LIBNAME).a
CHECK_FLAGS = $(CFLAGS) $(CPPFLAGS)
all: $(TARGET)
$(OBJS): Makefile
$(TARGET): $(OBJS)
@printf " LINK %s\n" $@
# @$(AR) $(ARFLAGS) $(STATICLIB) $(OBJS)
@$(CC) $(LDFLAGS) -shared $(OBJS) -lrt -lcrypt -Wl,-soname,$@ -o $@
@ln -sf $@ $(VERLIB)
install-exec: all
@install -d $(DESTDIR)$(libdir)
@echo " INSTALL $(DESTDIR)$(libdir)/$(TARGET)"
@$(STRIPINST) $(TARGET) $(DESTDIR)$(libdir)/$(TARGET)
@ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(VERLIB)
install-dev:
@for file in $(HEADERS); do \
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
done
install: install-dev install-exec
uninstall:
-@$(RM) $(DESTDIR)$(libdir)/$(VERLIB)
-@$(RM) $(DESTDIR)$(libdir)/$(TARGET)
clean: uninstall
-@$(RM) $(OBJS) $(DEPS) $(TARGET) $(VERLIB) $(STATICLIB)
distclean: clean
-@$(RM) $(JUNK) unittest *.o .*.d *.unittest
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),distclean)
-include $(DEPS)
endif
endif
+317
View File
@@ -0,0 +1,317 @@
/* Fastinit (finit) copyfile() implementation.
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "lite.h"
static int __isdir_and_append_filename(char *src, char **dst);
/**
* copyfile - Copy a file to another.
* @src: Source file.
* @dst: Target file.
* @len: Number of bytes to copy, zero (0) for entire file.
*
* This is a C implementation of the command line cp(1) utility. It is one
* of the classic missing links in the UNIX C library. This version is from
* the finit project, http://helllabs.org/finit/, which is a reimplementation
* of fastinit for the Asus EeePC.
*
* Only some small changes to save stack space have been introduced.
*
* Returns:
* The number of bytes copied, zero may be error (check errno!), but it
* may also indicate that @src was empty. If @src is a directory errno
* will be set to %EISDIR since copyfile() is not recursive.
*/
ssize_t copyfile(char *src, char *dst, int len)
{
char *buffer;
int s, d, n, isdir = 0, saved_errno = 0;
ssize_t size = 0;
errno = 0; /* Reset before leaving this function. */
// fprintf (stderr, "%s() src: %s, dst: %s, len: %d\n", __func__, src, dst, len);
buffer = malloc(BUFSIZ);
if (!buffer)
return 0;
if (fisdir(src)) {
errno = EISDIR; /* Error: source is a directory */
return 0;
}
/* Check if target is a directory, then append the src base filename. */
isdir = __isdir_and_append_filename(src, &dst);
/* Len == 0 means copy entire file */
if (len == 0) {
/* XXX: This feels a bit unsafe, but 2047 MiB may be sufficient for us.
* --Jocke 2008-08-11 */
len = INT_MAX;
}
if ((s = open(src, O_RDONLY)) >= 0) {
if ((d =
open(dst, O_WRONLY | O_CREAT | O_TRUNC,
fmode(src))) >= 0) {
do {
int clen = len > BUFSIZ ? BUFSIZ : len;
if ((n = read(s, buffer, clen)) > 0) {
size += write(d, buffer, n);
}
len -= clen;
}
while (len > 0 && n == BUFSIZ);
close(d);
} else {
saved_errno = errno;
}
close(s);
} else {
saved_errno = errno;
}
free(buffer);
if (isdir)
free(dst);
errno = saved_errno;
return size;
}
/**
* movefile - Move a file to another location
* @src: Source file.
* @dst: Target file, or location.
*
* This is a C implementation of the command line mv(1) utility.
* Usually the rename() API is sufficient, but not when moving across
* file system boundaries.
*
* The @src argument must include the full path to the source file,
* whereas the @dst argument may only be a directory, in which case the
* same file name from @src is used.
*
* Returns:
* POSIX OK(0), or non-zero with errno set.
*/
int movefile(char *src, char *dst)
{
int isdir, result = 0;
/* Check if target is a directory, then append the src base filename. */
isdir = __isdir_and_append_filename(src, &dst);
if (rename(src, dst)) {
if (errno == EXDEV) {
errno = 0;
copyfile(src, dst, 0);
if (errno)
result = 1;
else
result = remove(src);
} else {
result = 1;
}
}
if (isdir)
free(dst);
return result;
}
/**
* copy_filep - Copy between FILE *fp.
* @src: Source FILE.
* @dst: Destination FILE.
*
* Function takes signals into account and will restart the syscalls as long
* as error is %EINTR.
*
* Returns:
* POSIX OK(0), or non-zero with errno set on error.
*/
int copy_filep(FILE *src, FILE *dst)
{
int result = 0;
char *buf = (char *)malloc(BUFSIZ);
if (!src || !dst)
return EINVAL;
if (!buf)
return errno;
while (1) {
if (!fgets(buf, BUFSIZ, src)) {
if (errno == EINTR) {
clearerr(src);
continue;
}
if (feof(src))
break;
result = errno;
goto exit;
}
while (EOF == fputs(buf, dst)) {
if (errno == EINTR) {
clearerr(dst);
continue;
}
result = errno;
goto exit;
}
}
exit:
free(buf);
return errno = result;
}
/* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
static int __isdir_and_append_filename(char *src, char **dst)
{
int isdir = 0;
if (fisdir(*dst)) {
int slash = 0;
char *tmp, *ptr = strrchr(src, '/');
if (!ptr)
ptr = src;
else
ptr++;
// fprintf (stderr, "Yes %s is a dir --> add %s\n", *dst, ptr);
tmp = malloc(strlen(*dst) + strlen(ptr) + 2);
if (!tmp) {
errno = EISDIR;
return 0;
}
isdir = 1; /* Free dst before exit! */
slash = fisslashdir(*dst);
sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr);
*dst = tmp;
// fprintf (stderr, "NEW DST:%s\n", *dst);
}
return isdir;
}
#ifdef UNITTEST
#include <stdio.h>
/* XXX: Make a real set of unit tests ... */
#if 0
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr,
"Usage: %s <src> <dst>\nCopies the <src> file to the <dst> file.\n",
argv[0]);
return 1;
}
printf("Copying file %s to %s\n", argv[1], argv[2]);
return copyfile(argv[1], argv[2], 0);
}
#else
int main(void)
{
int i = 0;
char *files[] = {
"/etc/passwd", "/tmp/tok", "/tmp/tok",
"/etc/passwd", "/tmp/", "/tmp/passwd",
"/etc/passwd", "/tmp", "/tmp/passwd",
NULL
};
FILE *src, *dst;
fprintf(stderr, "\n=>Start testing copy_filep()\n");
while (files[i]) {
fprintf(stderr, "copy_filep(%s, %s)\t", files[i],
files[i + 1]);
src = fopen(files[i], "r");
dst = fopen(files[i + 1], "w");
if (copy_filep(src, dst))
perror("Failed");
if (src)
fclose(src);
if (dst)
fclose(dst);
if (fexist(files[i + 2]))
fprintf(stderr, "OK => %s\n", files[i + 2]);
remove(files[i + 2]);
i += 3;
}
printf("\n\n=>Start testing copyfile()\n");
i = 0;
while (files[i]) {
fprintf(stderr, "copyfile(%s, %s)\t", files[i], files[i + 1]);
if (!copyfile(files[i], files[i + 1], 0))
perror("Failed");
if (fexist(files[i + 2]))
fprintf(stderr, "OK => %s\n", files[i + 2]);
remove(files[i + 2]);
i += 3;
}
return 0;
}
#endif
#endif /* UNITTEST */
/**
* Local Variables:
* compile-command: "gcc -g -I../include -o unittest -DUNITTEST copyfile.c && ./unittest"
* version-control: t
* kept-new-versions: 2
* c-file-style: "ellemtel"
* End:
*/
+228
View File
@@ -0,0 +1,228 @@
/* Functions for operating on files in directories.
*
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *matcher_type = NULL;
static int (*matcher_filter) (const char *file) = NULL;
static int matcher(const struct dirent *entry)
{
char *pos = strrchr(entry->d_name, '.');
if (matcher_filter && !matcher_filter(entry->d_name))
/* User matcher overrides the rest. */
return 0;
/* Skip current dir "." from list of files. */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
/* filetype == "" */
if (matcher_type[0] == 0)
return 1;
/* Entry has no "." */
if (!pos)
return 0;
return !strcmp(pos, matcher_type);
}
/**
* dir - List all files of a certain type in the given directory.
* @dir: Base directory for dir operation.
* @type: File type suffix, e.g. ".cfg".
* @filter: Optional file name filter.
* @list: Pointer to an array of file names.
* @strip: Flag, if set dir() strips the file type.
*
* This function returns a @list of files, matching the @type suffix,
* in the given directory @dir.
*
* The @list argument is a pointer to where to store the dynamically
* allocated list of file names. This list should be free'd by first
* calling free() on each file name and then on the list itself.
*
* If @filter is not %NULL it will be called for each file found. If
* @filter returns non-zero the @file argument will be included in the
* resulting @list. If @filter returns zero for given @file it will
* be discarded.
*
* If the @strip flag is set the resulting @list of files has their
* file type stripped, including the dot. So a match "config0.cfg"
* would be returned as "config0".
*
* Returns:
* Number of files in @list, zero if no matching files of @type.
*/
int dir(const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip)
{
int i, n, num = 0;
char **files;
struct dirent **namelist;
assert(list);
if (!dir)
/* Assuming current directory */
dir = ".";
if (!type)
/* Assuming all files. */
type = "";
matcher_type = type;
matcher_filter = filter;
n = scandir(dir, &namelist, matcher, alphasort);
if (n < 0) {
perror("scandir");
} else {
files = (char **)malloc(n * sizeof(char *));
for (i = 0; i < n; i++) {
if (files) {
char *name = namelist[i]->d_name;
char *type = strrchr(name, '.');
if (type && strip)
*type = 0;
files[i] = strdup(name);
num++;
}
free(namelist[i]);
}
free(namelist);
}
if (num)
*list = files;
return num;
}
#ifdef UNITTEST
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "otn/c.h"
#define DIR_TYPE_IMAGE ".img"
#define DIR_TYPE_SYSLOG ""
#define DIR_TYPE_CONFIG ".cfg"
#define STARTUP_CONFIG "startup-config.cfg"
int simulate_files(int creat)
{
int i;
char *files[] =
{ "config0.cfg", "config1.cfg", "config2.cfg", "config3.cfg",
"rr109.img", "rx100.img", "rx107.img", "rm957.img", "messages"
};
for (i = 0; i < ARRAY_ELEMENTS(files); i++) {
if (creat)
touch(files[i]);
else
remove(files[i]);
}
if (creat)
symlink("config2.cfg", STARTUP_CONFIG);
else
remove(STARTUP_CONFIG);
return 0;
}
static int cfg_dir_filter(const char *file)
{
/* Skip the STARTUP_CONFIG file, it is a symbolic link to the
* current startup configuration. */
return ! !strcmp(file, STARTUP_CONFIG);
}
int main(int argc, char *argv[])
{
int i, num;
char *type = DIR_TYPE_CONFIG;
char **files;
int once = 1;
int is_startup_config(const char *entry) {
static char file[80];
if (once) {
int len = readlink(STARTUP_CONFIG, file, sizeof(file));
if (len == -1)
return 0;
file[len] = 0;
once = 0; /* Only once per call to dir() */
}
//printf ("Comparing link %s with entry %s\n", file, entry);
return !strcmp(file, entry);
}
simulate_files(1);
if (argc >= 2) {
if (!strcasecmp("CONFIG", argv[1])) {
type = DIR_TYPE_CONFIG;
system("ls -l *" DIR_TYPE_CONFIG);
}
if (!strcasecmp("IMAGE", argv[1])) {
type = DIR_TYPE_IMAGE;
system("ls -l *" DIR_TYPE_IMAGE);
}
if (!strcasecmp("SYSLOG", argv[1])) {
type = DIR_TYPE_SYSLOG;
system("ls -l *");
}
}
num = dir(NULL, type, cfg_dir_filter, &files, 0);
if (num) {
for (i = 0; i < num; i++) {
printf("%s", files[i]);
if (is_startup_config(files[i]))
printf(" --> startup-config");
printf("\n");
free(files[i]);
}
free(files);
}
simulate_files(0);
return 0;
}
#endif /* UNITTEST */
/**
* Local Variables:
* compile-command: "gcc -g -DUNITTEST -o unittest dir.c"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+53
View File
@@ -0,0 +1,53 @@
/* Check if file exists
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <unistd.h>
/**
* fexist - Check if a file exists in the file system.
* @file: File to look for, with full path.
*
* Returns:
* %TRUE(1) if the file exists, otherwise %FALSE(0).
*/
int fexist(char *file)
{
if (!file) {
errno = EINVAL;
return 0; /* Doesn't exist ... */
}
if (-1 == access(file, F_OK))
return 0;
return 1;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+51
View File
@@ -0,0 +1,51 @@
/* Check if directory exists
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/**
* fexist - Check if a file exists and is a directory.
* @file: File to look for, with full path.
*
* Returns:
* %TRUE(1) if the file exists and is a directory, otherwise %FALSE(0).
*/
int fisdir(char *file)
{
struct stat sb;
if (!stat(file, &sb) && S_ISDIR(sb.st_mode))
return 1;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+57
View File
@@ -0,0 +1,57 @@
/* File mode bit operations
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/**
* fmode - Return a file's mode bits.
* @file: File to look for, with full path.
*
* Returns:
* The mode bits or zero when @file does not exist and errno set to %ENOENT.
*/
mode_t fmode(char *file)
{
struct stat sb;
if (!file) {
errno = EINVAL;
return 0;
}
if (!stat(file, &sb))
return sb.st_mode;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+61
View File
@@ -0,0 +1,61 @@
/* Collection of utility funcs and C library extensions for finit & its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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_LITE_H_
#define FINIT_LITE_H_
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int fexist (char *file);
int fisdir (char *file);
mode_t fmode (char *file);
ssize_t copyfile (char *src, char *dst, int len);
int movefile (char *src, char *dst);
int copy_filep (FILE *src, FILE *dst);
int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip);
int rsync (char *src, char *dst, int delete, int (*filter) (const char *file));
static inline int fisslashdir(char *dir)
{
if (!dir) return 0;
if (strlen (dir) > 0) return dir[strlen (dir) - 1] == '/';
return 0;
}
#endif /* FINIT_LITE_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+280
View File
@@ -0,0 +1,280 @@
/* Micro "rsync" implementation.
*
* Copyright (c) 2011, 2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <stdlib.h> /* NULL, free() */
#include <string.h> /* strlen() */
#include <strings.h> /* rindex() */
#include <stdio.h>
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
#include <sys/stat.h>
#include <sys/types.h>
#include "lite.h"
#include "../helpers.h"
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
static int prune(char *dst, char **new_files, int new_num);
/**
* rsync - Synchronize contents and optionally remove non-existing backups
* @src: Source directory
* @dst: Destination directory
* @delete: Prune files from @dst that no longer exist in @src.
* @filter: Optional filtering function for source directory.
*
* This is a miniature implementation of the famous rsync for local use only.
* In fact, it is not even a true rsync since it copies all files from @src
* to @dst. The @delete option is useful for creating backups, when set all
* files removed from src since last backup are pruned from the destination
* (backup) directory.
*
* The filter callback, @filter, if provided, is used to determine what files to
* include from the source directory when backing up. If a file is to be skipped
* the callback should simply return zero.
*
* Returns:
* POSIX OK(0), or non-zero with @errno set on error.
*/
int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
{
char source[256];
char dest[256];
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);
if (errno) result++;
}
if (!fisdir(dst))
mkdir(dst, 0755);
if (!fisdir(src)) {
if (!fexist(src))
return 1;
copy(src, dst);
return errno;
}
/* Copy dir as well? */
if (!fisslashdir(src)) {
char *ptr = rindex(src, '/');
if (!ptr)
ptr = src;
else
ptr++;
if (makedir(dest, sizeof(dest), dst, ptr, fmode(src)))
return 1;
dst = dest;
}
num = dir(src, "", filter, &files, 0);
for (i = 0; i < num; i++) {
/* Recursively copy sub-directries */
snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]);
if (fisdir(source)) {
char dst2[256];
strcat(source, "/");
if (makedir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
result++;
continue;
}
rsync(source, dst2, delete, filter);
continue; /* Next file/dir in @src to copy... */
}
copy(source, dst);
}
/* We ignore any errors from the pruning, that phase albeit useful is only
* cosmetic. --Jocke 2011-03-24 */
if (delete)
prune(dst, files, num);
if (num) {
for (i = 0; i < num; i++)
free(files[i]);
free(files);
}
return result;
}
/* 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)
{
snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name);
if (mkdir(buf, mode))
return 1;
return 0;
}
/* Prune old files, no longer existing on source, from destination directory. */
static int prune(char *dst, char **new_files, int new_num)
{
int i, num, result = 0;
char **files;
int find(char *file) {
int n;
for (n = 0; n < new_num; n++)
if (!strncmp (new_files[n], file, MAX(strlen(new_files[n]), strlen(file))))
return 1;
return 0;
}
num = dir(dst, "", NULL, &files, 0);
if (num) {
for (i = 0; i < num; i++) {
if (!find(files[i])) {
char *name;
size_t len = strlen(files[i]) + 2 + strlen(dst);
name = malloc(len);
if (name) {
snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]);
if (remove(name))
result++;
free(name);
}
}
free(files[i]);
}
free(files);
}
return result;
}
#ifdef UNITTEST
#define BASE "/tmp/.unittest/"
#define SRC BASE "src/"
#define DST BASE "dst/"
char *files[] = {
SRC "sub1/1.tst",
SRC "sub1/2.tst",
SRC "sub1/3.tst",
SRC "sub2/4.tst",
SRC "sub2/5.tst",
SRC "sub2/6.tst",
SRC "sub3/7.tst",
SRC "sub3/8.tst",
SRC "sub3/9.tst",
NULL
};
void cleanup_test(void)
{
system("rm -rf " BASE);
}
void setup_test(void)
{
int i;
char cmd[256];
mode_t dir_modes[] = { 755, 700 };
mode_t file_modes[] = { 644, 600 };
cleanup_test();
mkdir(BASE, 0755);
mkdir(SRC, 0755);
mkdir(DST, 0755);
for (i = 0; files[i]; i++) {
snprintf(cmd, sizeof(cmd), "mkdir -m %d -p `dirname %s`",
dir_modes[i % 2], files[i]);
system(cmd);
snprintf(cmd, sizeof(cmd), "touch %s; chmod %d %s", files[i],
file_modes[i % 2], files[i]);
system(cmd);
}
}
int run_test()
{
#if 0
setup_test();
puts("Before:");
system("tree -np " BASE);
rsync(SRC, DST, 0, NULL);
puts("After:");
system("tree -np " BASE);
cleanup_test();
#endif
setup_test();
rsync(BASE "src", DST, 0, NULL);
puts("Only partial rsync of src <-- No slash!");
system("tree -np " BASE);
#if 0
cleanup_test();
setup_test();
rsync(BASE "src/sub1", BASE "dst", 0, NULL);
puts("Only partial rsync of src/sub1 <-- No slashes!!");
system("tree -np " BASE);
cleanup_test();
setup_test();
rsync(BASE "src/sub1/", DST, 0, NULL);
puts("Only partial rsync of src/sub1/");
system("tree -np " BASE);
cleanup_test();
setup_test();
rsync(BASE "src/sub1", DST, 0, NULL);
puts("Only partial rsync of src/sub1 <-- No slash!");
system("tree -np " BASE);
rsync("/etc", "/var/tmp", 0, NULL);
puts("Real life test:");
system("tree -np /var/tmp");
#endif
return 0;
}
int main(int argc, char *argv[])
{
atexit(cleanup_test);
return run_test();
}
#endif /* UNITTEST */
/**
* Local Variables:
* compile-command: "make -f rsync.mk"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+22
View File
@@ -0,0 +1,22 @@
# Unittest makefile for rsync.c
.PHONY: clean
SRC = rsync.c
OBJS = dir.o copyfile.o fexist.o fisdir.o fmode.o
UNIT = rsync.unittest
#CPPFLAGS = -DDEBUG -I../../.. -I../../include
all: build run
clean:
@rm -f $(OBJS) $(UNIT)
# This rule also forces a cleanup of built objects to not leave any object files
# lingering in the tree to interfere with cross builds.
build: clean $(OBJS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $(UNIT) -DUNITTEST $(SRC) $(OBJS)
@rm -f $(OBJS)
run:
@./$(UNIT)
+347
View File
@@ -0,0 +1,347 @@
/* Plugin based services architecture for finit
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <dlfcn.h> /* dlopen() et al */
#include <dirent.h> /* readdir() et al */
#include <poll.h>
#include <string.h>
#include "finit.h"
#include "private.h"
#include "helpers.h"
#include "plugin.h"
#include "queue.h" /* BSD sys/queue.h API */
#include "lite.h"
#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 void check_plugin_depends(plugin_t *plugin);
int plugin_register(plugin_t *plugin)
{
int i, inuse = 0;
if (!plugin) {
errno = EINVAL;
return 1;
}
if (!plugin->name) {
Dl_info info;
if (!dladdr(plugin, &info) || !info.dli_fname)
plugin->name = "unknown";
else
plugin->name = (char *)info.dli_fname;
}
/* Already registered? */
if (plugin_find(plugin->name)) {
_d("... %s already loaded.", plugin->name);
return 0;
}
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;
}
int plugin_unregister(plugin_t *plugin)
{
TAILQ_REMOVE(&plugins, plugin, link);
if (plugin->svc.cb) {
svc_t *svc = svc_find(plugin->name);
if (svc) {
svc->cb = NULL;
svc->dynamic = 0;
}
}
/* XXX: Unfinished, add cleanup code here! */
return 0;
}
#define SEARCH_PLUGIN(str) \
PLUGIN_ITERATOR(p, tmp) { \
_d("Comparing %s against plugin %s", str, p->name); \
if (!strcmp(p->name, str)) \
return p; \
}
/**
* plugin_find - Find a plugin by name
* @name: With or without path, or .so extension
*
* This function uses an opporunistic search for a suitable plugin and
* returns the first match. Albeit with at least some measure of
* heuristics.
*
* First it checks for an exact match. If no match is found and @name
* starts with a slash the search ends. Otherwise a new search with the
* plugin path prepended to @name is made. Also, if @name does not end
* with .so it too is added to @name before searching.
*
* Returns:
* On success the pointer to the matching &plugin_t is returned,
* otherwise %NULL is returned.
*/
plugin_t *plugin_find(char *name)
{
plugin_t *p, *tmp;
if (!name) {
errno = EINVAL;
return NULL;
}
SEARCH_PLUGIN(name);
if (name[0] != '/') {
int noext;
char path[CMD_SIZE];
noext = strcmp(name + strlen(name) - 3, ".so");
snprintf (path, sizeof(path), "%s%s%s%s", plugpath,
fisslashdir(plugpath) ? "" : "/",
name, noext ? ".so" : "");
SEARCH_PLUGIN(path);
}
errno = ENOENT;
return NULL;
}
/* Private daemon API *******************************************************/
void plugin_run_hooks(hook_point_t no)
{
plugin_t *p, *tmp;
PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) {
_d("Calling %s hook n:o %d from runloop...", basename(p->name), no);
p->hook[no].cb(p->hook[no].arg);
}
}
}
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
static void generic_io_cb(int fd, int events)
{
plugin_t *p, *tmp;
/* Find matching plugin, pick first matching fd */
PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p) && p->io.fd == fd) {
_d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, fd, events);
break;
}
}
}
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].fd, fds[i].revents);
}
break;
}
}
/* Setup any I/O callbacks for plugins that use them */
static void init_plugins(void)
{
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;
}
}
}
/**
* load_one - Load one plugin
* @path: Path to finit plugins, usually %PLUGIN_PATH
* @name: Name of plugin, optionally ending in ".so"
*
* Loads a plugin from @path/@name[.so]. Note, if ".so" is missing from
* the plugin @name it is added before attempting to load.
*
* It is up to the plugin itself ot register itself as a "ctor" with the
* %PLUGIN_INIT macro so that plugin_register() is called automatically.
*
* Returns:
* POSIX OK(0) on success, non-zero otherwise.
*/
static int load_one(char *path, char *name)
{
int noext;
char plugin[CMD_SIZE];
void *handle;
if (!path || !fisdir(path) || !name) {
errno = EINVAL;
return 1;
}
/* 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" : "");
_d("Loading plugin %s ...", basename(plugin));
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
_e("Failed loading plugin %s: %s", plugin, dlerror());
return 1;
}
return 0;
}
/**
* check_plugin_depends - Check and load any plugins this one depends on.
* @plugin: Plugin with possible depends to check
*
* Very simple dependency resolver, should actually load the plugin of
* the correct .name, but currently loads a matching filename.
*
* Works, but only for now. A better way might be to have a try_load()
* that actually loads all plugins and checks their &plugin_t for the
* correct .name.
*/
static void check_plugin_depends(plugin_t *plugin)
{
int i;
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
_d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
if (plugin_find(plugin->depends[i])) {
_d("OK plugin %s was already loaded.", plugin->depends[i]);
continue;
}
load_one(plugpath, plugin->depends[i]);
}
}
int plugin_load_all(char *path)
{
int fail = 0;
DIR *dp = opendir(path);
struct dirent *entry;
if (!dp) {
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1;
}
plugpath = path;
while ((entry = readdir(dp))) {
if (entry->d_name[0] == '.')
continue; /* Skip . and .. directories */
// print_desc(" Loading plugin ", basename(plugin));
if (load_one(path, entry->d_name))
fail++;
// print_result(result);
}
closedir(dp);
init_plugins();
return fail;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+124
View File
@@ -0,0 +1,124 @@
/* Plugin based services architecture for finit
*
* Copyright (c) 2012 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_PLUGIN_H_
#define FINIT_PLUGIN_H_
#include <poll.h>
#include "queue.h" /* BSD sys/queue.h API */
#include "svc.h"
#define PLUGIN_DEP_MAX 10
#define PLUGIN_IO_READ POLLIN
#define PLUGIN_IO_WRITE POLLOUT
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
/*
* Predefined hook points for easier plugin debugging
*/
typedef enum {
HOOK_ROOTFS_UP = 0,
HOOK_BASEFS_UP,
HOOK_NETWORK_UP,
HOOK_SYSTEM_UP,
HOOK_SVC_UP,
HOOK_SHUTDOWN,
HOOK_MAX_NUM
} hook_point_t;
/**
* plugin_t - Finit &plugin_t object
* @link: BSD sys/queue.h linked list node
* @name: Plugin name, or identifier to match against a &svc_t object
* @svc: Service callback for a loaded &svc_t object
* @hook: Hook callback definitions
* @io: I/O hook callback
*
* To setup an &svc_t object callback for a service monitor the @name
* must match the @svc_t @cmd exactly for them to "pair".
*
* A &plugin_t object may contain a service callback, several hooks
* and/or an I/O callback as well. This way all critical extensions
* to finit can fit in one single plugin, if needed.
*
* The "dynamic events" discussed in the svc callback is for external
* service plugins to implement. However, it can be anything that
* can cause a service to need to SIGHUP at runtime. E.g., acquiring
* a DHCP lease, or an interface going UP/DOWN. The event itself can
* be passed as an integer @event and any optional argument @event_arg
*
* It is up to the external service plugin to track these events and
* relay them to each @dynamic service plugins' callback. I.e., to
* all those with the dynamic flag set.
*/
typedef struct plugin {
/* BSD sys/queue.h linked list node. */
TAILQ_ENTRY(plugin) link;
/* Plugin name, defaults to basename of plugin path if unset.
* NOTE: Must be same as @cmd for service plugins! */
char *name;
/* Service callback to be called once per lap of runloop. */
struct {
int dynamic; /* Callback wants external events as well. */
int dynamic_stop; /* Callback allows dynamic events to stop. */
svc_cmd_t (*cb)(svc_t *svc, int event, void *event_arg);
} svc;
/* List of hook callbacks. */
struct {
void *arg; /* Optional argument to callback func. */
void (*cb)(void *arg);
} hook[HOOK_MAX_NUM];
/* I/O Plugin */
struct {
int fd, flags; /* 1:READ, 2:WRITE */
void *arg;
void (*cb)(void *arg, int fd, int events);
} io;
char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */
} plugin_t;
/* Public plugin API */
int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin);
/* Helper API */
plugin_t *plugin_find (char *name);
#endif /* FINIT_PLUGIN_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+36
View File
@@ -0,0 +1,36 @@
# Plugin based services architecture for finit -*-Makefile-*-
CFLAGS += -fPIC -Os
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
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
include ../common.mk
all: $(PLUGINS)
clean:
-@$(RM) $(PLUGINS) *.o
distclean: clean
-@$(RM) $(JUNK) $(DEPS)
install: all
@install -d $(DESTDIR)$(plugindir)
@for plugin in $(PLUGINS); do \
echo " INSTALL $(DESTDIR)$(plugindir)/$$plugin"; \
$(STRIPINST) $$plugin $(DESTDIR)$(plugindir)/$$plugin; \
done
uninstall:
-@for plugin in $(PLUGINS); do \
echo " RM $(DESTDIR)$(plugindir)/$$plugin"; \
rm $(DESTDIR)$(plugindir)/$$plugin 2>/dev/null; \
done
-@rmdir $(DESTDIR)$(plugindir) 2>/dev/null
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
+66
View File
@@ -0,0 +1,66 @@
/* Save and restore ALSA sound settings using alsactl
*
* Copyright (c) 2012 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 "finit.h"
#include "helpers.h"
#include "plugin.h"
static void save(void *UNUSED(arg))
{
_d("Saving sound settings ...");
run_interactive("/usr/sbin/alsactl -g store", "Saving sound settings");
}
static void restore(void *UNUSED(arg))
{
_d("Restoring sound settings ...");
run_interactive("/usr/sbin/alsactl -g restore", "Restoring sound settings");
}
static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = {
.cb = restore
},
.hook[HOOK_SHUTDOWN] = {
.cb = save
}
};
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:
*/
+110
View File
@@ -0,0 +1,110 @@
/* Setup necessary files for UTMP, tracking logins
*
* Copyright (c) 2012 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 <string.h>
#include <utmp.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
/*
* Setup standard FHS 2.3 structure in /var, and
* write runlevel to UTMP, needed by, e.g., printerdrake.
*/
static void setup(void *UNUSED(arg))
{
#ifdef RUNLEVEL
struct utmp entry;
#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);
_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));
entry.ut_type = RUN_LVL;
entry.ut_pid = '0' + RUNLEVEL;
setutent();
pututline(&entry);
endutent();
#endif
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
touch("/etc/network/run/ifstate");
#else
remove("/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 */
chown("/dev/xconsole", 0, getgroup("tty"));
}
static plugin_t plugin = {
.name = "bootmisc",
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
};
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:
*/
+63
View File
@@ -0,0 +1,63 @@
/* Setup and start system message bus, D-Bus
*
* Copyright (c) 2012 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 "finit.h"
#include "helpers.h"
#include "plugin.h"
static void setup(void *UNUSED(arg))
{
#ifdef USE_MESSAGE_BUS
_d("Starting D-Bus ...");
mkdir("/var/run/dbus", 0755);
mkdir("/var/lock/subsys/messagebus", 0755);
run("dbus-uuidgen --ensure");
remove("/var/run/dbus/pid");
run_interactive("dbus-daemon --system", "Starting D-Bus");
#endif
}
static plugin_t plugin = {
.hook[HOOK_NETWORK_UP] = {
.cb = setup
},
.depends = { "bootmisc", },
};
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:
*/
+67
View File
@@ -0,0 +1,67 @@
/* Save and restore RTC using hwclock
*
* Copyright (c) 2012 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 "finit.h"
#include "helpers.h"
#include "plugin.h"
static void save(void *UNUSED(arg))
{
_d("Saving system clock to RTC ...");
/* -w,--systohc, -u,--utc */
run_interactive("/sbin/hwclock -w -u", "Saving system time (UTC) to RTC");
}
static void restore(void *UNUSED(arg))
{
_d("Restoring system clock from RTC ...");
/* -s,--hctosys, -u,--utc */
run_interactive("/sbin/hwclock -s -u", "Restoring system clock (UTC) from RTC");
}
static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = {
.cb = restore
},
.hook[HOOK_SHUTDOWN] = {
.cb = save
}
};
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:
*/
+120
View File
@@ -0,0 +1,120 @@
/* Optional /dev/initctl FIFO monitor
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <fcntl.h> /* O_RDONLY et al */
#include <signal.h>
#include <string.h>
#include <unistd.h> /* read() */
#include "helpers.h"
#include "plugin.h"
#include "sig.h"
#define INIT_MAGIC 0x03091969
#define INIT_CMD_RUNLVL 1
struct init_request {
int magic; /* Magic number */
int cmd; /* What kind of request */
int runlevel; /* Runlevel to change to */
int sleeptime; /* Time between TERM and KILL */
char data[368];
};
/* Standard reboot/shutdown utilities talk to init using /dev/initctl.
* We should check if the fifo was recreated and reopen it.
*/
static void read_initctl(void *UNUSED(arg), int fd, int UNUSED(events))
{
struct init_request request;
_d("Receiving request on %s...", FINIT_FIFO);
while (1) {
ssize_t len = read(fd, &request, sizeof(request));
if (-1 == len) {
_d("Error %d", errno);
break; /* Probably EAGAIN */
}
if (request.magic != INIT_MAGIC)
continue;
_d("Magic OK...");
if (request.cmd == INIT_CMD_RUNLVL) {
switch (request.runlevel) {
case '0':
_d("Halting system (SIGUSR2)");
do_shutdown(SIGUSR2);
break;
case '6':
_d("Rebooting system (SIGUSR1)");
do_shutdown(SIGUSR1);
break;
default:
_d("Unsupported runlevel: %d", request.runlevel);
break;
}
} else {
_d("Unsupported cmd: %d", request.cmd);
}
}
}
static plugin_t plugin = {
.io = {
.cb = read_initctl,
.flags = PLUGIN_IO_READ,
},
};
PLUGIN_INIT(plugin_init)
{
_d("Setting up %s", FINIT_FIFO);
mkfifo(FINIT_FIFO, 0600);
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
if (-1 == plugin.io.fd) {
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
return;
}
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
plugin_unregister(&plugin);
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+90
View File
@@ -0,0 +1,90 @@
/* Setup necessary files for resolvconf
*
* Copyright (c) 2012 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 "finit.h"
#include "helpers.h"
#include "plugin.h"
static void setup(void *UNUSED(arg))
{
#ifdef USE_ETC_RESOLVCONF_RUN
DIR *dir;
struct dirent *d;
#endif
_d("Setting up the resolver ...");
#ifdef USE_ETC_RESOLVCONF_RUN
makepath("/dev/shm/network");
makepath("/dev/shm/resolvconf/interface");
if ((dir = opendir("/etc/resolvconf/run/interface")) != NULL) {
while ((d = readdir(dir)) != NULL) {
if (isalnum(d->d_name[0]))
continue;
snprintf(line, sizeof(line),
"/etc/resolvconf/run/interface/%s", d->d_name);
unlink(line);
}
closedir(dir);
}
#endif
#ifdef USE_VAR_RUN_RESOLVCONF
makepath("/var/run/resolvconf/interface");
symlink("../../../etc/resolv.conf", "/var/run/resolvconf/resolv.conf");
#endif
#ifdef USE_ETC_RESOLVCONF_RUN
touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface");
run_parts("/etc/resolvconf/update.d", "-i");
chdir("/");
#endif /* USE_ETC_RESOLVCONF_RUN */
}
static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc", },
};
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:
*/
+66
View File
@@ -0,0 +1,66 @@
/* Setup random seed
*
* Copyright (c) 2012 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 <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
static void setup(void *UNUSED(arg))
{
#ifdef RANDOMSEED
copyfile(RANDOMSEED, "/dev/urandom", 0);
unlink(RANDOMSEED);
umask(077);
copyfile("/dev/urandom", RANDOMSEED, 4096);
umask(0);
#endif
}
static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = {
.cb = setup
},
.depends = { "bootmisc", }
};
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:
*/
+78
View File
@@ -0,0 +1,78 @@
/* Console setup (for X)
*
* Copyright (c) 2012 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 <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
static void setup(void *UNUSED(arg))
{
#ifdef PAM_CONSOLE
int fd;
#endif
char line[LINE_SIZE];
makepath("/var/run/console");
snprintf(line, sizeof(line), "/var/run/console/%s", username);
touch(line);
#ifdef PAM_CONSOLE
if ((fd = open("/var/run/console/console.lock", O_CREAT|O_WRONLY|O_TRUNC, 0644)) >= 0) {
write(fd, username, strlen(username));
close(fd);
run("/sbin/pam_console_apply");
}
#endif
mkdir("/tmp/.X11-unix", 01777);
mkdir("/tmp/.ICE-unix", 01777);
}
static plugin_t plugin = {
.hook[HOOK_NETWORK_UP] = {
.cb = setup
},
.depends = { "bootmisc", }
};
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:
*/
+46
View File
@@ -0,0 +1,46 @@
/* Private header file for main finit daemon, not for plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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_PRIVATE_H_
#define FINIT_PRIVATE_H_
#include "svc.h"
#include "plugin.h"
void svc_start_all (void);
void svc_monitor (void);
void plugin_run_hooks (hook_point_t no);
int plugin_load_all (char *path);
void plugin_monitor (void);
#endif /* FINIT_PRIVATE_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+568
View File
@@ -0,0 +1,568 @@
/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(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); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */
+37
View File
@@ -0,0 +1,37 @@
/* reboot for finit
*
* Copyright (c) 2009-2012 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 <signal.h>
int main (void)
{
return !!kill(1, SIGINT);
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
Executable
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
# Alternative boot method for other distributions than Debian 6.0
# Place this file in a /etc/finit.d/ directory. At boot finit will
# run all files, using its built-in run_parts(), in that directory.
#
# Here we chain load Debian /etc/rcS.d and /etc/rc2.d directories
# in sequence using the standard run-parts tool.
run-parts /etc/rcS.d --arg=start
run-parts /etc/rc2.d --arg=start
# You can also place other commands in this services file, akin to
# the old finit /usr/sbin/services.sh (not supported anymore).
#service acpid start
#service networking start
#service ssh start
+189
View File
@@ -0,0 +1,189 @@
/* Signal management - Be conservative with what finit responds to!
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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.
*
* The standard SysV init only responds to the following signals:
* SIGHUP
* Has the same effect as telinit q.
*
* SIGUSR1
* On receipt of this signals, init closes and re-opens its control
* fifo, /dev/initctl. Useful for bootscripts when /dev is remounted.
*
* SIGINT
* Normally the kernel sends this signal to init when CTRL-ALT-DEL is
* pressed. It activates the ctrlaltdel action.
*
* SIGWINCH
* The kernel sends this signal when the KeyboardSignal key is hit.
* It activates the kbrequest action.
*/
#include <sys/reboot.h>
#include <sys/wait.h>
#include "finit.h"
#include "helpers.h"
#include "plugin.h"
#include "private.h"
#include "sig.h"
static int stopped = 0;
void do_shutdown (int sig)
{
touch(SYNC_SHUTDOWN);
if (sdown)
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
_d("Sending SIGTERM to all processes.");
kill(-1, SIGTERM);
sleep(1);
/* Call all shutdown hooks, this is the last call before rebooting... */
plugin_run_hooks(HOOK_SHUTDOWN);
_d("Sending SIGKILL to remaining processes.");
kill(-1, SIGKILL);
sync();
sync();
_d("Unmounting file systems, remounting / read-only.");
run("/bin/umount -fa 2>/dev/null");
run("/bin/mount -n -o remount,ro / 2>/dev/null");
run("/sbin/swapoff -ea");
_d("%s.", sig == SIGINT || sig == SIGUSR1 ? "Rebooting" : "Halting");
if (sig == SIGINT || sig == SIGUSR1)
reboot(RB_AUTOBOOT);
reboot(RB_POWER_OFF);
}
/*
* Shut down on INT USR1 USR2
*/
static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
{
_d("Rebooting on signal %d from %s (PID %d)",
sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
do_shutdown(sig);
}
/*
* SIGCHLD: one of our children has died
*/
static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(ctx))
{
/* Do nothing, the svc_monitor() is the designated child reaper. */
}
/*
* SIGSTOP: Paused by user or netflash
*/
static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
{
_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))
{
_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);
}
/*
* Is SIGSTOP asserted?
*/
int sig_stopped(void)
{
return stopped;
}
/*
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding
*/
void sig_init(void)
{
int i;
struct sigaction sa;
for (i = 1; i < NSIG; i++)
IGNSIG(sa, i, SA_RESTART);
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
}
/*
* Setup limited set of SysV compatible signals to respond to
*/
void sig_setup(void)
{
struct sigaction sa;
_d("Setup signals");
/* Cleanup any stale finit control files */
remove(SYNC_SHUTDOWN);
remove(SYNC_STOPPED);
/* Standard SysV init calls ctrl-alt-delete handler */
SETSIG(sa, SIGINT, shutdown_handler, 0);
SETSIG(sa, SIGPWR, shutdown_handler, 0);
/* 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);
/* Init must ignore SIGTERM. May otherwise get false SIGTERM in forked children! */
IGNSIG(sa, SIGTERM, 0);
/* Some C APIs may need SIGALRM for implementing timers. */
IGNSIG(sa, SIGALRM, 0);
/* We don't have any /etc/inittab yet, reread finit.conf? */
IGNSIG(sa, SIGHUP, 0);
/* Stopping init is a bit tricky. */
SETSIG(sa, SIGSTOP, sigstop_handler, 0);
SETSIG(sa, SIGTSTP, sigstop_handler, 0);
SETSIG(sa, SIGCONT, sigcont_handler, 0);
/* Disable CTRL-ALT-DELETE from kernel, we handle shutdown gracefully with SIGINT, above */
reboot(RB_DISABLE_CAD);
setsid();
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+70
View File
@@ -0,0 +1,70 @@
/* Signal management - Be conservative with what finit responds to!
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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_SIG_H_
#define FINIT_SIG_H_
#include <signal.h>
#define SYNC_SHUTDOWN "/var/lock/finit.shutdown"
#define SYNC_STOPPED "/var/lock/finit.stopped"
#define SETSIG(sa, sig, fun, flags) \
do { \
sa.sa_sigaction = fun; \
sa.sa_flags = SA_SIGINFO | flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while (0)
#define IGNSIG(sa, sig, flags) \
do { \
sa.sa_handler = SIG_IGN; \
sa.sa_flags = flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while (0)
#define DFLSIG(sa, sig, flags) \
do { \
sa.sa_handler = SIG_DFL; \
sa.sa_flags = flags; \
sigemptyset(&sa.sa_mask); \
sigaction(sig, &sa, NULL); \
} while (0)
void do_shutdown (int sig);
int sig_stopped (void);
void sig_init (void);
void sig_setup (void);
#endif /* FINIT_SIG_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+58
View File
@@ -0,0 +1,58 @@
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
/**
* Local Variables:
* version-control: t
* c-file-style: "bsd"
* End:
*/
+440
View File
@@ -0,0 +1,440 @@
/* Finit service monitor and heneric API for managing svc_t structures
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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 <string.h>
#include <sys/wait.h>
#include "finit.h"
#include "helpers.h"
#include "private.h"
#include "sig.h"
#include "lite.h"
#include "svc.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
* liveness state of all daemons that should run. */
static int svc_counter = 0; /* Number of registered services to monitor. */
static svc_t *services = NULL; /* List of registered services, in shared memory. */
/* Connect to/create shared memory area of registered servies */
static void __connect_shm(void)
{
if (!services) {
services = finit_svc_connect();
if (!services) {
/* This should never happen, but if it does we're probably
* knee-deep in more serious problems already... */
_e("Failed allocating shared memory, error %d: %s", errno, strerror (errno));
abort();
}
}
}
/**
* svc_new - Create a new service object
*
* Returns:
* A pointer to an empty &svc_t object, or %NULL if out of empty slots.
*/
svc_t *svc_new(void)
{
svc_t *svc = NULL;
__connect_shm();
if (svc_counter < MAX_NUM_SVC) {
svc = &services[svc_counter++];
memset(svc, 0, sizeof(*svc));
} else {
errno = ENOMEM;
return NULL;
}
return svc;
}
/**
* svc_find - Find a service object by its full path name
* @name: 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)
{
int i;
__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);
return svc;
}
}
return NULL;
}
/**
* svc_iterator - Iterates over all registered services.
* @restart: Get first &svc_t object by setting @restart
*
* Returns:
* The first &svc_t when @restart is set, otherwise the next &svc_t until
* the end when %NULL is returned.
*/
svc_t *svc_iterator(int restart)
{
static int i = 0;
__connect_shm();
if (restart)
i = 0;
if (i >= svc_counter)
return NULL;
return &services[i++];
}
/**
* svc_register - Register a non-backgrounding service to be monitored
* @line: A complete command line with -- separated description text
* @username: Optional username to run service as, or %NULL to run as root
*
* Actually, the @line can optionally start with a username, denoted by
* an @ character. Like this:
*
* "service [@username] /path/to/daemon -- Description text"
*
* The [] brackets means the argument is optional. If the username is
* left out the daemon is started as root.
*
* Returns:
* POSIX OK(0) on success, or non-zero errno exit status on failure.
*/
int svc_register(char *line, char *username)
{
int i = 0;
char *cmd, *desc;
svc_t *svc;
if (!line) {
_e("Invalid input argument.");
return errno = EINVAL;
}
desc = strstr(line, "-- ");
if (desc)
*desc = 0;
cmd = strtok(line, " ");
if (!cmd) {
incomplete:
_e("Incomplete service, cannot register.");
return errno = ENOENT;
}
if (cmd[0] == '@') { /* @username */
username = &cmd[1];
/* Check if valid command follows... */
cmd = strtok(NULL, " ");
if (!cmd)
goto incomplete;
}
svc = svc_new();
if (!svc) {
_e("Out of memorym, cannot register service %s", cmd);
return errno = ENOMEM;
}
if (desc)
strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
if (username)
strlcpy(svc->username, username, sizeof(svc->username));
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;
return 0;
}
/**
* svc_enabled - Should the service run?
* @svc: Pointer to &svc_t object
* @event: Dynamic event, opaque flag passed to callback
* @arg: Event argument, used only by external service plugins.
*
* This method calls an associated service callback, if registered by a
* plugin, and returns the &svc_cmd_t status. If no plugin is registered
* the service is statically enabled in /etc/finit.conf and the result
* will always be %SVC_START.
*
* Returns:
* Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
*/
svc_cmd_t svc_enabled(svc_t *svc, int event, void *arg)
{
if (!svc) {
errno = EINVAL;
return SVC_STOP;
}
/* Is there a service plugin registered? */
if (svc->cb)
return svc->cb(svc, event, arg);
/* No service plugin, default to start, since listed in finit.conf */
return SVC_START;
}
static int is_norespawn(void)
{
return sig_stopped() ||
fexist("/mnt/norespawn") ||
fexist("/tmp/norespawn");
}
void svc_monitor(void)
{
pid_t lost;
svc_t *svc;
lost = waitpid(-1, NULL, WNOHANG);
if (lost < 1)
return;
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
return;
/* Power user at the console, don't respawn tasks. */
if (is_norespawn())
return;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
char *name = basename(svc->cmd);
if (lost != svc->pid)
continue;
_d("Ouch, lost pid %d - %s(%d)", lost, name, svc->pid);
if (sig_stopped()) {
_e("Stopped, not respawning killed processes.");
break;
}
/* Cleanup any lingering or semi-restarting tasks before respawning */
_d("Sending SIGTERM to service group %s", name);
procname_kill(name, SIGTERM);
/* Restarting lost service. */
if (svc_enabled(svc, 0, NULL))
svc_start(svc);
break;
}
}
/* Remember: svc_enabled() must be called before calling svc_start() */
int svc_start(svc_t *svc)
{
int respawn = svc->pid != 0;
pid_t pid;
sigset_t nmask, omask;
char *args[MAX_NUM_SVC_ARGS];
if (!svc)
return 0;
/* Don't try and start service if it doesn't exist. */
if (!fexist(svc->cmd)) {
char msg[80];
snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd);
print_desc("", msg);
print_result(1);
return 0;
}
/* Ignore if finit is SIGSTOP'ed */
if (is_norespawn())
return 0;
if (!respawn)
print_desc("Starting ", svc->desc);
/* Block sigchild while forking. */
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork();
sigprocmask(SIG_SETMASK, &omask, NULL);
if (pid == 0) {
int i = 0;
int uid = getuser(svc->username);
struct sigaction sa;
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);
/* Set desired user */
if (uid >= 0)
setuid(uid);
/* Serve copy of args to process in case it modifies them. */
for (i = 0; svc->args[i][0] != 0 && i < MAX_NUM_SVC_ARGS; i++)
args[i] = svc->args[i];
args[i] = NULL;
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);
}
for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) {
char arg[MAX_ARG_LEN];
snprintf(arg, sizeof(arg), "%s ", args[i]);
if ((sizeof(buf) - strlen(buf)) < strlen(arg))
strcat(buf, arg);
}
_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);
/* Only reach this point if exec() fails. */
exit(0);
}
svc->pid = pid;
if (!respawn)
print_result(svc->pid > 1 ? 0 : 1);
return 0;
}
int svc_start_by_name(char *name)
{
svc_t *svc = svc_find(name);
if (svc && svc_enabled(svc, 0, NULL))
return svc_start(svc);
return 1;
}
int svc_stop(svc_t *svc)
{
int res = 1;
if (!svc) {
_e("Failed, no svc pointer.");
return 1;
}
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
svc->pid = 0;
return 1;
}
print_desc("Stopping ", svc->desc);
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, pid_get_name(svc->pid, NULL, 0));
res = kill(svc->pid, SIGTERM);
print_result(res);
svc->pid = 0;
return res;
}
int svc_reload(svc_t *svc)
{
/* Ignore if finit is SIGSTOP'ed */
if (is_norespawn())
return 0;
if (!svc) {
_e("Failed, no svc pointer.");
return 1;
}
if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGHUP", svc->pid, svc->cmd);
svc->pid = 0;
return 1;
}
_d("Sending SIGHUP to PID %d", svc->pid);
return kill(svc->pid, SIGHUP);
}
void svc_start_all(void)
{
svc_t *svc;
svc_cmd_t cmd;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
cmd = svc_enabled(svc, 0, NULL);
if (SVC_START == cmd || (SVC_RELOAD == cmd && svc->pid == 0))
svc_start(svc);
else if (SVC_RELOAD == cmd)
svc_reload(svc);
}
_d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
}
/**
* Local Variables:
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+106
View File
@@ -0,0 +1,106 @@
/* Finit service monitor and heneric API for managing svc_t structures
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 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_SVC_H_
#define FINIT_SVC_H_
#include <errno.h>
#include <string.h> /* strerror() */
#include <sys/shm.h> /* shmat() */
#include <sys/types.h> /* pid_t */
typedef enum {
SVC_STOP = 0, /* Disabled */
SVC_START, /* Enabled */
SVC_RELOAD /* Enabled, needs restart */
} svc_cmd_t;
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
#define MAX_ARG_LEN 64
#define MAX_STR_LEN 64
#define MAX_USER_LEN 16
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC 64 /* Enough? */
#define MAX_NUM_SVC_ARGS 16
/* Default enable for all services, can be stopped by means
* of issuing an initctl call. E.g.
* initctl <stop|start|restart> service */
typedef struct svc {
pid_t pid;
int reload;
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.
* @dynamic_stop: Set by plugins that allow dyn. events to stop it as well.
* @private: Can be used freely by plugin, e.g., to store "states".
*/
int dynamic;
int dynamic_stop;
int private;
svc_cmd_t (*cb)(struct svc *svc, int event, void *event_arg);
} svc_t;
typedef struct svc_map svc_map_t;
/* Put services array in shm */
static inline svc_t *finit_svc_connect(void)
{
const long ID = FINIT_SHM_ID;
static void *ptr = (void *)-1;
if ((void *)-1 == ptr) {
ptr = shmat (shmget (ID, sizeof(svc_t) * MAX_NUM_SVC, 0600 | IPC_CREAT), NULL, 0);
if ((void *)-1 == ptr)
return NULL;
}
return (svc_t *)ptr;
}
svc_t *svc_new (void);
svc_t *svc_find (char *name);
svc_t *svc_iterator (int restart);
int svc_register (char *line, char *username);
int svc_id_by_name (char *name);
svc_cmd_t svc_enabled (svc_t *svc, int event, void *arg);
int svc_start (svc_t *svc);
int svc_start_by_name (char *name);
int svc_stop (svc_t *svc);
int svc_reload (svc_t *svc);
#endif /* FINIT_SVC_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/