diff --git a/ChangeLog b/ChangeLog index 24404309..c00a03e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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) - Cleanup public plugin API a bit and add new pid/pidfile funcs - Add plugin hook to end of service startup diff --git a/Makefile b/Makefile index 8b08b444..66ef6d2f 100644 --- a/Makefile +++ b/Makefile @@ -26,14 +26,15 @@ uninstall-exec uninstall-data uninstall-dev # 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 ?= 1.4 -EXEC = finit -PKG = $(EXEC)-$(VERSION) -DEV = $(EXEC)-dev +NAME = finit +PKG = $(NAME)-$(VERSION) +DEV = $(NAME)-dev ARCHIVE = $(PKG).tar.xz +EXEC = finit reboot HEADERS = plugin.h svc.h helpers.h DISTFILES = LICENSE README ChangeLog finit.conf services OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o @@ -45,7 +46,8 @@ DEPS = $(addprefix .,$(SRCS:.c=.d)) prefix ?= /usr sysconfdir ?= /etc sbindir ?= /sbin -plugindir ?= /lib/finit/plugins +libdir ?= /lib +plugindir ?= $(libdir)/finit/plugins incdir = $(prefix)/include/finit datadir = $(prefix)/share/doc/finit mandir = $(prefix)/share/man/man8 @@ -58,22 +60,28 @@ FINIT_RCSD ?= $(sysconfdir)/finit.d CFLAGS += -W -Wall -Werror -Os # Disable annoying gcc warning for "warn_unused_result", see GIT 37af997 CPPFLAGS += -U_FORTIFY_SOURCE -CPPFLAGS += -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 += -DFINIT_FIFO=\"$(FINIT_FIFO)\" -DFINIT_CONF=\"$(FINIT_CONF)\" CPPFLAGS += -DFINIT_RCSD=\"$(FINIT_RCSD)\" -DPLUGIN_PATH=\"$(plugindir)\" -LDFLAGS += -rdynamic -LDLIBS += -ldl +LDFLAGS += -rdynamic -L$(ROOTDIR)/libite +DEPLIBS = libite/libite.so +LDLIBS += -ldl -lite include common.mk -export plugindir ROOTDIR CPPFLAGS +export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS -all: Makefile $(EXEC) +all: $(DEPLIBS) $(EXEC) $(MAKE) -C plugins $@ +$(DEPLIBS): Makefile + $(MAKE) -C libite all + $(OBJS): Makefile -$(EXEC): $(OBJS) +finit: $(OBJS) $(DEPLIBS) + +reboot: reboot.o $(DEPLIBS) install-exec: all @$(INSTALL) -d $(DESTDIR)$(FINIT_RCSD) @@ -82,6 +90,7 @@ install-exec: all printf " INSTALL $(DESTDIR)$(sbindir)/$$file\n"; \ $(STRIPINST) $$file $(DESTDIR)$(sbindir)/$$file; \ done + $(MAKE) -C libite install-exec $(MAKE) -C plugins install install-data: @@ -98,6 +107,7 @@ install-dev: 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 @@ -108,6 +118,7 @@ uninstall-exec: done -@rmdir $(DESTDIR)$(sbindir) 2>/dev/null -@rmdir $(DESTDIR)$(FINIT_RCSD) 2>/dev/null + $(MAKE) -C libite uninstall $(MAKE) -C plugins uninstall uninstall-data: @@ -119,20 +130,18 @@ uninstall-data: -@rmdir $(DESTDIR)$(datadir) uninstall-dev: - -@for file in $(HEADERS); do \ - printf " REMOVE $(DESTDIR)$(incdir)/$$file\n"; \ - rm $(DESTDIR)$(incdir)/$$file 2>/dev/null; \ - done - -@rmdir $(DESTDIR)$(incdir) + -@$(RM) -rf $(DESTDIR)$(incdir) uninstall: uninstall-exec uninstall-data uninstall-dev clean: -@$(RM) $(OBJS) $(DEPS) $(EXEC) + $(MAKE) -C libite $@ $(MAKE) -C plugins $@ distclean: clean - -@$(RM) $(JUNK) unittest *.o + -@$(RM) $(JUNK) unittest *.o .*.d + $(MAKE) -C libite $@ $(MAKE) -C plugins $@ dist: diff --git a/common.mk b/common.mk index f3232213..05f86e15 100644 --- a/common.mk +++ b/common.mk @@ -2,7 +2,7 @@ JUNK = *~ *.bak *.aux *.dvi *.idx *.ind *.log *.ps *.map .*.d DEADJOE semantic.cache *.gdb *.elf core core.* MAKE := @$(MAKE) MAKEFLAGS = --no-print-directory --silent -INSTALL := install -b +INSTALL := install --backup=off STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755 # Smart autodependecy generation via GCC -M. diff --git a/finit.c b/finit.c index 31fe7799..28ca7a7a 100644 --- a/finit.c +++ b/finit.c @@ -49,6 +49,7 @@ static void parse_kernel_cmdline(void) if ((fp = fopen("/proc/cmdline", "r")) != NULL) { fgets(line, sizeof(line), fp); + _d("Kernel command line: %s", line); if (strstr(line, "finit_debug") || strstr(line, "--debug")) debug = 1; @@ -91,15 +92,6 @@ int main(int UNUSED(args), char *argv[]) mount("none", "/dev/shm", "tmpfs", 0, NULL); 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 */ @@ -108,11 +100,28 @@ int main(int UNUSED(args), char *argv[]) cls(); echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")"); + /* + * Populate /dev and prepare for runtime events from kernel. + */ +#if defined(USE_UDEV) + run_interactive("udevd --daemon", "Populating device tree"); +#elif defined (MDEV) + run_interactive(MDEV " -s", "Populating device tree"); +#endif + /* * Parse configuration file */ parse_finit_conf(FINIT_CONF); + /* + * Load plugins. Must run after finit.conf has registered + * all services, or service plugins won't have anything to + * hook on to. + */ + print_desc("", "Loading plugins"); + print_result(plugin_load_all(PLUGIN_PATH)); + /* * Mount filesystems */ @@ -124,6 +133,9 @@ int main(int UNUSED(args), char *argv[]) #ifdef SYSROOT run(SYSROOT, "/", NULL, MS_MOVE, NULL); #endif + _d("Root FS up, calling hooks ..."); + plugin_run_hooks(HOOK_ROOTFS_UP); + umask(0); run("/bin/mount -na"); run("/sbin/swapon -ea"); @@ -137,13 +149,7 @@ int main(int UNUSED(args), char *argv[]) */ sig_setup(); - /* - * Load plugins and run first level hooks. - */ - _d("Loading plugins ..."); - plugin_load_all(PLUGIN_PATH); - - _d("Running first level hooks ..."); + _d("Base FS up, calling hooks ..."); plugin_run_hooks(HOOK_BASEFS_UP); /* diff --git a/finit.h b/finit.h index 1c3c0757..f530b598 100644 --- a/finit.h +++ b/finit.h @@ -39,14 +39,12 @@ # define CONSOLE "/dev/console" # define MDEV "/sbin/mdev" # define GETTY "/sbin/getty -L 115200 ttyS0 vt100" -# define LISTEN_INITCTL #else /* Debian/Ubuntu based distributions */ # define RANDOMSEED "/var/lib/urandom/random-seed" # define CONSOLE "/dev/tty1" # define GETTY "/sbin/getty -8 38400 tty1" # define REMOUNT_ROOTFS_RW # define RUNLEVEL 2 -# define LISTEN_INITCTL # define USE_UDEV # define USE_MESSAGE_BUS #endif diff --git a/helpers.c b/helpers.c index a1071267..04bd2cb5 100644 --- a/helpers.c +++ b/helpers.c @@ -48,6 +48,7 @@ #include "helpers.h" #include "private.h" #include "sig.h" +#include "lite.h" #define NUM_ARGS 16 #define NUM_SCRIPTS 128 /* ought to be enough for anyone */ @@ -93,10 +94,14 @@ void ifconfig(char *ifname, char *addr, char *mask, int up) ifr.ifr_addr.sa_family = AF_INET; if (up) { - inet_aton(addr, &a->sin_addr); - ioctl(sock, SIOCSIFADDR, &ifr); - inet_aton(mask, &a->sin_addr); - ioctl(sock, SIOCSIFNETMASK, &ifr); + if (addr) { + inet_aton(addr, &a->sin_addr); + ioctl(sock, SIOCSIFADDR, &ifr); + } + if (mask) { + inet_aton(mask, &a->sin_addr); + ioctl(sock, SIOCSIFNETMASK, &ifr); + } } ioctl(sock, SIOCGIFFLAGS, &ifr); @@ -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: File containing PID, usually in /var/run/.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 * 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; char buf[16]; @@ -189,7 +170,7 @@ pid_t pidfile_read(const char *pidfile) * Returns: * 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; int tries = 0; @@ -275,7 +256,7 @@ char *pid_get_name(pid_t pid, char *name, size_t len) * @name: New name of process * @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' */ @@ -296,7 +277,7 @@ void procname_set(const char *name, char *args[]) * Returns: * 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; char path[32], line[64]; @@ -327,7 +308,7 @@ int procname_kill (const char *name, int signo) int error = errno; 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 { 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) exit(1); - sigemptyset(&sa.sa_mask); - sa.sa_handler = SIG_DFL; - sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_UNBLOCK, &nmask, NULL); - for (i = 1; i < NSIG; i++) - sigaction(i, &sa, NULL); + /* Reset signal handlers that were set by the parent process */ + for (i = 1; i < NSIG; i++) + DFLSIG(sa, i, 0); dup2(0, STDIN_FILENO); dup2(0, STDOUT_FILENO); diff --git a/helpers.h b/helpers.h index c56191cd..9718b604 100644 --- a/helpers.h +++ b/helpers.h @@ -27,17 +27,18 @@ #include #include -#include /* access() */ #define DO_LOG(level, fmt, args...) \ { \ openlog("finit", LOG_CONS | LOG_PID, LOG_DAEMON); \ - syslog(LOG_DEBUG, fmt, ##args); \ + syslog(level, fmt, ##args); \ closelog(); \ } -#define DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args) -#define ERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt, ##args) +#define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, 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) @@ -48,34 +49,30 @@ #ifndef blkdev # define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min))) #endif -#ifndef fexist -# define fexist(x) (access(x, F_OK) != -1) -#endif #ifndef UNUSED #define UNUSED(x) UNUSED_ ## x __attribute__ ((unused)) #endif -#define echo(fmt, args...) do { if (verbose) { fprintf(stderr, fmt "\n", ##args); } } while (0) -#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0) -#define _e(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } while (0) -#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt ". Error %d: %s\n", __func__, ##args, errno, strerror(errno)); } while (0) +#define echo(fmt, args...) do { if (verbose) { fprintf(stderr, fmt "\n", ##args); } } while (0) +#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0) +#define _e(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __func__, ##args); } while (0) +#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt ". Error %d: %s\n", __FILE__, __func__, ##args, errno, strerror(errno)); } while (0) -extern int debug; -extern int verbose; +extern int debug; +extern int verbose; int makepath (char *path); 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_poll (char *cmd, const char *path); +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 (const char *name, char *args[]); -int procname_kill (const char *name, int signo); +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); diff --git a/libite/Makefile b/libite/Makefile new file mode 100644 index 00000000..daf0adba --- /dev/null +++ b/libite/Makefile @@ -0,0 +1,79 @@ +# Finit libite -- Light-weight utility functions and C-library extensions +# +# Copyright (c) 2008-2012 Joachim Nilsson +# +# 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 diff --git a/libite/copyfile.c b/libite/copyfile.c new file mode 100644 index 00000000..d0867f8d --- /dev/null +++ b/libite/copyfile.c @@ -0,0 +1,317 @@ +/* Fastinit (finit) copyfile() implementation. + * + * Copyright (c) 2008 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ +#include +#include +#include + +#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 +/* XXX: Make a real set of unit tests ... */ +#if 0 +int main(int argc, char *argv[]) +{ + if (argc < 3) { + fprintf(stderr, + "Usage: %s \nCopies the file to the 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: + */ diff --git a/libite/dir.c b/libite/dir.c new file mode 100644 index 00000000..3595d4d9 --- /dev/null +++ b/libite/dir.c @@ -0,0 +1,228 @@ +/* Functions for operating on files in directories. + * + * Copyright (c) 2008-2012 Joachim Nilsson + * + * 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 +#include +#include +#include +#include + +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 +#include +#include +#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: + */ diff --git a/libite/fexist.c b/libite/fexist.c new file mode 100644 index 00000000..88e6af73 --- /dev/null +++ b/libite/fexist.c @@ -0,0 +1,53 @@ +/* Check if file exists + * + * Copyright (c) 2008 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +/** + * 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: + */ diff --git a/libite/fisdir.c b/libite/fisdir.c new file mode 100644 index 00000000..2eeb98b9 --- /dev/null +++ b/libite/fisdir.c @@ -0,0 +1,51 @@ +/* Check if directory exists + * + * Copyright (c) 2008 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +/** + * 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: + */ diff --git a/libite/fmode.c b/libite/fmode.c new file mode 100644 index 00000000..7b3e68fc --- /dev/null +++ b/libite/fmode.c @@ -0,0 +1,57 @@ +/* File mode bit operations + * + * Copyright (c) 2008 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +/** + * 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: + */ diff --git a/libite/lite.h b/libite/lite.h new file mode 100644 index 00000000..6842e11d --- /dev/null +++ b/libite/lite.h @@ -0,0 +1,61 @@ +/* Collection of utility funcs and C library extensions for finit & its plugins + * + * Copyright (c) 2008-2010 Claudio Matsuoka + * Copyright (c) 2008-2012 Joachim Nilsson + * + * 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 +#include +#include +#include +#include + +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: + */ + diff --git a/libite/rsync.c b/libite/rsync.c new file mode 100644 index 00000000..7a660e31 --- /dev/null +++ b/libite/rsync.c @@ -0,0 +1,280 @@ +/* Micro "rsync" implementation. + * + * Copyright (c) 2011, 2012 Joachim Nilsson + * + * 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 +#include /* NULL, free() */ +#include /* strlen() */ +#include /* rindex() */ +#include +#include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ +#include +#include + +#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: + */ diff --git a/libite/rsync.mk b/libite/rsync.mk new file mode 100644 index 00000000..8556c786 --- /dev/null +++ b/libite/rsync.mk @@ -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) + diff --git a/plugin.c b/plugin.c index 2e6067a4..ff74228b 100644 --- a/plugin.c +++ b/plugin.c @@ -84,7 +84,7 @@ int plugin_register(plugin_t *plugin) } 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; } @@ -173,15 +173,16 @@ static void init_plugins(void) PLUGIN_ITERATOR(p) { if (is_io_plugin(p)) { _d("Initializing plugin %s for I/O", basename(p->name)); - fds[i].revents = 0; - fds[i].events = p->io.flags; - fds[num_fds++].fd = p->io.fd; + fds[i].revents = 0; + fds[i].events = p->io.flags; + fds[i++].fd = p->io.fd; } } } int plugin_load_all(char *path) { + int fail = 0; DIR *dp = opendir(path); struct dirent *entry; @@ -194,25 +195,28 @@ int plugin_load_all(char *path) char *ext = entry->d_name + strlen(entry->d_name) - 3; if (!strcmp(ext, ".so")) { + int result = 0; void *handle; char plugin[1024]; 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); if (!handle) { _d("Failed loading plugin %s: %s", plugin, dlerror()); - print_result(1); - return 1; + result = 1; } - print_result(0); + if (result) + fail++; + +// print_result(result); } } closedir(dp); init_plugins(); - return 0; + return fail; } /** diff --git a/plugin.h b/plugin.h index 235e55a8..6183379b 100644 --- a/plugin.h +++ b/plugin.h @@ -40,7 +40,8 @@ * Predefined hook points for easier plugin debugging */ typedef enum { - HOOK_BASEFS_UP = 0, + HOOK_ROOTFS_UP = 0, + HOOK_BASEFS_UP, HOOK_NETWORK_UP, HOOK_SYSTEM_UP, HOOK_SVC_UP, diff --git a/plugins/Makefile b/plugins/Makefile index 9b1aecb0..97eda6fc 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -1,7 +1,8 @@ # Plugin based services architecture for finit -*-Makefile-*- -CFLAGS = -fPIC -g -Os +CFLAGS += -fPIC -Os CPPFLAGS += -I.. -LDFLAGS = -shared +LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS)) +LDFLAGS += -shared PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \ resolvconf.so urandom.so x11-common.so DEPS = $(addprefix .,$(PLUGINS:.so=.d)) diff --git a/plugins/initctl.c b/plugins/initctl.c index e7b2efa2..714fde54 100644 --- a/plugins/initctl.c +++ b/plugins/initctl.c @@ -26,9 +26,11 @@ #include /* O_RDONLY et al */ #include #include +#include /* read() */ #include "helpers.h" #include "plugin.h" +#include "sig.h" #define INIT_MAGIC 0x03091969 #define INIT_CMD_RUNLVL 1 diff --git a/reboot.c b/reboot.c new file mode 100644 index 00000000..119f0ec7 --- /dev/null +++ b/reboot.c @@ -0,0 +1,37 @@ +/* reboot for finit + * + * Copyright (c) 2009-2012 Joachim Nilsson + * + * 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 + +int main (void) +{ + return !!kill(1, SIGINT); +} + +/** + * Local Variables: + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/svc.c b/svc.c index eb014834..63431bfd 100644 --- a/svc.c +++ b/svc.c @@ -29,6 +29,7 @@ #include "helpers.h" #include "private.h" #include "sig.h" +#include "lite.h" #include "svc.h" /* The registered services are kept in shared memory for easy read-only access @@ -269,6 +270,7 @@ int svc_start(svc_t *svc) { int respawn = svc->pid != 0; pid_t pid; + sigset_t nmask, omask; char *args[MAX_NUM_SVC_ARGS]; /* Ignore if finit is SIGSTOP'ed */ @@ -278,12 +280,22 @@ int svc_start(svc_t *svc) if (!respawn) print_desc("Starting ", svc->desc); + /* Block sigchild while forking. */ + sigemptyset(&nmask); + sigaddset(&nmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &nmask, &omask); + pid = fork(); + sigprocmask(SIG_SETMASK, &omask, NULL); if (pid == 0) { int i = 0; int uid = getuser(svc->username); struct sigaction sa; + sigemptyset(&nmask); + sigaddset(&nmask, SIGCHLD); + sigprocmask(SIG_UNBLOCK, &nmask, NULL); + /* Reset signal handlers that were set by the parent process */ for (i = 1; i < NSIG; i++) DFLSIG(sa, i, 0); @@ -298,16 +310,23 @@ int svc_start(svc_t *svc) args[i] = NULL; 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) { dup2(STDOUT_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++) - _e("%s ", args[i]); - _e(""); + for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) { + char arg[MAX_ARG_LEN]; + + snprintf(arg, sizeof(arg), "%s ", args[i]); + if ((sizeof(buf) - strlen(buf)) < strlen(arg)) + strcat(buf, arg); + } + _e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf); } execvp(svc->cmd, args); @@ -383,9 +402,9 @@ void svc_start_all(void) for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { cmd = svc_enabled(svc, 0, NULL); - if (cmd == SVC_START || (cmd == SVC_RELOAD && svc->pid == 0)) + if (SVC_START == cmd || (SVC_RELOAD == cmd && svc->pid == 0)) svc_start(svc); - else if (cmd == SVC_RELOAD) + else if (SVC_RELOAD == cmd) svc_reload(svc); }