diff options
author | Christian Pointner <equinox@spreadspace.org> | 2015-09-16 21:04:43 +0200 |
---|---|---|
committer | Christian Pointner <equinox@spreadspace.org> | 2015-09-16 21:04:43 +0200 |
commit | 6b4596fe0124fe5c5f5bd6e11b0fd3b2d416aa56 (patch) | |
tree | 66336cd27806f1141ddf799b4c13a7639fda6a62 /src | |
parent | make prefix variable more standard compliant (diff) |
removed debian dir
moved source code to src/ dir
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 140 | ||||
-rw-r--r-- | src/client_list.c | 116 | ||||
-rw-r--r-- | src/client_list.h | 41 | ||||
-rwxr-xr-x | src/configure | 162 | ||||
-rw-r--r-- | src/daemon.h | 173 | ||||
-rw-r--r-- | src/datatypes.h | 49 | ||||
-rw-r--r-- | src/log.c | 259 | ||||
-rw-r--r-- | src/log.h | 90 | ||||
-rw-r--r-- | src/log_targets.h | 363 | ||||
-rw-r--r-- | src/options.c | 282 | ||||
-rw-r--r-- | src/options.h | 52 | ||||
-rw-r--r-- | src/rhdropbox.c | 563 | ||||
-rw-r--r-- | src/sig_handler.c | 166 | ||||
-rw-r--r-- | src/sig_handler.h | 43 | ||||
-rw-r--r-- | src/string_list.c | 113 | ||||
-rw-r--r-- | src/string_list.h | 56 | ||||
-rw-r--r-- | src/sysexec.c | 371 | ||||
-rw-r--r-- | src/sysexec.h | 57 | ||||
-rw-r--r-- | src/utils.c | 173 | ||||
-rw-r--r-- | src/utils.h | 36 | ||||
-rw-r--r-- | src/watch_list.c | 163 | ||||
-rw-r--r-- | src/watch_list.h | 46 |
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 |