Fix I/O plugin watcher, load plugins earlier to enable a new early hook!

Restructure start by calling the plugin loader earlier. This makes it
possible to run hooks even earlier.  This also adds a new hook:
HOOK_ROOTFS_UP which runs as soon as the system root (/) is available.

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

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

Makefile:
        Add dependency handling for parallell build
        Add libite library

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

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

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

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

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

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

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

plugin.h:
        Add new HOOK_ROOTFS_UP hook

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

svc.c:
        svc_start(): Dito

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

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

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2012-10-02 08:25:50 +02:00
1 parent c002b5301f
commit 65c9e7ebc6
22 files changed
+1324 -111

No files matched your search

+12
View File
@@ -1,3 +1,15 @@
1.4 (20121002)
- Fix I/O plugin watcher and load plugins earlier for a new hook
- Start refactoring helpers.c into a libite.so (-lite). This
means other user space applications/daemons can make use of
the neat toolbox available in finit
- Use short-form -s/-w -u to work with BusyBox hwclock as well
- Use RTLD_GLOBAL flag to tell dynamic loader to load dependent
.so files as well. Lets other plugins use global symbols.
- Greatly simplify svc hook for external plugins and cleanup
the plugin API.
- And more... see the GIT log for more details.
1.3 (20120928) 1.3 (20120928)
- Cleanup public plugin API a bit and add new pid/pidfile funcs - Cleanup public plugin API a bit and add new pid/pidfile funcs
- Add plugin hook to end of service startup - Add plugin hook to end of service startup
+26 -17
View File
@@ -26,14 +26,15 @@
uninstall-exec uninstall-data uninstall-dev uninstall-exec uninstall-data uninstall-dev
# Top directory for building complete system, fall back to this directory # Top directory for building complete system, fall back to this directory
ROOTDIR ?= $(shell pwd) ROOTDIR = $(shell pwd)
#VERSION ?= $(shell git tag -l | tail -1) #VERSION ?= $(shell git tag -l | tail -1)
VERSION ?= 1.4 VERSION ?= 1.4
EXEC = finit NAME = finit
PKG = $(EXEC)-$(VERSION) PKG = $(NAME)-$(VERSION)
DEV = $(EXEC)-dev DEV = $(NAME)-dev
ARCHIVE = $(PKG).tar.xz ARCHIVE = $(PKG).tar.xz
EXEC = finit reboot
HEADERS = plugin.h svc.h helpers.h HEADERS = plugin.h svc.h helpers.h
DISTFILES = LICENSE README ChangeLog finit.conf services DISTFILES = LICENSE README ChangeLog finit.conf services
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o
@@ -45,7 +46,8 @@ DEPS = $(addprefix .,$(SRCS:.c=.d))
prefix ?= /usr prefix ?= /usr
sysconfdir ?= /etc sysconfdir ?= /etc
sbindir ?= /sbin sbindir ?= /sbin
plugindir ?= /lib/finit/plugins libdir ?= /lib
plugindir ?= $(libdir)/finit/plugins
incdir = $(prefix)/include/finit incdir = $(prefix)/include/finit
datadir = $(prefix)/share/doc/finit datadir = $(prefix)/share/doc/finit
mandir = $(prefix)/share/man/man8 mandir = $(prefix)/share/man/man8
@@ -58,22 +60,28 @@ FINIT_RCSD ?= $(sysconfdir)/finit.d
CFLAGS += -W -Wall -Werror -Os CFLAGS += -W -Wall -Werror -Os
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997 # Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
CPPFLAGS += -U_FORTIFY_SOURCE CPPFLAGS += -U_FORTIFY_SOURCE
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE CPPFLAGS += -Ilibite -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\" CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
CPPFLAGS += -DFINIT_FIFO=\"$(FINIT_FIFO)\" -DFINIT_CONF=\"$(FINIT_CONF)\" CPPFLAGS += -DFINIT_FIFO=\"$(FINIT_FIFO)\" -DFINIT_CONF=\"$(FINIT_CONF)\"
CPPFLAGS += -DFINIT_RCSD=\"$(FINIT_RCSD)\" -DPLUGIN_PATH=\"$(plugindir)\" CPPFLAGS += -DFINIT_RCSD=\"$(FINIT_RCSD)\" -DPLUGIN_PATH=\"$(plugindir)\"
LDFLAGS += -rdynamic LDFLAGS += -rdynamic -L$(ROOTDIR)/libite
LDLIBS += -ldl DEPLIBS = libite/libite.so
LDLIBS += -ldl -lite
include common.mk include common.mk
export plugindir 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)
reboot: reboot.o $(DEPLIBS)
install-exec: all install-exec: all
@$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD) @$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD)
@@ -82,6 +90,7 @@ install-exec: all
printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \ printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \
$(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \ $(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \
done done
$(MAKE) -C libite install-exec
$(MAKE) -C plugins install $(MAKE) -C plugins install
install-data: install-data:
@@ -98,6 +107,7 @@ install-dev:
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \ printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \ $(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
done done
$(MAKE) -C libite install-dev install-exec
install: install-exec install-data install-dev install: install-exec install-data install-dev
@@ -108,6 +118,7 @@ uninstall-exec:
done done
-@rmdir $(DESTDIR)$(sbindir) 2>/dev/null -@rmdir $(DESTDIR)$(sbindir) 2>/dev/null
-@rmdir $(DESTDIR)$(FINIT_RCSD) 2>/dev/null -@rmdir $(DESTDIR)$(FINIT_RCSD) 2>/dev/null
$(MAKE) -C libite uninstall
$(MAKE) -C plugins uninstall $(MAKE) -C plugins uninstall
uninstall-data: uninstall-data:
@@ -119,20 +130,18 @@ uninstall-data:
-@rmdir $(DESTDIR)$(datadir) -@rmdir $(DESTDIR)$(datadir)
uninstall-dev: uninstall-dev:
-@for file in $(HEADERS); do \ -@$(RM) -rf $(DESTDIR)$(incdir)
printf " REMOVE $(DESTDIR)$(incdir)/$$file\n"; \
rm $(DESTDIR)$(incdir)/$$file 2>/dev/null; \
done
-@rmdir $(DESTDIR)$(incdir)
uninstall: uninstall-exec uninstall-data uninstall-dev 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:
+1 -1
View File
@@ -2,7 +2,7 @@
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 --silent MAKEFLAGS = --no-print-directory --silent
INSTALL := install -b INSTALL := install --backup=off
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755 STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755
# Smart autodependecy generation via GCC -M. # Smart autodependecy generation via GCC -M.
+22 -16
View File
@@ -49,6 +49,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;
@@ -91,15 +92,6 @@ int main(int UNUSED(args), char *argv[])
mount("none", "/dev/shm", "tmpfs", 0, NULL); mount("none", "/dev/shm", "tmpfs", 0, NULL);
umask(022); umask(022);
/*
* Populate /dev and prepare for runtime events from kernel.
*/
#if defined(USE_UDEV)
run_interactive("udevd --daemon", "Populating device tree");
#elif defined (MDEV)
run_interactive(MDEV "-s", "Populating device tree");
#endif
/* /*
* Parse kernel parameters * Parse kernel parameters
*/ */
@@ -108,11 +100,28 @@ int main(int UNUSED(args), char *argv[])
cls(); cls();
echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")"); echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
/*
* Populate /dev and prepare for runtime events from kernel.
*/
#if defined(USE_UDEV)
run_interactive("udevd --daemon", "Populating device tree");
#elif defined (MDEV)
run_interactive(MDEV " -s", "Populating device tree");
#endif
/* /*
* Parse configuration file * Parse configuration file
*/ */
parse_finit_conf(FINIT_CONF); parse_finit_conf(FINIT_CONF);
/*
* Load plugins. Must run after finit.conf has registered
* all services, or service plugins won't have anything to
* hook on to.
*/
print_desc("", "Loading plugins");
print_result(plugin_load_all(PLUGIN_PATH));
/* /*
* Mount filesystems * Mount filesystems
*/ */
@@ -124,6 +133,9 @@ int main(int UNUSED(args), char *argv[])
#ifdef SYSROOT #ifdef SYSROOT
run(SYSROOT, "/", NULL, MS_MOVE, NULL); run(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif #endif
_d("Root FS up, calling hooks ...");
plugin_run_hooks(HOOK_ROOTFS_UP);
umask(0); umask(0);
run("/bin/mount -na"); run("/bin/mount -na");
run("/sbin/swapon -ea"); run("/sbin/swapon -ea");
@@ -137,13 +149,7 @@ int main(int UNUSED(args), char *argv[])
*/ */
sig_setup(); sig_setup();
/* _d("Base FS up, calling hooks ...");
* Load plugins and run first level hooks.
*/
_d("Loading plugins ...");
plugin_load_all(PLUGIN_PATH);
_d("Running first level hooks ...");
plugin_run_hooks(HOOK_BASEFS_UP); plugin_run_hooks(HOOK_BASEFS_UP);
/* /*
-2
View File
@@ -39,14 +39,12 @@
# define CONSOLE "/dev/console" # define CONSOLE "/dev/console"
# define MDEV "/sbin/mdev" # define MDEV "/sbin/mdev"
# define GETTY "/sbin/getty -L 115200 ttyS0 vt100" # define GETTY "/sbin/getty -L 115200 ttyS0 vt100"
# define LISTEN_INITCTL
#else /* Debian/Ubuntu based distributions */ #else /* Debian/Ubuntu based distributions */
# define RANDOMSEED "/var/lib/urandom/random-seed" # define RANDOMSEED "/var/lib/urandom/random-seed"
# define CONSOLE "/dev/tty1" # define CONSOLE "/dev/tty1"
# define GETTY "/sbin/getty -8 38400 tty1" # define GETTY "/sbin/getty -8 38400 tty1"
# define REMOUNT_ROOTFS_RW # define REMOUNT_ROOTFS_RW
# define RUNLEVEL 2 # define RUNLEVEL 2
# define LISTEN_INITCTL
# define USE_UDEV # define USE_UDEV
# define USE_MESSAGE_BUS # define USE_MESSAGE_BUS
#endif #endif
+17 -38
View File
@@ -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 */
@@ -93,10 +94,14 @@ void ifconfig(char *ifname, char *addr, char *mask, int up)
ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_family = AF_INET;
if (up) { if (up) {
inet_aton(addr, &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);
@@ -112,30 +117,6 @@ void ifconfig(char *ifname, char *addr, char *mask, int up)
} }
void copyfile(char *src, char *dst, int size)
{
char buffer[BUF_SIZE];
int s, d, n;
/* Size == 0 means copy entire file */
if (size == 0)
size = INT_MAX;
if ((s = open(src, O_RDONLY)) >= 0) {
if ((d = open(dst, O_WRONLY | O_CREAT, 0644)) >= 0) {
do {
int csize = size > BUF_SIZE ? BUF_SIZE : size;
if ((n = read(s, buffer, csize)) > 0)
write(d, buffer, n);
size -= csize;
} while (size > 0 && n == BUF_SIZE);
close(d);
}
close(s);
}
}
/** /**
* pidfile_read - Reads a PID value from a pidfile. * pidfile_read - Reads a PID value from a pidfile.
* @pidfile: File containing PID, usually in /var/run/<PROC>.pid * @pidfile: File containing PID, usually in /var/run/<PROC>.pid
@@ -148,7 +129,7 @@ void copyfile(char *src, char *dst, int size)
* be translated this function returns zero (0), on success this function returns * 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. * a PID value greater than one. PID 1 is reserved for the system init process.
*/ */
pid_t pidfile_read(const char *pidfile) pid_t pidfile_read(char *pidfile)
{ {
pid_t pid = 0; pid_t pid = 0;
char buf[16]; char buf[16];
@@ -189,7 +170,7 @@ pid_t pidfile_read(const char *pidfile)
* Returns: * Returns:
* The PID read from @path, or zero on timeout. * The PID read from @path, or zero on timeout.
*/ */
pid_t pidfile_poll(char *cmd, const char *path) pid_t pidfile_poll(char *cmd, char *path)
{ {
pid_t pid = 0; pid_t pid = 0;
int tries = 0; int tries = 0;
@@ -275,7 +256,7 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
* @name: New name of process * @name: New name of process
* @args: The process' argv[] vector. * @args: The process' argv[] vector.
*/ */
void procname_set(const char *name, char *args[]) void procname_set(char *name, char *args[])
{ {
size_t len = strlen(args[0]) + 1; /* Include terminating '\0' */ size_t len = strlen(args[0]) + 1; /* Include terminating '\0' */
@@ -296,7 +277,7 @@ void procname_set(const char *name, char *args[])
* Returns: * Returns:
* Number of signals sent, i.e. number of processes who have received the signal. * Number of signals sent, i.e. number of processes who have received the signal.
*/ */
int procname_kill (const char *name, int signo) int procname_kill (char *name, int signo)
{ {
int result = 0; int result = 0;
char path[32], line[64]; char path[32], line[64];
@@ -327,7 +308,7 @@ int procname_kill (const char *name, int signo)
int error = errno; int error = errno;
if (kill(atoi(entry->d_name), signo)) { if (kill(atoi(entry->d_name), signo)) {
ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error)); FLOG_ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
} else { } else {
result++; /* Track number of processes we deliver the signal to. */ result++; /* Track number of processes we deliver the signal to. */
} }
@@ -547,15 +528,13 @@ pid_t run_getty(char *cmd, char *argv[])
if (open(CONSOLE, O_RDWR) != 0) if (open(CONSOLE, O_RDWR) != 0)
exit(1); exit(1);
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
sigemptyset(&nmask); sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD); sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &nmask, NULL); sigprocmask(SIG_UNBLOCK, &nmask, NULL);
for (i = 1; i < NSIG; i++) /* Reset signal handlers that were set by the parent process */
sigaction(i, &sa, NULL); for (i = 1; i < NSIG; i++)
DFLSIG(sa, i, 0);
dup2(0, STDIN_FILENO); dup2(0, STDIN_FILENO);
dup2(0, STDOUT_FILENO); dup2(0, STDOUT_FILENO);
+15 -18
View File
@@ -27,17 +27,18 @@
#include <stdio.h> #include <stdio.h>
#include <syslog.h> #include <syslog.h>
#include <unistd.h> /* access() */
#define DO_LOG(level, fmt, args...) \ #define DO_LOG(level, fmt, args...) \
{ \ { \
openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \ openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \
syslog(LOG_DEBUG, fmt, ##args); \ syslog(level, fmt, ##args); \
closelog(); \ closelog(); \
} }
#define DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args) #define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args)
#define ERROR(fmt, args...) DO_LOG(LOG_CRIT, 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 #ifndef touch
# define touch(x) mknod((x), S_IFREG|0644, 0) # define touch(x) mknod((x), S_IFREG|0644, 0)
@@ -48,34 +49,30 @@
#ifndef blkdev #ifndef blkdev
# define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min))) # define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min)))
#endif #endif
#ifndef fexist
# define fexist(x) (access(x, F_OK) != -1)
#endif
#ifndef UNUSED #ifndef UNUSED
#define UNUSED(x) UNUSED_ ## x __attribute__ ((unused)) #define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
#endif #endif
#define echo(fmt, args...) do { if (verbose) { fprintf(stderr, fmt "\n", ##args); } } while (0) #define echo(fmt, args...) do { if (verbose) { fprintf(stderr, fmt "\n", ##args); } } while (0)
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0) #define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0)
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } while (0) #define _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() - " fmt ". Error %d: %s\n", __func__, ##args, errno, strerror(errno)); } while (0) #define _pe(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt ". Error %d: %s\n", __FILE__, __func__, ##args, errno, strerror(errno)); } while (0)
extern int debug; extern int debug;
extern int verbose; extern int verbose;
int makepath (char *path); int makepath (char *path);
void ifconfig (char *ifname, char *addr, char *mask, int up); void ifconfig (char *ifname, char *addr, char *mask, int up);
void copyfile (char *src, char *dst, int size);
pid_t pidfile_read (const char *pidfile); pid_t pidfile_read (char *pidfile);
pid_t pidfile_poll (char *cmd, const char *path); pid_t pidfile_poll (char *cmd, char *path);
int pid_alive (pid_t pid); int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len); char *pid_get_name (pid_t pid, char *name, size_t len);
void procname_set (const char *name, char *args[]); void procname_set (char *name, char *args[]);
int procname_kill (const char *name, int signo); int procname_kill (char *name, int signo);
void print_desc (char *action, char *desc); void print_desc (char *action, char *desc);
int print_result (int fail); int print_result (int fail);
+79
View File
@@ -0,0 +1,79 @@
# Finit libite -- Light-weight utility functions and C-library extensions
#
# Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
.EXPORT_ALL_VARIABLES:
.PHONY: all install clean
include ../common.mk
CFLAGS += -fPIC
HEADERS = lite.h
OBJS := copyfile.o dir.o fexist.o fisdir.o fmode.o rsync.o
DEPS := $(addprefix ., $(OBJS:.o=.d))
VER = 0
LIBNAME = libite
TARGET = $(LIBNAME).so
VERLIB = $(TARGET).$(VER)
STATICLIB = $(LIBNAME).a
CHECK_FLAGS = $(CFLAGS) $(CPPFLAGS)
all: $(TARGET)
$(OBJS): Makefile
$(TARGET): $(OBJS)
@printf " LINK %s\n" $@
# @$(AR) $(ARFLAGS) $(STATICLIB) $(OBJS)
@$(CC) $(LDFLAGS) -shared $(OBJS) -lrt -lcrypt -Wl,-soname,$@ -o $@
@ln -sf $@ $(VERLIB)
install-exec: all
@install -d $(DESTDIR)$(libdir)
@echo " INSTALL $(DESTDIR)$(libdir)/$(TARGET)"
@$(STRIPINST) $(TARGET) $(DESTDIR)$(libdir)/$(TARGET)
@ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(VERLIB)
install-dev:
@for file in $(HEADERS); do \
printf " INSTALL $(DESTDIR)$(incdir)/$$file\n"; \
$(INSTALL) -m 0644 $$file $(DESTDIR)$(incdir)/$$file; \
done
install: install-dev install-exec
uninstall:
-@$(RM) $(DESTDIR)$(libdir)/$(VERLIB)
-@$(RM) $(DESTDIR)$(libdir)/$(TARGET)
clean: uninstall
-@$(RM) $(OBJS) $(DEPS) $(TARGET) $(VERLIB) $(STATICLIB)
distclean: clean
-@$(RM) $(JUNK) unittest *.o .*.d *.unittest
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),distclean)
-include $(DEPS)
endif
endif
+317
View File
@@ -0,0 +1,317 @@
/* Fastinit (finit) copyfile() implementation.
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "lite.h"
static int __isdir_and_append_filename(char *src, char **dst);
/**
* copyfile - Copy a file to another.
* @src: Source file.
* @dst: Target file.
* @len: Number of bytes to copy, zero (0) for entire file.
*
* This is a C implementation of the command line cp(1) utility. It is one
* of the classic missing links in the UNIX C library. This version is from
* the finit project, http://helllabs.org/finit/, which is a reimplementation
* of fastinit for the Asus EeePC.
*
* Only some small changes to save stack space have been introduced.
*
* Returns:
* The number of bytes copied, zero may be error (check errno!), but it
* may also indicate that @src was empty. If @src is a directory errno
* will be set to %EISDIR since copyfile() is not recursive.
*/
ssize_t copyfile(char *src, char *dst, int len)
{
char *buffer;
int s, d, n, isdir = 0, saved_errno = 0;
ssize_t size = 0;
errno = 0; /* Reset before leaving this function. */
// fprintf (stderr, "%s() src: %s, dst: %s, len: %d\n", __func__, src, dst, len);
buffer = malloc(BUFSIZ);
if (!buffer)
return 0;
if (fisdir(src)) {
errno = EISDIR; /* Error: source is a directory */
return 0;
}
/* Check if target is a directory, then append the src base filename. */
isdir = __isdir_and_append_filename(src, &dst);
/* Len == 0 means copy entire file */
if (len == 0) {
/* XXX: This feels a bit unsafe, but 2047 MiB may be sufficient for us.
* --Jocke 2008-08-11 */
len = INT_MAX;
}
if ((s = open(src, O_RDONLY)) >= 0) {
if ((d =
open(dst, O_WRONLY | O_CREAT | O_TRUNC,
fmode(src))) >= 0) {
do {
int clen = len > BUFSIZ ? BUFSIZ : len;
if ((n = read(s, buffer, clen)) > 0) {
size += write(d, buffer, n);
}
len -= clen;
}
while (len > 0 && n == BUFSIZ);
close(d);
} else {
saved_errno = errno;
}
close(s);
} else {
saved_errno = errno;
}
free(buffer);
if (isdir)
free(dst);
errno = saved_errno;
return size;
}
/**
* movefile - Move a file to another location
* @src: Source file.
* @dst: Target file, or location.
*
* This is a C implementation of the command line mv(1) utility.
* Usually the rename() API is sufficient, but not when moving across
* file system boundaries.
*
* The @src argument must include the full path to the source file,
* whereas the @dst argument may only be a directory, in which case the
* same file name from @src is used.
*
* Returns:
* POSIX OK(0), or non-zero with errno set.
*/
int movefile(char *src, char *dst)
{
int isdir, result = 0;
/* Check if target is a directory, then append the src base filename. */
isdir = __isdir_and_append_filename(src, &dst);
if (rename(src, dst)) {
if (errno == EXDEV) {
errno = 0;
copyfile(src, dst, 0);
if (errno)
result = 1;
else
result = remove(src);
} else {
result = 1;
}
}
if (isdir)
free(dst);
return result;
}
/**
* copy_filep - Copy between FILE *fp.
* @src: Source FILE.
* @dst: Destination FILE.
*
* Function takes signals into account and will restart the syscalls as long
* as error is %EINTR.
*
* Returns:
* POSIX OK(0), or non-zero with errno set on error.
*/
int copy_filep(FILE *src, FILE *dst)
{
int result = 0;
char *buf = (char *)malloc(BUFSIZ);
if (!src || !dst)
return EINVAL;
if (!buf)
return errno;
while (1) {
if (!fgets(buf, BUFSIZ, src)) {
if (errno == EINTR) {
clearerr(src);
continue;
}
if (feof(src))
break;
result = errno;
goto exit;
}
while (EOF == fputs(buf, dst)) {
if (errno == EINTR) {
clearerr(dst);
continue;
}
result = errno;
goto exit;
}
}
exit:
free(buf);
return errno = result;
}
/* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
static int __isdir_and_append_filename(char *src, char **dst)
{
int isdir = 0;
if (fisdir(*dst)) {
int slash = 0;
char *tmp, *ptr = strrchr(src, '/');
if (!ptr)
ptr = src;
else
ptr++;
// fprintf (stderr, "Yes %s is a dir --> add %s\n", *dst, ptr);
tmp = malloc(strlen(*dst) + strlen(ptr) + 2);
if (!tmp) {
errno = EISDIR;
return 0;
}
isdir = 1; /* Free dst before exit! */
slash = fisslashdir(*dst);
sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr);
*dst = tmp;
// fprintf (stderr, "NEW DST:%s\n", *dst);
}
return isdir;
}
#ifdef UNITTEST
#include <stdio.h>
/* XXX: Make a real set of unit tests ... */
#if 0
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr,
"Usage: %s <src> <dst>\nCopies the <src> file to the <dst> file.\n",
argv[0]);
return 1;
}
printf("Copying file %s to %s\n", argv[1], argv[2]);
return copyfile(argv[1], argv[2], 0);
}
#else
int main(void)
{
int i = 0;
char *files[] = {
"/etc/passwd", "/tmp/tok", "/tmp/tok",
"/etc/passwd", "/tmp/", "/tmp/passwd",
"/etc/passwd", "/tmp", "/tmp/passwd",
NULL
};
FILE *src, *dst;
fprintf(stderr, "\n=>Start testing copy_filep()\n");
while (files[i]) {
fprintf(stderr, "copy_filep(%s, %s)\t", files[i],
files[i + 1]);
src = fopen(files[i], "r");
dst = fopen(files[i + 1], "w");
if (copy_filep(src, dst))
perror("Failed");
if (src)
fclose(src);
if (dst)
fclose(dst);
if (fexist(files[i + 2]))
fprintf(stderr, "OK => %s\n", files[i + 2]);
remove(files[i + 2]);
i += 3;
}
printf("\n\n=>Start testing copyfile()\n");
i = 0;
while (files[i]) {
fprintf(stderr, "copyfile(%s, %s)\t", files[i], files[i + 1]);
if (!copyfile(files[i], files[i + 1], 0))
perror("Failed");
if (fexist(files[i + 2]))
fprintf(stderr, "OK => %s\n", files[i + 2]);
remove(files[i + 2]);
i += 3;
}
return 0;
}
#endif
#endif /* UNITTEST */
/**
* Local Variables:
* compile-command: "gcc -g -I../include -o unittest -DUNITTEST copyfile.c && ./unittest"
* version-control: t
* kept-new-versions: 2
* c-file-style: "ellemtel"
* End:
*/
+228
View File
@@ -0,0 +1,228 @@
/* Functions for operating on files in directories.
*
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *matcher_type = NULL;
static int (*matcher_filter) (const char *file) = NULL;
static int matcher(const struct dirent *entry)
{
char *pos = strrchr(entry->d_name, '.');
if (matcher_filter && !matcher_filter(entry->d_name))
/* User matcher overrides the rest. */
return 0;
/* Skip current dir "." from list of files. */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
/* filetype == "" */
if (matcher_type[0] == 0)
return 1;
/* Entry has no "." */
if (!pos)
return 0;
return !strcmp(pos, matcher_type);
}
/**
* dir - List all files of a certain type in the given directory.
* @dir: Base directory for dir operation.
* @type: File type suffix, e.g. ".cfg".
* @filter: Optional file name filter.
* @list: Pointer to an array of file names.
* @strip: Flag, if set dir() strips the file type.
*
* This function returns a @list of files, matching the @type suffix,
* in the given directory @dir.
*
* The @list argument is a pointer to where to store the dynamically
* allocated list of file names. This list should be free'd by first
* calling free() on each file name and then on the list itself.
*
* If @filter is not %NULL it will be called for each file found. If
* @filter returns non-zero the @file argument will be included in the
* resulting @list. If @filter returns zero for given @file it will
* be discarded.
*
* If the @strip flag is set the resulting @list of files has their
* file type stripped, including the dot. So a match "config0.cfg"
* would be returned as "config0".
*
* Returns:
* Number of files in @list, zero if no matching files of @type.
*/
int dir(const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip)
{
int i, n, num = 0;
char **files;
struct dirent **namelist;
assert(list);
if (!dir)
/* Assuming current directory */
dir = ".";
if (!type)
/* Assuming all files. */
type = "";
matcher_type = type;
matcher_filter = filter;
n = scandir(dir, &namelist, matcher, alphasort);
if (n < 0) {
perror("scandir");
} else {
files = (char **)malloc(n * sizeof(char *));
for (i = 0; i < n; i++) {
if (files) {
char *name = namelist[i]->d_name;
char *type = strrchr(name, '.');
if (type && strip)
*type = 0;
files[i] = strdup(name);
num++;
}
free(namelist[i]);
}
free(namelist);
}
if (num)
*list = files;
return num;
}
#ifdef UNITTEST
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "otn/c.h"
#define DIR_TYPE_IMAGE ".img"
#define DIR_TYPE_SYSLOG ""
#define DIR_TYPE_CONFIG ".cfg"
#define STARTUP_CONFIG "startup-config.cfg"
int simulate_files(int creat)
{
int i;
char *files[] =
{ "config0.cfg", "config1.cfg", "config2.cfg", "config3.cfg",
"rr109.img", "rx100.img", "rx107.img", "rm957.img", "messages"
};
for (i = 0; i < ARRAY_ELEMENTS(files); i++) {
if (creat)
touch(files[i]);
else
remove(files[i]);
}
if (creat)
symlink("config2.cfg", STARTUP_CONFIG);
else
remove(STARTUP_CONFIG);
return 0;
}
static int cfg_dir_filter(const char *file)
{
/* Skip the STARTUP_CONFIG file, it is a symbolic link to the
* current startup configuration. */
return ! !strcmp(file, STARTUP_CONFIG);
}
int main(int argc, char *argv[])
{
int i, num;
char *type = DIR_TYPE_CONFIG;
char **files;
int once = 1;
int is_startup_config(const char *entry) {
static char file[80];
if (once) {
int len = readlink(STARTUP_CONFIG, file, sizeof(file));
if (len == -1)
return 0;
file[len] = 0;
once = 0; /* Only once per call to dir() */
}
//printf ("Comparing link %s with entry %s\n", file, entry);
return !strcmp(file, entry);
}
simulate_files(1);
if (argc >= 2) {
if (!strcasecmp("CONFIG", argv[1])) {
type = DIR_TYPE_CONFIG;
system("ls -l *" DIR_TYPE_CONFIG);
}
if (!strcasecmp("IMAGE", argv[1])) {
type = DIR_TYPE_IMAGE;
system("ls -l *" DIR_TYPE_IMAGE);
}
if (!strcasecmp("SYSLOG", argv[1])) {
type = DIR_TYPE_SYSLOG;
system("ls -l *");
}
}
num = dir(NULL, type, cfg_dir_filter, &files, 0);
if (num) {
for (i = 0; i < num; i++) {
printf("%s", files[i]);
if (is_startup_config(files[i]))
printf(" --> startup-config");
printf("\n");
free(files[i]);
}
free(files);
}
simulate_files(0);
return 0;
}
#endif /* UNITTEST */
/**
* Local Variables:
* compile-command: "gcc -g -DUNITTEST -o unittest dir.c"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+53
View File
@@ -0,0 +1,53 @@
/* Check if file exists
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <unistd.h>
/**
* fexist - Check if a file exists in the file system.
* @file: File to look for, with full path.
*
* Returns:
* %TRUE(1) if the file exists, otherwise %FALSE(0).
*/
int fexist(char *file)
{
if (!file) {
errno = EINVAL;
return 0; /* Doesn't exist ... */
}
if (-1 == access(file, F_OK))
return 0;
return 1;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+51
View File
@@ -0,0 +1,51 @@
/* Check if directory exists
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/**
* fexist - Check if a file exists and is a directory.
* @file: File to look for, with full path.
*
* Returns:
* %TRUE(1) if the file exists and is a directory, otherwise %FALSE(0).
*/
int fisdir(char *file)
{
struct stat sb;
if (!stat(file, &sb) && S_ISDIR(sb.st_mode))
return 1;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+57
View File
@@ -0,0 +1,57 @@
/* File mode bit operations
*
* Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/**
* fmode - Return a file's mode bits.
* @file: File to look for, with full path.
*
* Returns:
* The mode bits or zero when @file does not exist and errno set to %ENOENT.
*/
mode_t fmode(char *file)
{
struct stat sb;
if (!file) {
errno = EINVAL;
return 0; /* Doesn't exist ... */
}
if (!stat(file, &sb))
return sb.st_mode;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+61
View File
@@ -0,0 +1,61 @@
/* Collection of utility funcs and C library extensions for finit & its plugins
*
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_LITE_H_
#define FINIT_LITE_H_
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int fexist (char *file);
int fisdir (char *file);
mode_t fmode (char *file);
ssize_t copyfile (char *src, char *dst, int len);
int movefile (char *src, char *dst);
int copy_filep (FILE *src, FILE *dst);
int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip);
int rsync (char *src, char *dst, int delete, int (*filter) (const char *file));
static inline int fisslashdir(char *dir)
{
if (!dir) return 0;
if (strlen (dir) > 0) return dir[strlen (dir) - 1] == '/';
return 0;
}
#endif /* FINIT_LITE_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+280
View File
@@ -0,0 +1,280 @@
/* Micro "rsync" implementation.
*
* Copyright (c) 2011, 2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <stdlib.h> /* NULL, free() */
#include <string.h> /* strlen() */
#include <strings.h> /* rindex() */
#include <stdio.h>
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
#include <sys/stat.h>
#include <sys/types.h>
#include "lite.h"
#include "../helpers.h"
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
static int prune(char *dst, char **new_files, int new_num);
/**
* rsync - Synchronize contents and optionally remove non-existing backups
* @src: Source directory
* @dst: Destination directory
* @delete: Prune files from @dst that no longer exist in @src.
* @filter: Optional filtering function for source directory.
*
* This is a miniature implementation of the famous rsync for local use only.
* In fact, it is not even a true rsync since it copies all files from @src
* to @dst. The @delete option is useful for creating backups, when set all
* files removed from src since last backup are pruned from the destination
* (backup) directory.
*
* The filter callback, @filter, if provided, is used to determine what files to
* include from the source directory when backing up. If a file is to be skipped
* the callback should simply return zero.
*
* Returns:
* POSIX OK(0), or non-zero with @errno set on error.
*/
int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
{
char source[256];
char dest[256];
int i = 0, num = 0, result = 0;
char **files; /* Array of file names. */
void copy(char *src, char *dst)
{
errno = 0;
copyfile(src, dst, 0);
if (errno) result++;
}
if (!fisdir(dst))
mkdir(dst, 0755);
if (!fisdir(src)) {
if (!fexist(src))
return 1;
copy(src, dst);
return errno;
}
/* Copy dir as well? */
if (!fisslashdir(src)) {
char *ptr = rindex(src, '/');
if (!ptr)
ptr = src;
else
ptr++;
if (makedir(dest, sizeof(dest), dst, ptr, fmode(src)))
return 1;
dst = dest;
}
num = dir(src, "", filter, &files, 0);
for (i = 0; i < num; i++) {
/* Recursively copy sub-directries */
snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]);
if (fisdir(source)) {
char dst2[256];
strcat(source, "/");
if (makedir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
result++;
continue;
}
rsync(source, dst2, delete, filter);
continue; /* Next file/dir in @src to copy... */
}
copy(source, dst);
}
/* We ignore any errors from the pruning, that phase albeit useful is only
* cosmetic. --Jocke 2011-03-24 */
if (delete)
prune(dst, files, num);
if (num) {
for (i = 0; i < num; i++)
free(files[i]);
free(files);
}
return result;
}
/* Creates dir/name @mode ... skipping / if dir already ends so. */
static int makedir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode)
{
snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name);
if (mkdir(buf, mode))
return 1;
return 0;
}
/* Prune old files, no longer existing on source, from destination directory. */
static int prune(char *dst, char **new_files, int new_num)
{
int i, num, result = 0;
char **files;
int find(char *file) {
int n;
for (n = 0; n < new_num; n++)
if (!strncmp (new_files[n], file, MAX(strlen(new_files[n]), strlen(file))))
return 1;
return 0;
}
num = dir(dst, "", NULL, &files, 0);
if (num) {
for (i = 0; i < num; i++) {
if (!find(files[i])) {
char *name;
size_t len = strlen(files[i]) + 2 + strlen(dst);
name = malloc(len);
if (name) {
snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]);
if (remove(name))
result++;
free(name);
}
}
free(files[i]);
}
free(files);
}
return result;
}
#ifdef UNITTEST
#define BASE "/tmp/.unittest/"
#define SRC BASE "src/"
#define DST BASE "dst/"
char *files[] = {
SRC "sub1/1.tst",
SRC "sub1/2.tst",
SRC "sub1/3.tst",
SRC "sub2/4.tst",
SRC "sub2/5.tst",
SRC "sub2/6.tst",
SRC "sub3/7.tst",
SRC "sub3/8.tst",
SRC "sub3/9.tst",
NULL
};
void cleanup_test(void)
{
system("rm -rf " BASE);
}
void setup_test(void)
{
int i;
char cmd[256];
mode_t dir_modes[] = { 755, 700 };
mode_t file_modes[] = { 644, 600 };
cleanup_test();
mkdir(BASE, 0755);
mkdir(SRC, 0755);
mkdir(DST, 0755);
for (i = 0; files[i]; i++) {
snprintf(cmd, sizeof(cmd), "mkdir -m %d -p `dirname %s`",
dir_modes[i % 2], files[i]);
system(cmd);
snprintf(cmd, sizeof(cmd), "touch %s; chmod %d %s", files[i],
file_modes[i % 2], files[i]);
system(cmd);
}
}
int run_test()
{
#if 0
setup_test();
puts("Before:");
system("tree -np " BASE);
rsync(SRC, DST, 0, NULL);
puts("After:");
system("tree -np " BASE);
cleanup_test();
#endif
setup_test();
rsync(BASE "src", DST, 0, NULL);
puts("Only partial rsync of src <-- No slash!");
system("tree -np " BASE);
#if 0
cleanup_test();
setup_test();
rsync(BASE "src/sub1", BASE "dst", 0, NULL);
puts("Only partial rsync of src/sub1 <-- No slashes!!");
system("tree -np " BASE);
cleanup_test();
setup_test();
rsync(BASE "src/sub1/", DST, 0, NULL);
puts("Only partial rsync of src/sub1/");
system("tree -np " BASE);
cleanup_test();
setup_test();
rsync(BASE "src/sub1", DST, 0, NULL);
puts("Only partial rsync of src/sub1 <-- No slash!");
system("tree -np " BASE);
rsync("/etc", "/var/tmp", 0, NULL);
puts("Real life test:");
system("tree -np /var/tmp");
#endif
return 0;
}
int main(int argc, char *argv[])
{
atexit(cleanup_test);
return run_test();
}
#endif /* UNITTEST */
/**
* Local Variables:
* compile-command: "make -f rsync.mk"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+22
View File
@@ -0,0 +1,22 @@
# Unittest makefile for rsync.c
.PHONY: clean
SRC = rsync.c
OBJS = dir.o copyfile.o fexist.o fisdir.o fmode.o
UNIT = rsync.unittest
#CPPFLAGS = -DDEBUG -I../../.. -I../../include
all: build run
clean:
@rm -f $(OBJS) $(UNIT)
# This rule also forces a cleanup of built objects to not leave any object files
# lingering in the tree to interfere with cross builds.
build: clean $(OBJS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $(UNIT) -DUNITTEST $(SRC) $(OBJS)
@rm -f $(OBJS)
run:
@./$(UNIT)
+13 -9
View File
@@ -84,7 +84,7 @@ int plugin_register(plugin_t *plugin)
} }
if (!inuse) { if (!inuse) {
_e("No service \"%s\" loaded, and no I/O or finit hooks, 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;
} }
@@ -173,15 +173,16 @@ static void init_plugins(void)
PLUGIN_ITERATOR(p) { PLUGIN_ITERATOR(p) {
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 plugin_load_all(char *path) int plugin_load_all(char *path)
{ {
int fail = 0;
DIR *dp = opendir(path); DIR *dp = opendir(path);
struct dirent *entry; struct dirent *entry;
@@ -194,25 +195,28 @@ int plugin_load_all(char *path)
char *ext = entry->d_name + strlen(entry->d_name) - 3; char *ext = entry->d_name + strlen(entry->d_name) - 3;
if (!strcmp(ext, ".so")) { if (!strcmp(ext, ".so")) {
int result = 0;
void *handle; void *handle;
char plugin[1024]; char plugin[1024];
snprintf(plugin, sizeof(plugin), "%s/%s", path, entry->d_name); snprintf(plugin, sizeof(plugin), "%s/%s", path, entry->d_name);
print_desc(" Loading plugin ", basename(plugin)); // print_desc(" Loading plugin ", basename(plugin));
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL); handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) { if (!handle) {
_d("Failed loading plugin %s: %s", plugin, dlerror()); _d("Failed loading plugin %s: %s", plugin, dlerror());
print_result(1); result = 1;
return 1;
} }
print_result(0); if (result)
fail++;
// print_result(result);
} }
} }
closedir(dp); closedir(dp);
init_plugins(); init_plugins();
return 0; return fail;
} }
/** /**
+2 -1
View File
@@ -40,7 +40,8 @@
* 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_SVC_UP,
+3 -2
View File
@@ -1,7 +1,8 @@
# Plugin based services architecture for finit -*-Makefile-*- # Plugin based services architecture for finit -*-Makefile-*-
CFLAGS = -fPIC -g -Os CFLAGS += -fPIC -Os
CPPFLAGS += -I.. CPPFLAGS += -I..
LDFLAGS = -shared LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
LDFLAGS += -shared
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \ PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
resolvconf.so urandom.so x11-common.so resolvconf.so urandom.so x11-common.so
DEPS = $(addprefix .,$(PLUGINS:.so=.d)) DEPS = $(addprefix .,$(PLUGINS:.so=.d))
+2
View File
@@ -26,9 +26,11 @@
#include <fcntl.h> /* O_RDONLY et al */ #include <fcntl.h> /* O_RDONLY et al */
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <unistd.h> /* read() */
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "sig.h"
#define INIT_MAGIC 0x03091969 #define INIT_MAGIC 0x03091969
#define INIT_CMD_RUNLVL 1 #define INIT_CMD_RUNLVL 1
+37
View File
@@ -0,0 +1,37 @@
/* reboot for finit
*
* Copyright (c) 2009-2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <signal.h>
int main (void)
{
return !!kill(1, SIGINT);
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+26 -7
View File
@@ -29,6 +29,7 @@
#include "helpers.h" #include "helpers.h"
#include "private.h" #include "private.h"
#include "sig.h" #include "sig.h"
#include "lite.h"
#include "svc.h" #include "svc.h"
/* The registered services are kept in shared memory for easy read-only access /* The registered services are kept in shared memory for easy read-only access
@@ -269,6 +270,7 @@ 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];
/* Ignore if finit is SIGSTOP'ed */ /* Ignore if finit is SIGSTOP'ed */
@@ -278,12 +280,22 @@ int svc_start(svc_t *svc)
if (!respawn) if (!respawn)
print_desc("Starting ", svc->desc); 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);
@@ -298,16 +310,23 @@ 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[256] = "";
fd = open (CONSOLE, O_WRONLY | O_APPEND);
if (-1 != fd) { if (-1 != fd) {
dup2(STDOUT_FILENO, fd); dup2(STDOUT_FILENO, fd);
dup2(STDERR_FILENO, fd); dup2(STDERR_FILENO, fd);
} }
_e("%starting %s ", respawn ? "Res" : "S", svc->cmd); for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) {
for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) char arg[MAX_ARG_LEN];
_e("%s ", args[i]);
_e(""); snprintf(arg, sizeof(arg), "%s ", args[i]);
if ((sizeof(buf) - strlen(buf)) < strlen(arg))
strcat(buf, arg);
}
_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
} }
execvp(svc->cmd, args); execvp(svc->cmd, args);
@@ -383,9 +402,9 @@ void svc_start_all(void)
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
cmd = svc_enabled(svc, 0, NULL); cmd = svc_enabled(svc, 0, NULL);
if (cmd == SVC_START || (cmd == SVC_RELOAD && svc->pid == 0)) if (SVC_START == cmd || (SVC_RELOAD == cmd && svc->pid == 0))
svc_start(svc); svc_start(svc);
else if (cmd == SVC_RELOAD) else if (SVC_RELOAD == cmd)
svc_reload(svc); svc_reload(svc);
} }