mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
849f3fbb28 | ||
|
|
329acf24bb | ||
|
|
bf40278131 | ||
|
|
4f2cf52eff | ||
|
|
f96fb855ce | ||
|
|
65f0be8a55 | ||
|
|
bcfcafdb7c | ||
|
|
c57bd066d1 | ||
|
|
4171fbdfd3 | ||
|
|
0f60aa2289 | ||
|
|
f97aab4699 | ||
|
|
740d447442 | ||
|
|
6448d6ccfa | ||
|
|
25cc9f3b9b | ||
|
|
e776838de9 | ||
|
|
bf0cfe0693 | ||
|
|
f690767507 | ||
|
|
4d57b14b5b | ||
|
|
263036045a | ||
|
|
56ef3f75d3 | ||
|
|
d0c902ab78 | ||
|
|
5ba7bc1fdf | ||
|
|
7e9ac7ba67 | ||
|
|
e2eaee9620 | ||
|
|
b23ce7ab8e | ||
|
|
53127a7ce6 | ||
|
|
011b73ba39 | ||
|
|
5d04a72a61 | ||
|
|
c3b76fdccf | ||
|
|
ec95243dad | ||
|
|
ae5d4a0bb2 | ||
|
|
0231f858e9 | ||
|
|
5d6db9f76f | ||
|
|
3bcefd2160 | ||
|
|
89675403d8 | ||
|
|
f277166354 | ||
|
|
b212babc87 | ||
|
|
6518c2d246 | ||
|
|
c551228079 | ||
|
|
c1198087af | ||
|
|
990ccc6d8b | ||
|
|
56a16066b7 | ||
|
|
407a47dd12 | ||
|
|
73b43b4f2b | ||
|
|
5961748b2a | ||
|
|
a18c5f3441 | ||
|
|
03c47a4020 | ||
|
|
77b0df3d07 | ||
|
|
7e3465a022 | ||
|
|
04fd3f7a1b | ||
|
|
685618691d | ||
|
|
f29d2733c7 | ||
|
|
af59174e49 | ||
|
|
785c21d27b | ||
|
|
40c7a44d4f | ||
|
|
cbb50937cd | ||
|
|
83a4bf7947 | ||
|
|
0d7d5a9820 | ||
|
|
65c9e7ebc6 | ||
|
|
c002b5301f | ||
|
|
3f1c2f6211 | ||
|
|
5116cd66ef | ||
|
|
f55f68d032 | ||
|
|
5e99b5e735 | ||
|
|
d26852e07a | ||
|
|
a90a7c04e4 | ||
|
|
14a1fb9cde | ||
|
|
b373e199a8 | ||
|
|
149ed0e81c | ||
|
|
e32ca1fc83 | ||
|
|
9aa90763b0 | ||
|
|
06759d3544 | ||
|
|
ff73dab447 | ||
|
|
0d70fa4cf0 | ||
|
|
103bf33b3c |
@@ -1,62 +0,0 @@
|
|||||||
1.2 (20120927)
|
|
||||||
- Fix installation paths encoded in finit binary
|
|
||||||
- Update README with section on building and enviroment variables
|
|
||||||
|
|
||||||
1.1 (20120927)
|
|
||||||
- Build fixes for ARM eabi/uClibc
|
|
||||||
- Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
|
|
||||||
|
|
||||||
1.0 (20120926)
|
|
||||||
- New plugin based system for all odd extensions
|
|
||||||
- New service monitor that restarts services if they die
|
|
||||||
- New maintainer at GitHub http://github.com/troglobit/finit
|
|
||||||
- Add standard LICENSE and AUTHORS files
|
|
||||||
- New focus: embedded systems and small headless servers
|
|
||||||
|
|
||||||
0.6 (20100614)
|
|
||||||
- don't start consolekit manually, dbus starts it (rtp)
|
|
||||||
- unmount all filesystems before rebooting
|
|
||||||
- disable USE_VAR_RUN_RESOLVCONF for Mandriva
|
|
||||||
- unset terminal type in Mandriva before running X
|
|
||||||
- remove extra sleep in finit-alt before calling services.sh (caio)
|
|
||||||
|
|
||||||
0.5 (20080821)
|
|
||||||
- add option to start dbus and consolekit before the X server
|
|
||||||
- finit-alt listens to /dev/initctl to work with reboot(8) (smurfy)
|
|
||||||
- write runlevel to utmp, needed by Printerdrake (Pascal Terjan)
|
|
||||||
- fix ownership of /var/run/utmp (reported by Pascal Terjan)
|
|
||||||
- remove obsolete code to load AGP drivers
|
|
||||||
- conditional build of /etc/resolveconf/run support
|
|
||||||
- add support to /var/run/resolvconf in Mandriva (blino)
|
|
||||||
|
|
||||||
O.4 (20080516)
|
|
||||||
- default username for finit-alt configurable in Makefile
|
|
||||||
- create loopback device node in finit-alt (for squashfs)
|
|
||||||
- add option to use built-in run-parts instead of /bin/run-parts
|
|
||||||
- ignore signal instead of setting to an empty handler (Metalshark)
|
|
||||||
- handle pam_console permissions in finit-alt for Mandriva
|
|
||||||
- add services.sh example and nash-hotplug patch for Mandriva
|
|
||||||
- mount /proc/bus/usb in Mandriva
|
|
||||||
- add runtime debug to finit-alt if finit_debug parameter is specified
|
|
||||||
- read configuration from /etc/finit.conf
|
|
||||||
- run getty with openvt on the virtual terminal
|
|
||||||
|
|
||||||
0.3 (20080223)
|
|
||||||
- Change poweroff method to reboot(RB_POWER_OFF) (Metalshark)
|
|
||||||
- Remove duplicate unionctl() reimplementation error
|
|
||||||
- Fix string termination in path creation
|
|
||||||
- Mount /var/lock and /var/run as tmpfs
|
|
||||||
|
|
||||||
0.2 (20080219)
|
|
||||||
- replace system("touch") with touch() in finit-mod (Metalshark)
|
|
||||||
- disable NO_HCTOSYS by default to match stock Eeepc kernel
|
|
||||||
- drop system("rm -f") to clean /tmp, its a fresh mounted tmpfs
|
|
||||||
- write ACPI sleep state to /sys/power/state instead of
|
|
||||||
/proc/acpi/sleep (Metalshark)
|
|
||||||
- use direct calls to set loopback instead of system("ifconfig")
|
|
||||||
- replace system("cat") and system("dd") with C implementation
|
|
||||||
- moved finit-mod and finit-alt helpers to helpers.c
|
|
||||||
- replace system("echo;cat") to draw shutdown splash with C calls
|
|
||||||
|
|
||||||
0.1 (20080214)
|
|
||||||
- initial release
|
|
||||||
@@ -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,124 @@
|
|||||||
# 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
|
#VERSION ?= $(shell git tag -l | tail -1)
|
||||||
ROOTDIR ?= $(shell pwd)
|
VERSION ?= 1.8
|
||||||
|
NAME = finit
|
||||||
|
PKG = $(NAME)-$(VERSION)
|
||||||
|
DEV = $(NAME)-dev
|
||||||
|
ARCHIVE = $(PKG).tar.xz
|
||||||
|
EXEC = finit reboot
|
||||||
|
HEADERS = plugin.h svc.h helpers.h queue.h
|
||||||
|
DISTFILES = LICENSE README NEWS finit.conf services
|
||||||
|
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o
|
||||||
|
SRCS = $(OBJS:.o=.c)
|
||||||
|
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
||||||
|
|
||||||
# Installation paths, always prepended with DESTDIR if set
|
# Installation paths, always prepended with DESTDIR if set
|
||||||
prefix ?= /usr/local
|
TOPDIR = $(shell pwd)
|
||||||
|
prefix ?= /usr
|
||||||
sysconfdir ?= /etc
|
sysconfdir ?= /etc
|
||||||
|
sbindir ?= /sbin
|
||||||
|
libdir ?= /lib
|
||||||
|
plugindir ?= $(libdir)/finit/plugins
|
||||||
|
incdir = $(prefix)/include/finit
|
||||||
datadir = $(prefix)/share/doc/finit
|
datadir = $(prefix)/share/doc/finit
|
||||||
mandir = $(prefix)/share/man/man8
|
mandir = $(prefix)/share/man/man8
|
||||||
|
|
||||||
# Plugin directory, fall back to this directory if unset in environment
|
# The initctl FIFO, should probably be in /run, not /dev
|
||||||
PLUGIN_DIR ?= $(prefix)/lib/finit/plugins
|
FINIT_FIFO ?= /dev/initctl
|
||||||
|
FINIT_CONF ?= $(sysconfdir)/finit.conf
|
||||||
|
FINIT_RCSD ?= $(sysconfdir)/finit.d
|
||||||
|
|
||||||
#VERSION ?= $(shell git tag -l | tail -1)
|
|
||||||
VERSION ?= 1.2
|
|
||||||
EXEC = finit
|
|
||||||
PKG = $(EXEC)-$(VERSION)
|
|
||||||
ARCHIVE = $(PKG).tar.xz
|
|
||||||
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o
|
|
||||||
OBJS += strlcpy.o
|
|
||||||
SRCS = $(OBJS:.o=.c)
|
|
||||||
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
|
||||||
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$(TOPDIR)/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,9 +146,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)
|
||||||
|
|
||||||
# Include automatically generated rules, such as:
|
dev: distclean
|
||||||
# uncgi.o: .../some/dir/uncgi.c /usr/include/stdio.h
|
@echo "Building unstable xz $(DEV) in parent dir..."
|
||||||
# but don't bother during clean!
|
-@$(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)
|
||||||
|
|
||||||
ifneq ($(MAKECMDGOALS),clean)
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
ifneq ($(MAKECMDGOALS),distclean)
|
ifneq ($(MAKECMDGOALS),distclean)
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
==============================================================================
|
||||||
|
NEWS
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
1.8 (2013-06-07)
|
||||||
|
----------------
|
||||||
|
* Support for runlevels, with a bootstrap runlevel 'S'
|
||||||
|
* Support for saving previous and current runlevel to UTMP
|
||||||
|
* Support for new finit.conf commands: run, task, and runlevel
|
||||||
|
* Support for tty and console commands in finit.conf, like services but
|
||||||
|
for launching multiple getty logins
|
||||||
|
* New tty plugin to monitor TTYs coming and going, like USB TTYs
|
||||||
|
* Bugfixes to libite
|
||||||
|
|
||||||
|
|
||||||
|
1.7 (2012-10-08)
|
||||||
|
----------------
|
||||||
|
* Show __FILE__ in _d() debug messages, useful for plugins with
|
||||||
|
similarily named callbacks. Also, only in debug mode anyway
|
||||||
|
* Make sure to cleanup recorded PID when a service is lost. Required by
|
||||||
|
service plugins for their callbacks to work.
|
||||||
|
* Bugfix: Do not free() static string in finit.conf parser
|
||||||
|
* Only clear screen when in verbose mode. Maybe this should be removed
|
||||||
|
altogether?
|
||||||
|
|
||||||
|
|
||||||
|
1.6 (2012-10-06)
|
||||||
|
----------------
|
||||||
|
* Skip . and .. in plugin loader and display error on failed plugins
|
||||||
|
* Support for overriding /etc/finit.d with "runparts DIR" in finit.conf
|
||||||
|
* Revoke support for starting services not starting with a slash.
|
||||||
|
* Prevent endless restart of non-existing services in finit.conf
|
||||||
|
* Support for sysvinit style startstop scripts in /etc/finit.d
|
||||||
|
* Minor fix to alsa-utils plugin to silence on non-existing cards
|
||||||
|
|
||||||
|
|
||||||
|
1.5 (2012-10-03)
|
||||||
|
----------------
|
||||||
|
* Use bootmisc plugin to setup standard FHS 2.3 structure in /var
|
||||||
|
* Added FLOG_WARN() syslog macro, for plugins
|
||||||
|
* Add plugin dependency resolver. Checks plugin_t for .depends
|
||||||
|
|
||||||
|
|
||||||
|
1.4 (2012-10-02)
|
||||||
|
----------------
|
||||||
|
* Fix I/O plugin watcher and load plugins earlier for a new hook
|
||||||
|
* Start refactoring helpers.c into a libite.so (-lite). This means
|
||||||
|
other user space applications/daemons can make use of the neat toolbox
|
||||||
|
available in finit
|
||||||
|
* Use short-form -s/-w -u to work with BusyBox hwclock as well
|
||||||
|
* Use RTLD_GLOBAL flag to tell dynamic loader to load dependent .so
|
||||||
|
files as well. Lets other plugins use global symbols.
|
||||||
|
* Greatly simplify svc hook for external plugins and cleanup plugin API.
|
||||||
|
* And more... see the GIT log for more details.
|
||||||
|
|
||||||
|
|
||||||
|
1.3 (2012-09-28)
|
||||||
|
----------------
|
||||||
|
* Cleanup public plugin API a bit and add new pid/pidfile funcs
|
||||||
|
* Add plugin hook to end of service startup
|
||||||
|
* Remove finit.h from svc.h, plugins should not need this.
|
||||||
|
* Move utility macros etc. to helpers.h
|
||||||
|
* Make finit.h daemon internal, only
|
||||||
|
* Move defines of FIFO, conf and rcS.d to Makefile => correct paths
|
||||||
|
* Add support for installing required headers in system include dir
|
||||||
|
* Better support for distributions and packagers with install-exec,
|
||||||
|
install-data, and install-dev targets in Makefile. Useful if you want
|
||||||
|
to call targets with different $DESTDIR!
|
||||||
|
* Makefile fixes for installation, paths encoded wrong
|
||||||
|
* Strip binaries + .so files, support for $(CROSS) toolchain strip
|
||||||
|
* Default install is now to /sbin/finit and /usr/
|
||||||
|
* Note change in $PLUGIN_DIR environemnt variable to $plugindir
|
||||||
|
|
||||||
|
|
||||||
|
1.2 (2012-09-27)
|
||||||
|
----------------
|
||||||
|
* Fix installation paths encoded in finit binary
|
||||||
|
* Update README with section on building and enviroment variables
|
||||||
|
|
||||||
|
|
||||||
|
1.1 (2012-09-27)
|
||||||
|
----------------
|
||||||
|
* Build fixes for ARM eabi/uClibc
|
||||||
|
* Rename signal.[ch]-->sig.[ch] to avoid name clash w/ system headers
|
||||||
|
|
||||||
|
|
||||||
|
1.0 (2012-09-26)
|
||||||
|
----------------
|
||||||
|
* New plugin based system for all odd extensions
|
||||||
|
* New service monitor that restarts services if they die
|
||||||
|
* New maintainer at GitHub http://github.com/troglobit/finit
|
||||||
|
* Add standard LICENSE and AUTHORS files
|
||||||
|
* New focus: embedded systems and small headless servers
|
||||||
|
|
||||||
|
|
||||||
|
0.6 (2010-06-14)
|
||||||
|
----------------
|
||||||
|
* don't start consolekit manually, dbus starts it (rtp)
|
||||||
|
* unmount all filesystems before rebooting
|
||||||
|
* disable USE_VAR_RUN_RESOLVCONF for Mandriva
|
||||||
|
* unset terminal type in Mandriva before running X
|
||||||
|
* remove extra sleep in finit-alt before calling services.sh (caio)
|
||||||
|
|
||||||
|
|
||||||
|
0.5 (2008-08-21)
|
||||||
|
----------------
|
||||||
|
* add option to start dbus and consolekit before the X server
|
||||||
|
* finit-alt listens to /dev/initctl to work with reboot(8) (smurfy)
|
||||||
|
* write runlevel to utmp, needed by Printerdrake (Pascal Terjan)
|
||||||
|
* fix ownership of /var/run/utmp (reported by Pascal Terjan)
|
||||||
|
* remove obsolete code to load AGP drivers
|
||||||
|
* conditional build of /etc/resolveconf/run support
|
||||||
|
* add support to /var/run/resolvconf in Mandriva (blino)
|
||||||
|
|
||||||
|
|
||||||
|
O.4 (2008-05-16)
|
||||||
|
----------------
|
||||||
|
* default username for finit-alt configurable in Makefile
|
||||||
|
* create loopback device node in finit-alt (for squashfs)
|
||||||
|
* add option to use built-in run-parts instead of /bin/run-parts
|
||||||
|
* ignore signal instead of setting to an empty handler (Metalshark)
|
||||||
|
* handle pam_console permissions in finit-alt for Mandriva
|
||||||
|
* add services.sh example and nash-hotplug patch for Mandriva
|
||||||
|
* mount /proc/bus/usb in Mandriva
|
||||||
|
* add runtime debug to finit-alt if finit_debug parameter is specified
|
||||||
|
* read configuration from /etc/finit.conf
|
||||||
|
* run getty with openvt on the virtual terminal
|
||||||
|
|
||||||
|
|
||||||
|
0.3 (2008-02-23)
|
||||||
|
----------------
|
||||||
|
* Change poweroff method to reboot(RB_POWER_OFF) (Metalshark)
|
||||||
|
* Remove duplicate unionctl() reimplementation error
|
||||||
|
* Fix string termination in path creation
|
||||||
|
* Mount /var/lock and /var/run as tmpfs
|
||||||
|
|
||||||
|
|
||||||
|
0.2 (2008-02-19)
|
||||||
|
----------------
|
||||||
|
* replace system("touch") with touch() in finit-mod (Metalshark)
|
||||||
|
* disable NO_HCTOSYS by default to match stock Eeepc kernel
|
||||||
|
* drop system("rm -f") to clean /tmp, its a fresh mounted tmpfs
|
||||||
|
* write ACPI sleep state to /sys/power/state instead of /proc/acpi/sleep
|
||||||
|
(Metalshark)
|
||||||
|
* use direct calls to set loopback instead of system("ifconfig")
|
||||||
|
* replace system("cat") and system("dd") with C implementation
|
||||||
|
* moved finit-mod and finit-alt helpers to helpers.c
|
||||||
|
* replace system("echo;cat") to draw shutdown splash with C calls
|
||||||
|
|
||||||
|
|
||||||
|
0.1 (2008-02-14)
|
||||||
|
----------------
|
||||||
|
* initial release
|
||||||
|
|
||||||
|
|
||||||
|
.. Local Variables:
|
||||||
|
.. mode: rst
|
||||||
|
.. version-control: t
|
||||||
|
.. End:
|
||||||
+158
-55
@@ -1,37 +1,36 @@
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
Finit — A fast plugin-based /sbin/init with service monitoring
|
Finit — A fast plugin-based /sbin/init with service monitoring
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
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
|
||||||
--------
|
--------
|
||||||
|
|
||||||
**Service monitoring**
|
**Service monitoring**
|
||||||
Restarting a service when it exits
|
Restarting a service when it exits
|
||||||
|
|
||||||
**Plugins**
|
**Plugins**
|
||||||
Extend and modify finit behavior. See examples in plugins/ directory.
|
Extend and modify finit behavior. See examples in plugins/ directory.
|
||||||
Plugin capabilities:
|
Plugin capabilities:
|
||||||
|
|
||||||
* Service callbacks — should the service run/restart/stop?
|
* Service callbacks — should the service run/restart/stop?
|
||||||
* Hooks — hook into the boot at predefined points to extend finit
|
* Task/Run callbacks — a one-shot command, should it run in sequence?
|
||||||
* I/O — listen to external events and control finit behaviour/services
|
* Hooks — hook into the boot at predefined points to extend finit
|
||||||
|
* I/O — listen to external events and control finit behaviour/services
|
||||||
|
|
||||||
Most extensions and functionality not purely related to what an /sbin/init
|
Most extensions and functionality not purely related to what an /sbin/init
|
||||||
needs to start a system from the original finit_ has been reactored into
|
needs to start a system from the original finit_ has been reactored into
|
||||||
@@ -41,31 +40,110 @@ 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``. Below is a brief list, see the source code for the
|
||||||
|
full list:
|
||||||
|
|
||||||
Try out the examples/finit.conf for a Debian 6.0 example configuration
|
check
|
||||||
capable of service monitoring SSH, sysklogd, gdm and a console getty!
|
Run fsck on a file system before mounting it
|
||||||
|
|
||||||
|
module
|
||||||
|
Load a kernel module, with optional arguments
|
||||||
|
|
||||||
|
network
|
||||||
|
Script or program to bringup networking, with optional arguments
|
||||||
|
|
||||||
|
runlevel N
|
||||||
|
N is the runlevel number 1-9, where 6 is reserved for reboot
|
||||||
|
|
||||||
|
run [RUN_LVLS] /path/to/cmd ARGS -- Optional description
|
||||||
|
One-shot command to run in sequence when entering a runlevel, with
|
||||||
|
optional arguments and description. This command is guaranteed to
|
||||||
|
be completed before running the next command.
|
||||||
|
|
||||||
|
task [RUN_LVLS] /path/to/cmd ARGS -- Optional description
|
||||||
|
One-shot like 'run', but starts in parallel with the next command
|
||||||
|
|
||||||
|
service [RUN_LVLS] /path/to/daemon ARGS -- Optional description
|
||||||
|
Service, or daemon, to be monitored and automatically restarted if
|
||||||
|
it exits prematurely. Please note that you often need to provide
|
||||||
|
a --foreground or --no-background argument to most daemons to
|
||||||
|
prevent them from forking off to the background.
|
||||||
|
|
||||||
|
runparts
|
||||||
|
Call run-parts(8) on a directory other than the default /etc/finit.d
|
||||||
|
|
||||||
|
tty [RUN_LVLS] DEV
|
||||||
|
Run getty on the listed TTY device, in the given runlevels
|
||||||
|
|
||||||
|
console DEV
|
||||||
|
Some embedded systems have a dedicated console port. This command
|
||||||
|
tells finit to not start getty, but instead print a friendly message
|
||||||
|
and wait for the user to activate the console with a keypress before
|
||||||
|
starting getty.
|
||||||
|
|
||||||
|
When running ``make install`` no default ``/etc/finit.conf`` will be
|
||||||
|
provided since the system requirements differ too much. Try out the
|
||||||
|
Debian 6.0 example ``/usr/share/doc/finit/finit.conf`` configuration
|
||||||
|
that is capable of service monitoring SSH, sysklogd, gdm and a console
|
||||||
|
getty!
|
||||||
|
|
||||||
|
|
||||||
/etc/finit.d
|
/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, if
|
||||||
/ec/rc.local file works in most other inits, only finit runs a directory of
|
it exists. Similar to how the ``/ec/rc.local`` file works in most other
|
||||||
scripts. This replaces the earlier /usr/sbin/services.sh support of the
|
inits, only finit runs a directory of scripts. This replaces the
|
||||||
original finit_.
|
earlier support for a ``/usr/sbin/services.sh`` script in the original
|
||||||
|
finit_.
|
||||||
|
|
||||||
|
|
||||||
|
Runlevels
|
||||||
|
---------
|
||||||
|
|
||||||
|
Support for runlevels_ is included in Finit from v1.8. By default all
|
||||||
|
services, tasks, run commands and TTYs listed without a set of runlevels
|
||||||
|
get a default set [234] assigned. The default runlevel after boot is 2.
|
||||||
|
|
||||||
|
To specify an allowed set of runlevels for a service, run command, task,
|
||||||
|
or tty, add [NNN] to it in finit.conf, like this:
|
||||||
|
|
||||||
|
run [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||||
|
task [S] /etc/init.d/kbd start -- Preparing console
|
||||||
|
service [S12345] /sbin/klogd -n -x -- Kernel log daemon
|
||||||
|
tty [12345] /dev/tty1
|
||||||
|
tty [2] /dev/tty2
|
||||||
|
tty [2] /dev/tty3
|
||||||
|
tty [2] /dev/tty4
|
||||||
|
tty [2] /dev/tty5
|
||||||
|
tty [2] /dev/tty6
|
||||||
|
|
||||||
|
In this example acpid is started once at bootstrap using a conventional
|
||||||
|
SysV init script. Here the run command was used, meaning the following
|
||||||
|
task command is not run until the init script has fully completed.
|
||||||
|
|
||||||
|
Tasks and services are started in parallell, while run commands are run
|
||||||
|
in the order listed and subsequent commands are not started until a run
|
||||||
|
command has completed.
|
||||||
|
|
||||||
|
Existing finit.conf files that lack runlevel setting will get a default
|
||||||
|
runlevel assigned, [234].
|
||||||
|
|
||||||
|
Switching between runlevels can be done by calling init with a single
|
||||||
|
argument, e.g., 'init 5' switches to runlevel 5.
|
||||||
|
|
||||||
|
|
||||||
Rebooting and Halting
|
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 +158,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 +175,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 +221,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 +264,9 @@ 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/
|
||||||
|
.. _runlevels: http://en.wikipedia.org/wiki/Runlevel
|
||||||
.. _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,5 +0,0 @@
|
|||||||
* FHS changes, see http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run
|
|
||||||
* Look at Erik Troan's nash linuxrc parser for more inspiration
|
|
||||||
* Look at how to support a standard SysV init runlevel mechanism
|
|
||||||
* Arguments to finit, like to SysV init "init q" to reload finit.conf at runtime, etc.
|
|
||||||
* Implement initctl stop|start|reload <SVC> and service <SVC> stop|start|reload on top
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
==============================================================================
|
||||||
|
TODO
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Unsorted TODO list of features and points of investigation that could be
|
||||||
|
useful for improving finit. But what is an improvement to finit? Well,
|
||||||
|
similiar to launchd, the goal of finit is to be a FAST replacement for:
|
||||||
|
|
||||||
|
* init
|
||||||
|
* inetd
|
||||||
|
* crond
|
||||||
|
* watchdogd
|
||||||
|
|
||||||
|
The main goal is to be FAST. Secondary goals are to be: small, simple
|
||||||
|
and with a very low dependency on external packages.
|
||||||
|
|
||||||
|
|
||||||
|
Priority
|
||||||
|
--------
|
||||||
|
The intent of finit is to monitor not just processes, but also to
|
||||||
|
be able to supervise the entire system and the behavior of certain
|
||||||
|
processes that declare themselves to be finit compliant.
|
||||||
|
|
||||||
|
* Integrate the ingenious libev to handle all events: signals, polling
|
||||||
|
fd's, etc. Because "Event driven software improves concurrency" --
|
||||||
|
Dave Zarzycki, Apple. See the `launchd video`_ for more info!
|
||||||
|
* Add support for process and system supervision. Which could
|
||||||
|
be an API wrapping, e.g., TIPC and loadavg.
|
||||||
|
* Add support for a /dev/watchdog plugin to replace watchdogd
|
||||||
|
* Integrate watchdog plugin with process/system supervisor to
|
||||||
|
reboot the system when a process stops responding, or when
|
||||||
|
restarting it is imposible, as well as when system load gets
|
||||||
|
too high.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
* Add support for "init <q | reload-configuration>" and SIGHUP to
|
||||||
|
reload finit.conf, but also
|
||||||
|
* Add support for inotify to automatically reload finit.conf
|
||||||
|
|
||||||
|
|
||||||
|
Runlevels
|
||||||
|
---------
|
||||||
|
* Add support for "runlevel N" to finit.conf
|
||||||
|
* Improve support for runlevels: startup, running (services from
|
||||||
|
finit.conf), and shutdown as well as hooks when switching RL, startup
|
||||||
|
at RL 1 and a default run level.
|
||||||
|
* Implement initctl stop|start|restart|reload|status <SVC> and service
|
||||||
|
<SVC> stop|start|restart|reload|status on top
|
||||||
|
* Add PRE and POST hooks for when changing runlevels
|
||||||
|
* SysV init in Debian uses an rcS.d/ for scripts that should run once at
|
||||||
|
boot. These scripts are run before the actual runlevel, which is set
|
||||||
|
in inittab. We could use "task [S]" and "run [S]" (below) for this.
|
||||||
|
* Add support for a new runlevel 'S' that is started before all other
|
||||||
|
services in the selected runlevel.
|
||||||
|
|
||||||
|
|
||||||
|
Services and Tasks
|
||||||
|
------------------
|
||||||
|
Both the "task" and "boot" stanzas below are like "service", share the
|
||||||
|
same mechanism for registering callbacks. These callbacks can be used
|
||||||
|
to check a configuration db and return 1 (RUN), 2 (STOP), or 3 (RELOAD).
|
||||||
|
|
||||||
|
* Add support for "task [RUNLVL] ..." similar to services, but one-shot.
|
||||||
|
* Add support for "run [RUNLVL] ..." similar to task, but waits for
|
||||||
|
completion before continuing with the next task/service
|
||||||
|
* Add support for service/task/run dependencies
|
||||||
|
* Add support for "init list" to show running services
|
||||||
|
|
||||||
|
It is tempting to say that a task called at runlevel S would be a
|
||||||
|
blocking call, like run, but there are likely use-cases when you want to
|
||||||
|
call on-shot tasks in parallell with something else, even at boot. Hence
|
||||||
|
the differentiation between "task" and "run", which are essentially the
|
||||||
|
same, except for the "pause and wait for completion" that "run" has.
|
||||||
|
|
||||||
|
|
||||||
|
Miscellaneous
|
||||||
|
-------------
|
||||||
|
* Add support for "init -v,--version | version"
|
||||||
|
* Cleanup move sources from top-level directory to src/ and include/
|
||||||
|
* Make sure to install queue.h to $(PREFIX)/include/finit/queue.h
|
||||||
|
* Move finit.conf "check" command to plugin which checks /etc/fstab
|
||||||
|
instead. This is the de-facto practise. But keep the check command
|
||||||
|
for really low-end systems w/o /etc/fstab.
|
||||||
|
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
* Write man pages for finit and finit.conf, steal from pimd man pages...
|
||||||
|
* Update Debian finit.conf example with runlevels, tty/console and
|
||||||
|
dependency handling
|
||||||
|
|
||||||
|
|
||||||
|
Investigation
|
||||||
|
-------------
|
||||||
|
* `FHS changes`_ affecting runtime status, plugins, etc.
|
||||||
|
|
||||||
|
.. _`launchd video`: http://www.youtube.com/watch?v=cD_s6Fjdri8
|
||||||
|
.. _`FHS changes`: http://askubuntu.com/questions/57297/why-has-var-run-been-migrated-to-run
|
||||||
|
..
|
||||||
|
.. Local Variables:
|
||||||
|
.. mode: rst
|
||||||
|
.. version-control: t
|
||||||
|
.. End:
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
|
# Top directory for building complete system, fall back to this directory
|
||||||
|
ROOTDIR ?= $(TOPDIR)
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
@@ -21,41 +21,69 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
#include "tty.h"
|
||||||
|
#include "lite.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
/* Match one command. */
|
#define MATCH_CMD(l, c, x) \
|
||||||
#define MATCH_CMD(l, c, x) \
|
|
||||||
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||||
|
|
||||||
|
static char *strip_line(char *line)
|
||||||
static char *build_cmd(char *cmd, char *line, int len)
|
|
||||||
{
|
{
|
||||||
int l;
|
char *ptr;
|
||||||
char *c;
|
|
||||||
|
|
||||||
/* Trim leading whitespace */
|
/* Trim leading whitespace */
|
||||||
while (*line && (*line == ' ' || *line == '\t'))
|
while (*line && isblank(*line))
|
||||||
line++;
|
line++;
|
||||||
|
|
||||||
if (!cmd) {
|
/* Strip any comment at end of line */
|
||||||
cmd = malloc (strlen(line) + 1);
|
ptr = line;
|
||||||
if (!cmd) {
|
while (*ptr && *ptr != '#')
|
||||||
_e("No memory left for '%s'", line);
|
ptr++;
|
||||||
return NULL;
|
*ptr = 0;
|
||||||
}
|
|
||||||
*cmd = 0;
|
|
||||||
}
|
|
||||||
c = cmd + strlen(cmd);
|
|
||||||
for (l = 0; *line && *line != '#' && *line != '\t' && l < len; l++)
|
|
||||||
*c++ = *line++;
|
|
||||||
*c = 0;
|
|
||||||
|
|
||||||
_d("cmd = %s", cmd);
|
return line;
|
||||||
return cmd;
|
}
|
||||||
|
|
||||||
|
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||||
|
int parse_runlevels(char *runlevels)
|
||||||
|
{
|
||||||
|
int i, not = 0, bitmask = 0;
|
||||||
|
|
||||||
|
if (!runlevels)
|
||||||
|
runlevels = "[234]";
|
||||||
|
i = 1;
|
||||||
|
while (i) {
|
||||||
|
int level;
|
||||||
|
char lvl = runlevels[i++];
|
||||||
|
|
||||||
|
if (']' == lvl || 0 == lvl)
|
||||||
|
break;
|
||||||
|
if ('!' == lvl) {
|
||||||
|
not = 1;
|
||||||
|
bitmask = 0x3FE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('s' == lvl || 'S' == lvl)
|
||||||
|
lvl = '0';
|
||||||
|
|
||||||
|
level = lvl - '0';
|
||||||
|
if (level > 9 || level < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (not)
|
||||||
|
CLRBIT(bitmask, level);
|
||||||
|
else
|
||||||
|
SETBIT(bitmask, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_finit_conf(char *file)
|
void parse_finit_conf(char *file)
|
||||||
@@ -64,70 +92,130 @@ void parse_finit_conf(char *file)
|
|||||||
char line[LINE_SIZE];
|
char line[LINE_SIZE];
|
||||||
char cmd[CMD_SIZE];
|
char cmd[CMD_SIZE];
|
||||||
|
|
||||||
/* Default username and hostname */
|
|
||||||
username = strdup(DEFUSER);
|
username = strdup(DEFUSER);
|
||||||
hostname = strdup(DEFHOST);
|
hostname = strdup(DEFHOST);
|
||||||
|
rcsd = strdup(FINIT_RCSD);
|
||||||
|
|
||||||
if ((fp = fopen(file, "r")) != NULL) {
|
if ((fp = fopen(file, "r")) != NULL) {
|
||||||
char *x;
|
char *x;
|
||||||
|
const char *err = NULL;
|
||||||
|
|
||||||
_d("Parse %s ...", file);
|
_d("Parse %s ...", file);
|
||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
if (!fgets(line, sizeof(line), fp))
|
if (!fgets(line, sizeof(line), fp))
|
||||||
continue;
|
continue;
|
||||||
chomp(line);
|
chomp(line);
|
||||||
|
|
||||||
_d("conf: %s", line);
|
_d("conf: %s", line);
|
||||||
|
|
||||||
/* Skip comments. */
|
/* Skip comments. */
|
||||||
if (MATCH_CMD(line, "#", x)) {
|
if (MATCH_CMD(line, "#", x))
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
/* Do this before mounting / read-write */
|
/* Do this before mounting / read-write
|
||||||
|
* XXX: Move to plugin which checks /etc/fstab instead */
|
||||||
if (MATCH_CMD(line, "check ", x)) {
|
if (MATCH_CMD(line, "check ", x)) {
|
||||||
|
char *dev = strip_line(x);
|
||||||
|
|
||||||
strcpy(cmd, "/sbin/fsck -C -a ");
|
strcpy(cmd, "/sbin/fsck -C -a ");
|
||||||
build_cmd(cmd, x, CMD_SIZE);
|
strlcat(cmd, dev, sizeof(cmd));
|
||||||
run_interactive(cmd, "Checking file system integrity on %s", x);
|
run_interactive(cmd, "Checking file system %s", dev);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MATCH_CMD(line, "user ", x)) {
|
if (MATCH_CMD(line, "user ", x)) {
|
||||||
if (username) free(username);
|
if (username) free(username);
|
||||||
username = build_cmd(NULL, x, USERNAME_SIZE);
|
username = strdup(strip_line(x));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (MATCH_CMD(line, "host ", x)) {
|
if (MATCH_CMD(line, "host ", x)) {
|
||||||
if (hostname) free(hostname);
|
if (hostname) free(hostname);
|
||||||
hostname = build_cmd(NULL, x, HOSTNAME_SIZE);
|
hostname = strdup(strip_line(x));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "module ", x)) {
|
||||||
|
char *mod = strip_line(x);
|
||||||
|
|
||||||
|
strcpy(cmd, "/sbin/modprobe ");
|
||||||
|
strlcat(cmd, mod, sizeof(cmd));
|
||||||
|
run_interactive(cmd, "Loading kernel module %s", mod);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MATCH_CMD(line, "mknod ", x)) {
|
||||||
|
char *dev = strip_line(x);
|
||||||
|
|
||||||
|
strcpy(cmd, "/bin/mknod ");
|
||||||
|
strlcat(cmd, dev, sizeof(cmd));
|
||||||
|
run_interactive(cmd, "Creating device node %s", dev);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "network ", x)) {
|
||||||
|
if (network) free(network);
|
||||||
|
network = strdup(strip_line(x));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MATCH_CMD(line, "runparts ", x)) {
|
||||||
|
if (rcsd) free(rcsd);
|
||||||
|
rcsd = strdup(strip_line(x));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MATCH_CMD(line, "startx ", x)) {
|
||||||
|
svc_register(SVC_CMD_SERVICE, strip_line(x), username);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||||
if (sdown) free(sdown);
|
if (sdown) free(sdown);
|
||||||
sdown = build_cmd(NULL, x, CMD_SIZE);
|
sdown = strdup(strip_line(x));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (MATCH_CMD(line, "module ", x)) {
|
|
||||||
strcpy(cmd, "/sbin/modprobe ");
|
/* The desired runlevel to start when leaving
|
||||||
build_cmd(cmd, x, CMD_SIZE);
|
* bootstrap (S). Finit supports 1-9, but most
|
||||||
run_interactive(cmd, " Loading module %s", x);
|
* systems only use 1-6, where 6 is reserved for
|
||||||
continue;
|
* reboot */
|
||||||
}
|
if (MATCH_CMD(line, "runlevel ", x)) {
|
||||||
if (MATCH_CMD(line, "mknod ", x)) {
|
char *token = strip_line(x);
|
||||||
strcpy(cmd, "/bin/mknod ");
|
|
||||||
build_cmd(cmd, x, CMD_SIZE);
|
cfglevel = strtonum(token, 1, 9, &err);
|
||||||
run_interactive(cmd, " Creating device node %s", x);
|
if (err)
|
||||||
continue;
|
cfglevel = RUNLEVEL;
|
||||||
}
|
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
|
||||||
if (MATCH_CMD(line, "network ", x)) {
|
cfglevel = 2; /* Fallback */
|
||||||
if (network) free(network);
|
|
||||||
network = build_cmd(NULL, x, CMD_SIZE);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (MATCH_CMD(line, "startx ", x)) {
|
|
||||||
svc_register(x, username);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Monitored daemon, will be respawned on exit, as
|
||||||
|
* long as the (optional) service callback returns
|
||||||
|
* non-zero */
|
||||||
if (MATCH_CMD(line, "service ", x)) {
|
if (MATCH_CMD(line, "service ", x)) {
|
||||||
svc_register(x, NULL);
|
svc_register(SVC_CMD_SERVICE, x, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One-shot task, will not be respawned. Only runs if
|
||||||
|
* the (optional) service callback returns true */
|
||||||
|
if (MATCH_CMD(line, "task ", x)) {
|
||||||
|
svc_register(SVC_CMD_TASK, x, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like task but waits for completion, useful w/ [S] */
|
||||||
|
if (MATCH_CMD(line, "run ", x)) {
|
||||||
|
svc_register(SVC_CMD_RUN, x, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MATCH_CMD(line, "console ", x)) {
|
||||||
|
if (console) free(console);
|
||||||
|
console = strdup(strip_line(x));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MATCH_CMD(line, "tty ", x)) {
|
||||||
|
tty_register(strip_line(x));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+65
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: keyboard-setup
|
||||||
|
# Required-Start: mountkernfs
|
||||||
|
# Required-Stop:
|
||||||
|
# Should-Start: keymap udev
|
||||||
|
# X-Start-Before: checkroot
|
||||||
|
# Default-Start: S
|
||||||
|
# Default-Stop:
|
||||||
|
# X-Interactive: true
|
||||||
|
# Short-Description: Set preliminary keymap
|
||||||
|
# Description: Set the console keyboard as early as possible
|
||||||
|
# so during the file systems checks the administrator
|
||||||
|
# can interact. At this stage of the boot process
|
||||||
|
# only the ASCII symbols are supported.
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# This script is not used by keyboard-configuration. It is used
|
||||||
|
# jointly by console-setup and console-setup-mini
|
||||||
|
|
||||||
|
test -f /bin/setupcon || exit 0
|
||||||
|
|
||||||
|
if [ -f /lib/lsb/init-functions ]; then
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
else
|
||||||
|
log_action_begin_msg () {
|
||||||
|
echo -n "$@... "
|
||||||
|
}
|
||||||
|
|
||||||
|
log_action_end_msg () {
|
||||||
|
if [ "$1" -eq 0 ]; then
|
||||||
|
echo done.
|
||||||
|
else
|
||||||
|
echo failed.
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
stop)
|
||||||
|
# keyboard-setup isn't a daemon
|
||||||
|
;;
|
||||||
|
start|force-reload|restart|reload)
|
||||||
|
case `readlink /proc/self/fd/2` in
|
||||||
|
/dev/tty[0-9]*|/dev/vc/[0-9]*|/dev/console)
|
||||||
|
log_action_begin_msg "Setting preliminary keymap"
|
||||||
|
if setupcon -k; then
|
||||||
|
log_action_end_msg 0
|
||||||
|
else
|
||||||
|
log_action_end_msg $?
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo 'Usage: /etc/init.d/keyboard-setup {start|reload|restart|force-reload|stop}'
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
Executable
+81
@@ -0,0 +1,81 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: acpid
|
||||||
|
# Required-Start: $remote_fs $syslog
|
||||||
|
# Required-Stop: $remote_fs $syslog
|
||||||
|
# X-Start-Before: kdm gdm xdm hal
|
||||||
|
# X-Stop-After: kdm gdm xdm hal
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop:
|
||||||
|
# Short-Description: Start the Advanced Configuration and Power Interface daemon
|
||||||
|
# Description: Provide a socket for X11, hald and others to multiplex
|
||||||
|
# kernel ACPI events.
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ACPID="/usr/sbin/acpid"
|
||||||
|
DEFAULTS="/etc/default/acpid"
|
||||||
|
|
||||||
|
# Check for daemon presence
|
||||||
|
[ -x "$ACPID" ] || exit 0
|
||||||
|
|
||||||
|
OPTIONS=""
|
||||||
|
MODULES=""
|
||||||
|
# Include acpid defaults if available
|
||||||
|
[ -r "$DEFAULTS" ] && . "$DEFAULTS"
|
||||||
|
|
||||||
|
# Get lsb functions
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
# As the name says. If the kernel supports modules, it'll try to load
|
||||||
|
# the ones listed in "MODULES".
|
||||||
|
load_modules() {
|
||||||
|
[ -f /proc/modules ] || return 0
|
||||||
|
if [ "$MODULES" = "all" ]; then
|
||||||
|
MODULES="$(sed -rn 's#^(/lib/modules/[^/]+/)?kernel/(drivers|ubuntu)/acpi/([^/]+/)*(.*)\.ko:.*#\4#p' "/lib/modules/$(uname -r)/modules.dep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$MODULES" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_begin_msg "Loading ACPI kernel modules..."
|
||||||
|
# work around a bug in initramfs which leaks this env var Launchpad #291619
|
||||||
|
# and a bug in modprobe --all --quiet which doesn't load all modules and
|
||||||
|
# exits with non-zero exit status Debian #504088
|
||||||
|
unset MODPROBE_OPTIONS
|
||||||
|
modprobe --all --use-blacklist $MODULES 2>/dev/null
|
||||||
|
log_end_msg $?
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
load_modules || true
|
||||||
|
log_begin_msg "Starting ACPI services..."
|
||||||
|
start-stop-daemon --start --quiet --oknodo --exec "$ACPID" -- $OPTIONS
|
||||||
|
log_end_msg $?
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
log_begin_msg "Stopping ACPI services..."
|
||||||
|
start-stop-daemon --stop --quiet --oknodo --retry 2 --exec "$ACPID"
|
||||||
|
log_end_msg $?
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
$0 stop
|
||||||
|
sleep 1
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
reload|force-reload)
|
||||||
|
log_begin_msg "Reloading ACPI services..."
|
||||||
|
start-stop-daemon --stop --signal 1 --exec "$ACPID"
|
||||||
|
log_end_msg $?
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status_of_proc "$ACPID" acpid
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_success_msg "Usage: /etc/init.d/acpid {start|stop|restart|reload|force-reload|status}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
Executable
+259
@@ -0,0 +1,259 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: kbd
|
||||||
|
# Required-Start: $remote_fs
|
||||||
|
# Required-Stop:
|
||||||
|
# Default-Start: S
|
||||||
|
# Default-Stop:
|
||||||
|
# X-Interactive: true
|
||||||
|
# Description: Set console screen modes and fonts
|
||||||
|
# Short-Description: Prepare console
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# This is the boot script for the `kbd' package.
|
||||||
|
# It loads parameters from /etc/kbd/config and maybe loads
|
||||||
|
# default font and map.
|
||||||
|
# (c) 1997 Yann Dirson
|
||||||
|
|
||||||
|
PKG=kbd
|
||||||
|
if [ -r /etc/$PKG/config ]; then
|
||||||
|
. /etc/$PKG/config
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /etc/$PKG/config.d ]; then
|
||||||
|
for i in `run-parts --list /etc/$PKG/config.d `; do
|
||||||
|
. $i
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# do some magic with the variables for compatibility with the config
|
||||||
|
# file of console-tools
|
||||||
|
for vc in '' `set | grep "^.*_vc[0-9][0-9]*=" | sed 's/^.*\(_vc[0-9][0-9]*\)=.*/\1/'`
|
||||||
|
do
|
||||||
|
eval [ '"'\${SCREEN_FONT$vc}'"' ] && eval CONSOLE_FONT$vc=\${CONSOLE_FONT$vc:-\${SCREEN_FONT$vc}}
|
||||||
|
eval [ '"'\${SCREEN_FONT_MAP$vc}'"' ] && eval FONT_MAP$vc=\${FONT_MAP$vc:-\${SCREEN_FONT_MAP$vc}}
|
||||||
|
eval [ '"'\${APP_CHARSET_MAP$vc}'"' ] && eval CONSOLE_MAP$vc=\${CONSOLE_MAP$vc:-\${APP_CHARSET_MAP$vc}}
|
||||||
|
done
|
||||||
|
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||||
|
SETFONT_OPT="-v"
|
||||||
|
|
||||||
|
if which setupcon >/dev/null
|
||||||
|
then
|
||||||
|
HAVE_SETUPCON=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set DEVICE_PREFIX depending on devfs/udev
|
||||||
|
if [ -d /dev/vc ]; then
|
||||||
|
DEVICE_PREFIX="/dev/vc/"
|
||||||
|
else
|
||||||
|
DEVICE_PREFIX="/dev/tty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# determine the system charmap
|
||||||
|
ENV_FILE=''
|
||||||
|
[ -r /etc/environment ] && ENV_FILE="/etc/environment"
|
||||||
|
[ -r /etc/default/locale ] && ENV_FILE="/etc/default/locale"
|
||||||
|
[ "$ENV_FILE" ] && CHARMAP=$(set -a && . "$ENV_FILE" && locale charmap)
|
||||||
|
if [ "$CHARMAP" = "UTF-8" -a -z "$CONSOLE_MAP" ]
|
||||||
|
then
|
||||||
|
UNICODE_MODE=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
reset_vga_palette ()
|
||||||
|
{
|
||||||
|
if [ -f /proc/fb ]; then
|
||||||
|
# They have a framebuffer device.
|
||||||
|
# That means we have work to do...
|
||||||
|
echo -n "]R"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
unicode_start_stop ()
|
||||||
|
{
|
||||||
|
vc=$1
|
||||||
|
if [ -n "$UNICODE_MODE" -a -z "`eval echo \\$CONSOLE_MAP_vc$vc`" ]; then
|
||||||
|
action=unicode_start
|
||||||
|
else
|
||||||
|
action=unicode_stop
|
||||||
|
fi
|
||||||
|
if [ "${CONSOLE_FONT}" ]; then
|
||||||
|
$action "${CONSOLE_FONT}" < ${DEVICE_PREFIX}$vc > ${DEVICE_PREFIX}$vc 2> /dev/null || true
|
||||||
|
else
|
||||||
|
$action < ${DEVICE_PREFIX}$vc > ${DEVICE_PREFIX}$vc 2> /dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup ()
|
||||||
|
{
|
||||||
|
# be sure the main program is installed
|
||||||
|
which setfont >/dev/null || return
|
||||||
|
|
||||||
|
VT="no"
|
||||||
|
# If we can't access the console, quit
|
||||||
|
CONSOLE_TYPE=`fgconsole 2>/dev/null` || return
|
||||||
|
|
||||||
|
if [ ! $CONSOLE_TYPE = "serial" ]; then
|
||||||
|
readlink /proc/self/fd/0 | grep -q -e /dev/vc -e '/dev/tty[^p]' -e /dev/console
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
VT="yes"
|
||||||
|
reset_vga_palette
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ $VT = "no" ] && return
|
||||||
|
|
||||||
|
# start vcstime
|
||||||
|
if [ "${DO_VCSTIME}" = "yes" ] && which vcstime >/dev/null; then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Starting clock on text console"
|
||||||
|
vcstime &
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /etc/init ] && which initctl >/dev/null; then
|
||||||
|
# Upstart
|
||||||
|
LIST_CONSOLES=`cd /etc/init; find -name 'tty*.conf' -printf '%f ' | sed -e 's/[^0-9 ]//g'`
|
||||||
|
else
|
||||||
|
# traditional SysV init
|
||||||
|
LIST_CONSOLES=`sed -e '/^ *#/d' /etc/inittab | grep -e '\<tty[0-9]*\>' | awk -F: '{printf "%s ", $1}'`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Global default font+map
|
||||||
|
if [ -z "${HAVE_SETUPCON}" -a "${CONSOLE_FONT}" ]; then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting up general console font"
|
||||||
|
sfm="${FONT_MAP}" && [ "$sfm" ] && sfm="-u $sfm"
|
||||||
|
acm="${CONSOLE_MAP}" && [ "$acm" ] && acm="-m $acm"
|
||||||
|
|
||||||
|
# Set for the first 6 VCs (as they are allocated in /etc/inittab)
|
||||||
|
for vc in $LIST_CONSOLES
|
||||||
|
do
|
||||||
|
if ! ( unicode_start_stop $vc \
|
||||||
|
&& setfont -C ${DEVICE_PREFIX}$vc ${SETFONT_OPT} $sfm ${CONSOLE_FONT} $acm )
|
||||||
|
then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Default to Unicode mode for new VTs?
|
||||||
|
if [ -f /sys/module/vt/parameters/default_utf8 ]; then
|
||||||
|
if [ -n "$UNICODE_MODE" ]; then
|
||||||
|
echo 1
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi > /sys/module/vt/parameters/default_utf8
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Per-VC font+sfm
|
||||||
|
PERVC_FONTS="`set | grep "^CONSOLE_FONT_vc[0-9]*=" | tr -d \' `"
|
||||||
|
if [ -z "${HAVE_SETUPCON}" -a "${PERVC_FONTS}" ]; then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting up per-VC fonts"
|
||||||
|
for font in ${PERVC_FONTS}
|
||||||
|
do
|
||||||
|
# extract VC and FONTNAME info from variable setting
|
||||||
|
vc=`echo $font | cut -b16- | cut -d= -f1`
|
||||||
|
eval font=\$CONSOLE_FONT_vc$vc
|
||||||
|
# eventually find an associated SFM
|
||||||
|
eval sfm=\${FONT_MAP_vc${vc}}
|
||||||
|
[ "$sfm" ] && sfm="-u $sfm"
|
||||||
|
if ! ( unicode_start_stop $vc \
|
||||||
|
&& setfont -C ${DEVICE_PREFIX}$vc ${SETFONT_OPT} $sfm $font )
|
||||||
|
then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Per-VC ACMs
|
||||||
|
PERVC_ACMS="`set | grep "^CONSOLE_MAP_vc[0-9]*=" | tr -d \' `"
|
||||||
|
if [ -z "${HAVE_SETUPCON}" -a "${PERVC_ACMS}" ]; then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting up per-VC ACM's"
|
||||||
|
for acm in ${PERVC_ACMS}
|
||||||
|
do
|
||||||
|
# extract VC and ACM_FONTNAME info from variable setting
|
||||||
|
vc=`echo $acm | cut -b15- | cut -d= -f1`
|
||||||
|
eval acm=\$CONSOLE_MAP_vc$vc
|
||||||
|
if ! setfont -C "${DEVICE_PREFIX}$vc" ${SETFONT_OPT} -m "$acm"; then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# screensaver stuff
|
||||||
|
setterm_args=""
|
||||||
|
if [ "$BLANK_TIME" ]; then
|
||||||
|
setterm_args="$setterm_args -blank $BLANK_TIME"
|
||||||
|
fi
|
||||||
|
if [ "$BLANK_DPMS" ]; then
|
||||||
|
setterm_args="$setterm_args -powersave $BLANK_DPMS"
|
||||||
|
fi
|
||||||
|
if [ "$POWERDOWN_TIME" ]; then
|
||||||
|
setterm_args="$setterm_args -powerdown $POWERDOWN_TIME"
|
||||||
|
fi
|
||||||
|
if [ "$setterm_args" ]; then
|
||||||
|
setterm $setterm_args
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keyboard rate and delay
|
||||||
|
KBDRATE_ARGS=""
|
||||||
|
if [ -n "$KEYBOARD_RATE" ]; then
|
||||||
|
KBDRATE_ARGS="-r $KEYBOARD_RATE"
|
||||||
|
fi
|
||||||
|
if [ -n "$KEYBOARD_DELAY" ]; then
|
||||||
|
KBDRATE_ARGS="$KBDRATE_ARGS -d $KEYBOARD_DELAY"
|
||||||
|
fi
|
||||||
|
if [ -n "$KBDRATE_ARGS" ]; then
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_begin_msg "Setting keyboard rate and delay"
|
||||||
|
kbdrate -s $KBDRATE_ARGS
|
||||||
|
[ "$VERBOSE" != "no" ] && log_action_end_msg 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Inform gpm if present, of potential changes.
|
||||||
|
if [ -f /var/run/gpm.pid ]; then
|
||||||
|
kill -s WINCH `cat /var/run/gpm.pid` 2> /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Allow user to remap keys on the console
|
||||||
|
if [ -z "${HAVE_SETUPCON}" -a -r /etc/$PKG/remap ]; then
|
||||||
|
dumpkeys < ${DEVICE_PREFIX}1 | sed -f /etc/$PKG/remap | loadkeys --quiet
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set LEDS here
|
||||||
|
if [ -n "$LEDS" ]; then
|
||||||
|
for i in $LIST_CONSOLES
|
||||||
|
do
|
||||||
|
setleds -D $LEDS < $DEVICE_PREFIX$i
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start|reload|restart|force-reload)
|
||||||
|
if [ -n "$HAVE_SETUPCON" ]
|
||||||
|
then
|
||||||
|
log_action_msg "Setting console screen modes"
|
||||||
|
else
|
||||||
|
log_action_msg "Setting console screen modes and fonts"
|
||||||
|
fi
|
||||||
|
setup
|
||||||
|
[ -z "$HAVE_SETUPCON" ] || log_action_msg "Skipping font and keymap setup (handled by console-setup)"
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
setup
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Simple Debian finit.conf
|
|
||||||
check /dev/vda1
|
|
||||||
module evdev
|
|
||||||
module loop
|
|
||||||
module psmouse
|
|
||||||
user admin
|
|
||||||
host testbed
|
|
||||||
network service networking start
|
|
||||||
service /sbin/klogd -n -- Kernel logging server
|
|
||||||
service /sbin/syslogd -n -- Syslog server
|
|
||||||
service /usr/sbin/sshd -D -- OpenSSH server
|
|
||||||
service /usr/sbin/gdm -- GNOME Display Manager
|
|
||||||
@@ -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>
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/stat.h> /* umask(), mkdir() */
|
#include <sys/stat.h> /* umask(), mkdir() */
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
@@ -33,14 +34,20 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
|
#include "tty.h"
|
||||||
|
#include "lite.h"
|
||||||
|
|
||||||
int debug = 0;
|
int debug = 0;
|
||||||
int verbose = 1;
|
int verbose = 1;
|
||||||
char *sdown = NULL;
|
int runlevel = 0; /* Bootstrap */
|
||||||
char *network = NULL;
|
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||||
char *username = NULL;
|
int prevlevel = 0; /* HALT */
|
||||||
char *hostname = NULL;
|
char *sdown = NULL;
|
||||||
|
char *network = NULL;
|
||||||
|
char *username = NULL;
|
||||||
|
char *hostname = NULL;
|
||||||
|
char *rcsd = NULL;
|
||||||
|
char *console = NULL;
|
||||||
|
|
||||||
static void parse_kernel_cmdline(void)
|
static void parse_kernel_cmdline(void)
|
||||||
{
|
{
|
||||||
@@ -49,6 +56,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;
|
||||||
@@ -60,18 +68,106 @@ static void parse_kernel_cmdline(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int usage(int rc)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s [OPTIONS] [RUNLEVEL]\n\n"
|
||||||
|
" -d, --debug Enable/Disable debug\n"
|
||||||
|
" -h, --help This help text\n\n", __progname);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int client(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int c;
|
||||||
|
struct option long_options[] = {
|
||||||
|
{"debug", 0, NULL, 'd'},
|
||||||
|
{"help", 0, NULL, 'h'},
|
||||||
|
{NULL, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
struct init_request rq = {
|
||||||
|
.magic = INIT_MAGIC,
|
||||||
|
.cmd = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((c = getopt_long (argc, argv, "h?d", long_options, NULL)) != EOF) {
|
||||||
|
switch(c) {
|
||||||
|
case 'd':
|
||||||
|
rq.cmd = INIT_CMD_DEBUG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
case '?':
|
||||||
|
return usage(0);
|
||||||
|
default:
|
||||||
|
return usage(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rq.cmd) {
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Missing runlevel.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rq.cmd = INIT_CMD_RUNLVL;
|
||||||
|
rq.runlevel = (int)argv[1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fexist(FINIT_FIFO)) {
|
||||||
|
fprintf(stderr, "/sbin/init does not support %s!\n", FINIT_FIFO);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(FINIT_FIFO, O_WRONLY);
|
||||||
|
if (-1 == fd) {
|
||||||
|
perror("Failed opening " FINIT_FIFO);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
write(fd, &rq, sizeof(rq));
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int run_loop(void)
|
static int run_loop(void)
|
||||||
{
|
{
|
||||||
|
int delay = DELAY_TTY ?: 1;
|
||||||
|
|
||||||
|
_d("Entering main loop ...");
|
||||||
while (1) {
|
while (1) {
|
||||||
svc_monitor();
|
svc_monitor();
|
||||||
io_monitor();
|
plugin_monitor();
|
||||||
|
|
||||||
|
/* Delayed start of TTYs to let the system stabilize
|
||||||
|
* after switching runlevels. */
|
||||||
|
if (delay) {
|
||||||
|
if (--delay == 0)
|
||||||
|
tty_runlevel(runlevel);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int UNUSED(args), char *argv[])
|
static void banner(void)
|
||||||
{
|
{
|
||||||
|
delline();
|
||||||
|
echo("Finit version " VERSION " (" WHOAMI ") " __DATE__ " " __TIME__);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if (getpid() != 1)
|
||||||
|
return client(argc, argv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hello world.
|
||||||
|
*/
|
||||||
|
banner();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initial setup of signals, ignore all until we're up.
|
* Initial setup of signals, ignore all until we're up.
|
||||||
*/
|
*/
|
||||||
@@ -91,39 +187,42 @@ 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
|
||||||
*/
|
*/
|
||||||
parse_kernel_cmdline();
|
parse_kernel_cmdline();
|
||||||
|
|
||||||
cls();
|
/*
|
||||||
echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
|
* Populate /dev and prepare for runtime events from kernel.
|
||||||
|
*/
|
||||||
|
run_interactive(SETUP_DEVFS, "Populating device tree");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 Finit plugins");
|
||||||
|
print_result(plugin_load_all(PLUGIN_PATH));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mount filesystems
|
* Mount filesystems
|
||||||
*/
|
*/
|
||||||
_d("Mount filesystems in /etc/fstab ...");
|
|
||||||
|
|
||||||
#ifdef REMOUNT_ROOTFS_RW
|
#ifdef REMOUNT_ROOTFS_RW
|
||||||
run("/bin/mount -n -o remount,rw /");
|
run("/bin/mount -n -o remount,rw /");
|
||||||
#endif
|
#endif
|
||||||
#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");
|
||||||
@@ -132,19 +231,16 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
/* Cleanup stale files, if any still linger on. */
|
/* Cleanup stale files, if any still linger on. */
|
||||||
run_interactive("rm -rf /tmp/* /var/run/* /var/lock/*", "Cleanup temporary directories");
|
run_interactive("rm -rf /tmp/* /var/run/* /var/lock/*", "Cleanup temporary directories");
|
||||||
|
|
||||||
/*
|
/* Base FS up, enable standard SysV init signals */
|
||||||
* Base FS up, enable standard SysV init signals
|
|
||||||
*/
|
|
||||||
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);
|
* Start all bootstrap tasks, no network available!
|
||||||
|
*/
|
||||||
|
svc_bootstrap();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Network stuff
|
* Network stuff
|
||||||
@@ -161,36 +257,31 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
run_interactive(network, "Starting networking: %s", network);
|
run_interactive(network, "Starting networking: %s", network);
|
||||||
umask(022);
|
umask(022);
|
||||||
|
|
||||||
/*
|
/* Hooks that rely on loopback, or basic networking being up. */
|
||||||
* Hooks that rely on loopback, or basic networking being up.
|
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||||
*/
|
|
||||||
run_hooks(HOOK_NETWORK_UP);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start service monitor framework
|
* Start all tasks/services in the configured runlevel
|
||||||
*/
|
*/
|
||||||
_d("Starting all static services from %s", FINIT_CONF);
|
svc_runlevel(cfglevel);
|
||||||
svc_start_all();
|
|
||||||
|
_d("Running svc up hooks ...");
|
||||||
|
plugin_run_hooks(HOOK_SVC_UP);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||||
*/
|
|
||||||
run_hooks(HOOK_SYSTEM_UP);
|
|
||||||
|
|
||||||
/* Start GETTY on console */
|
|
||||||
_d("Starting getty on console ...");
|
|
||||||
run_getty(GETTY, argv);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enter main loop to monior /dev/initctl and services
|
* Enter main loop to monior /dev/initctl and services
|
||||||
*/
|
*/
|
||||||
_d("Entering main loop ...");
|
|
||||||
return run_loop();
|
return run_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# Sample Debian finit.conf
|
||||||
|
user admin
|
||||||
|
host testbed
|
||||||
|
|
||||||
|
# Devices to fsck at boot
|
||||||
|
check /dev/vda1
|
||||||
|
|
||||||
|
# Linux kernel modules to load
|
||||||
|
module button
|
||||||
|
module evdev
|
||||||
|
module loop
|
||||||
|
module psmouse
|
||||||
|
|
||||||
|
# Runlevel to start after bootstrap, runlevel 'S'
|
||||||
|
runlevel 2
|
||||||
|
|
||||||
|
# Network bringup script
|
||||||
|
network service networking start
|
||||||
|
|
||||||
|
# Virtual consoles to start getty on
|
||||||
|
tty /dev/tty1
|
||||||
|
tty /dev/tty2
|
||||||
|
tty /dev/tty3
|
||||||
|
|
||||||
|
# Alternative method instead of runparts
|
||||||
|
#task [S] /etc/init.d/keyboard-setup start -- Setting up preliminary keymap
|
||||||
|
#task [S] /etc/init.d/acpid start -- Starting ACPI Daemon
|
||||||
|
#task [S] /etc/init.d/kbd start -- Preparing console
|
||||||
|
#run [2] /etc/init.d/networking start -- Start networking
|
||||||
|
|
||||||
|
# Services to be monitored and respawned as needed
|
||||||
|
service [2345] /sbin/klogd -n -- Kernel logging server
|
||||||
|
service [2345] /sbin/syslogd -n -- Syslog server
|
||||||
|
service [2345] /usr/sbin/sshd -D -- OpenSSH server
|
||||||
|
service [3] /usr/sbin/gdm -- GNOME Display Manager
|
||||||
|
|
||||||
|
# By default Finit looks for /etc/finit.d
|
||||||
|
# runparts /etc/finit.d
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* 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-2013 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -33,87 +33,66 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <syslog.h>
|
/* Distribution specific configuration */
|
||||||
#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 */
|
|
||||||
|
|
||||||
#if defined EMBEDDED_SYSTEM
|
#if defined EMBEDDED_SYSTEM
|
||||||
# define CONSOLE "/dev/console"
|
# define CONSOLE "/dev/console"
|
||||||
# define MDEV "/sbin/mdev"
|
# define SETUP_DEVFS "/sbin/mdev -s"
|
||||||
# define GETTY "/sbin/getty -L 115200 ttyS0 vt100"
|
# define GETTY "/sbin/getty -L"
|
||||||
# define LISTEN_INITCTL
|
# define GETTY_BUSYBOX
|
||||||
|
# define BAUDRATE 115200
|
||||||
|
# define RUNLEVEL 2
|
||||||
#else /* Debian/Ubuntu based distributions */
|
#else /* Debian/Ubuntu based distributions */
|
||||||
# define RANDOMSEED "/var/lib/urandom/random-seed"
|
# define CONSOLE "/dev/tty1"
|
||||||
# define CONSOLE "/dev/tty1"
|
# define SETUP_DEVFS "/sbin/udevd --daemon"
|
||||||
# define GETTY "/sbin/getty -8 38400 tty1"
|
# define GETTY "/sbin/getty -8"
|
||||||
|
# define GETTY_AGETTY
|
||||||
|
# define BAUDRATE 38400
|
||||||
|
# define RUNLEVEL 2
|
||||||
|
# define RANDOMSEED "/var/lib/urandom/random-seed"
|
||||||
# define REMOUNT_ROOTFS_RW
|
# define REMOUNT_ROOTFS_RW
|
||||||
# define RUNLEVEL 2
|
# define HAVE_DBUS
|
||||||
# define LISTEN_INITCTL
|
|
||||||
# define USE_UDEV
|
|
||||||
# define USE_MESSAGE_BUS
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEFUSER
|
#ifndef DEFUSER
|
||||||
# define DEFUSER "root"
|
# define DEFUSER "root"
|
||||||
#endif
|
#endif
|
||||||
#ifndef DEFHOST
|
#ifndef DEFHOST
|
||||||
# define DEFHOST "noname"
|
# define DEFHOST "noname"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CMD_SIZE 256
|
/* Delay start of GETTY at boot. Must be > 0 */
|
||||||
#define LINE_SIZE 1024
|
#define DELAY_TTY 2
|
||||||
#define BUF_SIZE 4096
|
|
||||||
#define USERNAME_SIZE 16
|
|
||||||
#define HOSTNAME_SIZE 32
|
|
||||||
|
|
||||||
#ifndef touch
|
#define CMD_SIZE 256
|
||||||
# define touch(x) mknod((x), S_IFREG|0644, 0)
|
#define LINE_SIZE 1024
|
||||||
#endif
|
#define BUF_SIZE 4096
|
||||||
#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 INIT_MAGIC 0x03091969
|
||||||
#define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
|
#define INIT_CMD_RUNLVL 1
|
||||||
#endif
|
#define INIT_CMD_DEBUG 2
|
||||||
|
|
||||||
#define echo(fmt, args...) do { if (1) { fprintf(stderr, fmt "\n", ##args); } } while (0)
|
struct init_request {
|
||||||
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0)
|
int magic; /* Magic number */
|
||||||
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } while (0)
|
int cmd; /* What kind of request */
|
||||||
#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt ". Error %d: %s\n", __func__, ##args, errno, strerror(errno)); } while (0)
|
int runlevel; /* Runlevel to change to */
|
||||||
|
int sleeptime; /* Time between TERM and KILL */
|
||||||
|
char data[368];
|
||||||
|
};
|
||||||
|
|
||||||
extern int debug;
|
extern int runlevel;
|
||||||
extern int verbose;
|
extern int cfglevel;
|
||||||
extern char *sdown;
|
extern int prevlevel;
|
||||||
extern char *network;
|
extern char *sdown;
|
||||||
extern char *hostname;
|
extern char *network;
|
||||||
extern char *username;
|
extern char *hostname;
|
||||||
|
extern char *username;
|
||||||
/* strlcpy.c */
|
extern char *rcsd;
|
||||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
extern char *console;
|
||||||
|
extern char *__progname;
|
||||||
|
|
||||||
/* conf.c */
|
/* conf.c */
|
||||||
void parse_finit_conf(char *file);
|
int parse_runlevels (char *runlevels);
|
||||||
|
void parse_finit_conf (char *file);
|
||||||
|
|
||||||
#endif /* FINIT_H_ */
|
#endif /* FINIT_H_ */
|
||||||
|
|
||||||
|
|||||||
@@ -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,31 +117,199 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_uptime(void)
|
/**
|
||||||
|
* 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_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_timestamp(void)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_PRINTK_TIME)
|
#if defined(CONFIG_PRINTK_TIME)
|
||||||
FILE * uptimefile;
|
FILE * uptimefile;
|
||||||
@@ -150,18 +330,16 @@ 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 dots[] = " .....................................................................";
|
const char dots[] = " .....................................................................";
|
||||||
|
|
||||||
write(STDERR_FILENO, home, strlen(home));
|
delline();
|
||||||
|
print_timestamp();
|
||||||
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)
|
||||||
@@ -174,6 +352,25 @@ int print_result(int fail)
|
|||||||
return fail;
|
return fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wait for process completion, returns status of waitpid(2) syscall */
|
||||||
|
int complete(char *cmd, int pid)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
_e("Caught unblocked signal waiting for %s, aborting.", cmd);
|
||||||
|
else if (errno == ECHILD)
|
||||||
|
_e("Caught SIGCHLD waiting for %s, aborting.", cmd);
|
||||||
|
else
|
||||||
|
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
int run(char *cmd)
|
int run(char *cmd)
|
||||||
{
|
{
|
||||||
int status, result, i = 0;
|
int status, result, i = 0;
|
||||||
@@ -188,7 +385,7 @@ int run(char *cmd)
|
|||||||
|
|
||||||
/* Split command line into tokens of an argv[] array. */
|
/* Split command line into tokens of an argv[] array. */
|
||||||
args[i++] = strsep(&arg, "\t ");
|
args[i++] = strsep(&arg, "\t ");
|
||||||
do {
|
while (arg && i < NUM_ARGS) {
|
||||||
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
||||||
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
||||||
if (*arg == '\'' || *arg == '"') {
|
if (*arg == '\'' || *arg == '"') {
|
||||||
@@ -203,7 +400,7 @@ int run(char *cmd)
|
|||||||
} else {
|
} else {
|
||||||
args[i++] = strsep(&arg, "\t ");
|
args[i++] = strsep(&arg, "\t ");
|
||||||
}
|
}
|
||||||
} while (arg && i < NUM_ARGS);
|
}
|
||||||
args[i] = NULL;
|
args[i] = NULL;
|
||||||
#if 0
|
#if 0
|
||||||
_e("Splitting: '%s' =>", cmd);
|
_e("Splitting: '%s' =>", cmd);
|
||||||
@@ -243,14 +440,8 @@ int run(char *cmd)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitpid(pid, &status, 0) == -1) {
|
status = complete(args[0], pid);
|
||||||
if (errno == EINTR)
|
if (-1 == status) {
|
||||||
_e("Caught unblocked signal waiting for %s, aborting.", args[0]);
|
|
||||||
else if (errno == ECHILD)
|
|
||||||
_e("Caught SIGCHLD waiting for %s, aborting.", args[0]);
|
|
||||||
else
|
|
||||||
_e("Failed starting %s, error %d: %s", args[0], errno, strerror (errno));
|
|
||||||
|
|
||||||
if (fp) fclose(fp);
|
if (fp) fclose(fp);
|
||||||
free(backup);
|
free(backup);
|
||||||
|
|
||||||
@@ -285,7 +476,8 @@ 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);
|
if (fmt)
|
||||||
|
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);
|
||||||
@@ -297,7 +489,8 @@ int run_interactive(char *cmd, char *fmt, ...)
|
|||||||
dup2(oldout, STDOUT_FILENO);
|
dup2(oldout, STDOUT_FILENO);
|
||||||
dup2(olderr, STDERR_FILENO);
|
dup2(olderr, STDERR_FILENO);
|
||||||
}
|
}
|
||||||
print_result(status);
|
if (fmt)
|
||||||
|
print_result(status);
|
||||||
if (fp && !debug) {
|
if (fp && !debug) {
|
||||||
size_t len, written;
|
size_t len, written;
|
||||||
|
|
||||||
@@ -312,7 +505,7 @@ int run_interactive(char *cmd, char *fmt, ...)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t run_getty(char *cmd, char *argv[])
|
pid_t run_getty(char *cmd, char *args[], int console)
|
||||||
{
|
{
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
@@ -322,49 +515,49 @@ pid_t run_getty(char *cmd, char *argv[])
|
|||||||
sigset_t nmask;
|
sigset_t nmask;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
/* Detach from initial controlling TTY */
|
if (console) {
|
||||||
vhangup();
|
/* Detach from initial controlling TTY */
|
||||||
|
vhangup();
|
||||||
|
|
||||||
close(2);
|
close(2);
|
||||||
close(1);
|
close(1);
|
||||||
close(0);
|
close(0);
|
||||||
|
|
||||||
/* Attach TTY to console */
|
/* Attach TTY to console */
|
||||||
if (open(CONSOLE, O_RDWR) != 0)
|
if (open(CONSOLE, O_RDWR) != 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
dup2(0, STDIN_FILENO);
|
||||||
sa.sa_handler = SIG_DFL;
|
dup2(0, STDOUT_FILENO);
|
||||||
|
dup2(0, STDERR_FILENO);
|
||||||
|
|
||||||
|
prctl(PR_SET_NAME, "console", 0, 0, 0);
|
||||||
|
}
|
||||||
sigemptyset(&nmask);
|
sigemptyset(&nmask);
|
||||||
sigaddset(&nmask, SIGCHLD);
|
sigaddset(&nmask, SIGCHLD);
|
||||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||||
|
|
||||||
|
/* Reset signal handlers that were set by the parent process */
|
||||||
for (i = 1; i < NSIG; i++)
|
for (i = 1; i < NSIG; i++)
|
||||||
sigaction(i, &sa, NULL);
|
DFLSIG(sa, i, 0);
|
||||||
|
|
||||||
dup2(0, STDIN_FILENO);
|
|
||||||
dup2(0, STDOUT_FILENO);
|
|
||||||
dup2(0, STDERR_FILENO);
|
|
||||||
|
|
||||||
set_procname(argv, "console");
|
|
||||||
|
|
||||||
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.";
|
||||||
|
|
||||||
if (fexist(SYNC_STOPPED)) {
|
if (fexist(SYNC_STOPPED)) {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = write(STDERR_FILENO, msg, sizeof(msg) - 1);
|
if (console) {
|
||||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
i = write(STDERR_FILENO, msg, sizeof(msg));
|
||||||
continue;
|
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (fexist(SYNC_STOPPED))
|
if (fexist(SYNC_STOPPED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
run(cmd);
|
execv(cmd, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -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;
|
||||||
@@ -470,128 +649,19 @@ int getgroup(char *s)
|
|||||||
* Other convenience functions
|
* Other convenience functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void cls(void)
|
void chomp(char *str)
|
||||||
{
|
{
|
||||||
static const char cls[] = "\e[2J\e[1;1H";
|
char *x = strrchr(str, 0x0a);
|
||||||
|
|
||||||
if (!debug)
|
if (x)
|
||||||
fprintf (stderr, "%s", cls);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chomp(char *s)
|
|
||||||
{
|
|
||||||
char *x;
|
|
||||||
|
|
||||||
if ((x = strchr((s), 0x0a)) != NULL)
|
|
||||||
*x = 0;
|
*x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_procname(char *args[], char *name)
|
/* Signal safe sleep ... we get a lot of SIGCHLD at reboot */
|
||||||
|
void do_sleep(unsigned int sec)
|
||||||
{
|
{
|
||||||
size_t len = strlen(args[0]) + 1; /* Include terminating '\0' */
|
while ((sec = sleep(sec)))
|
||||||
|
;
|
||||||
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)
|
||||||
@@ -600,12 +670,20 @@ void set_hostname(char *hostname)
|
|||||||
|
|
||||||
_d("Set hostname: %s", hostname);
|
_d("Set hostname: %s", hostname);
|
||||||
if ((fp = fopen("/etc/hostname", "r")) != NULL) {
|
if ((fp = fopen("/etc/hostname", "r")) != NULL) {
|
||||||
fgets(hostname, HOSTNAME_SIZE, fp);
|
struct stat st;
|
||||||
|
|
||||||
|
fstat(fileno(fp), &st);
|
||||||
|
hostname = realloc(hostname, st.st_size);
|
||||||
|
if (!hostname)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fgets(hostname, st.st_size, fp);
|
||||||
chomp(hostname);
|
chomp(hostname);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
sethostname(hostname, strlen(hostname));
|
if (hostname)
|
||||||
|
sethostname(hostname, strlen(hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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,88 @@
|
|||||||
#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
|
||||||
|
/* Esc[2JEsc[1;1H Clear screen and move cursor to 1,1 (upper left) pos. */
|
||||||
|
#define clrscr() fputs("\e[2J\e[1;1H", stderr)
|
||||||
|
/* Esc[K Erases from the current cursor position to the end of the current line. */
|
||||||
|
#define clreol() fputs("\e[K", stderr)
|
||||||
|
/* Esc[2K Erases the entire current line. */
|
||||||
|
#define delline() fputs("\e[2K", stderr)
|
||||||
|
/* Esc[Line;ColumnH Moves the cursor to the specified position (coordinates) */
|
||||||
|
#define gotoxy(x,y) fprintf(stderr, "\e[%d;%dH", y, x)
|
||||||
|
/* Esc[?25l (lower case L) Hide Cursor */
|
||||||
|
#define hidecursor() fputs("\e[?25l", stderr)
|
||||||
|
/* Esc[?25h (lower case H) Show Cursor */
|
||||||
|
#define showcursor() fputs("\e[?25h", stderr)
|
||||||
|
|
||||||
|
#define echo(fmt, args...) do { fprintf(stderr, fmt "\n", ##args); } while (0)
|
||||||
|
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __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)
|
||||||
|
|
||||||
|
#define ISSET(a,i) ((a & (1 << (i)) ? 1 : 0))
|
||||||
|
#define SETBIT(a,i) (a |= (1 << (i)))
|
||||||
|
#define CLRBIT(a,i) (a &= ~(1 << (i)))
|
||||||
|
|
||||||
|
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 chomp (char *str);
|
||||||
void chomp (char *);
|
void do_sleep (unsigned int sec);
|
||||||
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 complete (char *cmd, int pid);
|
||||||
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 *args[], int console);
|
||||||
int run_parts (char *dir, ...);
|
int run_parts (char *dir, char *cmd);
|
||||||
|
|
||||||
#endif /* FINIT_HELPERS_H_ */
|
#endif /* FINIT_HELPERS_H_ */
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# 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
|
||||||
|
OBJS += strlcpy.o strlcat.o strtonum.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
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
/* 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) {
|
||||||
|
errno = 0;
|
||||||
|
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:
|
||||||
|
*/
|
||||||
+229
@@ -0,0 +1,229 @@
|
|||||||
|
/* 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 if (n > 0) {
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
if (num)
|
||||||
|
*list = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namelist)
|
||||||
|
free(namelist);
|
||||||
|
|
||||||
|
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:
|
||||||
|
*/
|
||||||
@@ -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:
|
||||||
|
*/
|
||||||
@@ -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:
|
||||||
|
*/
|
||||||
@@ -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:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/* 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));
|
||||||
|
|
||||||
|
#ifndef strlcpy
|
||||||
|
size_t strlcpy (char *dst, const char *src, size_t siz);
|
||||||
|
#endif
|
||||||
|
#ifndef strlcat
|
||||||
|
size_t strlcat (char *dst, const char *src, size_t siz);
|
||||||
|
#endif
|
||||||
|
#ifndef strtonum
|
||||||
|
long long strtonum (const char *numstr, long long minval, long long maxval, const char **errstrp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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:
|
||||||
|
*/
|
||||||
|
|
||||||
+289
@@ -0,0 +1,289 @@
|
|||||||
|
/* 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) {
|
||||||
|
if (errno == EEXIST)
|
||||||
|
errno = 0;
|
||||||
|
else
|
||||||
|
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)) {
|
||||||
|
if (EEXIST != errno)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
*/
|
||||||
@@ -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)
|
||||||
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef strlcat
|
||||||
|
/*
|
||||||
|
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||||
|
* full size of dst, not space left). At most siz-1 characters
|
||||||
|
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||||
|
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||||
|
* If retval >= siz, truncation occurred.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcat(char *dst, const char *src, size_t siz)
|
||||||
|
{
|
||||||
|
char *d = dst;
|
||||||
|
const char *s = src;
|
||||||
|
size_t n = siz;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||||
|
while (n-- != 0 && *d != '\0')
|
||||||
|
d++;
|
||||||
|
dlen = d - dst;
|
||||||
|
n = siz - dlen;
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
return(dlen + strlen(s));
|
||||||
|
while (*s != '\0') {
|
||||||
|
if (n != 1) {
|
||||||
|
*d++ = *s;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
|
||||||
|
return(dlen + (s - src)); /* count does not include NUL */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* c-file-style: "bsd"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef strlcpy
|
||||||
/*
|
/*
|
||||||
* Copy src to string dst of size siz. At most siz-1 characters
|
* Copy src to string dst of size siz. At most siz-1 characters
|
||||||
* will be copied. Always NUL terminates (unless siz == 0).
|
* will be copied. Always NUL terminates (unless siz == 0).
|
||||||
@@ -50,6 +51,8 @@ strlcpy(char *dst, const char *src, size_t siz)
|
|||||||
return(s - src - 1); /* count does not include NUL */
|
return(s - src - 1); /* count does not include NUL */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* version-control: t
|
* version-control: t
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2004 Ted Unangst and Todd Miller
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef strtonum
|
||||||
|
#define INVALID 1
|
||||||
|
#define TOOSMALL 2
|
||||||
|
#define TOOLARGE 3
|
||||||
|
|
||||||
|
long long
|
||||||
|
strtonum(const char *numstr, long long minval, long long maxval,
|
||||||
|
const char **errstrp)
|
||||||
|
{
|
||||||
|
long long ll = 0;
|
||||||
|
char *ep;
|
||||||
|
int error = 0;
|
||||||
|
struct errval {
|
||||||
|
const char *errstr;
|
||||||
|
int err;
|
||||||
|
} ev[4] = {
|
||||||
|
{ NULL, 0 },
|
||||||
|
{ "invalid", EINVAL },
|
||||||
|
{ "too small", ERANGE },
|
||||||
|
{ "too large", ERANGE },
|
||||||
|
};
|
||||||
|
|
||||||
|
ev[0].err = errno;
|
||||||
|
errno = 0;
|
||||||
|
if (minval > maxval)
|
||||||
|
error = INVALID;
|
||||||
|
else {
|
||||||
|
ll = strtoll(numstr, &ep, 10);
|
||||||
|
if (numstr == ep || *ep != '\0')
|
||||||
|
error = INVALID;
|
||||||
|
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
|
||||||
|
error = TOOSMALL;
|
||||||
|
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
|
||||||
|
error = TOOLARGE;
|
||||||
|
}
|
||||||
|
if (errstrp != NULL)
|
||||||
|
*errstrp = ev[error].errstr;
|
||||||
|
errno = ev[error].err;
|
||||||
|
if (error)
|
||||||
|
ll = 0;
|
||||||
|
|
||||||
|
return (ll);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -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) {
|
// _d("Comparing %s against plugin %s", str, p->name);
|
||||||
|
#define SEARCH_PLUGIN(str) \
|
||||||
|
PLUGIN_ITERATOR(p, tmp) { \
|
||||||
|
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,34 +188,27 @@ 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(struct pollfd *io)
|
||||||
{
|
{
|
||||||
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 == io->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, io->fd, io->events);
|
||||||
|
|
||||||
|
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||||
|
io->fd = p->io.fd;
|
||||||
|
io->events = p->io.flags;
|
||||||
|
io->revents = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void io_monitor(void)
|
void plugin_monitor(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -155,7 +226,7 @@ void io_monitor(void)
|
|||||||
/* Traverse all I/O fds and run callbacks */
|
/* Traverse all I/O fds and run callbacks */
|
||||||
for (i = 0; i < num_fds; i++) {
|
for (i = 0; i < num_fds; i++) {
|
||||||
if (fds[i].revents)
|
if (fds[i].revents)
|
||||||
generic_io_cb(fds[i].fd, fds[i].revents);
|
generic_io_cb(&fds[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -166,20 +237,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 +325,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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_ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+11
-9
@@ -1,10 +1,12 @@
|
|||||||
# 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 tty.so
|
||||||
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
|
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
|
||||||
|
TOPDIR ?= $(shell pwd)
|
||||||
|
|
||||||
include ../common.mk
|
include ../common.mk
|
||||||
|
|
||||||
@@ -17,18 +19,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)
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-1
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
static void setup(void *UNUSED(arg))
|
static void setup(void *UNUSED(arg))
|
||||||
{
|
{
|
||||||
#ifdef USE_MESSAGE_BUS
|
#ifdef HAVE_DBUS
|
||||||
_d("Starting D-Bus ...");
|
_d("Starting D-Bus ...");
|
||||||
mkdir("/var/run/dbus", 0755);
|
mkdir("/var/run/dbus", 0755);
|
||||||
mkdir("/var/lock/subsys/messagebus", 0755);
|
mkdir("/var/lock/subsys/messagebus", 0755);
|
||||||
@@ -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
@@ -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 = {
|
||||||
|
|||||||
+61
-37
@@ -23,84 +23,110 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#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 <sys/stat.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "sig.h"
|
||||||
|
#include "finit.h"
|
||||||
|
|
||||||
#define INIT_MAGIC 0x03091969
|
static void parse(void *arg, int fd, int events);
|
||||||
#define INIT_CMD_RUNLVL 1
|
|
||||||
|
|
||||||
struct init_request {
|
static plugin_t plugin = {
|
||||||
int magic; /* Magic number */
|
.io = {
|
||||||
int cmd; /* What kind of request */
|
.cb = parse,
|
||||||
int runlevel; /* Runlevel to change to */
|
.flags = PLUGIN_IO_READ,
|
||||||
int sleeptime; /* Time between TERM and KILL */
|
},
|
||||||
char data[368];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef LISTEN_INITCTL
|
static void fifo_open(void)
|
||||||
|
{
|
||||||
|
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
|
||||||
|
if (-1 == plugin.io.fd) {
|
||||||
|
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 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.
|
||||||
*/
|
*/
|
||||||
static void read_initctl(void *UNUSED(arg), int fd, int UNUSED(events))
|
static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||||
{
|
{
|
||||||
struct init_request request;
|
struct init_request rq;
|
||||||
|
|
||||||
_d("Receiving request on %s...", FINIT_FIFO);
|
_d("Receiving request on %s...", FINIT_FIFO);
|
||||||
while (1) {
|
while (1) {
|
||||||
ssize_t len = read(fd, &request, sizeof(request));
|
ssize_t len = read(fd, &rq, sizeof(rq));
|
||||||
|
|
||||||
if (-1 == len) {
|
if (len <= 0) {
|
||||||
_d("Error %d", errno);
|
if (-1 == len) {
|
||||||
break; /* Probably EAGAIN */
|
if (EINTR == errno)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (EAGAIN == errno)
|
||||||
|
break;
|
||||||
|
|
||||||
|
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.magic != INIT_MAGIC)
|
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||||
continue;
|
_e("Invalid initctl request.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
_d("Magic OK...");
|
_d("Magic OK...");
|
||||||
if (request.cmd == INIT_CMD_RUNLVL) {
|
if (rq.cmd == INIT_CMD_RUNLVL) {
|
||||||
switch (request.runlevel) {
|
switch (rq.runlevel) {
|
||||||
case '0':
|
case '0':
|
||||||
_d("Halting system (SIGUSR2)");
|
_d("Halting system (SIGUSR2)");
|
||||||
do_shutdown(SIGUSR2);
|
do_shutdown(SIGUSR2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
_d("Cannot enter bootstrap after boot ...");
|
||||||
|
rq.runlevel = '1';
|
||||||
|
/* Fall through to regular processing */
|
||||||
|
|
||||||
|
case '1'...'5':
|
||||||
|
case '7'...'9':
|
||||||
|
_d("Setting new runlevel %c", rq.runlevel);
|
||||||
|
svc_runlevel(rq.runlevel - '0');
|
||||||
|
break;
|
||||||
|
|
||||||
case '6':
|
case '6':
|
||||||
_d("Rebooting system (SIGUSR1)");
|
_d("Rebooting system (SIGUSR1)");
|
||||||
do_shutdown(SIGUSR1);
|
do_shutdown(SIGUSR1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_d("Unsupported runlevel: %d", request.runlevel);
|
_d("Unsupported runlevel: %d", rq.runlevel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (rq.cmd == INIT_CMD_DEBUG) {
|
||||||
|
debug = !debug;
|
||||||
} else {
|
} else {
|
||||||
_d("Unsupported cmd: %d", request.cmd);
|
_d("Unsupported cmd: %d", rq.cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static plugin_t plugin = {
|
close(fd);
|
||||||
.io = {
|
fifo_open();
|
||||||
.cb = read_initctl,
|
}
|
||||||
.flags = PLUGIN_IO_READ,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
PLUGIN_INIT(plugin_init)
|
PLUGIN_INIT(plugin_init)
|
||||||
{
|
{
|
||||||
_d("Setting up %s", FINIT_FIFO);
|
_d("Setting up %s", FINIT_FIFO);
|
||||||
mkfifo(FINIT_FIFO, 0600);
|
mkfifo(FINIT_FIFO, 0600);
|
||||||
|
|
||||||
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
|
fifo_open();
|
||||||
if (-1 == plugin.io.fd) {
|
|
||||||
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin_register(&plugin);
|
plugin_register(&plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +135,6 @@ PLUGIN_EXIT(plugin_exit)
|
|||||||
plugin_unregister(&plugin);
|
plugin_unregister(&plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* LISTEN_INITCTL */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* version-control: t
|
* version-control: t
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||||
|
* Copyright (c) 2013 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h> /* O_RDONLY et al */
|
||||||
|
#include <unistd.h> /* read() */
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
|
static void watcher(void *arg, int fd, int events);
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.io = {
|
||||||
|
.cb = watcher,
|
||||||
|
.flags = PLUGIN_IO_READ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void setup(void)
|
||||||
|
{
|
||||||
|
plugin.io.fd = inotify_init1(IN_NONBLOCK);
|
||||||
|
if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
|
||||||
|
_e("Failed starting TTY watcher: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void watcher(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
char buf[EVENT_SIZE], name[42];
|
||||||
|
tty_node_t *entry;
|
||||||
|
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||||
|
|
||||||
|
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||||
|
if (-1 == len) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||||
|
entry = tty_find(name);
|
||||||
|
if (entry && tty_enabled(&entry->data, runlevel)) {
|
||||||
|
if (notified->mask & IN_CREATE)
|
||||||
|
tty_start(&entry->data);
|
||||||
|
else if (entry->data.pid)
|
||||||
|
tty_stop(&entry->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
setup();
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Private header file for main finit daemon, not for plugins
|
/* Private header file for main finit daemon, not for 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-2013 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -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_bootstrap (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_ */
|
||||||
|
|
||||||
|
|||||||
@@ -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_ */
|
||||||
@@ -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:
|
||||||
|
*/
|
||||||
@@ -49,7 +49,6 @@
|
|||||||
|
|
||||||
static int stopped = 0;
|
static int stopped = 0;
|
||||||
|
|
||||||
|
|
||||||
void do_shutdown (int sig)
|
void do_shutdown (int sig)
|
||||||
{
|
{
|
||||||
touch(SYNC_SHUTDOWN);
|
touch(SYNC_SHUTDOWN);
|
||||||
@@ -57,12 +56,15 @@ void do_shutdown (int sig)
|
|||||||
if (sdown)
|
if (sdown)
|
||||||
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
||||||
|
|
||||||
|
/* Call all shutdown hooks before rebooting... */
|
||||||
|
plugin_run_hooks(HOOK_SHUTDOWN);
|
||||||
|
|
||||||
|
/* Here is where we signal watchdogd to do a forced reset for us */
|
||||||
_d("Sending SIGTERM to all processes.");
|
_d("Sending SIGTERM to all processes.");
|
||||||
kill(-1, SIGTERM);
|
kill(-1, SIGTERM);
|
||||||
|
|
||||||
sleep(1);
|
/* Wait for WDT to timeout, should be no more than ~1 sec. */
|
||||||
/* Call all shutdown hooks, this is the last call before rebooting... */
|
do_sleep(2);
|
||||||
run_hooks(HOOK_SHUTDOWN);
|
|
||||||
|
|
||||||
_d("Sending SIGKILL to remaining processes.");
|
_d("Sending SIGKILL to remaining processes.");
|
||||||
kill(-1, SIGKILL);
|
kill(-1, SIGKILL);
|
||||||
@@ -87,7 +89,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 +108,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* Generic API for managing svc_t structures
|
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||||
*
|
*
|
||||||
* 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-2013 Joachim Nilsson <troglobit@gmail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,11 +24,14 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <utmp.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
|
#include "tty.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 +49,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 +67,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 +78,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,38 +111,141 @@ 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++];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* svc_register - Register a non-backgrounding service to be monitored
|
* svc_bootstrap - Start bootstrap services and tasks
|
||||||
|
*
|
||||||
|
* System startup, runlevel S, where only services, tasks and
|
||||||
|
* run commands absolutely essential to bootstrap are located.
|
||||||
|
*/
|
||||||
|
void svc_bootstrap(void)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
svc_cmd_t cmd;
|
||||||
|
|
||||||
|
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
cmd = svc_enabled(svc, 0, NULL);
|
||||||
|
if (SVC_START == cmd || (SVC_RELOAD == cmd))
|
||||||
|
svc_start(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void utmp_save(int pre, int now)
|
||||||
|
{
|
||||||
|
struct utmp utent;
|
||||||
|
|
||||||
|
int encode(int lvl)
|
||||||
|
{
|
||||||
|
if (!lvl) return 0;
|
||||||
|
return lvl + '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
utent.ut_type = RUN_LVL;
|
||||||
|
utent.ut_pid = (encode(pre) << 8) | (encode(now) & 0xFF);
|
||||||
|
strlcpy(utent.ut_user, "runlevel", sizeof(utent.ut_user));
|
||||||
|
|
||||||
|
setutent();
|
||||||
|
pututline(&utent);
|
||||||
|
endutent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_runlevel - Change to a new runlevel
|
||||||
|
* @newlevel: New runlevel to activate
|
||||||
|
*
|
||||||
|
* Stops all services not in @newlevel and starts, or lets continue to run,
|
||||||
|
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
||||||
|
*/
|
||||||
|
void svc_runlevel(int newlevel)
|
||||||
|
{
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
if (runlevel == newlevel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (newlevel < 0 || newlevel > 9)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prevlevel = runlevel;
|
||||||
|
runlevel = newlevel;
|
||||||
|
utmp_save(prevlevel, newlevel);
|
||||||
|
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
int run = svc_enabled(svc, 0, NULL);
|
||||||
|
|
||||||
|
if (svc->pid) {
|
||||||
|
if (run == SVC_STOP)
|
||||||
|
svc_stop(svc);
|
||||||
|
} else {
|
||||||
|
if (run == SVC_START)
|
||||||
|
svc_start(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == runlevel) {
|
||||||
|
do_shutdown(SIGUSR2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (6 == runlevel) {
|
||||||
|
do_shutdown(SIGUSR1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runlevel == 1)
|
||||||
|
touch("/etc/nologin"); /* Disable login in single-user mode */
|
||||||
|
else
|
||||||
|
remove("/etc/nologin");
|
||||||
|
|
||||||
|
if (0 != prevlevel)
|
||||||
|
tty_runlevel(runlevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_register - Register service, task or run commands
|
||||||
|
* @type: %SVC_CMD_SERVICE(0), %SVC_CMD_TASK(1), %SVC_CMD_RUN(2)
|
||||||
* @line: A complete command line with -- separated description text
|
* @line: A complete command line with -- separated description text
|
||||||
* @username: Optional username to run service as, or %NULL to run as root
|
* @username: Optional username to run service as, or %NULL to run as root
|
||||||
*
|
*
|
||||||
* Actually, the @line can optionally start with a username, denoted by
|
* This function is used to register commands to be run on different
|
||||||
* an @ character. Like this:
|
* system runlevels with optional username. The @type argument details
|
||||||
|
* if it's service to bo monitored/respawned (daemon), a one-shot task
|
||||||
|
* or a command that must run in sequence and not in parallell, like
|
||||||
|
* service and task commands do.
|
||||||
*
|
*
|
||||||
* "service [@username] /path/to/daemon -- Description text"
|
* The @line can optionally start with a username, denoted by an @
|
||||||
|
* character. Like this:
|
||||||
*
|
*
|
||||||
* The [] brackets means the argument is optional. If the username is
|
* "service @username [!0-6,S] /path/to/daemon arg -- Description text"
|
||||||
* left out the daemon is started as root.
|
* "task @username [!0-6,S] /path/to/task arg -- Description text"
|
||||||
|
* "run @username [!0-6,S] /path/to/cmd arg -- Description text"
|
||||||
|
*
|
||||||
|
* If the username is left out the command is started as root.
|
||||||
|
|
||||||
|
* The [] brackets are there to denote the allowed runlevels. Allowed
|
||||||
|
* runlevels mimic that of SysV init with the addition of the 'S'
|
||||||
|
* runlevel, which is only run once at startup. It can be seen as the
|
||||||
|
* system bootstrap. If a task or run command is listed in more than the
|
||||||
|
* [S] runlevel they will be called when changing runlevel.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||||
*/
|
*/
|
||||||
int svc_register(char *line, char *username)
|
int svc_register(int type, char *line, char *username)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
char *cmd, *descr;
|
char *cmd, *desc, *runlevels = NULL;
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
if (!line) {
|
if (!line) {
|
||||||
@@ -164,9 +253,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) {
|
||||||
@@ -175,8 +264,13 @@ int svc_register(char *line, char *username)
|
|||||||
return errno = ENOENT;
|
return errno = ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd[0] == '@') { /* @username */
|
while (cmd) {
|
||||||
username = &cmd[1];
|
if (cmd[0] == '@') /* @username */
|
||||||
|
username = &cmd[1];
|
||||||
|
else if (cmd[0] == '[') /* [runlevels] */
|
||||||
|
runlevels = &cmd[0];
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
/* Check if valid command follows... */
|
/* Check if valid command follows... */
|
||||||
cmd = strtok(NULL, " ");
|
cmd = strtok(NULL, " ");
|
||||||
@@ -186,29 +280,37 @@ int svc_register(char *line, char *username)
|
|||||||
|
|
||||||
svc = svc_new();
|
svc = svc_new();
|
||||||
if (!svc) {
|
if (!svc) {
|
||||||
_e("Out of memorym, cannot register service %s", cmd);
|
_e("Out of memory, cannot register service %s", cmd);
|
||||||
return errno = ENOMEM;
|
return errno = ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descr)
|
svc->type = type;
|
||||||
strlcpy(svc->descr, descr + 3, sizeof(svc->descr));
|
|
||||||
|
if (desc)
|
||||||
|
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->args[i++], cmd, sizeof(svc->args[0]));
|
|
||||||
|
|
||||||
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
|
||||||
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
while ((cmd = strtok(NULL, " ")))
|
while ((cmd = strtok(NULL, " ")))
|
||||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
svc->args[i][0] = 0;
|
svc->args[i][0] = 0;
|
||||||
|
|
||||||
|
svc->runlevels = parse_runlevels(runlevels);
|
||||||
|
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 +320,22 @@ 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) {
|
if (!ISSET(svc->runlevels, runlevel))
|
||||||
/* Unknown service, default to enabled. */
|
return SVC_STOP;
|
||||||
return SVC_START;
|
|
||||||
}
|
|
||||||
|
|
||||||
return svc->plugin->cb(svc, dynamic);
|
/* Is there a service plugin registered? */
|
||||||
|
if (svc->cb)
|
||||||
|
return svc->cb(svc, event, arg);
|
||||||
|
|
||||||
|
/* No service plugin, default to start, since listed in finit.conf */
|
||||||
|
return SVC_START;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_norespawn(void)
|
static int is_norespawn(void)
|
||||||
@@ -256,49 +361,89 @@ void svc_monitor(void)
|
|||||||
if (is_norespawn())
|
if (is_norespawn())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
if (tty_respawn(lost))
|
||||||
char *name = basename(svc->cmd);
|
return;
|
||||||
|
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
if (lost != svc->pid)
|
if (lost != svc->pid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("Ouch, lost pid %d - %s(%d)", lost, name, svc->pid);
|
if (SVC_CMD_SERVICE != svc->type) {
|
||||||
|
svc->pid = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Ouch, lost pid %d - %s(%d)", lost, basename(svc->cmd), svc->pid);
|
||||||
|
|
||||||
|
/* No longer running, update books. */
|
||||||
|
svc->pid = 0;
|
||||||
|
|
||||||
if (sig_stopped()) {
|
if (sig_stopped()) {
|
||||||
_e("Stopped, not respawning killed processes.");
|
_e("Stopped, not respawning killed processes.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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", basename(svc->cmd));
|
||||||
kill_procname(name, SIGTERM);
|
procname_kill(basename(svc->cmd), SIGTERM);
|
||||||
|
|
||||||
/* Restarting lost service. */
|
/* Restarting lost service. */
|
||||||
if (svc_enabled(svc, 0))
|
if (svc_enabled(svc, 0, NULL)) {
|
||||||
|
svc->restart_counter++;
|
||||||
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 (SVC_CMD_SERVICE != svc->type)
|
||||||
print_descr("Starting ", svc->descr);
|
print_desc("", svc->desc);
|
||||||
|
else if (!respawn)
|
||||||
|
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,24 +458,36 @@ 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 (strlen(arg) < (sizeof(buf) - strlen(buf)))
|
||||||
|
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;
|
||||||
|
|
||||||
if (!respawn)
|
if (SVC_CMD_RUN == svc->type)
|
||||||
|
print_result(WEXITSTATUS(complete(svc->cmd, pid)));
|
||||||
|
else if (!respawn)
|
||||||
print_result(svc->pid > 1 ? 0 : 1);
|
print_result(svc->pid > 1 ? 0 : 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -338,9 +495,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,22 +513,33 @@ 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;
|
||||||
|
svc->restart_counter = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_descr("Stopping ", svc->descr);
|
if (SVC_CMD_SERVICE != svc->type)
|
||||||
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, get_pidname(svc->pid, NULL, 0));
|
return 0;
|
||||||
|
|
||||||
|
if (runlevel != 1)
|
||||||
|
print_desc("Stopping ", svc->desc);
|
||||||
|
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||||
res = kill(svc->pid, SIGTERM);
|
res = kill(svc->pid, SIGTERM);
|
||||||
print_result(res);
|
if (runlevel != 1)
|
||||||
|
print_result(res);
|
||||||
svc->pid = 0;
|
svc->pid = 0;
|
||||||
|
svc->restart_counter = 0;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -387,23 +555,6 @@ int svc_reload(svc_t *svc)
|
|||||||
return kill(svc->pid, SIGHUP);
|
return kill(svc->pid, SIGHUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void svc_start_all(void)
|
|
||||||
{
|
|
||||||
svc_t *svc;
|
|
||||||
svc_cmd_t cmd;
|
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
cmd = svc_enabled(svc, 0);
|
|
||||||
if (cmd == SVC_START || (cmd == SVC_RELOAD && svc->pid == 0))
|
|
||||||
svc_start(svc);
|
|
||||||
else if (cmd == SVC_RELOAD)
|
|
||||||
svc_reload(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: Hook point for plugins. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Variables:
|
* Local Variables:
|
||||||
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
|
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Generic API for managing svc_t structures
|
/* Finit service monitor and generic 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,15 +30,18 @@
|
|||||||
#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 */
|
||||||
SVC_RELOAD /* Enabled, needs restart */
|
SVC_RELOAD /* Enabled, needs restart */
|
||||||
} svc_cmd_t;
|
} svc_cmd_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SVC_CMD_SERVICE = 0, /* Monitored, will be respawned */
|
||||||
|
SVC_CMD_TASK, /* One-shot, runs in parallell */
|
||||||
|
SVC_CMD_RUN /* Like task, but wait for completion */
|
||||||
|
} svc_type_t;
|
||||||
|
|
||||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||||
#define MAX_ARG_LEN 64
|
#define MAX_ARG_LEN 64
|
||||||
#define MAX_STR_LEN 64
|
#define MAX_STR_LEN 64
|
||||||
@@ -51,16 +54,25 @@ 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;
|
||||||
|
svc_type_t type;
|
||||||
|
int reload;
|
||||||
|
int runlevels;
|
||||||
|
unsigned int restart_counter; /* Incremented for each restart by service monitor. */
|
||||||
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 +85,25 @@ 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);
|
||||||
|
void svc_runlevel (int newlevel);
|
||||||
|
|
||||||
int svc_register (char *line, char *username);
|
int svc_register (int type, 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_ */
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
/* Finit TTY handling
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||||
|
* Copyright (c) 2013 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>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "lite.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
|
LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||||
|
|
||||||
|
|
||||||
|
/* tty [!1-9,S] <DEV> [BAUD[,BAUD,...]] [TERM] */
|
||||||
|
int tty_register(char *line)
|
||||||
|
{
|
||||||
|
tty_node_t *entry;
|
||||||
|
int insert = 0, baud = 0;
|
||||||
|
char *cmd, *dev;
|
||||||
|
char *runlevels = NULL, *term = NULL;
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
_e("Invalid input argument.");
|
||||||
|
return errno = EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = strtok(line, " ");
|
||||||
|
if (!cmd) {
|
||||||
|
incomplete:
|
||||||
|
_e("Incomplete tty, cannot register.");
|
||||||
|
return errno = EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd[0] == '[') { /* [runlevels] */
|
||||||
|
runlevels = &cmd[0];
|
||||||
|
dev = strtok(NULL, " ");
|
||||||
|
if (!dev)
|
||||||
|
goto incomplete;
|
||||||
|
} else {
|
||||||
|
dev = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = strtok(NULL, " ");
|
||||||
|
if (cmd) {
|
||||||
|
baud = strtonum(cmd, 50, 4000000, NULL);
|
||||||
|
term = strtok(NULL, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = tty_find(dev);
|
||||||
|
if (!entry) {
|
||||||
|
insert = 1;
|
||||||
|
entry = calloc(1, sizeof(*entry));
|
||||||
|
if (!entry)
|
||||||
|
return errno = ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->data.name = strdup(dev);
|
||||||
|
if (!baud)
|
||||||
|
baud = BAUDRATE;
|
||||||
|
entry->data.baud = baud;
|
||||||
|
entry->data.term = term ? strdup(term) : NULL;
|
||||||
|
entry->data.runlevels = parse_runlevels(runlevels);
|
||||||
|
_d("Registering tty %s at %d baud with term=%s on runlevels %s", dev, baud, term ?: "N/A", runlevels);
|
||||||
|
|
||||||
|
if (insert)
|
||||||
|
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty_node_t *tty_find(char *dev)
|
||||||
|
{
|
||||||
|
tty_node_t *entry;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
|
if (!strcmp(dev, entry->data.name))
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty_node_t *tty_find_by_pid(pid_t pid)
|
||||||
|
{
|
||||||
|
tty_node_t *entry;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
|
if (entry->data.pid == pid)
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tty_start(finit_tty_t *tty)
|
||||||
|
{
|
||||||
|
int i = 0, is_console = 0;
|
||||||
|
char *cmd, *arg;
|
||||||
|
char line[FILENAME_MAX];
|
||||||
|
char *args[42];
|
||||||
|
|
||||||
|
if (tty->pid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (console && !strcmp(tty->name, console))
|
||||||
|
is_console = 1;
|
||||||
|
|
||||||
|
#if defined(GETTY_AGETTY)
|
||||||
|
snprintf(line, sizeof(line), "%s %s %d %s", GETTY, tty->name, tty->baud, tty->term ?: "");
|
||||||
|
#elif defined(GETTY_BUSYBOX)
|
||||||
|
snprintf(line, sizeof(line), "%s %d %s %s", GETTY, tty->baud, tty->name, tty->term ?: "");
|
||||||
|
#else
|
||||||
|
#error No GETTY defined!
|
||||||
|
#endif
|
||||||
|
cmd = strtok(line, " ");
|
||||||
|
args[i++] = basename(cmd);
|
||||||
|
while ((arg = strtok(NULL, " ")))
|
||||||
|
args[i++] = arg;
|
||||||
|
args[i] = NULL;
|
||||||
|
|
||||||
|
_d("Starting %s: %s on %s", is_console ? "console" : "TTY", cmd, tty->name);
|
||||||
|
tty->pid = run_getty(cmd, args, is_console);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tty_stop(finit_tty_t *tty)
|
||||||
|
{
|
||||||
|
if (!tty->pid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_d("Stopping TTY %s", tty->name);
|
||||||
|
kill(tty->pid, SIGTERM);
|
||||||
|
do_sleep(2);
|
||||||
|
kill(tty->pid, SIGKILL);
|
||||||
|
waitpid(tty->pid, NULL, 0);
|
||||||
|
tty->pid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tty_enabled(finit_tty_t *tty, int runlevel)
|
||||||
|
{
|
||||||
|
if (!tty)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ISSET(tty->runlevels, runlevel))
|
||||||
|
return 0;
|
||||||
|
else if (fexist(tty->name))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TTY monitor, called by svc_monitor() */
|
||||||
|
int tty_respawn(pid_t pid)
|
||||||
|
{
|
||||||
|
tty_node_t *entry = tty_find_by_pid(pid);
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Clear PID to be able to respawn it. */
|
||||||
|
entry->data.pid = 0;
|
||||||
|
|
||||||
|
if (!tty_enabled(&entry->data, runlevel))
|
||||||
|
tty_stop(&entry->data);
|
||||||
|
else
|
||||||
|
tty_start(&entry->data);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
||||||
|
void tty_runlevel(int runlevel)
|
||||||
|
{
|
||||||
|
tty_node_t *entry;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &tty_list, link) {
|
||||||
|
if (!tty_enabled(&entry->data, runlevel))
|
||||||
|
tty_stop(&entry->data);
|
||||||
|
else
|
||||||
|
tty_start(&entry->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/* Finit TTY handling
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||||
|
* Copyright (c) 2013 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_TTY_H_
|
||||||
|
#define FINIT_TTY_H_
|
||||||
|
|
||||||
|
#include "queue.h" /* BSD sys/queue.h API */
|
||||||
|
|
||||||
|
#define EVENT_SIZE ((sizeof(struct inotify_event) + 20))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *name;
|
||||||
|
int baud;
|
||||||
|
char *term;
|
||||||
|
int runlevels;
|
||||||
|
|
||||||
|
int pid;
|
||||||
|
} finit_tty_t;
|
||||||
|
|
||||||
|
typedef struct tty_node {
|
||||||
|
LIST_ENTRY(tty_node) link;
|
||||||
|
finit_tty_t data;
|
||||||
|
} tty_node_t;
|
||||||
|
|
||||||
|
//extern LIST_HEAD(, tty_node) tty_list;
|
||||||
|
|
||||||
|
int tty_register (char *line);
|
||||||
|
tty_node_t *tty_find (char *dev);
|
||||||
|
tty_node_t *tty_find_by_pid (pid_t pid);
|
||||||
|
void tty_start (finit_tty_t *tty);
|
||||||
|
void tty_stop (finit_tty_t *tty);
|
||||||
|
int tty_enabled (finit_tty_t *tty, int runlevel);
|
||||||
|
int tty_respawn (pid_t pid);
|
||||||
|
void tty_runlevel (int runlevel);
|
||||||
|
|
||||||
|
#endif /* FINIT_TTY_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
Reference in New Issue
Block a user