summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2015-09-16 21:04:43 +0200
committerChristian Pointner <equinox@spreadspace.org>2015-09-16 21:04:43 +0200
commit6b4596fe0124fe5c5f5bd6e11b0fd3b2d416aa56 (patch)
tree66336cd27806f1141ddf799b4c13a7639fda6a62 /src
parentmake prefix variable more standard compliant (diff)
removed debian dir
moved source code to src/ dir
Diffstat (limited to 'src')
-rw-r--r--src/Makefile140
-rw-r--r--src/client_list.c116
-rw-r--r--src/client_list.h41
-rwxr-xr-xsrc/configure162
-rw-r--r--src/daemon.h173
-rw-r--r--src/datatypes.h49
-rw-r--r--src/log.c259
-rw-r--r--src/log.h90
-rw-r--r--src/log_targets.h363
-rw-r--r--src/options.c282
-rw-r--r--src/options.h52
-rw-r--r--src/rhdropbox.c563
-rw-r--r--src/sig_handler.c166
-rw-r--r--src/sig_handler.h43
-rw-r--r--src/string_list.c113
-rw-r--r--src/string_list.h56
-rw-r--r--src/sysexec.c371
-rw-r--r--src/sysexec.h57
-rw-r--r--src/utils.c173
-rw-r--r--src/utils.h36
-rw-r--r--src/watch_list.c163
-rw-r--r--src/watch_list.h46
22 files changed, 3514 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..15670ce
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,140 @@
+##
+## rhdropbox
+##
+## Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+##
+## This file is part of rhdropbox.
+##
+## rhdropbox is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## any later version.
+##
+## rhdropbox is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+##
+
+ifneq ($(MAKECMDGOALS),distclean)
+include include.mk
+endif
+
+EXECUTABLE := rhdropbox
+
+OBJ := log.o \
+ sig_handler.o \
+ string_list.o \
+ utils.o \
+ client_list.o \
+ watch_list.o \
+ options.o \
+ sysexec.o \
+ rhdropbox.o
+
+SRC := $(OBJ:%.o=%.c)
+
+.PHONY: clean distclean
+
+all: $(EXECUTABLE)
+
+%.d: %.c
+ @set -e; rm -f $@; \
+ $(CC) -MM $(CFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$; echo '(re)building $@'
+
+ifneq ($(MAKECMDGOALS),distclean)
+-include $(SRC:%.c=%.d)
+endif
+
+$(EXECUTABLE): $(OBJ)
+ $(CC) $(OBJ) -o $@ $(LDFLAGS)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
+
+
+distclean: cleanall
+ find . -name *.o -exec rm -f {} \;
+ find . -name "*.\~*" -exec rm -rf {} \;
+ rm -f include.mk
+
+clean:
+ rm -f *.o
+ rm -f *.d
+ rm -f *.d.*
+ rm -f $(EXECUTABLE)
+
+cleanall: clean
+ $(MAKE) --directory="../doc/" clean
+
+manpage:
+ $(MAKE) --directory="../doc/" manpage
+
+
+INSTALL_TARGETS := install-bin install-etc
+REMOVE_TARGETS := remove-bin remove-etc
+
+ifdef MANDIR
+INSTALL_TARGETS += install-man
+REMOVE_TARGETS += remove-man
+endif
+
+ifdef EXAMPLESDIR
+INSTALL_TARGETS += install-examples
+REMOVE_TARGETS += remove-examples
+endif
+
+install: all $(INSTALL_TARGETS)
+
+install-bin: $(EXECUTABLE)
+ $(INSTALL) -d $(DESTDIR)$(BINDIR)
+ $(INSTALL) -m 755 $(EXECUTABLE) $(DESTDIR)$(BINDIR)
+
+install-etc:
+ $(INSTALL) -d $(DESTDIR)$(ETCDIR)/$(EXECUTABLE)
+ @ echo "example configurations can be found at $(EXAMPLESDIR)/$(EXECUTABLE)" > $(DESTDIR)$(ETCDIR)/$(EXECUTABLE)/README
+
+install-man: manpage
+ $(INSTALL) -d $(DESTDIR)$(MANDIR)/man8/
+ $(INSTALL) -m 644 ../doc/rhdropbox.8 $(DESTDIR)$(MANDIR)/man8/$(EXECUTABLE).8
+
+install-examples:
+ $(INSTALL) -d $(DESTDIR)$(EXAMPLESDIR)/$(EXECUTABLE)
+ $(INSTALL) -m 644 etc/rhdropbox/autostart $(DESTDIR)$(EXAMPLESDIR)/$(EXECUTABLE)/autostart
+ @( cd 'etc/rhdropbox/' ; \
+ for dir in `ls`; do \
+ if [ -d $$dir ]; then \
+ echo "install $$dir configuration" ; \
+ cd $$dir ; \
+ $(INSTALL) -d $(DESTDIR)$(EXAMPLESDIR)/$(EXECUTABLE)/$$dir ; \
+ $(INSTALL) -m 644 config $(DESTDIR)$(EXAMPLESDIR)/$(EXECUTABLE)/$$dir/config ; \
+ if [ -e 'newfile.sh' ]; then \
+ $(INSTALL) -m 755 newfile.sh $(DESTDIR)$(EXAMPLESDIR)/$(EXECUTABLE)/$$dir/newfile.sh ; \
+ fi ; \
+ cd .. ; \
+ fi ; \
+ done \
+ )
+
+uninstall: remove
+
+remove: $(REMOVE_TARGETS)
+
+remove-bin:
+ rm -f $(DESTDIR)$(BINDIR)/$(EXECUTABLE)
+
+remove-etc:
+
+remove-examples:
+ rm -rf $(DESTDIR)$(EXAMPLESDIR)/$(EXECUTABLE)/
+
+remove-man:
+ rm -f $(DESTDIR)$(MANDIR)/man8/$(EXECUTABLE).8
+
+purge: remove
+ rm -rf $(DESTDIR)$(ETCDIR)/$(EXECUTABLE)/
diff --git a/src/client_list.c b/src/client_list.c
new file mode 100644
index 0000000..8379dd9
--- /dev/null
+++ b/src/client_list.c
@@ -0,0 +1,116 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#include "client_list.h"
+#include "datatypes.h"
+
+client_t* client_get_last(client_t* first)
+{
+ if(!first)
+ return NULL;
+
+ while(first->next) {
+ first = first->next;
+ }
+
+ return first;
+}
+
+int client_add(client_t** first, int fd)
+{
+ if(!first)
+ return -1;
+
+ client_t* new_client = malloc(sizeof(client_t));
+ if(!new_client)
+ return -2;
+
+ new_client->fd = fd;
+ new_client->status_listener = 0;
+ new_client->request_listener = 0;
+ new_client->next = NULL;
+ new_client->buffer.offset = 0;
+
+ if(!(*first)) {
+ *first = new_client;
+ return 0;
+ }
+
+ client_get_last(*first)->next = new_client;
+
+ return 0;
+}
+
+void client_remove(client_t** first, int fd)
+{
+ if(!first || !(*first))
+ return;
+
+ client_t* deletee = *first;
+ if((*first)->fd == fd) {
+ *first = (*first)->next;
+ close(deletee->fd);
+ free(deletee);
+ return;
+ }
+
+ client_t* prev = deletee;
+ deletee = deletee->next;
+ while(deletee) {
+ if(deletee->fd == fd) {
+ prev->next = deletee->next;
+ close(deletee->fd);
+ free(deletee);
+ return;
+ }
+ prev = deletee;
+ deletee = deletee->next;
+ }
+}
+
+client_t* client_find(client_t* first, int fd)
+{
+ if(!first)
+ return NULL;
+
+ while(first) {
+ if(first->fd == fd)
+ return first;
+
+ first = first->next;
+ }
+ return NULL;
+}
+
+void client_clear(client_t** first)
+{
+ if(!first || !(*first))
+ return;
+
+ while(*first) {
+ client_t* deletee = *first;
+ *first = (*first)->next;
+ close(deletee->fd);
+ free(deletee);
+ }
+}
diff --git a/src/client_list.h b/src/client_list.h
new file mode 100644
index 0000000..5cc34ae
--- /dev/null
+++ b/src/client_list.h
@@ -0,0 +1,41 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHDROPBOX_client_list_h_INCLUDED
+#define RHDROPBOX_client_list_h_INCLUDED
+
+#include "datatypes.h"
+
+struct client_struct {
+ int fd;
+ int status_listener;
+ int request_listener;
+ struct client_struct* next;
+ read_buffer_t buffer;
+};
+typedef struct client_struct client_t;
+
+int client_add(client_t** first, int fd);
+void client_remove(client_t** first, int fd);
+client_t* client_find(client_t* first, int fd);
+void client_clear(client_t** first);
+
+#endif
diff --git a/src/configure b/src/configure
new file mode 100755
index 0000000..4b93d79
--- /dev/null
+++ b/src/configure
@@ -0,0 +1,162 @@
+#!/bin/sh
+#
+#
+# rhdropbox
+#
+# Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+#
+# This file is part of rhdropbox.
+#
+# rhdropbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# rhdropbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+#
+
+TARGET=`uname -s`
+
+EBUILD_COMPAT=0
+
+CFLAGS='-g -O2'
+LDFLAGS='-g -Wall -O2'
+
+PREFIX='/usr/local'
+BINDIR=''
+ETCDIR=''
+MANDIR=''
+INSTALLMANPAGE=1
+EXAMPLESDIR=''
+INSTALLEXAMPLES=1
+
+print_usage() {
+ echo "configure --help print this"
+ echo " --target=<TARGET> build target i.e. Linux (default: autodetect)"
+ echo " --prefix=<PREFIX> the installation prefix (default: /usr/local)"
+ echo " --bindir=<DIR> the path to the bin directory (default: $PREFIX/bin)"
+ echo " --sysconfdir=<DIR> the path to the system configuration directory (default: $PREFIX/etc"
+ echo " --mandir=<DIR> the path to the system man pages (default: $PREFIX/share/man)"
+ echo " --no-manpage dont't install manpage"
+ echo " --examplesdir=<DIR> the path to the examples files (default: $PREFIX/share/examples)"
+ echo " --no-examples dont't install example files"
+}
+
+for arg
+do
+ case $arg in
+ --target=*)
+ TARGET=${arg#--target=}
+ ;;
+ --prefix=*)
+ PREFIX=${arg#--prefix=}
+ ;;
+ --bindir=*)
+ BINDIR=${arg#--bindir=}
+ ;;
+ --sysconfdir=*)
+ ETCDIR=${arg#--sysconfdir=}
+ ;;
+ --mandir=*)
+ MANDIR=${arg#--mandir=}
+ ;;
+ --no-manpage)
+ INSTALLMANPAGE=0
+ ;;
+ --examplesdir=*)
+ EXAMPLESDIR=${arg#--examplesdir=}
+ ;;
+ --no-examples)
+ INSTALLEXAMPLES=0
+ ;;
+ --ebuild-compat)
+ EBUILD_COMPAT=1
+ ;;
+ --help)
+ print_usage
+ exit 0
+ ;;
+ *)
+ ERRORS="$ERRORS $arg"
+ ;;
+ esac
+done
+
+if [ -n "$ERRORS" ] && [ $EBUILD_COMPAT -ne 1 ]; then
+ for error in $ERRORS; do
+ echo "Unknown argument: $error"
+ done
+
+ print_usage
+ exit 1
+fi
+
+rm -f include.mk
+case $TARGET in
+ Linux)
+ echo "Linux specific build options"
+ ;;
+ OpenBSD|FreeBSD|NetBSD)
+ echo "BSD specific build options"
+ CFLAGS=$CFLAGS' -I/usr/local/include'
+ LDFLAGS=$LDFLAGS' -L/usr/local/lib'
+ ;;
+ *)
+ echo "Plattform not supported"
+ exit 1;
+ ;;
+esac
+
+if [ -z "$BINDIR" ]; then
+ BINDIR=$PREFIX/bin
+fi
+
+if [ -z "$ETCDIR" ]; then
+ ETCDIR=$PREFIX/etc
+fi
+
+if [ -z "$MANDIR" ]; then
+ MANDIR=$PREFIX/share/man
+fi
+
+if [ -z "$EXAMPLESDIR" ]; then
+ EXAMPLESDIR=$PREFIX/share/examples
+fi
+
+cat >> include.mk <<EOF
+# this file was created automatically
+# do not edit this file directly
+# use ./configure instead
+
+TARGET := $TARGET
+CC := gcc
+CFLAGS := $CFLAGS
+LDFLAGS := $LDFLAGS
+INSTALL := install
+
+prefix := $PREFIX
+BINDIR := $BINDIR
+ETCDIR := $ETCDIR
+EOF
+
+if [ $INSTALLMANPAGE -eq 1 ]; then
+ echo "MANDIR := $MANDIR" >> include.mk
+ echo "installing manpage"
+else
+ echo "not installing manpage"
+fi
+
+if [ $INSTALLEXAMPLES -eq 1 ]; then
+ echo "EXAMPLESDIR := $EXAMPLESDIR" >> include.mk
+ echo "installing example files"
+else
+ echo "not installing example files"
+fi
+
+exit 0
diff --git a/src/daemon.h b/src/daemon.h
new file mode 100644
index 0000000..22e0ac0
--- /dev/null
+++ b/src/daemon.h
@@ -0,0 +1,173 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UANYTUN_daemon_h_INCLUDED
+#define UANYTUN_daemon_h_INCLUDED
+
+#include <poll.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct priv_info_struct {
+ struct passwd* pw_;
+ struct group* gr_;
+};
+typedef struct priv_info_struct priv_info_t;
+
+int priv_init(priv_info_t* priv, const char* username, const char* groupname)
+{
+ if(!priv)
+ return -1;
+
+ priv->pw_ = NULL;
+ priv->gr_ = NULL;
+
+ priv->pw_ = getpwnam(username);
+ if(!priv->pw_) {
+ log_printf(ERROR, "unkown user %s", username);
+ return -1;
+ }
+
+ if(groupname)
+ priv->gr_ = getgrnam(groupname);
+ else
+ priv->gr_ = getgrgid(priv->pw_->pw_gid);
+
+ if(!priv->gr_) {
+ log_printf(ERROR, "unkown group %s", groupname);
+ return -1;
+ }
+
+ return 0;
+}
+
+int priv_drop(priv_info_t* priv)
+{
+ if(!priv || !priv->pw_ || !priv->gr_) {
+ log_printf(ERROR, "privileges not initialized properly");
+ return -1;
+ }
+
+ if(setgid(priv->gr_->gr_gid)) {
+ log_printf(ERROR, "setgid('%s') failed: %s", priv->gr_->gr_name, strerror(errno));
+ return -1;
+ }
+
+ gid_t gr_list[1];
+ gr_list[0] = priv->gr_->gr_gid;
+ if(setgroups (1, gr_list)) {
+ log_printf(ERROR, "setgroups(['%s']) failed: %s", priv->gr_->gr_name, strerror(errno));
+ return -1;
+ }
+
+ if(setuid(priv->pw_->pw_uid)) {
+ log_printf(ERROR, "setuid('%s') failed: %s", priv->pw_->pw_name, strerror(errno));
+ return -1;
+ }
+
+ log_printf(NOTICE, "dropped privileges to %s:%s", priv->pw_->pw_name, priv->gr_->gr_name);
+ return 0;
+}
+
+
+int do_chroot(const char* chrootdir)
+{
+ if(getuid() != 0) {
+ log_printf(ERROR, "this programm has to be run as root in order to run in a chroot");
+ return -1;
+ }
+
+ if(chroot(chrootdir)) {
+ log_printf(ERROR, "can't chroot to %s: %s", chrootdir, strerror(errno));
+ return -1;
+ }
+ log_printf(NOTICE, "we are in chroot jail (%s) now", chrootdir);
+ if(chdir("/")) {
+ log_printf(ERROR, "can't change to /: %s", strerror(errno));
+ return -1;
+ }
+}
+
+void daemonize()
+{
+ pid_t pid;
+
+ pid = fork();
+ if(pid < 0) {
+ log_printf(ERROR, "daemonizing failed at fork(): %s, exitting", strerror(errno));
+ exit(-1);
+ }
+ if(pid) exit(0);
+
+ umask(0);
+
+ if(setsid() < 0) {
+ log_printf(ERROR, "daemonizing failed at setsid(): %s, exitting", strerror(errno));
+ exit(-1);
+ }
+
+ pid = fork();
+ if(pid < 0) {
+ log_printf(ERROR, "daemonizing failed at fork(): %s, exitting", strerror(errno));
+ exit(-1);
+ }
+ if(pid) exit(0);
+
+ if ((chdir("/")) < 0) {
+ log_printf(ERROR, "daemonizing failed at chdir(): %s, exitting", strerror(errno));
+ exit(-1);
+ }
+
+ int fd;
+ for (fd=0;fd<=2;fd++) // close all file descriptors
+ close(fd);
+ fd = open("/dev/null",O_RDWR); // stdin
+ if(fd == -1)
+ log_printf(WARNING, "can't open stdin (chroot and no link to /dev/null?)");
+ else {
+ if(dup(fd) == -1) // stdout
+ log_printf(WARNING, "can't open stdout");
+ if(dup(fd) == -1) // stderr
+ log_printf(WARNING, "can't open stderr");
+ }
+ umask(027);
+}
+
+#endif
+
diff --git a/src/datatypes.h b/src/datatypes.h
new file mode 100644
index 0000000..1168ec6
--- /dev/null
+++ b/src/datatypes.h
@@ -0,0 +1,49 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHDROPBOX_datatypes_h_INCLUDED
+#define RHDROPBOX_datatypes_h_INCLUDED
+
+#include <stdint.h>
+#include <arpa/inet.h>
+
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+/* typedef int8_t int8_t; */
+/* typedef int16_t int16_t; */
+/* typedef int32_t int32_t; */
+/* typedef int64_t int64_t; */
+
+struct buffer_struct {
+ u_int32_t length_;
+ u_int8_t* buf_;
+};
+typedef struct buffer_struct buffer_t;
+
+struct read_buffer_struct {
+ u_int32_t offset;
+ u_int8_t buf[1024];
+};
+typedef struct read_buffer_struct read_buffer_t;
+
+#endif
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..43a0a5a
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,259 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define SYSLOG_NAMES
+#include <syslog.h>
+
+#include "log.h"
+
+log_t stdlog;
+
+#include "log_targets.h"
+
+const char* log_prio_to_string(log_prio_t prio)
+{
+ switch(prio) {
+ case ERROR: return "ERROR";
+ case WARNING: return "WARNING";
+ case NOTICE: return "NOTICE";
+ case INFO: return "INFO";
+ case DEBUG: return "DEBUG";
+ }
+ return "UNKNOWN";
+}
+
+log_target_type_t log_target_parse_type(const char* conf)
+{
+ if(!conf)
+ return TARGET_UNKNOWN;
+
+ if(!strncmp(conf, "syslog", 6)) return TARGET_SYSLOG;
+ if(!strncmp(conf, "file", 4)) return TARGET_FILE;
+ if(!strncmp(conf, "stdout", 6)) return TARGET_STDOUT;
+ if(!strncmp(conf, "stderr", 6)) return TARGET_STDERR;
+
+ return TARGET_UNKNOWN;
+}
+
+int log_targets_target_exists(log_targets_t* targets, log_target_type_t type)
+{
+ if(!targets && !targets->first_)
+ return 0;
+
+ log_target_t* tmp = targets->first_;
+ while(tmp) {
+ if(tmp->type_ == type)
+ return 1;
+ tmp = tmp->next_;
+ }
+ return 0;
+}
+
+int log_targets_add(log_targets_t* targets, const char* conf)
+{
+ if(!targets)
+ return -1;
+
+ log_target_t* new_target = NULL;
+ int duplicates_allowed = 0;
+ switch(log_target_parse_type(conf)) {
+ case TARGET_SYSLOG: new_target = log_target_syslog_new(); break;
+ case TARGET_FILE: new_target = log_target_file_new(); duplicates_allowed = 1; break;
+ case TARGET_STDOUT: new_target = log_target_stdout_new(); break;
+ case TARGET_STDERR: new_target = log_target_stderr_new(); break;
+ default: return -3;
+ }
+ if(!new_target)
+ return -2;
+
+ if(!duplicates_allowed && log_targets_target_exists(targets, new_target->type_)) {
+ free(new_target);
+ return -4;
+ }
+
+ const char* prioptr = strchr(conf, ':');
+ if(!prioptr || prioptr[1] == 0) {
+ free(new_target);
+ return -1;
+ }
+ prioptr++;
+ if(!isdigit(prioptr[0]) || (prioptr[1] != 0 && prioptr[1] != ',')) {
+ free(new_target);
+ return -1;
+ }
+ new_target->max_prio_ = prioptr[0] - '0';
+ if(new_target->max_prio_ > 0)
+ new_target->enabled_ = 1;
+
+ if(new_target->init != NULL) {
+ const char* confptr = NULL;
+ if(prioptr[1] != 0)
+ confptr = prioptr+2;
+
+ int ret = (*new_target->init)(new_target, confptr);
+ if(ret) {
+ free(new_target);
+ return ret;
+ }
+ }
+
+ if(new_target->open != NULL)
+ (*new_target->open)(new_target);
+
+
+ if(!targets->first_) {
+ targets->first_ = new_target;
+ }
+ else {
+ log_target_t* tmp = targets->first_;
+ while(tmp->next_)
+ tmp = tmp->next_;
+
+ tmp->next_ = new_target;
+ }
+ return 0;
+}
+
+void log_targets_log(log_targets_t* targets, log_prio_t prio, const char* msg)
+{
+ if(!targets)
+ return;
+
+ log_target_t* tmp = targets->first_;
+ while(tmp) {
+ if(tmp->log != NULL && tmp->enabled_ && tmp->max_prio_ >= prio)
+ (*tmp->log)(tmp, prio, msg);
+
+ tmp = tmp->next_;
+ }
+}
+
+void log_targets_clear(log_targets_t* targets)
+{
+ if(!targets)
+ return;
+
+ while(targets->first_) {
+ log_target_t* tmp = targets->first_;
+ targets->first_ = tmp->next_;
+ if(tmp->close != NULL)
+ (*tmp->close)(tmp);
+ if(tmp->clear != NULL)
+ (*tmp->clear)(tmp);
+ free(tmp);
+ }
+}
+
+
+void log_init()
+{
+ stdlog.max_prio_ = 0;
+ stdlog.targets_.first_ = NULL;
+}
+
+void log_close()
+{
+ log_targets_clear(&stdlog.targets_);
+}
+
+void update_max_prio()
+{
+ log_target_t* tmp = stdlog.targets_.first_;
+ while(tmp) {
+ if(tmp->enabled_ && tmp->max_prio_ > stdlog.max_prio_)
+ stdlog.max_prio_ = tmp->max_prio_;
+
+ tmp = tmp->next_;
+ }
+}
+
+int log_add_target(const char* conf)
+{
+ if(!conf)
+ return -1;
+
+ int ret = log_targets_add(&stdlog.targets_, conf);
+ if(!ret) update_max_prio();
+ return ret;
+}
+
+void log_printf(log_prio_t prio, const char* fmt, ...)
+{
+ if(stdlog.max_prio_ < prio)
+ return;
+
+ static char msg[MSG_LENGTH_MAX];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(msg, MSG_LENGTH_MAX, fmt, args);
+ va_end(args);
+
+ log_targets_log(&stdlog.targets_, prio, msg);
+}
+
+void log_print_hex_dump(log_prio_t prio, const u_int8_t* buf, u_int32_t len)
+{
+ if(stdlog.max_prio_ < prio)
+ return;
+
+ static char msg[MSG_LENGTH_MAX];
+
+ if(!buf) {
+ snprintf(msg, MSG_LENGTH_MAX, "(NULL)");
+ }
+ else {
+ u_int32_t i;
+ int offset = snprintf(msg, MSG_LENGTH_MAX, "dump(%d): ", len);
+ if(offset < 0)
+ return;
+ u_int8_t* ptr = &msg[offset];
+
+ for(i=0; i < len; i++) {
+ if(((i+1)*3) >= (MSG_LENGTH_MAX - offset))
+ break;
+ snprintf(ptr, 3, "%02X ", buf[i]);
+ ptr+=3;
+ }
+ }
+ log_targets_log(&stdlog.targets_, prio, msg);
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..2717622
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,90 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UANYTUN_log_h_INCLUDED
+#define UANYTUN_log_h_INCLUDED
+
+#define MSG_LENGTH_MAX 150
+
+enum log_prio_enum { ERROR = 1, WARNING = 2, NOTICE = 3,
+ INFO = 4, DEBUG = 5 };
+typedef enum log_prio_enum log_prio_t;
+
+const char* log_prio_to_string(log_prio_t prio);
+
+enum log_target_type_enum { TARGET_SYSLOG , TARGET_STDOUT, TARGET_STDERR, TARGET_FILE , TARGET_UNKNOWN };
+typedef enum log_target_type_enum log_target_type_t;
+
+struct log_target_struct {
+ log_target_type_t type_;
+ int (*init)(struct log_target_struct* self, const char* conf);
+ void (*open)(struct log_target_struct* self);
+ void (*log)(struct log_target_struct* self, log_prio_t prio, const char* msg);
+ void (*close)(struct log_target_struct* self);
+ void (*clear)(struct log_target_struct* self);
+ int opened_;
+ int enabled_;
+ log_prio_t max_prio_;
+ void* param_;
+ struct log_target_struct* next_;
+};
+typedef struct log_target_struct log_target_t;
+
+
+struct log_targets_struct {
+ log_target_t* first_;
+};
+typedef struct log_targets_struct log_targets_t;
+
+int log_targets_target_exists(log_targets_t* targets, log_target_type_t type);
+int log_targets_add(log_targets_t* targets, const char* conf);
+void log_targets_log(log_targets_t* targets, log_prio_t prio, const char* msg);
+void log_targets_clear(log_targets_t* targets);
+
+
+struct log_struct {
+ log_prio_t max_prio_;
+ log_targets_t targets_;
+};
+typedef struct log_struct log_t;
+
+void log_init();
+void log_close();
+void update_max_prio();
+int log_add_target(const char* conf);
+void log_printf(log_prio_t prio, const char* fmt, ...);
+void log_print_hex_dump(log_prio_t prio, const u_int8_t* buf, u_int32_t len);
+
+#endif
diff --git a/src/log_targets.h b/src/log_targets.h
new file mode 100644
index 0000000..8db3812
--- /dev/null
+++ b/src/log_targets.h
@@ -0,0 +1,363 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UANYTUN_log_targets_h_INCLUDED
+#define UANYTUN_log_targets_h_INCLUDED
+
+#include <time.h>
+
+static char* get_time_formatted()
+{
+ char* time_string;
+ time_t t = time(NULL);
+ if(t < 0)
+ time_string = "<time read error>";
+ else {
+ time_string = ctime(&t);
+ if(!time_string)
+ time_string = "<time format error>";
+ else {
+ char* newline = strchr(time_string, '\n');
+ if(newline)
+ newline[0] = 0;
+ }
+ }
+ return time_string;
+}
+
+enum syslog_facility_enum { USER = LOG_USER, MAIL = LOG_MAIL,
+ DAEMON = LOG_DAEMON, AUTH = LOG_AUTH,
+ SYSLOG = LOG_SYSLOG, LPR = LOG_LPR,
+ NEWS = LOG_NEWS, UUCP = LOG_UUCP,
+ CRON = LOG_CRON, AUTHPRIV = LOG_AUTHPRIV,
+ FTP = LOG_FTP, LOCAL0 = LOG_LOCAL0,
+ LOCAL1 = LOG_LOCAL1, LOCAL2 = LOG_LOCAL2,
+ LOCAL3 = LOG_LOCAL3, LOCAL4 = LOG_LOCAL4,
+ LOCAL5 = LOG_LOCAL5, LOCAL6 = LOG_LOCAL6,
+ LOCAL7 = LOG_LOCAL7 };
+typedef enum syslog_facility_enum syslog_facility_t;
+
+struct log_target_syslog_param_struct {
+ char* logname_;
+ syslog_facility_t facility_;
+};
+typedef struct log_target_syslog_param_struct log_target_syslog_param_t;
+
+int log_target_syslog_init(log_target_t* self, const char* conf)
+{
+ if(!self || (conf && conf[0] == 0))
+ return -1;
+
+ self->param_ = malloc(sizeof(log_target_syslog_param_t));
+ if(!self->param_)
+ return -2;
+
+ char* logname;
+ const char* end = NULL;
+ if(!conf)
+ logname = strdup("uanytun");
+ else {
+ end = strchr(conf, ',');
+ if(end) {
+ size_t len = (size_t)(end - conf);
+ if(!len) {
+ free(self->param_);
+ return -1;
+ }
+ logname = malloc(len+1);
+ if(logname) {
+ strncpy(logname, conf, len);
+ logname[len] = 0;
+ }
+ }
+ else
+ logname = strdup(conf);
+ }
+
+ if(!logname) {
+ free(self->param_);
+ return -2;
+ }
+ ((log_target_syslog_param_t*)(self->param_))->logname_ = logname;
+
+ if(!end) {
+ ((log_target_syslog_param_t*)(self->param_))->facility_ = DAEMON;
+ return 0;
+ }
+
+ if(end[1] == 0 || end[1] == ',') {
+ free(logname);
+ free(self->param_);
+ return -1;
+ }
+
+ const char* start = end + 1;
+ end = strchr(start, ',');
+ int i;
+ for(i=0;;++i) {
+ if(facilitynames[i].c_name == NULL) {
+ free(logname);
+ free(self->param_);
+ return -1;
+ }
+
+ if(( end && !strncmp(start, facilitynames[i].c_name, (size_t)(end - start)) && facilitynames[i].c_name[(size_t)(end-start)] == 0) ||
+ (!end && !strcmp(start, facilitynames[i].c_name))) {
+ ((log_target_syslog_param_t*)(self->param_))->facility_ = facilitynames[i].c_val;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void log_target_syslog_open(log_target_t* self)
+{
+ if(!self || !self->param_)
+ return;
+
+ openlog(((log_target_syslog_param_t*)(self->param_))->logname_, LOG_PID, ((log_target_syslog_param_t*)(self->param_))->facility_);
+ self->opened_ = 1;
+}
+
+void log_target_syslog_log(log_target_t* self, log_prio_t prio, const char* msg)
+{
+ if(!self || !self->param_ || !self->opened_)
+ return;
+
+ syslog((prio + 2) | ((log_target_syslog_param_t*)(self->param_))->facility_, "%s", msg);
+}
+
+void log_target_syslog_close(log_target_t* self)
+{
+ closelog();
+ self->opened_ = 0;
+}
+
+void log_target_syslog_clear(log_target_t* self)
+{
+ if(!self || !self->param_)
+ return;
+
+ if(((log_target_syslog_param_t*)(self->param_))->logname_)
+ free(((log_target_syslog_param_t*)(self->param_))->logname_);
+
+ free(self->param_);
+}
+
+log_target_t* log_target_syslog_new()
+{
+ log_target_t* tmp = malloc(sizeof(log_target_t));
+ if(!tmp)
+ return NULL;
+
+ tmp->type_ = TARGET_SYSLOG;
+ tmp->init = &log_target_syslog_init;
+ tmp->open = &log_target_syslog_open;
+ tmp->log = &log_target_syslog_log;
+ tmp->close = &log_target_syslog_close;
+ tmp->clear = &log_target_syslog_clear;
+ tmp->opened_ = 0;
+ tmp->enabled_ = 0;
+ tmp->max_prio_ = NOTICE;
+ tmp->param_ = NULL;
+ tmp->next_ = NULL;
+
+ return tmp;
+}
+
+
+struct log_target_file_param_struct {
+ char* logfilename_;
+ FILE* file_;
+};
+typedef struct log_target_file_param_struct log_target_file_param_t;
+
+int log_target_file_init(log_target_t* self, const char* conf)
+{
+ if(!self || (conf && conf[0] == 0))
+ return -1;
+
+ self->param_ = malloc(sizeof(log_target_file_param_t));
+ if(!self->param_)
+ return -2;
+
+ char* logfilename;
+ if(!conf)
+ logfilename = strdup("uanytun.log");
+ else {
+ const char* end = strchr(conf, ',');
+ if(end) {
+ size_t len = (size_t)(end - conf);
+ if(!len) {
+ free(self->param_);
+ return -1;
+ }
+ logfilename = malloc(len+1);
+ if(logfilename) {
+ strncpy(logfilename, conf, len);
+ logfilename[len] = 0;
+ }
+ }
+ else
+ logfilename = strdup(conf);
+ }
+
+ if(!logfilename) {
+ free(self->param_);
+ return -2;
+ }
+ ((log_target_file_param_t*)(self->param_))->logfilename_ = logfilename;
+ ((log_target_file_param_t*)(self->param_))->file_ = NULL;
+
+ return 0;
+}
+
+void log_target_file_open(log_target_t* self)
+{
+ if(!self || !self->param_)
+ return;
+
+ ((log_target_file_param_t*)(self->param_))->file_ = fopen(((log_target_file_param_t*)(self->param_))->logfilename_, "w");
+ if(((log_target_file_param_t*)(self->param_))->file_)
+ self->opened_ = 1;
+}
+
+void log_target_file_log(log_target_t* self, log_prio_t prio, const char* msg)
+{
+ if(!self || !self->param_ || !self->opened_)
+ return;
+
+ fprintf(((log_target_file_param_t*)(self->param_))->file_, "%s %s: %s\n", get_time_formatted(), log_prio_to_string(prio), msg);
+ fflush(((log_target_file_param_t*)(self->param_))->file_);
+}
+
+void log_target_file_close(log_target_t* self)
+{
+ if(!self || !self->param_)
+ return;
+
+ fclose(((log_target_file_param_t*)(self->param_))->file_);
+ self->opened_ = 0;
+}
+
+void log_target_file_clear(log_target_t* self)
+{
+ if(!self || !self->param_)
+ return;
+
+ if(((log_target_file_param_t*)(self->param_))->logfilename_)
+ free(((log_target_file_param_t*)(self->param_))->logfilename_);
+
+ free(self->param_);
+}
+
+
+log_target_t* log_target_file_new()
+{
+ log_target_t* tmp = malloc(sizeof(log_target_t));
+ if(!tmp)
+ return NULL;
+
+ tmp->type_ = TARGET_FILE;
+ tmp->init = &log_target_file_init;
+ tmp->open = &log_target_file_open;
+ tmp->log = &log_target_file_log;
+ tmp->close = &log_target_file_close;
+ tmp->clear = &log_target_file_clear;
+ tmp->opened_ = 0;
+ tmp->enabled_ = 0;
+ tmp->max_prio_ = NOTICE;
+ tmp->param_ = NULL;
+ tmp->next_ = NULL;
+
+ return tmp;
+}
+
+
+void log_target_stdout_log(log_target_t* self, log_prio_t prio, const char* msg)
+{
+ printf("%s %s: %s\n", get_time_formatted(), log_prio_to_string(prio), msg);
+}
+
+log_target_t* log_target_stdout_new()
+{
+ log_target_t* tmp = malloc(sizeof(log_target_t));
+ if(!tmp)
+ return NULL;
+
+ tmp->type_ = TARGET_STDOUT;
+ tmp->init = NULL;
+ tmp->open = NULL;
+ tmp->log = &log_target_stdout_log;
+ tmp->close = NULL;
+ tmp->clear = NULL;
+ tmp->opened_ = 0;
+ tmp->enabled_ = 0;
+ tmp->max_prio_ = NOTICE;
+ tmp->param_ = NULL;
+ tmp->next_ = NULL;
+
+ return tmp;
+}
+
+
+void log_target_stderr_log(log_target_t* self, log_prio_t prio, const char* msg)
+{
+ fprintf(stderr, "%s %s: %s\n", get_time_formatted(), log_prio_to_string(prio), msg);
+}
+
+log_target_t* log_target_stderr_new()
+{
+ log_target_t* tmp = malloc(sizeof(log_target_t));
+ if(!tmp)
+ return NULL;
+
+ tmp->type_ = TARGET_STDERR;
+ tmp->init = NULL;
+ tmp->open = NULL;
+ tmp->log = &log_target_stderr_log;
+ tmp->close = NULL;
+ tmp->clear = NULL;
+ tmp->opened_ = 0;
+ tmp->enabled_ = 0;
+ tmp->max_prio_ = NOTICE;
+ tmp->param_ = NULL;
+ tmp->next_ = NULL;
+
+ return tmp;
+}
+
+#endif
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..6298f9d
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,282 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include "options.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "log.h"
+
+#define PARSE_BOOL_PARAM(SHORT, LONG, VALUE) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ VALUE = 1;
+
+#define PARSE_INVERSE_BOOL_PARAM(SHORT, LONG, VALUE) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ VALUE = 0;
+
+#define PARSE_INT_PARAM(SHORT, LONG, VALUE) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ { \
+ if(argc < 1) \
+ return i; \
+ VALUE = atoi(argv[i+1]); \
+ argc--; \
+ i++; \
+ }
+
+#define PARSE_STRING_PARAM(SHORT, LONG, VALUE) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ { \
+ if(argc < 1 || argv[i+1][0] == '-') \
+ return i; \
+ if(VALUE) free(VALUE); \
+ VALUE = strdup(argv[i+1]); \
+ if(!VALUE) \
+ return -2; \
+ argc--; \
+ i++; \
+ }
+
+#define PARSE_STRING_PARAM_SEC(SHORT, LONG, VALUE) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ { \
+ if(argc < 1 || argv[i+1][0] == '-') \
+ return i; \
+ if(VALUE) free(VALUE); \
+ VALUE = strdup(argv[i+1]); \
+ if(!VALUE) \
+ return -2; \
+ size_t j; \
+ for(j=0; j < strlen(argv[i+1]); ++j) \
+ argv[i+1][j] = '#'; \
+ argc--; \
+ i++; \
+ }
+
+#define PARSE_HEXSTRING_PARAM_SEC(SHORT, LONG, VALUE) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ { \
+ if(argc < 1 || argv[i+1][0] == '-') \
+ return i; \
+ int ret; \
+ ret = options_parse_hex_string(argv[i+1], &VALUE); \
+ if(ret > 0) \
+ return i+1; \
+ else if(ret < 0) \
+ return ret; \
+ size_t j; \
+ for(j=0; j < strlen(argv[i+1]); ++j) \
+ argv[i+1][j] = '#'; \
+ argc--; \
+ i++; \
+ }
+
+#define PARSE_STRING_LIST(SHORT, LONG, LIST) \
+ else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
+ { \
+ if(argc < 1 || argv[i+1][0] == '-') \
+ return i; \
+ int ret = string_list_add(&LIST, argv[i+1]); \
+ if(ret == -2) \
+ return ret; \
+ else if(ret) \
+ return i+1; \
+ argc--; \
+ i++; \
+ }
+
+int options_parse_hex_string(const char* hex, buffer_t* buffer)
+{
+ if(!hex || !buffer)
+ return -1;
+
+ u_int32_t hex_len = strlen(hex);
+ if(hex_len%2)
+ return 1;
+
+ if(buffer->buf_)
+ free(buffer->buf_);
+
+ buffer->length_ = hex_len/2;
+ buffer->buf_ = malloc(buffer->length_);
+ if(!buffer->buf_) {
+ buffer->length_ = 0;
+ return -2;
+ }
+
+ const char* ptr = hex;
+ int i;
+ for(i=0;i<buffer->length_;++i) {
+ u_int32_t tmp;
+ sscanf(ptr, "%2X", &tmp);
+ buffer->buf_[i] = (u_int8_t)tmp;
+ ptr += 2;
+ }
+
+ return 0;
+}
+
+
+
+int options_parse(options_t* opt, int argc, char* argv[])
+{
+ if(!opt)
+ return -1;
+
+ options_default(opt);
+
+ if(opt->progname_)
+ free(opt->progname_);
+ opt->progname_ = strdup(argv[0]);
+ if(!opt->progname_)
+ return -2;
+
+ argc--;
+
+ int i;
+ for(i=1; argc > 0; ++i)
+ {
+ char* str = argv[i];
+ argc--;
+
+ if(!strcmp(str,"-h") || !strcmp(str,"--help"))
+ return -1;
+ PARSE_INVERSE_BOOL_PARAM("-D","--nodaemonize", opt->daemonize_)
+ PARSE_STRING_PARAM("-u","--username", opt->username_)
+ PARSE_STRING_PARAM("-g","--groupname", opt->groupname_)
+ PARSE_STRING_PARAM("-C","--chroot", opt->chroot_dir_)
+ PARSE_STRING_PARAM("-P","--write-pid", opt->pid_file_)
+ PARSE_STRING_LIST("-L","--log", opt->log_targets_)
+ PARSE_STRING_PARAM("-s","--command-sock", opt->command_sock_)
+ PARSE_STRING_PARAM("-x","--script", opt->script_)
+ PARSE_INT_PARAM("-m","--max-children", opt->max_children_)
+ PARSE_STRING_LIST("-d","--dir", opt->dirs_)
+ else
+ return i;
+ }
+
+ return 0;
+}
+
+int options_parse_post(options_t* opt)
+{
+ if(!opt)
+ return -1;
+
+ if(opt->max_children_ < 0) {
+ log_printf(ERROR, "-m|--max-children invalid value %d", opt->max_children_);
+ return 1;
+ }
+
+ if(opt->max_children_ == 0)
+ log_printf(WARNING, "disabling child limit, this may be harmful to your system");
+
+ return 0;
+}
+
+void options_default(options_t* opt)
+{
+ if(!opt)
+ return;
+
+ opt->progname_ = strdup("rhdropbox");
+
+ opt->daemonize_ = 1;
+ opt->username_ = NULL;
+ opt->groupname_ = NULL;
+ opt->chroot_dir_ = NULL;
+ opt->pid_file_ = NULL;
+ string_list_init(&opt->log_targets_);
+
+ opt->command_sock_ = strdup("/var/run/rhdropbox/cmd.sock");
+ opt->script_ = strdup("newfile.sh");
+ opt->max_children_ = 8;
+ string_list_init(&opt->dirs_);
+}
+
+void options_clear(options_t* opt)
+{
+ if(!opt)
+ return;
+
+ if(opt->progname_)
+ free(opt->progname_);
+ if(opt->username_)
+ free(opt->username_);
+ if(opt->groupname_)
+ free(opt->groupname_);
+ if(opt->chroot_dir_)
+ free(opt->chroot_dir_);
+ if(opt->pid_file_)
+ free(opt->pid_file_);
+ string_list_clear(&opt->log_targets_);
+
+ if(opt->command_sock_)
+ free(opt->command_sock_);
+ if(opt->script_)
+ free(opt->script_);
+ string_list_clear(&opt->dirs_);
+}
+
+void options_print_usage()
+{
+ printf("USAGE:\n");
+ printf("rhdropbox\n");
+ printf(" [-h|--help] prints this...\n");
+ printf(" [-D|--nodaemonize] don't run in background\n");
+ printf(" [-u|--username] <username> change to this user\n");
+ printf(" [-g|--groupname] <groupname> change to this group\n");
+ printf(" [-C|--chroot] <path> chroot to this directory\n");
+ printf(" [-P|--write-pid] <path> write pid to this file\n");
+ printf(" [-L|--log] <target>:<level>[,<param1>[,<param2>..]]\n");
+ printf(" add a log target, can be invoked several times\n");
+ printf(" [-s|--command-sock] <unix sock> the command socket e.g. /var/run/rhdropbox/cmd.sock\n");
+ printf(" [-x|--script] <script> the command socket e.g. newfile.sh\n");
+ printf(" [-m|--max-children] <#of children> limit of children to be started e.g. 8\n");
+ printf(" [-d|--dir] <path> add a path to the watch list, can be invoked several times\n");
+}
+
+void options_print(options_t* opt)
+{
+ if(!opt)
+ return;
+
+ printf("progname: '%s'\n", opt->progname_);
+ printf("daemonize: %d\n", opt->daemonize_);
+ printf("username: '%s'\n", opt->username_);
+ printf("groupname: '%s'\n", opt->groupname_);
+ printf("chroot_dir: '%s'\n", opt->chroot_dir_);
+ printf("pid_file: '%s'\n", opt->pid_file_);
+ printf("log_targets: \n");
+ string_list_print(&opt->log_targets_, " '", "'\n");
+
+ printf("command_sock: '%s'\n", opt->command_sock_);
+ printf("script: '%s'\n", opt->script_);
+ printf("max_children: %d\n", opt->max_children_);
+ string_list_print(&opt->dirs_, " '", "'\n");
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644
index 0000000..e98655b
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,52 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHDROPBOX_options_h_INCLUDED
+#define RHDROPBOX_options_h_INCLUDED
+
+#include "string_list.h"
+
+struct options_struct {
+ char* progname_;
+ int daemonize_;
+ char* username_;
+ char* groupname_;
+ char* chroot_dir_;
+ char* pid_file_;
+ string_list_t log_targets_;
+
+ char* command_sock_;
+ char* script_;
+ int max_children_;
+ string_list_t dirs_;
+};
+typedef struct options_struct options_t;
+
+int options_parse_hex_string(const char* hex, buffer_t* buffer);
+
+int options_parse(options_t* opt, int argc, char* argv[]);
+int options_parse_post(options_t* opt);
+void options_default(options_t* opt);
+void options_clear(options_t* opt);
+void options_print_usage();
+void options_print(options_t* opt);
+
+#endif
diff --git a/src/rhdropbox.c b/src/rhdropbox.c
new file mode 100644
index 0000000..3503fd2
--- /dev/null
+++ b/src/rhdropbox.c
@@ -0,0 +1,563 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/inotify.h>
+
+#include "log.h"
+#include "sig_handler.h"
+#include "options.h"
+
+#include "client_list.h"
+
+#include "daemon.h"
+#include "utils.h"
+#include "sysexec.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+enum cmd_id_enum { ADD, REMOVE, STATUS, LOG, LISTEN };
+typedef enum cmd_id_enum cmd_id_t;
+
+int send_response(int fd, const char* response)
+{
+ if(!response)
+ return -1;
+
+ if(fd < 0)
+ return 0;
+
+ int ret = send_string(fd, response);
+ do {
+ ret = write(fd, "\n", 1);
+ } while(!ret || (ret == -1 && errno == EINTR));
+
+ if(ret > 0)
+ return 0;
+
+ return ret;
+}
+
+int process_watch(int inotify_fd, read_buffer_t* buffer, watch_list_t* watch_lst, options_t* opt, client_t* client_lst, child_list_t* child_lst)
+{
+ log_printf(DEBUG, "something changed on watch descriptor '%d'", inotify_fd);
+
+ int ret = 0;
+ for(;;) {
+ ret = read(inotify_fd, &buffer->buf[buffer->offset], sizeof(buffer->buf) - buffer->offset);
+ if(!ret) {
+ log_printf(ERROR, "read from inotfiy fd returned end of file!?");
+ return -1;
+ }
+ if(ret == -1 && errno == EAGAIN)
+ return 0;
+ else if(ret < 0)
+ return ret;
+
+ buffer->offset += ret;
+ for(;;) {
+ if(buffer->offset >= sizeof(struct inotify_event)) {
+ struct inotify_event* event;
+ event = (struct inotify_event*)buffer->buf;
+ u_int32_t len = sizeof(struct inotify_event) + event->len;
+ if(buffer->offset >= len) {
+ char* path = watch_list_find_path(watch_lst, event->wd);
+ log_printf(DEBUG, "received event for %d (mask=0x%08X, cookie=0x%08X name[%u]='%s')",
+ event->wd, event->mask, event->cookie, event->len, event->len > 0 ? event->name : "");
+
+ if(event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
+ log_printf(NOTICE, "removing deleted or moved directory ('%s') from watch list", path);
+ watch_list_rm(watch_lst, path);
+ }
+ else if(event->mask & IN_IGNORED) {
+ log_printf(DEBUG, "ignoring inotify_rm_watch based events");
+ }
+ else {
+ struct stat stat_buf;
+ char buf[1024];
+ snprintf(buf, 1024, "%s/%s", path, event->len > 0 ? event->name : "");
+ int err = stat(buf, &stat_buf);
+ if(err < 0) {
+ log_printf(WARNING, "error at stat() for file %s", buf);
+ buf[0] = 0;
+ }
+ else
+ snprintf(buf, 1024, "%lu", stat_buf.st_size);
+
+ char* const argv[] = { opt->script_, path, event->len > 0 ? event->name : "", buf, NULL };
+ char* const evp[] = { NULL };
+ rh_exec(opt->script_, argv, evp, child_lst, opt);
+
+ snprintf(buf, 100, "new file in '%s', name='%s'", path, event->len > 0 ? event->name : "");
+ log_printf(NOTICE, "%s, executing script %s", buf, opt->script_);
+ client_t* client;
+ int listener_cnt = 0;
+ for(client = client_lst; client; client = client->next)
+ if(client->status_listener) {
+ send_response(client->fd, buf);
+ listener_cnt++;
+ }
+ log_printf(DEBUG, "sent status to %d additional listeners", listener_cnt);
+ }
+
+ if(buffer->offset > len) {
+ memmove(buffer->buf, &buffer->buf[len], buffer->offset - len);
+ buffer->offset -= len;
+ }
+ else
+ buffer->offset = 0;
+ }
+ }
+ else
+ break;
+ }
+ }
+ return 0;
+}
+
+void send_status_watch_list(int fd, watch_list_t* watch_lst, client_t* client_lst)
+{
+ if(fd <= 0 && !client_lst)
+ return;
+
+ if(!watch_lst || !watch_lst->first_) {
+ if(fd > 0)
+ send_response(fd, "currently no paths in watch list");
+ return;
+ }
+
+ char buf[100];
+ watch_list_element_t* tmp = watch_lst->first_;
+ while(tmp) {
+ snprintf(buf, 100, "%3d: %s", tmp->watch_fd_, tmp->path_);
+ if(fd > 0)
+ send_response(fd, buf);
+ client_t* client;
+ for(client = client_lst; client; client = client->next)
+ if(client->status_listener && client->fd != fd)
+ send_response(client->fd, buf);
+ tmp = tmp->next_;
+ }
+ if(fd > 0)
+ send_response(fd, "end of watch list");
+ int listener_cnt = 0;
+ client_t* client;
+ for(client = client_lst; client; client = client->next)
+ if(client->status_listener && client->fd != fd) {
+ send_response(client->fd, "end of watch list");
+ listener_cnt++;
+ }
+ log_printf(DEBUG, "sent status to %d additional listeners", listener_cnt);
+
+ return;
+}
+
+void process_cmd_add_remove(cmd_id_t cmd_id, const char* path, int fd, int inotify_fd, watch_list_t* watch_lst, client_t* client_lst)
+{
+ int wd = 0;
+ int tmp_fd = watch_list_find_fd(watch_lst, path);
+ if(cmd_id == ADD) {
+ if(tmp_fd < 0)
+ wd = inotify_add_watch(inotify_fd, path, IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MOVE_SELF | IN_ONLYDIR);
+ else {
+ log_printf(DEBUG, "cmd_add_remove: path already in watch list");
+ return;
+ }
+ }
+ else if(cmd_id == REMOVE) {
+ if(tmp_fd >= 0)
+ wd = inotify_rm_watch(inotify_fd, tmp_fd);
+ else {
+ log_printf(ERROR, "cmd_add_remove path not found in watch list");
+ if(fd > 0)
+ send_response(fd, "Error: path not found in watch list");
+ return;
+ }
+ }
+ else {
+ log_printf(WARNING, "cmd_add_remove ignoring wrong cmd_id");
+ return;
+ }
+ if(wd < 0) {
+ log_printf(ERROR, "inotify %s '%s' failed: %s", cmd_id == ADD ? "adding" : "removing", path, strerror(errno));
+ if(fd > 0)
+ send_response(fd, "Error: add/remove failed");
+ return;
+ }
+
+ int ret = 0;
+ if(cmd_id == ADD) {
+ ret = watch_list_add(watch_lst, wd, path);
+ if(ret) {
+ log_printf(ERROR, "can't add path to watch list");
+ if(fd > 0)
+ send_response(fd, "Error: can't add path to watch list");
+ }
+ }
+ else {
+ watch_list_rm(watch_lst, path);
+ }
+
+ if(!ret) {
+ send_status_watch_list(-1, watch_lst, client_lst);
+ log_printf(NOTICE, "inotify %s '%s'", cmd_id == ADD ? "added" : "removed", path);
+ }
+}
+
+void process_cmd_status(int fd, watch_list_t* watch_lst, client_t* client_lst)
+{
+ send_status_watch_list(fd, watch_lst, client_lst);
+}
+
+void process_cmd_listen(const char* param, int fd, client_t* client_lst)
+{
+ client_t* listener = client_find(client_lst, fd);
+ if(listener) {
+ if(!param) {
+ listener->status_listener = 1;
+ listener->request_listener = 1;
+ }
+ else {
+ if(!strncmp(param, "status", 6))
+ listener->status_listener = 1;
+ else if(!strncmp(param, "request", 7))
+ listener->request_listener = 1;
+ else if(!strncmp(param, "none", 4)) {
+ listener->request_listener = 0;
+ listener->status_listener = 0;
+ }
+ else {
+ log_printf(DEBUG, "unkown listener type '%s'", param);
+ send_response(fd, "Error: listen: unkown type");
+ return;
+ }
+ }
+ log_printf(DEBUG, "listener %d requests %s messages", fd, param ? param:"all");
+ }
+ else {
+
+ log_printf(ERROR, "unable to add listener %d", fd);
+ send_response(fd, "Error: listen: client not found in client list?!");
+ }
+}
+
+int process_cmd(const char* cmd, int fd, int inotify_fd, watch_list_t* watch_lst, client_t* client_lst, options_t* opt)
+{
+ log_printf(DEBUG, "processing command from %d", fd);
+
+ if(!cmd)
+ return -1;
+
+ cmd_id_t cmd_id;
+ if(!strncmp(cmd, "add", 3))
+ cmd_id = ADD;
+ else if(!strncmp(cmd, "remove", 6))
+ cmd_id = REMOVE;
+ else if(!strncmp(cmd, "status", 6))
+ cmd_id = STATUS;
+ else if(!strncmp(cmd, "log", 3))
+ cmd_id = LOG;
+ else if(!strncmp(cmd, "listen", 6)) {
+ cmd_id = LISTEN;
+ }
+ else {
+ log_printf(WARNING, "unknown command '%s'", cmd);
+ send_response(fd, "Error: unknown command");
+ return 0;
+ }
+ char* param = strchr(cmd, ' ');
+ if(param)
+ param++;
+
+ if(cmd_id == ADD || cmd_id == REMOVE) {
+ char* resp;
+ asprintf(&resp, "Request: %s", cmd);
+ if(resp) {
+ char* linefeed = strchr(resp, '\n');
+ if(linefeed) linefeed[0] = 0;
+ client_t* client;
+ int listener_cnt = 0;
+ for(client = client_lst; client; client = client->next)
+ if(client->request_listener && client->fd != fd) {
+ send_response(client->fd, resp);
+ listener_cnt++;
+ }
+ free(resp);
+ log_printf(DEBUG, "sent request to %d additional listeners", listener_cnt);
+ }
+// else silently ignore memory alloc error
+ }
+
+ switch(cmd_id) {
+ case ADD:
+ case REMOVE: process_cmd_add_remove(cmd_id, param, fd, inotify_fd, watch_lst, client_lst); break;
+ case STATUS: process_cmd_status(fd, watch_lst, client_lst); break;
+ case LOG: {
+ if(param && param[0])
+ log_printf(NOTICE, "ext msg: %s", param);
+ else
+ log_printf(DEBUG, "ignoring empty ext log message");
+ break;
+ }
+ case LISTEN: process_cmd_listen(param, fd, client_lst); break;
+ }
+
+ return 0;
+}
+
+int main_loop(int cmd_listen_fd, int inotify_fd, options_t* opt)
+{
+ log_printf(NOTICE, "entering main loop");
+
+ fd_set readfds, tmpfds;
+ FD_ZERO(&readfds);
+ FD_SET(cmd_listen_fd, &readfds);
+ FD_SET(inotify_fd, &readfds);
+ int max_fd = (cmd_listen_fd < inotify_fd) ? inotify_fd : cmd_listen_fd;
+ client_t* client_lst = NULL;
+
+ read_buffer_t event_buffer;
+ event_buffer.offset = 0;
+
+ watch_list_t watch_lst;
+ watch_list_init(&watch_lst);
+
+ child_list_t child_lst;
+ child_list_init(&child_lst);
+
+ int sig_fd = signal_init();
+ if(sig_fd < 0)
+ return -1;
+ FD_SET(sig_fd, &readfds);
+ max_fd = (max_fd < sig_fd) ? sig_fd : max_fd;
+
+ string_list_element_t* dir = opt->dirs_.first_;
+ for(;dir;dir=dir->next_)
+ process_cmd_add_remove(ADD, dir->string_, -1, inotify_fd, &watch_lst, client_lst);
+
+ struct timeval timeout;
+ int return_value = 0;
+ while(!return_value) {
+ memcpy(&tmpfds, &readfds, sizeof(tmpfds));
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 200000;
+ int ret = select(max_fd+1, &tmpfds, NULL, NULL, &timeout);
+ if(ret == -1 && errno != EINTR) {
+ log_printf(ERROR, "select returned with error: %s", strerror(errno));
+ return_value = -1;
+ break;
+ }
+ if(ret == -1)
+ continue;
+ if(!ret) {
+ rh_waitpid(&child_lst, opt);
+ continue;
+ }
+
+ if(FD_ISSET(sig_fd, &tmpfds)) {
+ if(signal_handle()) {
+ return_value = 1;
+ break;
+ }
+ }
+
+ if(FD_ISSET(inotify_fd, &tmpfds)) {
+ return_value = process_watch(inotify_fd, &event_buffer, &watch_lst, opt, client_lst, &child_lst);
+ if(return_value)
+ break;
+ }
+
+ if(FD_ISSET(cmd_listen_fd, &tmpfds)) {
+ int new_fd = accept(cmd_listen_fd, NULL, NULL);
+ if(new_fd < 0) {
+ log_printf(ERROR, "accept returned with error: %s", strerror(errno));
+ return_value = -1;
+ break;
+ }
+ log_printf(DEBUG, "new command connection (fd=%d)", new_fd);
+ FD_SET(new_fd, &readfds);
+ max_fd = (max_fd < new_fd) ? new_fd : max_fd;
+ fcntl(new_fd, F_SETFL, O_NONBLOCK);
+ int o_sndbuf = 128*1024;
+ setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
+ client_add(&client_lst, new_fd);
+ }
+
+ client_t* lst = client_lst;
+ while(lst) {
+ if(FD_ISSET(lst->fd, &tmpfds)) {
+ return_value = nonblock_recvline(&(lst->buffer), lst->fd, inotify_fd, &watch_lst, client_lst, opt);
+ if(return_value == 2) {
+ log_printf(DEBUG, "removing closed command connection (fd=%d)", lst->fd);
+ client_t* deletee = lst;
+ lst = lst->next;
+ FD_CLR(deletee->fd, &readfds);
+ client_remove(&client_lst, deletee->fd);
+ return_value = 0;
+ continue;
+ }
+ if(return_value)
+ break;
+
+ }
+ if(lst)
+ lst = lst->next;
+ }
+ }
+
+ child_list_clear(&child_lst);
+ watch_list_clear(&watch_lst);
+ client_clear(&client_lst);
+ signal_stop();
+ return return_value;
+}
+
+int main(int argc, char* argv[])
+{
+ log_init();
+
+ options_t opt;
+ int ret = options_parse(&opt, argc, argv);
+ if(ret) {
+ if(ret > 0) {
+ fprintf(stderr, "syntax error near: %s\n\n", argv[ret]);
+ }
+ if(ret == -2) {
+ fprintf(stderr, "memory error on options_parse, exiting\n");
+ }
+
+ if(ret != -2)
+ options_print_usage();
+
+ options_clear(&opt);
+ log_close();
+ exit(ret);
+ }
+ string_list_element_t* tmp = opt.log_targets_.first_;
+ if(!tmp) {
+ log_add_target("syslog:3,rhdropbox,daemon");
+ }
+ else {
+ while(tmp) {
+ ret = log_add_target(tmp->string_);
+ if(ret) {
+ switch(ret) {
+ case -2: fprintf(stderr, "memory error on log_add_target, exitting\n"); break;
+ case -3: fprintf(stderr, "unknown log target: '%s', exitting\n", tmp->string_); break;
+ case -4: fprintf(stderr, "this log target is only allowed once: '%s', exitting\n", tmp->string_); break;
+ default: fprintf(stderr, "syntax error near: '%s', exitting\n", tmp->string_); break;
+ }
+
+ options_clear(&opt);
+ log_close();
+ exit(ret);
+ }
+ tmp = tmp->next_;
+ }
+ }
+ log_printf(NOTICE, "just started...");
+ if(options_parse_post(&opt)) {
+ options_clear(&opt);
+ log_close();
+ exit(-1);
+ }
+
+ priv_info_t priv;
+ if(opt.username_)
+ if(priv_init(&priv, opt.username_, opt.groupname_)) {
+ options_clear(&opt);
+ log_close();
+ exit(-1);
+ }
+
+ FILE* pid_file = NULL;
+ if(opt.pid_file_) {
+ pid_file = fopen(opt.pid_file_, "w");
+ if(!pid_file) {
+ log_printf(WARNING, "unable to open pid file: %s", strerror(errno));
+ }
+ }
+
+ if(opt.chroot_dir_)
+ if(do_chroot(opt.chroot_dir_)) {
+ options_clear(&opt);
+ log_close();
+ exit(-1);
+ }
+ if(opt.username_)
+ if(priv_drop(&priv)) {
+ options_clear(&opt);
+ log_close();
+ exit(-1);
+ }
+
+ if(opt.daemonize_) {
+ pid_t oldpid = getpid();
+ daemonize();
+ log_printf(INFO, "running in background now (old pid: %d)", oldpid);
+ }
+
+ if(pid_file) {
+ pid_t pid = getpid();
+ fprintf(pid_file, "%d", pid);
+ fclose(pid_file);
+ }
+
+ int cmd_listen_fd = init_command_socket(opt.command_sock_);
+ if(cmd_listen_fd < 0) {
+ options_clear(&opt);
+ log_close();
+ exit(-1);
+ }
+
+ int inotify_fd = create_inotify();
+ if(inotify_fd < 0) {
+ close(cmd_listen_fd);
+ options_clear(&opt);
+ log_close();
+ exit(-1);
+ }
+
+ ret = main_loop(cmd_listen_fd, inotify_fd, &opt);
+
+ close(cmd_listen_fd);
+ close(inotify_fd);
+
+ if(!ret)
+ log_printf(NOTICE, "normal shutdown");
+ else if(ret < 0)
+ log_printf(NOTICE, "shutdown after error");
+ else
+ log_printf(NOTICE, "shutdown after signal");
+
+ options_clear(&opt);
+ log_close();
+
+ return ret;
+}
diff --git a/src/sig_handler.c b/src/sig_handler.c
new file mode 100644
index 0000000..efb6ee6
--- /dev/null
+++ b/src/sig_handler.c
@@ -0,0 +1,166 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include "log.h"
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <errno.h>
+
+#include "sig_handler.h"
+
+#include <stdio.h>
+
+
+static int sig_pipe_fds[2];
+
+
+static void sig_handler(int sig)
+{
+ sigset_t set;
+ int ret = read(sig_pipe_fds[0], &set, sizeof(sigset_t));
+ if(ret != sizeof(sigset_t))
+ sigemptyset(&set);
+
+ sigaddset(&set, sig);
+ ret = write(sig_pipe_fds[1], &set, sizeof(sigset_t));
+}
+
+
+int signal_init()
+{
+ if(pipe(sig_pipe_fds)) {
+ log_printf(ERROR, "signal handling init failed (pipe error: %s)", strerror(errno));
+ return -1;
+ }
+
+ int i;
+ for(i=0; i<2; ++i) {
+ int fd_flags = fcntl(sig_pipe_fds[i], F_GETFL);
+ if(fd_flags == -1) {
+ log_printf(ERROR, "signal handling init failed (pipe fd[%d] read flags error: %s)", i, strerror(errno));
+ return -1;
+ }
+ if(fcntl(sig_pipe_fds[i], F_SETFL, fd_flags | O_NONBLOCK) == -1){
+ log_printf(ERROR, "signal handling init failed (pipe fd[%d] write flags error: %s)", i, strerror(errno));
+ return -1;
+ }
+ }
+
+ struct sigaction act, ign;
+ act.sa_handler = sig_handler;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ ign.sa_handler = SIG_IGN;
+ sigfillset(&ign.sa_mask);
+ ign.sa_flags = 0;
+
+ if((sigaction(SIGINT, &act, NULL) < 0) ||
+ (sigaction(SIGQUIT, &act, NULL) < 0) ||
+ (sigaction(SIGTERM, &act, NULL) < 0) ||
+ (sigaction(SIGHUP, &act, NULL) < 0) ||
+ (sigaction(SIGUSR1, &act, NULL) < 0) ||
+ (sigaction(SIGUSR2, &act, NULL) < 0) ||
+ (sigaction(SIGCHLD, &ign, NULL) < 0) ||
+ (sigaction(SIGPIPE, &ign, NULL) < 0)) {
+
+ log_printf(ERROR, "signal handling init failed (sigaction error: %s)", strerror(errno));
+ close(sig_pipe_fds[0]);
+ close(sig_pipe_fds[1]);
+ }
+
+ return sig_pipe_fds[0];
+}
+
+int signal_handle()
+{
+ sigset_t set, oldset, tmpset;
+
+ sigemptyset(&tmpset);
+ sigaddset(&tmpset, SIGINT);
+ sigaddset(&tmpset, SIGQUIT);
+ sigaddset(&tmpset, SIGTERM);
+ sigaddset(&tmpset, SIGHUP);
+ sigaddset(&tmpset, SIGUSR1);
+ sigaddset(&tmpset, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &tmpset, &oldset);
+
+ int ret = read(sig_pipe_fds[0], &set, sizeof(sigset_t));
+ if(ret != sizeof(sigset_t))
+ sigemptyset(&set);
+
+ int return_value = 0;
+ int sig;
+ for(sig=1; sig < NSIG; ++sig) {
+ if(sigismember(&set, sig)) {
+ switch(sig) {
+ case SIGINT: log_printf(NOTICE, "SIG-Int caught, exitting"); return_value = 1; break;
+ case SIGQUIT: log_printf(NOTICE, "SIG-Quit caught, exitting"); return_value = 1; break;
+ case SIGTERM: log_printf(NOTICE, "SIG-Term caught, exitting"); return_value = 1; break;
+ case SIGHUP: log_printf(NOTICE, "SIG-Hup caught"); break;
+ case SIGUSR1: log_printf(NOTICE, "SIG-Usr1 caught"); break;
+ case SIGUSR2: log_printf(NOTICE, "SIG-Usr2 caught"); break;
+ default: log_printf(WARNING, "unknown signal %d caught, ignoring", sig); break;
+ }
+ sigdelset(&set, sig);
+ }
+ }
+
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return return_value;
+}
+
+void signal_stop()
+{
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGUSR1, &act, NULL);
+ sigaction(SIGUSR2, &act, NULL);
+ sigaction(SIGCHLD, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+
+ close(sig_pipe_fds[0]);
+ close(sig_pipe_fds[1]);
+}
diff --git a/src/sig_handler.h b/src/sig_handler.h
new file mode 100644
index 0000000..1995e1a
--- /dev/null
+++ b/src/sig_handler.h
@@ -0,0 +1,43 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UANYTUN_sig_handler_h_INCLUDED
+#define UANYTUN_sig_handler_h_INCLUDED
+
+int signal_init();
+int signal_handle();
+void signal_stop();
+
+#endif
diff --git a/src/string_list.c b/src/string_list.c
new file mode 100644
index 0000000..a4f4ab8
--- /dev/null
+++ b/src/string_list.c
@@ -0,0 +1,113 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "string_list.h"
+
+void string_list_init(string_list_t* list)
+{
+ if(!list)
+ return;
+
+ list->first_ = NULL;
+}
+
+void string_list_clear(string_list_t* list)
+{
+ if(!list)
+ return;
+
+ while(list->first_) {
+ string_list_element_t* tmp;
+ tmp = list->first_;
+ list->first_ = tmp->next_;
+ if(tmp->string_)
+ free(tmp->string_);
+ free(tmp);
+ }
+}
+
+int string_list_add(string_list_t* list, const char* string)
+{
+ if(!list)
+ return -1;
+
+ if(!list->first_) {
+ list->first_ = malloc(sizeof(string_list_element_t));
+ if(!list->first_)
+ return -2;
+
+ list->first_->next_ = 0;
+ list->first_->string_ = strdup(string);
+ if(!list->first_->string_) {
+ free(list->first_);
+ list->first_ = NULL;
+ return -2;
+ }
+ }
+ else {
+ string_list_element_t* tmp = list->first_;
+ while(tmp->next_)
+ tmp = tmp->next_;
+
+ tmp->next_ = malloc(sizeof(string_list_element_t));
+ if(!tmp->next_)
+ return -2;
+
+ tmp->next_->next_ = 0;
+ tmp->next_->string_ = strdup(string);
+ if(!tmp->next_->string_) {
+ free(tmp->next_);
+ tmp->next_ = NULL;
+ return -2;
+ }
+ }
+ return 0;
+}
+
+void string_list_print(string_list_t* list, const char* head, const char* tail)
+{
+ if(!list)
+ return;
+
+ string_list_element_t* tmp = list->first_;
+ while(tmp) {
+ printf("%s%s%s", head, tmp->string_, tail);
+ tmp = tmp->next_;
+ }
+}
diff --git a/src/string_list.h b/src/string_list.h
new file mode 100644
index 0000000..cd054cb
--- /dev/null
+++ b/src/string_list.h
@@ -0,0 +1,56 @@
+/*
+ * uAnytun
+ *
+ * uAnytun is a tiny implementation of SATP. Unlike Anytun which is a full
+ * featured implementation uAnytun has no support for multiple connections
+ * or synchronisation. It is a small single threaded implementation intended
+ * to act as a client on small platforms.
+ * The secure anycast tunneling protocol (satp) defines a protocol used
+ * for communication between any combination of unicast and anycast
+ * tunnel endpoints. It has less protocol overhead than IPSec in Tunnel
+ * mode and allows tunneling of every ETHER TYPE protocol (e.g.
+ * ethernet, ip, arp ...). satp directly includes cryptography and
+ * message authentication based on the methodes used by SRTP. It is
+ * intended to deliver a generic, scaleable and secure solution for
+ * tunneling and relaying of packets of any protocol.
+ *
+ *
+ * Copyright (C) 2007-2008 Christian Pointner <equinox@anytun.org>
+ *
+ * This file is part of uAnytun.
+ *
+ * uAnytun is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * uAnytun is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with uAnytun. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UANYTUN_string_list_h_INCLUDED
+#define UANYTUN_string_list_h_INCLUDED
+
+struct string_list_element_struct {
+ char* string_;
+ struct string_list_element_struct* next_;
+};
+typedef struct string_list_element_struct string_list_element_t;
+
+struct string_list_struct {
+ string_list_element_t* first_;
+};
+typedef struct string_list_struct string_list_t;
+
+void string_list_init(string_list_t* list);
+void string_list_clear(string_list_t* list);
+int string_list_add(string_list_t* list, const char* string);
+
+void string_list_print(string_list_t* list, const char* head, const char* tail);
+
+#endif
diff --git a/src/sysexec.c b/src/sysexec.c
new file mode 100644
index 0000000..79f4e74
--- /dev/null
+++ b/src/sysexec.c
@@ -0,0 +1,371 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysexec.h"
+#include "log.h"
+
+char** dup_ptrptr(char* const ptrptr[])
+{
+ if(!ptrptr)
+ return NULL;
+
+ int n = 0;
+ while(ptrptr[n])
+ n++;
+
+ char** my_ptrptr;
+ my_ptrptr = malloc((n+1)*sizeof(char*));
+ if(!my_ptrptr)
+ return NULL;
+
+ int i;
+ for(i = 0; i < n; ++i) {
+ my_ptrptr[i] = strdup(ptrptr[i]);
+ if(!my_ptrptr[i]) {
+ i--;
+ for(; i >= 0; --i)
+ free(my_ptrptr[i]);
+
+ free(my_ptrptr);
+ return NULL;
+ }
+ }
+
+ my_ptrptr[n] = NULL;
+
+ return my_ptrptr;
+}
+
+void free_ptrptr(char** ptrptr)
+{
+ if(!ptrptr)
+ return;
+
+ int i;
+ for(i = 0; ptrptr[i]; ++i)
+ free(ptrptr[i]);
+
+ free(ptrptr);
+}
+
+void child_list_init(child_list_t* list)
+{
+ if(!list)
+ return;
+
+ list->first_ = NULL;
+}
+
+void child_list_clear(child_list_t* list)
+{
+ if(!list)
+ return;
+
+ while(list->first_) {
+ child_list_element_t* tmp;
+ tmp = list->first_;
+ list->first_ = tmp->next_;
+ if(tmp->script_)
+ free(tmp->script_);
+ free(tmp);
+ }
+}
+
+child_list_element_t* child_list_new(const char* script, char* const argv[], char* const evp[])
+{
+ child_list_element_t* new_child;
+
+ new_child = malloc(sizeof(child_list_element_t));
+ if(!new_child)
+ return NULL;
+
+ new_child->next_ = 0;
+ new_child->pid_ = -1;
+ new_child->err_fd_ = -1;
+ new_child->running_ = 0;
+ new_child->script_ = strdup(script);
+ if(!new_child->script_) {
+ free(new_child);
+ return NULL;
+ }
+
+ new_child->argv_ = dup_ptrptr(argv);
+ if(!new_child->argv_) {
+ free(new_child->script_);
+ free(new_child);
+ return NULL;
+
+ }
+
+ new_child->evp_ = dup_ptrptr(evp);
+ if(!new_child->evp_) {
+ free_ptrptr(new_child->argv_);
+ free(new_child->script_);
+ free(new_child);
+ return NULL;
+ }
+ return new_child;
+}
+
+child_list_element_t* child_list_add(child_list_t* list, const char* script, char* const argv[], char* const evp[])
+{
+ if(!list)
+ return NULL;
+
+ if(!list->first_) {
+ list->first_ = child_list_new(script, argv, evp);
+ return list->first_;
+ }
+ else {
+ child_list_element_t* tmp = list->first_;
+ while(tmp->next_)
+ tmp = tmp->next_;
+
+ tmp->next_ = child_list_new(script, argv, evp);
+ return tmp->next_;
+ }
+}
+
+void child_list_rm(child_list_t* list, child_list_element_t* child)
+{
+ if(!list || !child)
+ return;
+
+ if(child == list->first_) {
+ list->first_ = list->first_->next_;
+ free_ptrptr(child->argv_);
+ free_ptrptr(child->evp_);
+ if(child->script_)
+ free(child->script_);
+ free(child);
+ return;
+ }
+ else {
+ child_list_element_t* tmp = list->first_;
+ while(tmp) {
+ if(tmp->next_ == child) {
+ tmp->next_ = tmp->next_->next_;
+ free_ptrptr(child->argv_);
+ free_ptrptr(child->evp_);
+ if(child->script_)
+ free(child->script_);
+ free(child);
+ return;
+ }
+ tmp = tmp->next_;
+ }
+ }
+}
+
+void child_list_rm_pid(child_list_t* list, pid_t pid)
+{
+ if(!list)
+ return;
+
+ child_list_element_t* tmp = NULL;
+ if(list->first_->pid_ == pid) {
+ tmp = list->first_;
+ list->first_ = list->first_->next_;
+
+ free_ptrptr(tmp->argv_);
+ free_ptrptr(tmp->evp_);
+ if(tmp->script_)
+ free(tmp->script_);
+ free(tmp);
+ return;
+ }
+
+ child_list_element_t* prev = list->first_;
+ tmp = list->first_->next_;
+ while(tmp) {
+ if(tmp->pid_ == pid) {
+ prev->next_ = tmp->next_;
+
+ free_ptrptr(tmp->argv_);
+ free_ptrptr(tmp->evp_);
+ if(tmp->script_)
+ free(tmp->script_);
+ free(tmp);
+ return;
+ }
+ prev = tmp;
+ tmp = tmp->next_;
+ }
+}
+
+child_list_element_t* child_list_find(child_list_t* list, pid_t pid)
+{
+ if(!list)
+ return NULL;
+
+ child_list_element_t* tmp = list->first_;
+ while(tmp) {
+ if(tmp->pid_ == pid)
+ return tmp;
+ tmp = tmp->next_;
+ }
+
+ return NULL;
+}
+
+int child_list_num_running(child_list_t* list)
+{
+ int num = 0;
+
+ if(!list)
+ return 0;
+
+ child_list_element_t* tmp = list->first_;
+ for(;tmp;tmp=tmp->next_)
+ if(tmp->running_) num++;
+
+ return num;
+}
+
+int rh_exec(const char* script, char* const argv[], char* const evp[], child_list_t* child_lst, options_t* opt)
+{
+ if(!script)
+ return -1;
+
+ child_list_element_t* child = child_list_add(child_lst, script, argv, evp);
+ if(!child)
+ return -2;
+
+ if(child_list_num_running(child_lst) >= opt->max_children_) {
+ log_printf(INFO, "deferring script execution '%s'", script);
+ return 0;
+ }
+
+ int ret = rh_exec_child(child);
+ if(ret)
+ child_list_rm(child_lst, child);
+
+ return ret;
+}
+
+int rh_exec_child(child_list_element_t* child)
+{
+ if(!child || child->pid_ != -1)
+ return -1;
+
+ int pipefd[2];
+ if(pipe(pipefd) == -1) {
+ log_printf(ERROR, "executing script '%s' failed: pipe() error: %s", child->script_, strerror(errno));
+ return -1;
+ }
+
+ pid_t pid;
+ pid = fork();
+ if(pid == -1) {
+ log_printf(ERROR, "executing script '%s' failed: fork() error: %s", child->script_, strerror(errno));
+ return -1;
+ }
+
+ if(!pid) {
+ int fd;
+ for (fd=getdtablesize();fd>=0;--fd) // close all file descriptors
+ if(fd != pipefd[1]) close(fd);
+
+ fd = open("/dev/null",O_RDWR); // stdin
+ if(fd == -1)
+ log_printf(WARNING, "can't open stdin");
+ else {
+ if(dup(fd) == -1) // stdout
+ log_printf(WARNING, "can't open stdout");
+ if(dup(fd) == -1) // stderr
+ log_printf(WARNING, "can't open stderr");
+ }
+ execve(child->script_, child->argv_, child->evp_);
+ // if execve returns, an error occurred, but logging doesn't work
+ // because we closed all file descriptors, so just write errno to
+ // pipe and call exit
+ write(pipefd[1], (void*)(&errno), sizeof(errno));
+ exit(-1);
+ }
+ close(pipefd[1]);
+
+ child->pid_ = pid;
+ child->err_fd_ = pipefd[0];
+ child->running_ = 1;
+
+ log_printf(INFO, "called script '%s' with pid %d", child->script_, child->pid_);
+
+ return 0;
+}
+
+int rh_waitpid(child_list_t* child_lst, options_t* opt)
+{
+ int status = 0;
+ pid_t pid = waitpid(-1, &status, WNOHANG);
+ if(!pid || (pid < 0 && errno == ECHILD))
+ return 0;
+ if(pid < 0) {
+ log_printf(ERROR, "waitpid returned with error: %s", strerror(errno));
+ return pid;
+ }
+
+ child_list_element_t* child = child_list_find(child_lst, pid);
+ if(!child) {
+ log_printf(ERROR, "waitpid returned unknown child pid (%d)", pid);
+ return 0;
+ }
+
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(child->err_fd_, &rfds);
+ struct timeval tv = { 0 , 0 };
+ if(select(child->err_fd_+1, &rfds, NULL, NULL, &tv) == 1) {
+ int err = 0;
+ if(read(child->err_fd_, (void*)(&err), sizeof(err)) >= sizeof(err)) {
+ log_printf(INFO, "script '%s' exec() error: %s", child->script_, strerror(err));
+ close(child->err_fd_);
+ child_list_rm_pid(child_lst, pid);
+ return -1;
+ }
+ }
+ if(WIFEXITED(status))
+ log_printf(INFO, "script '%s' (pid %d) returned %d", child->script_, child->pid_, WEXITSTATUS(status));
+ else if(WIFSIGNALED(status))
+ log_printf(INFO, "script '%s' (pid %d) terminated after signal %d", child->script_, child->pid_, WTERMSIG(status));
+ else
+ log_printf(INFO, "executing script '%s' (pid %d): unkown error", child->script_, child->pid_);
+
+ close(child->err_fd_);
+
+ child_list_rm_pid(child_lst, pid);
+
+ if(child_list_num_running(child_lst) < opt->max_children_)
+ rh_exec_child(child_list_find(child_lst, -1));
+
+ return status;
+}
+
diff --git a/src/sysexec.h b/src/sysexec.h
new file mode 100644
index 0000000..8954622
--- /dev/null
+++ b/src/sysexec.h
@@ -0,0 +1,57 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHDROPBOX_sysexec_h_INCLUDED
+#define RHDROPBOX_sysexec_h_INCLUDED
+
+#include <sys/types.h>
+#include "options.h"
+
+struct child_list_element_struct {
+ pid_t pid_;
+ char* script_;
+ int err_fd_;
+ int running_;
+ char** argv_;
+ char** evp_;
+ struct child_list_element_struct* next_;
+};
+typedef struct child_list_element_struct child_list_element_t;
+
+struct child_list_struct {
+ child_list_element_t* first_;
+};
+typedef struct child_list_struct child_list_t;
+
+void child_list_init(child_list_t* list);
+void child_list_clear(child_list_t* list);
+child_list_element_t* child_list_new(const char* script, char* const argv[], char* const evp[]);
+child_list_element_t* child_list_add(child_list_t* list, const char* script, char* const argv[], char* const evp[]);
+void child_list_rm(child_list_t* list, child_list_element_t* child);
+void child_list_rm_pid(child_list_t* list, pid_t pid);
+child_list_element_t* child_list_find(child_list_t* list, pid_t pid);
+int child_list_num_running(child_list_t* list);
+
+int rh_exec(const char* script, char* const argv[], char* const evp[], child_list_t* child_lst, options_t* opt);
+int rh_exec_child(child_list_element_t* child);
+int rh_waitpid(child_list_t* child_lst, options_t* opt);
+
+#endif
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..a61ca1c
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,173 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+
+#include "log.h"
+
+#include "utils.h"
+
+int init_command_socket(const char* path)
+{
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(fd < 0) {
+ log_printf(ERROR, "unable to open socket: %s", strerror(errno));
+ return -1;
+ }
+
+ struct sockaddr_un local;
+ local.sun_family = AF_UNIX;
+ if(sizeof(local.sun_path) <= strlen(path)) {
+ log_printf(ERROR, "socket path is to long (max %d)", sizeof(local.sun_path)-1);
+ return -1;
+ }
+ strcpy(local.sun_path, path);
+ unlink(local.sun_path);
+ int len = SUN_LEN(&local);
+ int ret = bind(fd, (struct sockaddr*)&local, len);
+ if(ret) {
+ log_printf(ERROR, "unable to bind to '%s': %s", local.sun_path, strerror(errno));
+ return -1;
+ }
+
+ ret = listen(fd, 4);
+ if(ret) {
+ log_printf(ERROR, "unable to listen on command socket: %s", local.sun_path, strerror(errno));
+ return -1;
+ }
+
+ log_printf(INFO, "now listening on %s for incoming commands", path);
+
+ return fd;
+}
+
+int connect_command_socket(const char* path)
+{
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(fd < 0) {
+ log_printf(ERROR, "unable to open socket: %s", strerror(errno));
+ return -1;
+ }
+
+ struct sockaddr_un remote;
+ remote.sun_family = AF_UNIX;
+ if(sizeof(remote.sun_path) <= strlen(path)) {
+ log_printf(ERROR, "socket path is to long (max %d)", sizeof(remote.sun_path)-1);
+ return -1;
+ }
+ strcpy(remote.sun_path, path);
+ int len = SUN_LEN(&remote);
+ int ret = connect(fd, (struct sockaddr*)&remote, len);
+ if(ret) {
+ log_printf(ERROR, "unable to connect to '%s': %s", remote.sun_path, strerror(errno));
+ return -1;
+ }
+
+ return fd;
+}
+
+int send_string(int fd, const char* string)
+{
+ int len = strlen(string);
+ int offset = 0;
+ int ret;
+ for(;;) {
+ ret = write(fd, &string[offset], len - offset);
+ if(ret < 0) {
+ if(errno != EINTR)
+ return ret;
+
+ ret = 0;
+ }
+
+ offset += ret;
+ if(offset+1 >= len)
+ break;
+ }
+ return ret;
+}
+
+int nonblock_recvline(read_buffer_t* buffer, int fd, int inotify_fd, watch_list_t* watch_lst, client_t* client_lst, options_t* opt)
+{
+ int ret = 0;
+ for(;;) {
+ ret = recv(fd, &buffer->buf[buffer->offset], 1, 0);
+ if(!ret)
+ return 2;
+ if(ret == -1 && errno == EAGAIN)
+ return 0;
+ else if(ret < 0)
+ return 2;
+
+ if(buffer->buf[buffer->offset] == '\n') {
+ buffer->buf[buffer->offset] = 0;
+ ret = process_cmd(buffer->buf, fd, inotify_fd, watch_lst, client_lst, opt);
+ buffer->offset = 0;
+ break;
+ }
+
+ buffer->offset++;
+ if(buffer->offset >= sizeof(buffer->buf)) {
+ log_printf(DEBUG, "string too long (fd=%d)", fd);
+ buffer->offset = 0;
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+int create_inotify()
+{
+ int fd = inotify_init();
+ if(fd < 0) {
+ log_printf(ERROR, "error at inotify_init: %s", strerror(errno));
+ return -1;
+ }
+
+ int fs_flags = fcntl(fd, F_GETFL);
+ if(fs_flags == -1) {
+ log_printf(ERROR, "inotify init failed (fcntl read flags error: %s)", strerror(errno));
+ return -1;
+ }
+ if(fcntl(fd, F_SETFL, fs_flags | O_NONBLOCK) == -1) {
+ log_printf(ERROR, "inotify init failed (fcntl write flags error: %s)", strerror(errno));
+ return -1;
+ }
+
+ int fd_flags = fcntl(fd, F_GETFD);
+ if(fd_flags == -1) {
+ log_printf(ERROR, "inotify init failed (fcntl read flags error: %s)", strerror(errno));
+ return -1;
+ }
+ if(fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1) {
+ log_printf(ERROR, "inotify init failed (fcntl write flags error: %s)", strerror(errno));
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..b3cebc1
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,36 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHDROPBOX_utils_h_INCLUDED
+#define RHDROPBOX_utils_h_INCLUDED
+
+#include "client_list.h"
+#include "watch_list.h"
+#include "options.h"
+#include <termios.h>
+
+int init_command_socket(const char* path);
+int connect_command_socket(const char* path);
+int send_string(int fd, const char* string);
+int nonblock_recvline(read_buffer_t* buffer, int fd, int inotify_fd, watch_list_t* watch_lst, client_t* client_lst, options_t* opt);
+int create_inotify();
+
+#endif
diff --git a/src/watch_list.c b/src/watch_list.c
new file mode 100644
index 0000000..39c61f2
--- /dev/null
+++ b/src/watch_list.c
@@ -0,0 +1,163 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "watch_list.h"
+
+void watch_list_init(watch_list_t* list)
+{
+ if(!list)
+ return;
+
+ list->first_ = NULL;
+}
+
+void watch_list_clear(watch_list_t* list)
+{
+ if(!list)
+ return;
+
+ while(list->first_) {
+ watch_list_element_t* tmp;
+ tmp = list->first_;
+ list->first_ = tmp->next_;
+ if(tmp->path_)
+ free(tmp->path_);
+ free(tmp);
+ }
+}
+
+int watch_list_add(watch_list_t* list, int watch_fd, const char* string)
+{
+ if(!list)
+ return -1;
+
+ if(!list->first_) {
+ list->first_ = malloc(sizeof(watch_list_element_t));
+ if(!list->first_)
+ return -2;
+
+ list->first_->next_ = 0;
+ list->first_->watch_fd_ = watch_fd;
+ list->first_->path_ = strdup(string);
+ if(!list->first_->path_) {
+ free(list->first_);
+ list->first_ = NULL;
+ return -2;
+ }
+ }
+ else {
+ watch_list_element_t* tmp = list->first_;
+ while(tmp->next_)
+ tmp = tmp->next_;
+
+ tmp->next_ = malloc(sizeof(watch_list_element_t));
+ if(!tmp->next_)
+ return -2;
+
+ tmp->next_->next_ = 0;
+ tmp->next_->watch_fd_ = watch_fd;
+ tmp->next_->path_ = strdup(string);
+ if(!tmp->next_->path_) {
+ free(tmp->next_);
+ tmp->next_ = NULL;
+ return -2;
+ }
+ }
+ return 0;
+}
+
+void watch_list_rm(watch_list_t* list, const char* path)
+{
+ if(!list || !path)
+ return;
+
+ watch_list_element_t* tmp = NULL;
+ if(list->first_->path_ && !strcmp(path, list->first_->path_)) {
+ tmp = list->first_;
+ list->first_ = list->first_->next_;
+
+ if(tmp->path_)
+ free(tmp->path_);
+ free(tmp);
+ return;
+ }
+
+ watch_list_element_t* prev = list->first_;
+ tmp = list->first_->next_;
+ while(tmp) {
+ if(tmp->path_ && !strcmp(path, tmp->path_)) {
+ prev->next_ = tmp->next_;
+
+ if(tmp->path_)
+ free(tmp->path_);
+ free(tmp);
+ return;
+ }
+ prev = tmp;
+ tmp = tmp->next_;
+ }
+}
+
+char* watch_list_find_path(watch_list_t* list, int watch_fd)
+{
+ if(!list)
+ return NULL;
+
+ watch_list_element_t* tmp = list->first_;
+ while(tmp) {
+ if(tmp->watch_fd_ == watch_fd)
+ return tmp->path_;
+ tmp = tmp->next_;
+ }
+
+ return NULL;
+}
+
+int watch_list_find_fd(watch_list_t* list, const char* path)
+{
+ if(!list || !path)
+ return -1;
+
+ watch_list_element_t* tmp = list->first_;
+ while(tmp) {
+ if(tmp->path_ && !strcmp(path, tmp->path_))
+ return tmp->watch_fd_;
+ tmp = tmp->next_;
+ }
+
+ return -1;
+}
+
+void watch_list_print(watch_list_t* list, const char* head, const char* sep, const char* tail)
+{
+ if(!list)
+ return;
+
+ watch_list_element_t* tmp = list->first_;
+ while(tmp) {
+ printf("%s%d%s%s%s", head, tmp->watch_fd_, sep, tmp->path_, tail);
+ tmp = tmp->next_;
+ }
+}
diff --git a/src/watch_list.h b/src/watch_list.h
new file mode 100644
index 0000000..bd428ab
--- /dev/null
+++ b/src/watch_list.h
@@ -0,0 +1,46 @@
+/*
+ * rhdropbox
+ *
+ * Copyright (C) 2009 Christian Pointner <equinox@helsinki.at>
+ *
+ * This file is part of rhdropbox.
+ *
+ * rhdropbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * rhdropbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with rhdropbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHDROPBOX_watch_list_h_INCLUDED
+#define RHDROPBOX_watch_list_h_INCLUDED
+
+struct watch_list_element_struct {
+ char* path_;
+ int watch_fd_;
+ struct watch_list_element_struct* next_;
+};
+typedef struct watch_list_element_struct watch_list_element_t;
+
+struct watch_list_struct {
+ watch_list_element_t* first_;
+};
+typedef struct watch_list_struct watch_list_t;
+
+void watch_list_init(watch_list_t* list);
+void watch_list_clear(watch_list_t* list);
+int watch_list_add(watch_list_t* list, int watch_fd, const char* path);
+void watch_list_rm(watch_list_t* list, const char* path);
+char* watch_list_find_path(watch_list_t* list, int watch_fd);
+int watch_list_find_fd(watch_list_t* list, const char* path);
+
+void watch_list_print(watch_list_t* list, const char* head, const char* sep, const char* tail);
+
+#endif