Compare commits

..
25 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
38 changed files with 2714 additions and 561 deletions
+41
View File
@@ -1,3 +1,44 @@
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) 1.2 (20120927)
- Fix installation paths encoded in finit binary - Fix installation paths encoded in finit binary
- Update README with section on building and enviroment variables - Update README with section on building and enviroment variables
+100 -37
View File
@@ -1,4 +1,4 @@
# finit - The Improved fast init # Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
# #
# Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> # Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
# Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> # Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -21,73 +21,127 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
.PHONY: all romfs clean .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 # Top directory for building complete system, fall back to this directory
ROOTDIR ?= $(shell pwd) ROOTDIR = $(shell pwd)
# Installation paths, always prepended with DESTDIR if set
prefix ?= /usr/local
sysconfdir ?= /etc
datadir = $(prefix)/share/doc/finit
mandir = $(prefix)/share/man/man8
# Plugin directory, fall back to this directory if unset in environment
PLUGIN_DIR ?= $(prefix)/lib/finit/plugins
#VERSION ?= $(shell git tag -l | tail -1) #VERSION ?= $(shell git tag -l | tail -1)
VERSION ?= 1.2 VERSION ?= 1.6
EXEC = finit NAME = finit
PKG = $(EXEC)-$(VERSION) PKG = $(NAME)-$(VERSION)
DEV = $(NAME)-dev
ARCHIVE = $(PKG).tar.xz 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 = finit.o conf.o helpers.o sig.o svc.o plugin.o
OBJS += strlcpy.o OBJS += strlcpy.o
SRCS = $(OBJS:.o=.c) SRCS = $(OBJS:.o=.c)
DEPS = $(addprefix .,$(SRCS:.c=.d)) DEPS = $(addprefix .,$(SRCS:.c=.d))
# 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
# The initctl FIFO, should probably be in /run, not /dev
FINIT_FIFO ?= /dev/initctl
FINIT_CONF ?= $(sysconfdir)/finit.conf
FINIT_RCSD ?= $(sysconfdir)/finit.d
CFLAGS += -W -Wall -Werror -Os CFLAGS += -W -Wall -Werror -Os
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997 # Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
CPPFLAGS += -U_FORTIFY_SOURCE CPPFLAGS += -U_FORTIFY_SOURCE
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE CPPFLAGS += -Ilibite -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\" CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
CPPFLAGS += -DPLUGIN_PATH=\"$(PLUGIN_DIR)\" CPPFLAGS += -DFINIT_FIFO=\"$(FINIT_FIFO)\" -DFINIT_CONF=\"$(FINIT_CONF)\"
LDFLAGS += -rdynamic CPPFLAGS += -DFINIT_RCSD=\"$(FINIT_RCSD)\" -DPLUGIN_PATH=\"$(plugindir)\"
LDLIBS += -ldl LDFLAGS += -rdynamic -L$(ROOTDIR)/libite
DEPLIBS = libite/libite.so
LDLIBS += -ldl -lite
include common.mk include common.mk
export PLUGIN_DIR ROOTDIR CPPFLAGS export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS
all: Makefile $(EXEC) all: $(DEPLIBS) $(EXEC)
$(MAKE) -C plugins $@ $(MAKE) -C plugins $@
$(DEPLIBS): Makefile
$(MAKE) -C libite all
$(OBJS): Makefile $(OBJS): Makefile
$(EXEC): $(OBJS) finit: $(OBJS) $(DEPLIBS)
# @ln -sf /sbin/finit /sbin/init reboot: reboot.o $(DEPLIBS)
install: all
@install -d $(DESTDIR)$(prefix)/sbin install-exec: all
@install -d $(DESTDIR)$(sysconfdir) @$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD)
@install -d $(DESTDIR)$(datadir) @$(INSTALL) -d $(DESTDIR)$(sbindir)
@install -d $(DESTDIR)$(mandir)
@for file in $(EXEC); do \ @for file in $(EXEC); do \
printf " INSTALL $(DESTDIR)$(prefix)/sbin/$$file\n"; \ printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
install -m 0755 $$file $(DESTDIR)$(prefix)/sbin/$$file; \ $(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
done done
$(MAKE) -C plugins $@ $(MAKE) -C libite install-exec
$(MAKE) -C plugins install
uninstall: install-data:
-@for file in $(EXEC); do \ @$(INSTALL) -d $(DESTDIR)$(datadir)
printf " REMOVE $(DESTDIR)$(prefix)/sbin/$$file\n"; \ @$(INSTALL) -d $(DESTDIR)$(mandir)
rm $(DESTDIR)$(prefix)/sbin/$$file 2>/dev/null; \ @for file in $(DISTFILES); do \
printf " INSTALL $(DESTDIR)$(datadir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(datadir)/$$file; \
done done
$(MAKE) -C plugins $@
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
install: install-exec install-data install-dev
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
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)
uninstall-dev:
-@$(RM) -rf $(DESTDIR)$(incdir)
uninstall: uninstall-exec uninstall-data uninstall-dev
clean: clean:
-@$(RM) $(OBJS) $(DEPS) $(EXEC) -@$(RM) $(OBJS) $(DEPS) $(EXEC)
$(MAKE) -C libite $@
$(MAKE) -C plugins $@ $(MAKE) -C plugins $@
distclean: clean distclean: clean
-@$(RM) $(JUNK) unittest *.o -@$(RM) $(JUNK) unittest *.o .*.d
$(MAKE) -C libite $@
$(MAKE) -C plugins $@ $(MAKE) -C plugins $@
dist: dist:
@@ -95,6 +149,15 @@ dist:
git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE) git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE)
@(cd ..; md5sum $(ARCHIVE) | tee $(ARCHIVE).md5) @(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: # Include automatically generated rules, such as:
# uncgi.o: .../some/dir/uncgi.c /usr/include/stdio.h # uncgi.o: .../some/dir/uncgi.c /usr/include/stdio.h
# but don't bother during clean! # but don't bother during clean!
+74 -48
View File
@@ -4,19 +4,17 @@
Welcome to finit! Welcome to finit!
This is a heavily extended version of the original /sbin/init replacement This is an extremely fast /sbin/init replacement with focus on small
finit_. The original was a reimplementation of the EeePC "fastinit" daemon embedded GNU/Linux systems. Based on Claudio Matsuoka's original finit_
based on its system calls with gaps filled with frog DNA. 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.
Focus of this extended version is on small embedded GNU/Linux systems and In the world of finit we write C plugins that hook into and extend the
headless miniature servers with a need for extremely fast boot. In such ``main()`` loop of ``/sbin/init``, this is what gives the system its raw
environments configurations rarely change and setup is rather static and performance and the user full control.
straight forward.
In the world of finit we write C "scripts" to boot our system, this is what If you chose finit you know what you want, a fast no hassles boot!
gives the system its raw performance and the user full control.
If you chose finit you know what you want, a fast boot with no hassle.
Features Features
@@ -41,31 +39,35 @@ plugins with either hooks or I/O demands.
/etc/finit.conf /etc/finit.conf
--------------- ---------------
Contrary to most script based init alternatives (SysV init_, upstart_, OpenRC_ Contrary to most script based init alternatives (`SysV init`_, upstart_,
and the likes) finit reads its configuration from /etc/finit.conf, see the OpenRC_ and the likes) finit instead reads its configuration from
source code for available options. ``/etc/finit.conf``, see the source code for available options.
Try out the examples/finit.conf for a Debian 6.0 example configuration When running ``make install`` no default ``/etc/finit.conf`` will be
capable of service monitoring SSH, sysklogd, gdm and a console getty! 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 /etc/finit.d
------------ ------------
At the end of the boot, when networking and all services are up, finit calls At the end of the boot, when networking and all services are up, finit
its built-in run-parts(8) on the /etc/finit.d/ directory, akin to how the calls its built-in run-parts(8) on the ``/etc/finit.d/`` directory, akin
/ec/rc.local file works in most other inits, only finit runs a directory of to how the ``/ec/rc.local`` file works in most other inits, only finit
scripts. This replaces the earlier /usr/sbin/services.sh support of the runs a directory of scripts. This replaces the earlier
original finit_. ``/usr/sbin/services.sh`` support of the original finit_.
Rebooting and Halting Rebooting and Halting
--------------------- ---------------------
Finit handles SIGUSR1 and SIGUSR2 for reboot and halt, and listens to Finit handles SIGUSR1 and SIGUSR2 for reboot and halt, and listens to
/dev/initctl so standard Linux reboot and halt commands should also ``/dev/initctl`` so standard Linux reboot and halt commands should also
work. work.
Building Building
-------- --------
@@ -80,15 +82,8 @@ what is built and where resulting binaries are installed.
**ROOTDIR=** **ROOTDIR=**
Top directory for building complete system, used in pretty printing Top directory for building complete system, used in pretty printing
**PLUGINS=**
List of stock finit plugins to build and install.
**PLUGIN_DIR=**
Absolute path to where finit should look for dynamically loadable plugins
at runtime. At installation prepended by ''DESTDIR'' and ''prefix''-
**VERSION=** **VERSION=**
Defaults to the currently released version of finit, e.g., 1.2 but can 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. be overridden by packages to add a suffix or completely alter the version.
**CFLAGS=** **CFLAGS=**
@@ -104,24 +99,37 @@ what is built and where resulting binaries are installed.
Standard LIBLIBS are inherited from the build enviornmant. Standard LIBLIBS are inherited from the build enviornmant.
**prefix=** **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 Path to where resulting binaries should install to. Used in concert
with the ''DESTDIR'' variable. Defaults to /usr/local with the ``DESTDIR`` variable. Defaults to ``/sbin``
**sysconfdir=** **sysconfdir=**
Used after ''prefix'' to create the path to finit.conf configuration Path to where finit configuration files should install to. Used in
file. Defaults to /etc 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=** **DESTDIR=**
Used by packagers and distributions when building a relocatable Used by packagers and distributions when building a relocatable
bundle of files. Alwawys prepended to the ''prefix'' destination bundle of files. Alwawys prepended to the ``prefix`` destination
directory. directory.
**Example**:: **Example**::
$ tar xfJ finit-1.2.tar.xz $ tar xfJ finit-1.3.tar.xz
$ PLUGINS="initctl.so hwclock.so" prefix= DESTDIR=/tmp/finit/dst \ $ PLUGINS="initctl.so hwclock.so" DESTDIR=/tmp/finit/dst \
make -C finit-1.2/ clean install make -C finit-1.3/ clean install
make: Entering directory `/home/troglobit/finit-1.2' make: Entering directory `/home/troglobit/finit-1.3'
CC finit.o CC finit.o
CC conf.o CC conf.o
CC helpers.o CC helpers.o
@@ -137,25 +145,42 @@ what is built and where resulting binaries are installed.
INSTALL /tmp/finit/dst/sbin/finit INSTALL /tmp/finit/dst/sbin/finit
INSTALL /tmp/finit/dst/lib/finit/plugins/initctl.so INSTALL /tmp/finit/dst/lib/finit/plugins/initctl.so
INSTALL /tmp/finit/dst/lib/finit/plugins/hwclock.so INSTALL /tmp/finit/dst/lib/finit/plugins/hwclock.so
make: Leaving directory `/home/troglobit/finit-1.2' make: Leaving directory `/home/troglobit/finit-1.3'
In this example the finit-1.2.tar.xz archive is unpacked to the user's In this example the `finit-1.3.tar.xz`_ archive is unpacked to the
home directory, built and installed to a temporary staging directory. user's home directory, built and installed to a temporary staging
The enviroment variables ''prefix'', ''DESTDIR'' and ''PLUGINS'' are all directory. The enviroment variables ``DESTDIR`` and ``PLUGINS`` are
changed to suit this particular build. 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 Debugging
--------- ---------
Add finit_debug, or --debug, to the kernel command line to enable trace Add ``finit_debug``, or ``--debug``, to the kernel command line to
messages. A console getty is always started, see the file finit.h for enable trace messages. A console getty is always started, see the file
more useful comile-time tweaks. ``finit.h`` for more useful comile-time tweaks::
init=/sbin/finit --debug
Contact Contact
------- -------
Finit is maintained collaborativly at https://github.com/troglobit/finit — 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 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. proposed extensions, or become a co-maintainer by contacting the main author.
@@ -163,7 +188,8 @@ Regards
/Joachim Nilsson <troglobit@gmail.com> /Joachim Nilsson <troglobit@gmail.com>
.. _finit: http://helllabs.org/finit/ .. _finit: http://helllabs.org/finit/
.. _init: http://savannah.nongnu.org/projects/sysvinit .. _fastinit: http://wiki.eeeuser.com/boot_process:the_boot_process
.. _`SysV init`: http://savannah.nongnu.org/projects/sysvinit
.. _upstart: http://upstart.ubuntu.com/ .. _upstart: http://upstart.ubuntu.com/
.. _openrc: http://www.gentoo.org/proj/en/base/openrc/ .. _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
+1
View File
@@ -3,3 +3,4 @@
* Look at how to support a standard SysV init runlevel mechanism * 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. * 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 * 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...
+3 -1
View File
@@ -1,7 +1,9 @@
# Some junk files we always want to be removed when doing a make clean. # 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.* JUNK = *~ *.bak *.aux *.dvi *.idx *.ind *.log *.ps *.map .*.d DEADJOE semantic.cache *.gdb *.elf core core.*
MAKE := @$(MAKE) MAKE := @$(MAKE)
MAKEFLAGS = --no-print-directory MAKEFLAGS = --no-print-directory --silent
INSTALL := install --backup=off
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755
# Smart autodependecy generation via GCC -M. # Smart autodependecy generation via GCC -M.
.%.d: %.c .%.d: %.c
+5
View File
@@ -122,6 +122,11 @@ void parse_finit_conf(char *file)
network = build_cmd(NULL, x, CMD_SIZE); network = build_cmd(NULL, x, CMD_SIZE);
continue; 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)) { if (MATCH_CMD(line, "startx ", x)) {
svc_register(x, username); svc_register(x, username);
continue; continue;
+33 -23
View File
@@ -1,4 +1,4 @@
/* Improved fast init /* Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -33,6 +33,7 @@
#include "plugin.h" #include "plugin.h"
#include "svc.h" #include "svc.h"
#include "sig.h" #include "sig.h"
#include "lite.h"
int debug = 0; int debug = 0;
int verbose = 1; int verbose = 1;
@@ -40,6 +41,7 @@ char *sdown = NULL;
char *network = NULL; char *network = NULL;
char *username = NULL; char *username = NULL;
char *hostname = NULL; char *hostname = NULL;
char *rcsd = FINIT_RCSD;
static void parse_kernel_cmdline(void) static void parse_kernel_cmdline(void)
@@ -49,6 +51,7 @@ static void parse_kernel_cmdline(void)
if ((fp = fopen("/proc/cmdline", "r")) != NULL) { if ((fp = fopen("/proc/cmdline", "r")) != NULL) {
fgets(line, sizeof(line), fp); fgets(line, sizeof(line), fp);
_d("Kernel command line: %s", line);
if (strstr(line, "finit_debug") || strstr(line, "--debug")) if (strstr(line, "finit_debug") || strstr(line, "--debug"))
debug = 1; debug = 1;
@@ -64,7 +67,7 @@ static int run_loop(void)
{ {
while (1) { while (1) {
svc_monitor(); svc_monitor();
io_monitor(); plugin_monitor();
} }
return 0; return 0;
@@ -91,15 +94,6 @@ int main(int UNUSED(args), char *argv[])
mount("none", "/dev/shm", "tmpfs", 0, NULL); mount("none", "/dev/shm", "tmpfs", 0, NULL);
umask(022); umask(022);
/*
* 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 kernel parameters * Parse kernel parameters
*/ */
@@ -108,11 +102,28 @@ int main(int UNUSED(args), char *argv[])
cls(); cls();
echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")"); 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 configuration file
*/ */
parse_finit_conf(FINIT_CONF); 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 filesystems
*/ */
@@ -124,6 +135,9 @@ int main(int UNUSED(args), char *argv[])
#ifdef SYSROOT #ifdef SYSROOT
run(SYSROOT, "/", NULL, MS_MOVE, NULL); run(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif #endif
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
umask(0); umask(0);
run("/bin/mount -na"); run("/bin/mount -na");
run("/sbin/swapon -ea"); run("/sbin/swapon -ea");
@@ -137,14 +151,8 @@ int main(int UNUSED(args), char *argv[])
*/ */
sig_setup(); sig_setup();
/* _d("Base FS up, calling hooks ...");
* Load plugins and run first level hooks. plugin_run_hooks(HOOK_BASEFS_UP);
*/
_d("Loading plugins ...");
load_plugins(PLUGIN_PATH);
_d("Running first level hooks ...");
run_hooks(HOOK_BASEFS_UP);
/* /*
* Network stuff * Network stuff
@@ -164,7 +172,7 @@ int main(int UNUSED(args), char *argv[])
/* /*
* Hooks that rely on loopback, or basic networking being up. * Hooks that rely on loopback, or basic networking being up.
*/ */
run_hooks(HOOK_NETWORK_UP); plugin_run_hooks(HOOK_NETWORK_UP);
/* /*
* Start service monitor framework * Start service monitor framework
@@ -175,13 +183,15 @@ int main(int UNUSED(args), char *argv[])
/* /*
* Run startup scripts in /etc/finit.d/, if any. * Run startup scripts in /etc/finit.d/, if any.
*/ */
_d("Running startup scripts in /etc/finit.d/ ..."); if (rcsd && fisdir(rcsd)) {
run_parts(FINIT_RCSD); _d("Running startup scripts in %s ...", rcsd);
run_parts(rcsd, NULL);
}
/* /*
* Hooks that should run at the very end * Hooks that should run at the very end
*/ */
run_hooks(HOOK_SYSTEM_UP); plugin_run_hooks(HOOK_SYSTEM_UP);
/* Start GETTY on console */ /* Start GETTY on console */
_d("Starting getty on console ..."); _d("Starting getty on console ...");
View File
+2 -46
View File
@@ -1,4 +1,4 @@
/* Improved fast init /* Finit - Extremely fast /sbin/init replacement w/ I/O, hook & service plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -33,35 +33,18 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <syslog.h>
#define DO_LOG(level, fmt, args...) \
{ \
openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \
syslog(LOG_DEBUG, fmt, ##args); \
closelog(); \
}
#define DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args)
#define ERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt, ##args)
#define FINIT_FIFO "/dev/initctl"
#define FINIT_CONF "/etc/finit.conf"
#define FINIT_RCSD "/etc/finit.d"
/* Distribution configuration */ /* Distribution configuration */
#if defined EMBEDDED_SYSTEM #if defined EMBEDDED_SYSTEM
# define CONSOLE "/dev/console" # define CONSOLE "/dev/console"
# define MDEV "/sbin/mdev" # define MDEV "/sbin/mdev"
# define GETTY "/sbin/getty -L 115200 ttyS0 vt100" # define GETTY "/sbin/getty -L 115200 ttyS0 vt100"
# define LISTEN_INITCTL
#else /* Debian/Ubuntu based distributions */ #else /* Debian/Ubuntu based distributions */
# define RANDOMSEED "/var/lib/urandom/random-seed" # define RANDOMSEED "/var/lib/urandom/random-seed"
# define CONSOLE "/dev/tty1" # define CONSOLE "/dev/tty1"
# define GETTY "/sbin/getty -8 38400 tty1" # define GETTY "/sbin/getty -8 38400 tty1"
# define REMOUNT_ROOTFS_RW # define REMOUNT_ROOTFS_RW
# define RUNLEVEL 2 # define RUNLEVEL 2
# define LISTEN_INITCTL
# define USE_UDEV # define USE_UDEV
# define USE_MESSAGE_BUS # define USE_MESSAGE_BUS
#endif #endif
@@ -79,42 +62,15 @@
#define USERNAME_SIZE 16 #define USERNAME_SIZE 16
#define HOSTNAME_SIZE 32 #define HOSTNAME_SIZE 32
#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 fexist
# define fexist(x) (access(x, F_OK) != -1)
#endif
#ifndef UNUSED
#define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
#endif
#define echo(fmt, args...) do { if (1) { 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() - " fmt "\n", __func__, ##args); } while (0)
#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt ". Error %d: %s\n", __func__, ##args, errno, strerror(errno)); } while (0)
extern int debug;
extern int verbose;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
extern char *hostname; extern char *hostname;
extern char *username; extern char *username;
extern char *rcsd;
/* strlcpy.c */
size_t strlcpy(char *dst, const char *src, size_t siz);
/* conf.c */ /* conf.c */
void parse_finit_conf(char *file); void parse_finit_conf(char *file);
#endif /* FINIT_H_ */ #endif /* FINIT_H_ */
/** /**
+281 -210
View File
@@ -1,4 +1,4 @@
/* Misc. utility functions and C-library extensions to simplify finit system setup. /* Misc. utility functions and C-library extensions for finit and its plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -48,6 +48,7 @@
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "lite.h"
#define NUM_ARGS 16 #define NUM_ARGS 16
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */ #define NUM_SCRIPTS 128 /* ought to be enough for anyone */
@@ -56,23 +57,30 @@
* Helpers to replace system() calls * 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; int ret;
x = path; if (!path) {
errno = EINVAL;
return -1;
}
do { do {
do { *x++ = *p++; } while (*p && *p != '/'); do {
*x++ = *path++;
} while (*path && *path != '/');
*x = 0; *x = 0;
ret = mkdir(path, 0777); ret = mkdir(buf, 0777);
} while (*p && (*p != '/' || *(p + 1))); /* ignore trailing slash */ } while (*path && (*path != '/' || *(path + 1))); /* ignore trailing slash */
return ret; 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 ifreq ifr;
struct sockaddr_in *a = (struct sockaddr_in *)&ifr.ifr_addr; struct sockaddr_in *a = (struct sockaddr_in *)&ifr.ifr_addr;
@@ -82,14 +90,18 @@ void ifconfig(char *name, char *inet, char *mask, int up)
return; return;
memset(&ifr, 0, sizeof (ifr)); memset(&ifr, 0, sizeof (ifr));
strlcpy(ifr.ifr_name, name, IFNAMSIZ); strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_family = AF_INET;
if (up) { if (up) {
inet_aton(inet, &a->sin_addr); if (addr) {
ioctl(sock, SIOCSIFADDR, &ifr); inet_aton(addr, &a->sin_addr);
inet_aton(mask, &a->sin_addr); ioctl(sock, SIOCSIFADDR, &ifr);
ioctl(sock, SIOCSIFNETMASK, &ifr); }
if (mask) {
inet_aton(mask, &a->sin_addr);
ioctl(sock, SIOCSIFNETMASK, &ifr);
}
} }
ioctl(sock, SIOCGIFFLAGS, &ifr); ioctl(sock, SIOCGIFFLAGS, &ifr);
@@ -105,30 +117,213 @@ void ifconfig(char *name, char *inet, char *mask, int up)
} }
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]; pid_t pid = 0;
int s, d, n; char buf[16];
FILE *fp;
/* Size == 0 means copy entire file */ if (!pidfile) {
if (size == 0) errno = EINVAL;
size = INT_MAX; return -1;
}
if ((s = open(src, O_RDONLY)) >= 0) { if (!fexist(pidfile))
if ((d = open(dst, O_WRONLY | O_CREAT, 0644)) >= 0) { return -1;
do {
int csize = size > BUF_SIZE ? BUF_SIZE : size; fp = fopen(pidfile, "r");
if (!fp)
if ((n = read(s, buffer, csize)) > 0) return -1;
write(d, buffer, n);
size -= csize; if (fgets(buf, sizeof(buf), fp)) {
} while (size > 0 && n == BUF_SIZE); errno = 0;
close(d); pid = strtoul(buf, NULL, 0);
} if (errno)
close(s); 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;
}
/**
* 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)
{
char name[24]; /* Enough for max pid_t */
snprintf(name, sizeof(name), "/proc/%d", pid);
return fexist(name);
}
/**
* 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)
{
int ret = 1;
FILE *fp;
char path[32];
static char line[64];
if (!name || len == 0)
name = line;
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" */
chomp(pname);
strlcpy(name, pname, len);
ret = 0; /* Found it! */
}
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(dir);
return result;
}
static int print_uptime(void) static int print_uptime(void)
{ {
#if defined(CONFIG_PRINTK_TIME) #if defined(CONFIG_PRINTK_TIME)
@@ -150,7 +345,7 @@ static int print_uptime(void)
return 0; return 0;
} }
void print_descr(char *action, char *descr) void print_desc(char *action, char *desc)
{ {
const char home[] = "\r\e[K"; const char home[] = "\r\e[K";
const char dots[] = " ....................................................................."; const char dots[] = " .....................................................................";
@@ -160,8 +355,8 @@ void print_descr(char *action, char *descr)
print_uptime(); print_uptime();
write(STDERR_FILENO, action, strlen(action)); write(STDERR_FILENO, action, strlen(action));
write(STDERR_FILENO, descr, strlen(descr)); write(STDERR_FILENO, desc, strlen(desc));
write(STDERR_FILENO, dots, 60 - strlen(descr) - strlen(action)); /* pad with dots. */ write(STDERR_FILENO, dots, 60 - strlen(desc) - strlen(action)); /* pad with dots. */
} }
int print_result(int fail) int print_result(int fail)
@@ -285,7 +480,7 @@ int run_interactive(char *cmd, char *fmt, ...)
vsnprintf(line, sizeof(line), fmt, ap); vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap); va_end(ap);
print_descr("", line); print_desc("", line);
if (fp && !debug) { if (fp && !debug) {
oldout = dup(STDOUT_FILENO); oldout = dup(STDOUT_FILENO);
olderr = dup(STDERR_FILENO); olderr = dup(STDERR_FILENO);
@@ -333,21 +528,19 @@ pid_t run_getty(char *cmd, char *argv[])
if (open(CONSOLE, O_RDWR) != 0) if (open(CONSOLE, O_RDWR) != 0)
exit(1); exit(1);
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
sigemptyset(&nmask); sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD); sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask, NULL); sigprocmask(SIG_UNBLOCK, &nmask, NULL);
for (i = 1; i < NSIG; i++) /* Reset signal handlers that were set by the parent process */
sigaction(i, &sa, NULL); for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
dup2(0, STDIN_FILENO); dup2(0, STDIN_FILENO);
dup2(0, STDOUT_FILENO); dup2(0, STDOUT_FILENO);
dup2(0, STDERR_FILENO); dup2(0, STDERR_FILENO);
set_procname(argv, "console"); procname_set("console", argv);
while (!fexist(SYNC_SHUTDOWN)) { while (!fexist(SYNC_SHUTDOWN)) {
static const char msg[] = "\nPlease press Enter to activate this console. "; static const char msg[] = "\nPlease press Enter to activate this console. ";
@@ -373,94 +566,80 @@ pid_t run_getty(char *cmd, char *argv[])
return pid; return pid;
} }
static int cmp(const void *s1, const void *s2) int run_parts(char *dir, char *cmd)
{ {
return strcmp(*(char **)s1, *(char **)s2); struct dirent **e;
} int i, num;
int run_parts(char *dir, ...) num = scandir(dir, &e, NULL, alphasort);
{ if (num < 0) {
DIR *d; _d("No files found in %s, skipping ...", dir);
struct dirent *e;
struct stat st;
char *oldpwd = NULL;
char *ent[NUM_SCRIPTS];
int i, num = 0, argnum = 1;
char *args[NUM_ARGS];
va_list ap;
oldpwd = getcwd (NULL, 0);
if (chdir(dir)) {
if (oldpwd) free(oldpwd);
return -1; return -1;
} }
if ((d = opendir(dir)) == NULL) {
if (oldpwd) free(oldpwd);
return -1;
}
va_start(ap, dir);
while (argnum < NUM_ARGS && (args[argnum++] = va_arg(ap, char *)));
va_end(ap);
while ((e = readdir(d))) {
if (e->d_type == DT_REG && stat(e->d_name, &st) == 0) {
_d("Found %s/%s ...", dir, e->d_name);
if (st.st_mode & S_IXUSR) {
ent[num++] = strdup(e->d_name);
if (num >= NUM_SCRIPTS)
break;
}
}
}
closedir(d);
if (num == 0) {
if (oldpwd) free(oldpwd);
return 0;
}
qsort(ent, num, sizeof(char *), cmp);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
int j = 0, status;
pid_t pid = 0; pid_t pid = 0;
int status; mode_t mode;
char *args[NUM_ARGS];
char *name = e[i]->d_name;
char path[CMD_SIZE];
args[0] = ent[i]; snprintf(path, sizeof(path), "%s/%s", dir, name);
args[1] = NULL; 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(); pid = fork();
if (!pid) { if (!pid) {
_d("Calling %s ...", ent[i]); _d("Calling %s ...", path);
execv(ent[i], args); execv(path, args);
exit(0); exit(0);
} }
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
free(ent[i]);
} }
chdir(oldpwd); while (num--)
free(oldpwd); free(e[num]);
free(e);
return 0; return 0;
} }
int getuser(char *s) int getuser(char *username)
{ {
struct passwd *usr; struct passwd *usr;
if (!s || (usr = getpwnam(s)) == NULL) if (!username || (usr = getpwnam(username)) == NULL)
return -1; return -1;
return usr->pw_uid; return usr->pw_uid;
} }
int getgroup(char *s) int getgroup(char *group)
{ {
struct group *grp; struct group *grp;
if ((grp = getgrnam(s)) == NULL) if ((grp = getgrnam(group)) == NULL)
return -1; return -1;
return grp->gr_gid; return grp->gr_gid;
@@ -478,122 +657,14 @@ void cls(void)
fprintf (stderr, "%s", cls); fprintf (stderr, "%s", cls);
} }
void chomp(char *s) void chomp(char *str)
{ {
char *x; char *x;
if ((x = strchr((s), 0x0a)) != NULL) if ((x = strchr((str), 0x0a)) != NULL)
*x = 0; *x = 0;
} }
void set_procname(char *args[], char *name)
{
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);
}
/**
* get_pidname - 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 *get_pidname(pid_t pid, char *name, size_t len)
{
int ret = 1;
FILE *fp;
char path[32];
static char line[64];
if (!name || len == 0)
name = line;
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" */
chomp(pname);
strlcpy(name, pname, len);
ret = 0; /* Found it! */
}
fclose(fp);
}
if (ret)
return NULL;
return name;
}
/**
* kill_procname - 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 kill_procname (const 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)) {
ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
} else {
result++; /* Track number of processes we deliver the signal to. */
}
}
}
fclose(fp);
}
closedir(dir);
return result;
}
void set_hostname(char *hostname) void set_hostname(char *hostname)
{ {
FILE *fp; FILE *fp;
+61 -12
View File
@@ -1,4 +1,4 @@
/* Misc. utility functions and C-library extensions to simplify finit system setup. /* Misc. utility functions and C-library extensions for finit and its plugins
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -25,25 +25,74 @@
#ifndef FINIT_HELPERS_H_ #ifndef FINIT_HELPERS_H_
#define FINIT_HELPERS_H_ #define FINIT_HELPERS_H_
int makepath (char *); #include <stdio.h>
void ifconfig (char *, char *, char *, int); #include <syslog.h>
void copyfile (char *, char *, int);
void print_descr (char *action, char *descr); #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
#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 print_result (int fail);
int start_process (char *cmd, char *args[], int console); int start_process (char *cmd, char *args[], int console);
void cls (void); void cls (void);
void chomp (char *); void chomp (char *str);
int getuser (char *); int getuser (char *username);
int getgroup (char *); int getgroup (char *group);
void set_procname (char *args[], char *name);
char *get_pidname (pid_t pid, char *name, size_t len);
int kill_procname (const char *name, int signo);
void set_hostname (char *hostname); void set_hostname (char *hostname);
int run (char *cmd); int run (char *cmd);
int run_interactive (char *cmd, char *fmt, ...); int run_interactive (char *cmd, char *fmt, ...);
pid_t run_getty (char *cmd, char *argv[]); pid_t run_getty (char *cmd, char *argv[]);
int run_parts (char *dir, ...); int run_parts (char *dir, char *cmd);
/* strlcpy.c */
size_t strlcpy(char *dst, const char *src, size_t siz);
#endif /* FINIT_HELPERS_H_ */ #endif /* FINIT_HELPERS_H_ */
+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)
+178 -53
View File
@@ -26,23 +26,26 @@
#include <dirent.h> /* readdir() et al */ #include <dirent.h> /* readdir() et al */
#include <poll.h> #include <poll.h>
#include <string.h> #include <string.h>
#include <sys/queue.h> /* BSD sys/queue.h API */
#include "finit.h" #include "finit.h"
#include "private.h" #include "private.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "svc.h" #include "queue.h" /* BSD sys/queue.h API */
#include "lite.h"
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0) #define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0)
static size_t num_fds = 0; static char *plugpath = NULL; /* Set by first load. */
static size_t num_fds = 0;
static struct pollfd fds[MAX_NUM_FDS]; static struct pollfd fds[MAX_NUM_FDS];
static LIST_HEAD(, plugin) plugins = LIST_HEAD_INITIALIZER(); static TAILQ_HEAD(, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
static void check_plugin_depends(plugin_t *plugin);
int plugin_register(plugin_t *plugin) int plugin_register(plugin_t *plugin)
{ {
int inuse = 0; int i, inuse = 0;
if (!plugin) { if (!plugin) {
errno = EINVAL; errno = EINVAL;
@@ -58,6 +61,14 @@ int plugin_register(plugin_t *plugin)
plugin->name = (char *)info.dli_fname; 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 (is_io_plugin(plugin)) {
if (num_fds + 1 >= MAX_NUM_FDS) { if (num_fds + 1 >= MAX_NUM_FDS) {
num_fds = MAX_NUM_SVC; num_fds = MAX_NUM_SVC;
@@ -69,40 +80,107 @@ int plugin_register(plugin_t *plugin)
} }
if (plugin->svc.cb) { if (plugin->svc.cb) {
svc_t *svc = svc_find_by_name(plugin->name); svc_t *svc = svc_find(plugin->name);
if (svc) { if (svc) {
plugin->svc.id = svc_id(svc);
svc->plugin = &plugin->svc;
inuse++; 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) { if (!inuse) {
_e("No service \"%s\" loaded, skipping plugin.", basename(plugin->name)); _d("No service \"%s\" loaded, and no I/O or finit hooks, skipping plugin.", basename(plugin->name));
return 1; return 1;
} }
LIST_INSERT_HEAD(&plugins, plugin, link); TAILQ_INSERT_TAIL(&plugins, plugin, link);
return 0; return 0;
} }
int plugin_unregister(plugin_t *plugin) int plugin_unregister(plugin_t *plugin)
{ {
LIST_REMOVE(plugin, link); 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! */ /* XXX: Unfinished, add cleanup code here! */
return 0; return 0;
} }
/* Private daemon API *******************************************************/
void run_hooks(hook_point_t no)
{
plugin_t *p;
PLUGIN_ITERATOR(p) { #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) { if (p->hook[no].cb) {
_d("Calling %s hook n:o %d from runloop...", basename(p->name), no); _d("Calling %s hook n:o %d from runloop...", basename(p->name), no);
p->hook[no].cb(p->hook[no].arg); p->hook[no].cb(p->hook[no].arg);
@@ -110,25 +188,13 @@ void run_hooks(hook_point_t no)
} }
} }
void run_services(void)
{
plugin_t *p;
PLUGIN_ITERATOR(p) {
if (p->svc.cb) {
_d("Calling svc %s from runloop...", basename(p->name));
p->svc.cb(p->svc.arg, 0);
}
}
}
/* Generic libev I/O callback, looks up correct plugin and calls its callback */ /* Generic libev I/O callback, looks up correct plugin and calls its callback */
static void generic_io_cb(int fd, int events) static void generic_io_cb(int fd, int events)
{ {
plugin_t *p; plugin_t *p, *tmp;
/* Find matching plugin, pick first matching fd */ /* Find matching plugin, pick first matching fd */
PLUGIN_ITERATOR(p) { PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p) && p->io.fd == fd) { if (is_io_plugin(p) && p->io.fd == fd) {
_d("Calling I/O %s from runloop...", basename(p->name)); _d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, fd, events); p->io.cb(p->io.arg, fd, events);
@@ -137,7 +203,7 @@ static void generic_io_cb(int fd, int events)
} }
} }
void io_monitor(void) void plugin_monitor(void)
{ {
int ret; int ret;
size_t i; size_t i;
@@ -166,20 +232,87 @@ void io_monitor(void)
static void init_plugins(void) static void init_plugins(void)
{ {
int i = 0; int i = 0;
plugin_t *p; plugin_t *p, *tmp;
PLUGIN_ITERATOR(p) { PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p)) { if (is_io_plugin(p)) {
_d("Initializing plugin %s for I/O", basename(p->name)); _d("Initializing plugin %s for I/O", basename(p->name));
fds[i].revents = 0; fds[i].revents = 0;
fds[i].events = p->io.flags; fds[i].events = p->io.flags;
fds[num_fds++].fd = p->io.fd; fds[i++].fd = p->io.fd;
} }
} }
} }
int load_plugins(char *path)
/**
* 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); DIR *dp = opendir(path);
struct dirent *entry; struct dirent *entry;
@@ -187,30 +320,22 @@ int load_plugins(char *path)
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno)); _e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1; return 1;
} }
plugpath = path;
while ((entry = readdir(dp))) { while ((entry = readdir(dp))) {
char *ext = entry->d_name + strlen(entry->d_name) - 3; if (entry->d_name[0] == '.')
continue; /* Skip . and .. directories */
if (!strcmp(ext, ".so")) { // print_desc(" Loading plugin ", basename(plugin));
void *handle; if (load_one(path, entry->d_name))
char filename[1024]; fail++;
// print_result(result);
snprintf(filename, sizeof(filename), "%s/%s", path, entry->d_name);
print_descr(" Loading plugin ", basename(filename));
handle = dlopen(filename, RTLD_LAZY);
if (!handle) {
_d("Failed loading plugin %s: %s", filename, dlerror());
print_result(1);
return 1;
}
print_result(0);
}
} }
closedir(dp); closedir(dp);
init_plugins(); init_plugins();
return 0; return fail;
} }
/** /**
+20 -17
View File
@@ -25,23 +25,27 @@
#define FINIT_PLUGIN_H_ #define FINIT_PLUGIN_H_
#include <poll.h> #include <poll.h>
#include <sys/queue.h> /* BSD sys/queue.h API */ #include "queue.h" /* BSD sys/queue.h API */
#include "svc.h"
#define PLUGIN_DEP_MAX 10
#define PLUGIN_IO_READ POLLIN #define PLUGIN_IO_READ POLLIN
#define PLUGIN_IO_WRITE POLLOUT #define PLUGIN_IO_WRITE POLLOUT
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void) #define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void) #define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
#define PLUGIN_ITERATOR(x) LIST_FOREACH(x, &plugins, link) #define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
/* /*
* Predefined hook points for easier plugin debugging * Predefined hook points for easier plugin debugging
*/ */
typedef enum { typedef enum {
HOOK_BASEFS_UP = 0, HOOK_ROOTFS_UP = 0,
HOOK_BASEFS_UP,
HOOK_NETWORK_UP, HOOK_NETWORK_UP,
HOOK_SYSTEM_UP, HOOK_SYSTEM_UP,
HOOK_SVC_UP,
HOOK_SHUTDOWN, HOOK_SHUTDOWN,
HOOK_MAX_NUM HOOK_MAX_NUM
} hook_point_t; } hook_point_t;
@@ -64,7 +68,8 @@ typedef enum {
* The "dynamic events" discussed in the svc callback is for external * The "dynamic events" discussed in the svc callback is for external
* service plugins to implement. However, it can be anything that * service plugins to implement. However, it can be anything that
* can cause a service to need to SIGHUP at runtime. E.g., acquiring * can cause a service to need to SIGHUP at runtime. E.g., acquiring
* a DHCP lease, or an interface going UP/DOWN. * 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 * It is up to the external service plugin to track these events and
* relay them to each @dynamic service plugins' callback. I.e., to * relay them to each @dynamic service plugins' callback. I.e., to
@@ -72,21 +77,17 @@ typedef enum {
*/ */
typedef struct plugin { typedef struct plugin {
/* BSD sys/queue.h linked list node. */ /* BSD sys/queue.h linked list node. */
LIST_ENTRY(plugin) link; TAILQ_ENTRY(plugin) link;
/* Plugin name, defaults to plugin path if unset. /* Plugin name, defaults to basename of plugin path if unset.
* NOTE: Must be same as @cmd for service plugins! */ * NOTE: Must be same as @cmd for service plugins! */
char *name; char *name;
/* Service callback to be called once per lap of runloop. */ /* Service callback to be called once per lap of runloop. */
struct plugin_svc { struct {
/* Private */ int dynamic; /* Callback wants external events as well. */
int id; /* Service ID# to match this service plugin against, set on installation. */ int dynamic_stop; /* Callback allows dynamic events to stop. */
/* Public */ svc_cmd_t (*cb)(svc_t *svc, int event, void *event_arg);
void *arg; /* Optional argument to callback func, set by plugin. */
int dynamic; /* Reload (SIGHUP) on dynamic event? Set by plugin. */
int private; /* For callbacks to use freely, possibly to store "states", set by plugin. */
int (*cb)(void *arg, int event);
} svc; } svc;
/* List of hook callbacks. */ /* List of hook callbacks. */
@@ -101,15 +102,17 @@ typedef struct plugin {
void *arg; void *arg;
void (*cb)(void *arg, int fd, int events); void (*cb)(void *arg, int fd, int events);
} io; } io;
} plugin_t;
/* Used by svc.h */ char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */
typedef struct plugin_svc plugin_svc_t; } plugin_t;
/* Public plugin API */ /* Public plugin API */
int plugin_register (plugin_t *plugin); int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin); int plugin_unregister (plugin_t *plugin);
/* Helper API */
plugin_t *plugin_find (char *name);
#endif /* FINIT_PLUGIN_H_ */ #endif /* FINIT_PLUGIN_H_ */
/** /**
+9 -8
View File
@@ -1,7 +1,8 @@
# Plugin based services architecture for finit -*-Makefile-*- # Plugin based services architecture for finit -*-Makefile-*-
CFLAGS = -fPIC -g -Os CFLAGS += -fPIC -Os
CPPFLAGS += -I.. CPPFLAGS += -I..
LDFLAGS = -shared LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
LDFLAGS += -shared
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \ PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
resolvconf.so urandom.so x11-common.so resolvconf.so urandom.so x11-common.so
DEPS = $(addprefix .,$(PLUGINS:.so=.d)) DEPS = $(addprefix .,$(PLUGINS:.so=.d))
@@ -17,18 +18,18 @@ distclean: clean
-@$(RM) $(JUNK) $(DEPS) -@$(RM) $(JUNK) $(DEPS)
install: all install: all
@install -d $(DESTDIR)$(prefix)$(PLUGIN_DIR) @install -d $(DESTDIR)$(plugindir)
@for plugin in $(PLUGINS); do \ @for plugin in $(PLUGINS); do \
echo " INSTALL $(DESTDIR)$(prefix)$(PLUGIN_DIR)/$$plugin"; \ echo " INSTALL $(DESTDIR)$(plugindir)/$$plugin"; \
cp $$plugin $(DESTDIR)$(prefix)$(PLUGIN_DIR); \ $(STRIPINST) $$plugin $(DESTDIR)$(plugindir)/$$plugin; \
done done
uninstall: uninstall:
-@for plugin in $(PLUGINS); do \ -@for plugin in $(PLUGINS); do \
echo " RM $(PLUGIN_DIR)/$$plugin"; \ echo " RM $(DESTDIR)$(plugindir)/$$plugin"; \
rm $(DESTDIR)$(prefix)$(PLUGIN_DIR)/$$plugin 2>/dev/null; \ rm $(DESTDIR)$(plugindir)/$$plugin 2>/dev/null; \
done done
-@rmdir $(DESTDIR)$(prefix)$(PLUGIN_DIR) 2>/dev/null -@rmdir $(DESTDIR)$(plugindir) 2>/dev/null
ifneq ($(MAKECMDGOALS),clean) ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS) -include $(DEPS)
+4 -4
View File
@@ -27,14 +27,14 @@
static void save(void *UNUSED(arg)) static void save(void *UNUSED(arg))
{ {
_d("Saving system clock to RTC ..."); _d("Saving sound settings ...");
run_interactive("/usr/sbin/alsactl store > /dev/null 2>&1", "Saving sound settings"); run_interactive("/usr/sbin/alsactl -g store", "Saving sound settings");
} }
static void restore(void *UNUSED(arg)) static void restore(void *UNUSED(arg))
{ {
_d("Restoring system clock from RTC ..."); _d("Restoring sound settings ...");
run_interactive("/usr/sbin/alsactl restore > /dev/null 2>&1", "Restoring sound settings"); run_interactive("/usr/sbin/alsactl -g restore", "Restoring sound settings");
} }
static plugin_t plugin = { static plugin_t plugin = {
+25
View File
@@ -28,12 +28,32 @@
#include "helpers.h" #include "helpers.h"
#include "plugin.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)) static void setup(void *UNUSED(arg))
{ {
#ifdef RUNLEVEL #ifdef RUNLEVEL
struct utmp entry; struct utmp entry;
#endif #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 ..."); _d("Setting up necessary UTMP files ...");
touch("/var/run/utmp"); touch("/var/run/utmp");
chown("/var/run/utmp", 0, getgroup("utmp")); chown("/var/run/utmp", 0, getgroup("utmp"));
@@ -55,12 +75,17 @@ static void setup(void *UNUSED(arg))
#endif #endif
_d("Setting up misc files ..."); _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 */ mkdir("/var/run/sshd", 01755); /* OpenSSH */
mkfifo("/dev/xconsole", 0640); /* sysklogd */ mkfifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, getgroup("tty")); chown("/dev/xconsole", 0, getgroup("tty"));
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = "bootmisc",
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
+1
View File
@@ -41,6 +41,7 @@ static plugin_t plugin = {
.hook[HOOK_NETWORK_UP] = { .hook[HOOK_NETWORK_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+4 -2
View File
@@ -28,13 +28,15 @@
static void save(void *UNUSED(arg)) static void save(void *UNUSED(arg))
{ {
_d("Saving system clock to RTC ..."); _d("Saving system clock to RTC ...");
run_interactive("/sbin/hwclock --systohc --utc", "Saving system time (UTC) to RTC"); /* -w,--systohc, -u,--utc */
run_interactive("/sbin/hwclock -w -u", "Saving system time (UTC) to RTC");
} }
static void restore(void *UNUSED(arg)) static void restore(void *UNUSED(arg))
{ {
_d("Restoring system clock from RTC ..."); _d("Restoring system clock from RTC ...");
run_interactive("/sbin/hwclock --hctosys --utc", "Restoring system clock (UTC) from RTC"); /* -s,--hctosys, -u,--utc */
run_interactive("/sbin/hwclock -s -u", "Restoring system clock (UTC) from RTC");
} }
static plugin_t plugin = { static plugin_t plugin = {
+4 -4
View File
@@ -23,11 +23,14 @@
*/ */
#include <errno.h> #include <errno.h>
#include <fcntl.h> /* O_RDONLY et al */
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <unistd.h> /* read() */
#include "finit.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "sig.h"
#define INIT_MAGIC 0x03091969 #define INIT_MAGIC 0x03091969
#define INIT_CMD_RUNLVL 1 #define INIT_CMD_RUNLVL 1
@@ -40,7 +43,6 @@ struct init_request {
char data[368]; char data[368];
}; };
#ifdef LISTEN_INITCTL
/* Standard reboot/shutdown utilities talk to init using /dev/initctl. /* Standard reboot/shutdown utilities talk to init using /dev/initctl.
* We should check if the fifo was recreated and reopen it. * We should check if the fifo was recreated and reopen it.
*/ */
@@ -109,8 +111,6 @@ PLUGIN_EXIT(plugin_exit)
plugin_unregister(&plugin); plugin_unregister(&plugin);
} }
#endif /* LISTEN_INITCTL */
/** /**
* Local Variables: * Local Variables:
* version-control: t * version-control: t
+2 -1
View File
@@ -59,7 +59,7 @@ static void setup(void *UNUSED(arg))
touch("/etc/resolvconf/run/enable-updates"); touch("/etc/resolvconf/run/enable-updates");
chdir("/etc/resolvconf/run/interface"); chdir("/etc/resolvconf/run/interface");
run_parts("/etc/resolvconf/update.d", "-i", NULL); run_parts("/etc/resolvconf/update.d", "-i");
chdir("/"); chdir("/");
#endif /* USE_ETC_RESOLVCONF_RUN */ #endif /* USE_ETC_RESOLVCONF_RUN */
} }
@@ -68,6 +68,7 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+1
View File
@@ -44,6 +44,7 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+1
View File
@@ -56,6 +56,7 @@ static plugin_t plugin = {
.hook[HOOK_NETWORK_UP] = { .hook[HOOK_NETWORK_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+7 -5
View File
@@ -25,13 +25,15 @@
#ifndef FINIT_PRIVATE_H_ #ifndef FINIT_PRIVATE_H_
#define FINIT_PRIVATE_H_ #define FINIT_PRIVATE_H_
#include "svc.h"
#include "plugin.h" #include "plugin.h"
/* plugin.c */ void svc_start_all (void);
void run_hooks (hook_point_t no); void svc_monitor (void);
void run_services (void);
int load_plugins (char *path); void plugin_run_hooks (hook_point_t no);
void io_monitor (void); int plugin_load_all (char *path);
void plugin_monitor (void);
#endif /* FINIT_PRIVATE_H_ */ #endif /* FINIT_PRIVATE_H_ */
+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:
*/
View File
+4 -4
View File
@@ -62,7 +62,7 @@ void do_shutdown (int sig)
sleep(1); sleep(1);
/* Call all shutdown hooks, this is the last call before rebooting... */ /* Call all shutdown hooks, this is the last call before rebooting... */
run_hooks(HOOK_SHUTDOWN); plugin_run_hooks(HOOK_SHUTDOWN);
_d("Sending SIGKILL to remaining processes."); _d("Sending SIGKILL to remaining processes.");
kill(-1, SIGKILL); kill(-1, SIGKILL);
@@ -87,7 +87,7 @@ void do_shutdown (int sig)
static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx)) static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
{ {
_d("Rebooting on signal %d from %s (PID %d)", _d("Rebooting on signal %d from %s (PID %d)",
sig, get_pidname(info->si_pid, NULL, 0), info->si_pid); sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
do_shutdown(sig); do_shutdown(sig);
} }
@@ -106,14 +106,14 @@ static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(
static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx)) static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
{ {
_d("Received SIGSTOP(%d) from %s (PID %d)", _d("Received SIGSTOP(%d) from %s (PID %d)",
sig, get_pidname(info->si_pid, NULL, 0), info->si_pid); sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
touch(SYNC_STOPPED); touch(SYNC_STOPPED);
stopped ++; stopped ++;
} }
static void sigcont_handler(int sig, siginfo_t *info, void *UNUSED(ctx)) static void sigcont_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
{ {
_d("Received SIGCONT(%d) from %s (PID %d)", _d("Received SIGCONT(%d) from %s (PID %d)",
sig, get_pidname(info->si_pid, NULL, 0), info->si_pid); sig, pid_get_name(info->si_pid, NULL, 0), info->si_pid);
stopped = 0; stopped = 0;
remove(SYNC_STOPPED); remove(SYNC_STOPPED);
} }
+84 -58
View File
@@ -1,4 +1,4 @@
/* Generic API for managing svc_t structures /* Finit service monitor and heneric API for managing svc_t structures
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -29,6 +29,7 @@
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "lite.h"
#include "svc.h" #include "svc.h"
/* The registered services are kept in shared memory for easy read-only access /* The registered services are kept in shared memory for easy read-only access
@@ -46,7 +47,7 @@ static void __connect_shm(void)
if (!services) { if (!services) {
/* This should never happen, but if it does we're probably /* This should never happen, but if it does we're probably
* knee-deep in more serious problems already... */ * knee-deep in more serious problems already... */
_e("Failed setting up shared memory for service monitor.\n"); _e("Failed allocating shared memory, error %d: %s", errno, strerror (errno));
abort(); abort();
} }
} }
@@ -64,9 +65,8 @@ svc_t *svc_new(void)
__connect_shm(); __connect_shm();
if (svc_counter < MAX_NUM_SVC) { if (svc_counter < MAX_NUM_SVC) {
svc = &services[svc_counter]; svc = &services[svc_counter++];
memset(svc, 0, sizeof(*svc)); memset(svc, 0, sizeof(*svc));
svc->id = svc_counter++;
} else { } else {
errno = ENOMEM; errno = ENOMEM;
return NULL; return NULL;
@@ -76,29 +76,13 @@ svc_t *svc_new(void)
} }
/** /**
* svc_find_by_id - Find a service object by its logical ID# * svc_find - Find a service object by its full path name
* @id: Logical ID#
*
* Returns:
* A pointer to an &svc_t object, or %NULL if not found.
*/
svc_t *svc_find_by_id(int id)
{
__connect_shm();
if (id >= svc_counter)
return NULL;
return &services[id];
}
/**
* svc_find_by_name - Find a service object by its full path name
* @name: Full path name, e.g., /sbin/syslogd * @name: Full path name, e.g., /sbin/syslogd
* *
* Returns: * Returns:
* A pointer to an &svc_t object, or %NULL if not found. * A pointer to an &svc_t object, or %NULL if not found.
*/ */
svc_t *svc_find_by_name(char *name) svc_t *svc_find(char *name)
{ {
int i; int i;
@@ -125,16 +109,16 @@ svc_t *svc_find_by_name(char *name)
*/ */
svc_t *svc_iterator(int restart) svc_t *svc_iterator(int restart)
{ {
static int id = 0; static int i = 0;
__connect_shm(); __connect_shm();
if (restart) if (restart)
id = 0; i = 0;
if (id >= svc_counter) if (i >= svc_counter)
return NULL; return NULL;
return &services[id++]; return &services[i++];
} }
/** /**
@@ -156,7 +140,7 @@ svc_t *svc_iterator(int restart)
int svc_register(char *line, char *username) int svc_register(char *line, char *username)
{ {
int i = 0; int i = 0;
char *cmd, *descr; char *cmd, *desc;
svc_t *svc; svc_t *svc;
if (!line) { if (!line) {
@@ -164,9 +148,9 @@ int svc_register(char *line, char *username)
return errno = EINVAL; return errno = EINVAL;
} }
descr = strstr(line, "-- "); desc = strstr(line, "-- ");
if (descr) if (desc)
*descr = 0; *desc = 0;
cmd = strtok(line, " "); cmd = strtok(line, " ");
if (!cmd) { if (!cmd) {
@@ -190,8 +174,8 @@ int svc_register(char *line, char *username)
return errno = ENOMEM; return errno = ENOMEM;
} }
if (descr) if (desc)
strlcpy(svc->descr, descr + 3, sizeof(svc->descr)); strlcpy(svc->desc, desc + 3, sizeof(svc->desc));
if (username) if (username)
strlcpy(svc->username, username, sizeof(svc->username)); strlcpy(svc->username, username, sizeof(svc->username));
strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
@@ -207,8 +191,9 @@ int svc_register(char *line, char *username)
/** /**
* svc_enabled - Should the service run? * svc_enabled - Should the service run?
* @svc: Pointer to &svc_t object * @svc: Pointer to &svc_t object
* @dynamic: Dynamic event, opaque flag passed to callback * @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 * This method calls an associated service callback, if registered by a
* plugin, and returns the &svc_cmd_t status. If no plugin is registered * plugin, and returns the &svc_cmd_t status. If no plugin is registered
@@ -218,19 +203,19 @@ int svc_register(char *line, char *username)
* Returns: * Returns:
* Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD. * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
*/ */
svc_cmd_t svc_enabled(svc_t *svc, int dynamic) svc_cmd_t svc_enabled(svc_t *svc, int event, void *arg)
{ {
if (!svc) { if (!svc) {
errno = EINVAL; errno = EINVAL;
return SVC_STOP; return SVC_STOP;
} }
if (!svc->plugin) { /* Is there a service plugin registered? */
/* Unknown service, default to enabled. */ if (svc->cb)
return SVC_START; return svc->cb(svc, event, arg);
}
return svc->plugin->cb(svc, dynamic); /* No service plugin, default to start, since listed in finit.conf */
return SVC_START;
} }
static int is_norespawn(void) static int is_norespawn(void)
@@ -270,35 +255,61 @@ void svc_monitor(void)
/* Cleanup any lingering or semi-restarting tasks before respawning */ /* Cleanup any lingering or semi-restarting tasks before respawning */
_d("Sending SIGTERM to service group %s", name); _d("Sending SIGTERM to service group %s", name);
kill_procname(name, SIGTERM); procname_kill(name, SIGTERM);
/* Restarting lost service. */ /* Restarting lost service. */
if (svc_enabled(svc, 0)) if (svc_enabled(svc, 0, NULL))
svc_start(svc); svc_start(svc);
break; break;
} }
} }
/* Remember: svc_enabled() must be called before calling svc_start() */
int svc_start(svc_t *svc) int svc_start(svc_t *svc)
{ {
int respawn = svc->pid != 0; int respawn = svc->pid != 0;
pid_t pid; pid_t pid;
sigset_t nmask, omask;
char *args[MAX_NUM_SVC_ARGS]; 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 */ /* Ignore if finit is SIGSTOP'ed */
if (is_norespawn()) if (is_norespawn())
return 0; return 0;
if (!respawn) if (!respawn)
print_descr("Starting ", svc->descr); print_desc("Starting ", svc->desc);
/* Block sigchild while forking. */
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork(); pid = fork();
sigprocmask(SIG_SETMASK, &omask, NULL);
if (pid == 0) { if (pid == 0) {
int i = 0; int i = 0;
int uid = getuser(svc->username); int uid = getuser(svc->username);
struct sigaction sa; struct sigaction sa;
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
/* Reset signal handlers that were set by the parent process */ /* Reset signal handlers that were set by the parent process */
for (i = 1; i < NSIG; i++) for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0); DFLSIG(sa, i, 0);
@@ -313,19 +324,29 @@ int svc_start(svc_t *svc)
args[i] = NULL; args[i] = NULL;
if (debug) { if (debug) {
int fd = open (CONSOLE, O_WRONLY | O_APPEND); int fd;
char buf[CMD_SIZE] = "";
fd = open (CONSOLE, O_WRONLY | O_APPEND);
if (-1 != fd) { if (-1 != fd) {
dup2(STDOUT_FILENO, fd); dup2(STDOUT_FILENO, fd);
dup2(STDERR_FILENO, fd); dup2(STDERR_FILENO, fd);
} }
_e("%starting %s ", respawn ? "Res" : "S", svc->cmd); for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) {
for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) char arg[MAX_ARG_LEN];
_e("%s ", args[i]);
_e(""); 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);
} }
execvp(svc->cmd, args); /* 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); exit(0);
} }
svc->pid = pid; svc->pid = pid;
@@ -338,9 +359,9 @@ int svc_start(svc_t *svc)
int svc_start_by_name(char *name) int svc_start_by_name(char *name)
{ {
svc_t *svc = svc_find_by_name(name); svc_t *svc = svc_find(name);
if (svc && svc_enabled(svc, 0)) if (svc && svc_enabled(svc, 0, NULL))
return svc_start(svc); return svc_start(svc);
return 1; return 1;
@@ -356,13 +377,13 @@ int svc_stop(svc_t *svc)
} }
if (svc->pid <= 1) { if (svc->pid <= 1) {
_d("Bad PID %d for %s, SIGTERM", svc->pid, svc->descr); _d("Bad PID %d for %s, SIGTERM", svc->pid, svc->desc);
svc->pid = 0; svc->pid = 0;
return 1; return 1;
} }
print_descr("Stopping ", svc->descr); print_desc("Stopping ", svc->desc);
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, get_pidname(svc->pid, NULL, 0)); _d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, pid_get_name(svc->pid, NULL, 0));
res = kill(svc->pid, SIGTERM); res = kill(svc->pid, SIGTERM);
print_result(res); print_result(res);
svc->pid = 0; svc->pid = 0;
@@ -372,6 +393,10 @@ int svc_stop(svc_t *svc)
int svc_reload(svc_t *svc) int svc_reload(svc_t *svc)
{ {
/* Ignore if finit is SIGSTOP'ed */
if (is_norespawn())
return 0;
if (!svc) { if (!svc) {
_e("Failed, no svc pointer."); _e("Failed, no svc pointer.");
return 1; return 1;
@@ -393,14 +418,15 @@ void svc_start_all(void)
svc_cmd_t cmd; svc_cmd_t cmd;
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
cmd = svc_enabled(svc, 0); cmd = svc_enabled(svc, 0, NULL);
if (cmd == SVC_START || (cmd == SVC_RELOAD && svc->pid == 0)) if (SVC_START == cmd || (SVC_RELOAD == cmd && svc->pid == 0))
svc_start(svc); svc_start(svc);
else if (cmd == SVC_RELOAD) else if (SVC_RELOAD == cmd)
svc_reload(svc); svc_reload(svc);
} }
/* XXX: Hook point for plugins. */ _d("Running svc up hooks ...");
plugin_run_hooks(HOOK_SVC_UP);
} }
+16 -28
View File
@@ -1,4 +1,4 @@
/* Generic API for managing svc_t structures /* Finit service monitor and heneric API for managing svc_t structures
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com> * Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
@@ -30,9 +30,6 @@
#include <sys/shm.h> /* shmat() */ #include <sys/shm.h> /* shmat() */
#include <sys/types.h> /* pid_t */ #include <sys/types.h> /* pid_t */
#include "finit.h"
#include "plugin.h"
typedef enum { typedef enum {
SVC_STOP = 0, /* Disabled */ SVC_STOP = 0, /* Disabled */
SVC_START, /* Enabled */ SVC_START, /* Enabled */
@@ -51,16 +48,22 @@ typedef enum {
* of issuing an initctl call. E.g. * of issuing an initctl call. E.g.
* initctl <stop|start|restart> service */ * initctl <stop|start|restart> service */
typedef struct svc { typedef struct svc {
/* Private */
int id; /* Service ID#, set by svc_new() */
pid_t pid; pid_t pid;
int reload;
char cmd[MAX_ARG_LEN]; char cmd[MAX_ARG_LEN];
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN]; char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
char descr[MAX_STR_LEN]; char desc[MAX_STR_LEN];
char username[MAX_USER_LEN]; char username[MAX_USER_LEN];
/* Public */
int delayed_reload; /* For external service plugin. */ /* For external plugins. If @cb is set a plugin is loaded.
plugin_svc_t *plugin; * @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; } svc_t;
typedef struct svc_map svc_map_t; typedef struct svc_map svc_map_t;
@@ -73,39 +76,24 @@ static inline svc_t *finit_svc_connect(void)
if ((void *)-1 == ptr) { if ((void *)-1 == ptr) {
ptr = shmat (shmget (ID, sizeof(svc_t) * MAX_NUM_SVC, 0600 | IPC_CREAT), NULL, 0); ptr = shmat (shmget (ID, sizeof(svc_t) * MAX_NUM_SVC, 0600 | IPC_CREAT), NULL, 0);
if ((void *)-1 == ptr) { if ((void *)-1 == ptr)
_d("Failed allocating shared memory, error %d: %s", errno, strerror (errno));
return NULL; return NULL;
}
} }
return (svc_t *)ptr; return (svc_t *)ptr;
} }
static inline int svc_id(svc_t *svc)
{
if (!svc) {
errno = EINVAL;
return -1;
}
return svc->id;
}
svc_t *svc_new (void); svc_t *svc_new (void);
svc_t *svc_find_by_id (int id); svc_t *svc_find (char *name);
svc_t *svc_find_by_name (char *name);
svc_t *svc_iterator (int restart); svc_t *svc_iterator (int restart);
int svc_register (char *line, char *username); int svc_register (char *line, char *username);
int svc_id_by_name (char *name); int svc_id_by_name (char *name);
svc_cmd_t svc_enabled (svc_t *svc, int dynamic); svc_cmd_t svc_enabled (svc_t *svc, int event, void *arg);
int svc_start (svc_t *svc); int svc_start (svc_t *svc);
int svc_start_by_name (char *name); int svc_start_by_name (char *name);
int svc_stop (svc_t *svc); int svc_stop (svc_t *svc);
int svc_reload (svc_t *svc); int svc_reload (svc_t *svc);
void svc_start_all (void);
void svc_monitor (void);
#endif /* FINIT_SVC_H_ */ #endif /* FINIT_SVC_H_ */