From 1324242685b63a511be3b3625f78e1ebf2dafc76 Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@spreadspace.org>
Date: Sun, 26 Jul 2015 19:13:42 +0200
Subject: moved sources to src/


diff --git a/.gitignore b/.gitignore
index a525fc0..874fe0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,9 @@
-*.d
-*.o
-include.mk
-switchctl
-serialclient
-stdioclient
-heartbeatclient
-luaclient
+src/*.d
+src/*.o
+src/include.mk
+src/switchctl
+src/serialclient
+src/stdioclient
+src/heartbeatclient
+src/luaclient
 
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 7e7b9dc..0000000
--- a/Makefile
+++ /dev/null
@@ -1,122 +0,0 @@
-##
-##  rhctl
-##
-##  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
-##
-##  This file is part of rhctl.
-##
-##  rhctl 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.
-##
-##  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
-##
-
-ifneq ($(MAKECMDGOALS),distclean)
-include include.mk
-endif
-
-EXE_SWITCHCTL := switchctl
-EXE_SERIALCLIENT := serialclient
-EXE_STDIOCLIENT := stdioclient
-EXE_HEARTBEATCLIENT := heartbeatclient
-EXE_LUACLIENT := luaclient
-
-COMMONOBJ := log.o \
-             sig_handler.o \
-             string_list.o \
-             key_value_storage.o \
-             utils.o
-
-SWITCHCTLOBJ := command_queue.o \
-                client_list.o \
-                opt-switchctl.o \
-                switchctl.o
-
-SERIALCLIENTOBJ := opt-serialclient.o \
-                   serialclient.o
-
-STDIOCLIENTOBJ := opt-stdioclient.o \
-                  stdioclient.o
-
-HEARTBEATCLIENTOBJ := opt-heartbeatclient.o \
-                      heartbeatclient.o
-
-LUACLIENTOBJ := opt-luaclient.o \
-                l_log.o \
-                l_sig_handler.o \
-                l_cmd.o \
-                luaclient.o
-
-
-SRC := $(COMMONOBJ:%.o=%.c) $(SWITCHCTLOBJ:%.o=%.c) $(SERIALCLIENTOBJ:%.o=%.c) $(STDIOCLIENTOBJ:%.o=%.c) $(HEARTBEATCLIENTOBJ:%.o=%.c) $(LUACLIENTOBJ:%.o=%.c) options.c
-
-.PHONY: clean distclean
-
-all: $(EXE_SWITCHCTL) $(EXE_SERIALCLIENT) $(EXE_STDIOCLIENT) $(EXE_HEARTBEATCLIENT) $(EXE_LUACLIENT)
-
-%.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
-
-$(EXE_SWITCHCTL): $(COMMONOBJ) $(SWITCHCTLOBJ)
-	$(CC) $(COMMONOBJ) $(SWITCHCTLOBJ) -o $@ $(LDFLAGS)
-
-$(EXE_SERIALCLIENT): $(COMMONOBJ) $(SERIALCLIENTOBJ)
-	$(CC) $(COMMONOBJ) $(SERIALCLIENTOBJ) -o $@ $(LDFLAGS)
-
-$(EXE_STDIOCLIENT): $(COMMONOBJ) $(STDIOCLIENTOBJ)
-	$(CC) $(COMMONOBJ) $(STDIOCLIENTOBJ) -o $@ $(LDFLAGS)
-
-$(EXE_HEARTBEATCLIENT): $(COMMONOBJ) $(HEARTBEATCLIENTOBJ)
-	$(CC) $(COMMONOBJ) $(HEARTBEATCLIENTOBJ) -o $@ $(LDFLAGS)
-
-$(EXE_LUACLIENT): $(COMMONOBJ) $(LUACLIENTOBJ)
-	$(CC) $(COMMONOBJ) $(LUACLIENTOBJ) -o $@ $(LDFLAGS) $(LUA_LDFLAGS)
-
-opt-switchctl.o: options.c
-	$(CC) $(CFLAGS) -DOPT_SWITCHCTL -o $@ -c $<
-
-opt-serialclient.o: options.c
-	$(CC) $(CFLAGS) -DOPT_SERIALCLIENT -o $@ -c $<
-
-opt-stdioclient.o: options.c
-	$(CC) $(CFLAGS) -DOPT_STDIOCLIENT -o $@ -c $<
-
-opt-heartbeatclient.o: options.c
-	$(CC) $(CFLAGS) -DOPT_HEARTBEATCLIENT -o $@ -c $<
-
-opt-luaclient.o: options.c
-	$(CC) $(CFLAGS) -DOPT_LUACLIENT -o $@ -c $<
-
-%.o: %.c
-	$(CC) $(CFLAGS) -c $<
-
-
-distclean: clean
-	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 $(EXE_SWITCHCTL)
-	rm -f $(EXE_SERIALCLIENT)
-	rm -f $(EXE_STDIOCLIENT)
-	rm -f $(EXE_HEARTBEATCLIENT)
-	rm -f $(EXE_LUACLIENT)
\ No newline at end of file
diff --git a/client_list.c b/client_list.c
deleted file mode 100644
index 141a3a4..0000000
--- a/client_list.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "client_list.h"
-#include "datatypes.h"
-
-char* client_type_tostring(client_type_t type)
-{
-  switch(type)
-  {
-  case MASTER: return "master";
-  case STANDBY: return "standby";
-  case HB_MASTER: return "hb_master";
-  case HB_STANDBY: return "hb_standby";
-  case DEFAULT: return "unspecified";
-  }
-  return "<invalid>";
-}
-
-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->type = DEFAULT;
-  new_client->request_listener = 0;
-  new_client->mode_listener = 0;
-  new_client->status_listener = 0;
-  new_client->gpi_listener = 0;
-  new_client->oc_listener = 0;
-  new_client->relay_listener = 0;
-  new_client->silence_listener = 0;
-  new_client->health_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/client_list.h b/client_list.h
deleted file mode 100644
index 2f3d5f2..0000000
--- a/client_list.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_client_list_h_INCLUDED
-#define RHCTL_client_list_h_INCLUDED
-
-#include "datatypes.h"
-
-enum client_type_enum { DEFAULT, MASTER, STANDBY, HB_MASTER, HB_STANDBY };
-typedef enum client_type_enum client_type_t;
-char* client_type_tostring(client_type_t);
-
-struct client_struct {
-  int fd;
-  client_type_t type;
-  int request_listener;
-  int mode_listener;
-  int status_listener;
-  int gpi_listener;
-  int oc_listener;
-  int relay_listener;
-  int silence_listener;
-  int health_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/command_queue.c b/command_queue.c
deleted file mode 100644
index b72d05e..0000000
--- a/command_queue.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "command_queue.h"
-#include "datatypes.h"
-
-cmd_t* cmd_get_last(cmd_t* first)
-{
-  if(!first)
-    return NULL;
-
-  while(first->next) {
-    first = first->next;
-  }
-
-  return first;
-}
-
-int cmd_push(cmd_t** first, int fd, cmd_id_t cmd, const char* param)
-{
-  if(!first)
-    return -1;
-
-  cmd_t* new_cmd = malloc(sizeof(cmd_t));
-  if(!new_cmd)
-    return -2;
-
-  new_cmd->fd = fd;
-  new_cmd->cmd = cmd;
-  if(param) {
-    new_cmd->param = strdup(param);
-    if(!new_cmd->param) {
-      free(new_cmd);
-      return -2;
-    }
-  }
-  else
-    new_cmd->param = NULL;
-  new_cmd->sent = 0;
-  new_cmd->tv_start.tv_sec = 0;
-  new_cmd->tv_start.tv_usec = 0;
-  new_cmd->next = NULL;
-
-  if(!(*first)) {
-    *first = new_cmd;
-    return 0;
-  }
-
-  cmd_get_last(*first)->next = new_cmd;
-
-  return 0;
-}
-
-void cmd_sent(cmd_t* cmd)
-{
-  if(!cmd)
-    return;
-
-  cmd->sent = 1;
-  gettimeofday(&cmd->tv_start, NULL);
-}
-
-int cmd_has_expired(cmd_t cmd)
-{
-  struct timeval now;
-  timerclear(&now);
-  gettimeofday(&now, NULL);
-  cmd.tv_start.tv_sec++;
-
-  return timercmp(&cmd.tv_start, &now, <);
-}
-
-void cmd_pop(cmd_t** first)
-{
-  if(!first || !(*first))
-    return;
-
-  cmd_t* deletee = *first;
-  *first = (*first)->next;
-  if(deletee->param)
-    free(deletee->param);
-  free(deletee);
-}
-
-void cmd_clear(cmd_t** first)
-{
-  if(!first || !(*first))
-    return;
-
-  while(*first) {
-    cmd_t* deletee = *first;
-    *first = (*first)->next;
-    if(deletee->param)
-      free(deletee->param);
-    free(deletee);
-  }
-}
diff --git a/command_queue.h b/command_queue.h
deleted file mode 100644
index 4cdcf9f..0000000
--- a/command_queue.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_command_queue_h_INCLUDED
-#define RHCTL_command_queue_h_INCLUDED
-
-#include <sys/time.h>
-
-enum cmd_id_enum { SWITCH, CHANNEL, TYPE, MODE, HEARTBEAT, STATUS, HEALTH, LOG, LISTEN };
-typedef enum cmd_id_enum cmd_id_t;
-
-struct cmd_struct {
-  int fd;
-  cmd_id_t cmd;
-  char* param;
-  int sent;
-  struct timeval tv_start;
-  struct cmd_struct* next;
-};
-typedef struct cmd_struct cmd_t;
-
-int cmd_push(cmd_t** first, int fd, cmd_id_t cmd, const char* param);
-void cmd_sent(cmd_t* cmd);
-int cmd_has_expired(cmd_t cmd);
-void cmd_pop(cmd_t** first);
-void cmd_clear(cmd_t** first);
-
-#endif
diff --git a/configure b/configure
deleted file mode 100755
index 83fe4d3..0000000
--- a/configure
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/bin/sh
-#
-#  rhctl
-#
-#  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
-#
-#  This file is part of rhctl.
-#
-#  rhctl 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.
-#
-#  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
-#
-
-TARGET=`uname -s`
-
-USE_CLANG=0
-
-LUA_LDFLAGS='-lm'
-LUA_DIR=''
-
-print_usage() {
-  echo "configure --help                    print this"
-  echo "          --target=<TARGET>         build target i.e. Linux (default: autodetect)"
-  echo "          --with-lua=<DIR>          use this lua tree instead of system default"
-  echo "          --use-clang               use clang/llvm as compiler/linker"
-}
-
-for arg
-do
-  case $arg in
-  --target=*)
-    TARGET=${arg#--target=}
-  ;;
-  --use-clang)
-    USE_CLANG=1
-  ;;
-  --with-lua=*)
-    LUA_DIR=${arg#--with-lua=}
-  ;;
-  --help)
-    print_usage
-    exit 0
-  ;;
-  *)
-    echo "Unknown argument: $arg"
-    print_usage
-    exit 1
-  ;;
-  esac
-done
-
-if [ $USE_CLANG -eq 0 ]; then
-  CFLAGS='-g -Wall -O2'
-  LDFLAGS='-g -Wall -O2'
-  COMPILER='gcc'
-else
-  CFLAGS='-g -O2'
-  LDFLAGS='-g -O2'
-  COMPILER='clang'
-fi
-
-rm -f include.mk
-case $TARGET in
-  Linux)
-    echo "Linux specific build options"
-    LUA_LDFLAGS=$LUA_LDFLAGS' -ldl'
-  ;;
-  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
-
-test_lua_version()
-{
-  LUA_VERSION=`cat $1 | grep "#define LUA_VERSION[ 	]" | cut -f2- | tr -d '"' | sed -e 's/Lua \([0-9][0-9.]*\)/\1/'`
-  LUA_VERSION_NUM=`cat $1 | grep "#define LUA_VERSION_NUM" | awk '{ print $3 }'`
-  LUA_RELEASE=`cat $1 | grep "#define LUA_RELEASE[ 	]" | cut -f2-`
-
-  if [ $LUA_VERSION_NUM -ge 501 ]; then
-    return 1;
-  else
-    return 0;
-  fi
-}
-
-if [ -z "$LUA_DIR" ]; then
-  for prefix in /usr /usr/local; do
-    if [ -e $prefix/include/lua.h ]; then
-      test_lua_version $prefix/include/lua.h
-      if [ $? -eq 1 ]; then
-        echo "using Lua $LUA_VERSION ($LUA_RELEASE) found at $prefix/include"
-        CFLAGS="$CFLAGS -I$prefix/include"
-        LUA_LDFLAGS="$LUA_LDFLAGS -L$prefix/lib -llua"
-        LUA_DIR=found
-        break
-      fi
-    else
-      for dir in `ls -d $prefix/include/lua* 2> /dev/null`; do
-        if [ -e $dir/lua.h ]; then
-          test_lua_version $dir/lua.h
-          if [ $? -eq 1 ]; then
-            echo "using Lua $LUA_VERSION ($LUA_RELEASE) found at $dir"
-            CFLAGS="$CFLAGS -I$dir"
-            if [ -x "$prefix/bin/lua$LUA_VERSION" ]; then
-              LUA_LDFLAGS="$LUA_LDFLAGS -L$prefix/lib -llua$LUA_VERSION"
-              LUA_DIR=found
-            elif [ -x "$prefix/bin/lua-$LUA_VERSION" ]; then
-              LUA_LDFLAGS="$LUA_LDFLAGS -L$prefix/lib -llua-$LUA_VERSION"
-              LUA_DIR=found
-            else
-              echo "ERROR: found lua.h at $dir/lua.h but no matching lua library"
-              return 1
-            fi
-            break
-          fi
-        fi
-      done
-      if [ -n "$LUA_DIR" ]; then
-        break
-      fi
-    fi
-  done
-
-  if [ -z "$LUA_DIR" ]; then
-    echo "ERROR: no suitable lua found .. please install lua 5.1 or higher or use --with-lua"
-    return 1
-  fi
-
-else
-  CFLAGS="$CFLAGS -I$LUA_DIR/include"
-  LUA_LDFLAGS="$LUA_LDFLAGS $LUA_DIR/lib/liblua.a"
-fi
-
-
-cat >> include.mk <<EOF
-# this file was created automatically
-# do not edit this file directly
-# use ./configure instead
-
-TARGET := $TARGET
-CC := $COMPILER
-CFLAGS := $CFLAGS
-LDFLAGS := $LDFLAGS
-LUA_LDFLAGS := $LUA_LDFLAGS
-EOF
-
-exit 0
diff --git a/daemon.h b/daemon.h
deleted file mode 100644
index 9e6f1b2..0000000
--- a/daemon.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- *  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-2010 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, "unknown 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, "unknown 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 program 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;
-  }
-
-  return 0;
-}
-
-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/datatypes.h b/datatypes.h
deleted file mode 100644
index 3a72adc..0000000
--- a/datatypes.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_datatypes_h_INCLUDED
-#define RHCTL_datatypes_h_INCLUDED
-
-#define _GNU_SOURCE
-#include <stdint.h>
-#include <arpa/inet.h>
-
-typedef uint8_t bool;
-#define FALSE 0
-#define TRUE 1
-
-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[100];
-};
-typedef struct read_buffer_struct read_buffer_t;
-
-#endif
diff --git a/health-watch.lua b/health-watch.lua
deleted file mode 100644
index 082c19d..0000000
--- a/health-watch.lua
+++ /dev/null
@@ -1,107 +0,0 @@
---
---  rhctl
---
---  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
---
---  This file is part of rhctl.
---
---  rhctl 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.
---
---  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
---
-
-package.path = package.path .. ';/usr/share/rhctl/?.lua'
-
-socket = require("socket")
-utils = require("utils")
-
-old_state = {}
-current_state = {}
-
-function state_changed()
-   for k,v in pairs(old_state) do
-       if v ~= current_state[k] then
-           return true
-       end
-   end
-   for k,v in pairs(current_state) do
-       if old_state[k] == nil then
-           return true
-       end
-   end
-   return false
-end
-
-function process_cmd(message)
-   local exps = { hs = "^Health: (%a+)$", mc = "^Master: (%a+)$", sc = "^Standby: (%a+)$", hbm = "^Hearbeat Master: (%a+, %a+)$", hbs = "^Hearbeat Standby: (%a+, %a+)$" }
-   for key, exp in pairs(exps) do
-      local state = string.match(message, exp)
-      if(state) then
-         if(key == 'hs') then
-            current_state = {}
-         end
-         current_state[key] = state
-      end
-   end
-
-   if(current_state['hbs'] ~= nil) then
-      if(state_changed()) then
-         body = "RHCTL Health Output:\n\n"
-         body = body .. "Health: " .. current_state['hs'] .. "\n"
-         body = body .. "Master: " .. current_state['mc'] .. "\n"
-         body = body .. "Standby: " .. current_state['sc'] .. "\n"
-         body = body .. "Master Hearbeat: " .. current_state['hbm'] .. "\n"
-         body = body .. "Standby Hearbeat: " .. current_state['hbs'] .. "\n"
-         utils.send_mail("logs@helsinki.at", "[RHCTL] health: " .. current_state['hs'], body)
-         old_state = current_state
-      end
-   end
-
-   return 0
-end
-
-function main_loop(opt)
-   log.printf(log.NOTICE, "main_loop started")
-   local sig = signal.init()
-   local cmdfd = cmd.init()
-
-   cmd.send_string("listen health");
-   cmd.send_string("health");
-
-   local return_value = 0
-   while return_value == 0 do
-      local readable, _, err = socket.select({ sig , cmdfd }, nil)
-      if(err) then
-         log.printf(log.ERROR, "select returned with error: %s", err)
-         return_value = -1
-      else
-         for _, input in ipairs(readable) do
-            if(input == sig) then
-               return_value = signal.handle()
-               if(return_value == 1) then break end
-            else
-               if(input == cmdfd) then
-                  return_value = cmd.recv_data(process_cmd)
-                  if(return_value ~= 0) then break end
-               else
-                  log.printf(log.ERROR, "select returned invalid handle??")
-                  return_value = -1
-                  break;
-               end
-            end
-         end
-      end
-   end
-
-   signal.stop()
-   return return_value
-end
diff --git a/heartbeatclient.c b/heartbeatclient.c
deleted file mode 100644
index 3fe8423..0000000
--- a/heartbeatclient.c
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "datatypes.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include "log.h"
-#include "sig_handler.h"
-#include "options.h"
-
-#include "daemon.h"
-#include "utils.h"
-
-int process_serial(read_buffer_t* buffer, int serial_fd, int led_pipe_fd, bool *state)
-{
-  int ret = 0;
-  struct timeval tv;
-  fd_set fds;
-  FD_ZERO(&fds);
-  FD_SET(serial_fd, &fds);
-
-  for(;;) {
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-    ret = select(serial_fd+1, &fds, NULL, NULL, &tv);
-    if(!ret)
-      return 0;
-    else if(ret < 0)
-      return ret;
-
-    ret = read(serial_fd, &buffer->buf[buffer->offset], 1);
-    if(!ret)
-      return 2;
-    if(ret == -1 && errno == EAGAIN)
-      return 0;
-    else if(ret < 0)
-      break;
-
-    if(buffer->buf[buffer->offset] == '\n') {
-      buffer->buf[buffer->offset] = 0;
-
-      if(buffer->offset > 0 && buffer->buf[buffer->offset-1] == '\r')
-        buffer->buf[buffer->offset-1] = 0;
-
-      if(strlen((char *)(buffer->buf))) {
-        *state = TRUE;
-        if(led_pipe_fd >= 0) {
-          char buf = '1';
-          ret = write(led_pipe_fd, &buf, 1);
-          if(ret == -1 && errno != EAGAIN)
-            log_printf(WARNING, "write to led process pipe returned with error: %s", strerror(errno));
-        }
-      }
-
-      buffer->offset = 0;
-      return 0;
-    }
-
-    buffer->offset++;
-    if(buffer->offset >= sizeof(buffer->buf)) {
-      log_printf(DEBUG, "string too long (fd=%d)", serial_fd);
-      buffer->offset = 0;
-      return 0;
-    }
-  }
-
-  return ret;
-}
-
-int main_loop(int serial_fd, int cmd_fd, int led_pipe_fd, options_t* opt)
-{
-  log_printf(NOTICE, "entering main loop");
-
-  fd_set readfds, tmpfds;
-  FD_ZERO(&readfds);
-  FD_SET(serial_fd, &readfds);
-  FD_SET(cmd_fd, &readfds);
-  int max_fd = serial_fd > cmd_fd ? serial_fd : cmd_fd;
-
-  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;
-
-  int return_value = 0;
-  return_value = send_string(cmd_fd, "type heartbeat\n");
-  if(return_value <= 0) {
-    log_printf(ERROR, "error setting client type");
-    return_value = -1;
-  }
-  else {
-    log_printf(NOTICE, "connecting as type heartbeat");
-    return_value = 0;
-  }
-
-  bool state = FALSE;
-  send_string(cmd_fd, "heartbeat 0\n");
-
-  read_buffer_t serial_buffer;
-  serial_buffer.offset = 0;
-  struct timeval timeout;
-  u_int32_t time = 0;
-  while(!return_value) {
-    memcpy(&tmpfds, &readfds, sizeof(tmpfds));
-
-    timeout.tv_sec = 0;
-    timeout.tv_usec = 100000;
-    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) {
-      time++;
-      if(time >= opt->timeout_) {
-        if(state == TRUE) {
-          log_printf(WARNING, "heartbeat timeout");
-          send_string(cmd_fd, "heartbeat 0\n");
-          state = FALSE;
-        }
-        time = 0;
-      }
-      continue;
-    }
-
-    if(FD_ISSET(sig_fd, &tmpfds))
-      if(signal_handle())
-        return_value = 1;
-
-    if(FD_ISSET(serial_fd, &tmpfds)) {
-      bool old_state = state;
-      return_value = process_serial(&serial_buffer, serial_fd, led_pipe_fd, &state);
-      if(return_value)
-        break;
-      if(state) {
-        if(!old_state) {
-          log_printf(WARNING, "heartbeat returned");
-          send_string(cmd_fd, "heartbeat 1\n");
-        }
-        time = 0;
-      }
-    }
-
-    if(FD_ISSET(cmd_fd, &tmpfds)) {
-      u_int8_t buf[100];
-      int ret = recv(cmd_fd, buf, 100, 0);
-      if(!ret)
-        return_value = 3;
-      if(ret == -1 && errno == EAGAIN)
-        return_value = 0;
-      else if(ret < 0)
-        return_value = ret;
-    }
-  }
-
-  signal_stop();
-  return return_value;
-}
-
-int led_process(int pipe_fd, int led_fd)
-{
-  struct timeval timeout;
-  char buf;
-  int ret;
-
-  log_printf(INFO, "led_process: starting up");
-  for(;;) {
-    ret = read(pipe_fd, &buf, 1);
-    if(ret == -1 && errno == EAGAIN)
-      continue;
-    else if(!ret || ret < 0) {
-      log_printf(ERROR, "led_process: read returned with error: %s", strerror(errno));
-      break;
-    }
-
-    buf = '1';
-    ret = write(led_fd, &buf, 1);
-    if(ret == -1 && errno != EAGAIN) {
-      log_printf(ERROR, "led_process: write returned with error: %s", strerror(errno));
-      break;
-    }
-
-    timeout.tv_sec = 0;
-    timeout.tv_usec = 100000;
-    ret = select(0, NULL, NULL, NULL, &timeout);
-    if(ret == -1 && errno != EINTR) {
-      log_printf(ERROR, "led_process: select returned with error: %s", strerror(errno));
-      break;
-    }
-
-    buf = '0';
-    ret = write(led_fd, &buf, 1);
-    if(ret == -1 && errno != EAGAIN) {
-      log_printf(ERROR, "led_process: write returned with error: %s", strerror(errno));
-      break;
-    }
-  }
-  log_printf(INFO, "led_process: stopped");
-  return -1;
-}
-
-int start_led_process(options_t* opt)
-{
-  int pipefd[2];
-  int led_fd;
-  pid_t cpid;
-
-  led_fd = open(opt->led_filename_, O_WRONLY);
-  if(led_fd < 0) {
-    log_printf(ERROR, "led_process: open() failed: %s", strerror(errno));
-    return -2;
-  }
-
-  if (pipe(pipefd) == -1) {
-    log_printf(ERROR, "led_process: pipe() failed: %s", strerror(errno));
-    close(led_fd);
-    return -2;
-  }
-
-  cpid = fork();
-  if (cpid == -1) {
-    log_printf(ERROR, "led_process: fork() failed: %s", strerror(errno));
-    close(pipefd[0]);
-    close(pipefd[1]);
-    close(led_fd);
-    return -2;
-  }
-
-  if (cpid == 0) {
-    close(pipefd[1]);
-    return led_process(pipefd[0], led_fd);
-  }
-
-  close(pipefd[0]);
-  close(led_fd);
-  return pipefd[1];
-}
-
-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 == -5) {
-      fprintf(stderr, "syntax error: invalid baudrate\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,heartbeatclient,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 led_pipe_fd = -1;
-  if(opt.led_filename_) {
-    log_printf(NOTICE, "starting led blink process");
-    led_pipe_fd = start_led_process(&opt);
-    if(led_pipe_fd == -1) {
-      options_clear(&opt);
-      log_close();
-      exit(0);
-    }
-    if(led_pipe_fd < -1) {
-      options_clear(&opt);
-      log_close();
-      exit(-1);
-    }
-  }
-
-  int cmd_fd = 0;
-  int serial_fd = 0;
-  for(;;) {
-    cmd_fd = connect_command_socket(opt.command_sock_);
-    if(cmd_fd < 0)
-      ret = 3;
-    else {
-      serial_fd = open(opt.serial_dev_, O_RDWR | O_NOCTTY);
-      if(serial_fd < 0)
-        ret = 2;
-      else {
-        ret = setup_tty(serial_fd, opt.baudrate_);
-        if(ret)
-          ret = 2;
-        else
-          ret = main_loop(serial_fd, cmd_fd, led_pipe_fd, &opt);
-      }
-    }
-
-    if(ret == 2 || ret == 3) {
-      if(ret == 2)
-        log_printf(ERROR, "%s error, trying to reopen in 5 seconds..", opt.serial_dev_);
-      if(ret == 3)
-        log_printf(ERROR, "socket error, trying to reconnect in 5 seconds..");
-
-      if(cmd_fd > 0)
-        close(cmd_fd);
-      if(serial_fd > 0)
-        close(serial_fd);
-      sleep(5);
-    }
-    else
-      break;
-  }
-
-  if(cmd_fd > 0)
-    close(cmd_fd);
-  if(serial_fd > 0)
-    close(serial_fd);
-
-  if(!ret)
-    log_printf(NOTICE, "normal shutdown");
-  else if(ret < 0)
-    log_printf(NOTICE, "shutdown after error (code %d)", ret);
-  else
-    log_printf(NOTICE, "shutdown after signal");
-
-  options_clear(&opt);
-  log_close();
-
-  return ret;
-}
diff --git a/key_value_storage.c b/key_value_storage.c
deleted file mode 100644
index c6341e6..0000000
--- a/key_value_storage.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "key_value_storage.h"
-
-void key_value_storage_init(key_value_storage_t* stor)
-{
-  if(!stor)
-    return;
-
-  string_list_init(&(stor->keys_));
-  string_list_init(&(stor->values_));
-}
-
-void key_value_storage_clear(key_value_storage_t* stor)
-{
-  if(!stor)
-    return;
-
-  string_list_clear(&(stor->keys_));
-  string_list_clear(&(stor->values_));
-}
-
-int key_value_storage_add(key_value_storage_t* stor, const char* key, const char* value)
-{
-  if(!stor || !key || !value)
-    return -1;
-
-  int ret = string_list_add(&(stor->keys_), key);
-  if(ret!=0)
-    return ret;
-
-  ret = string_list_add(&(stor->values_), value);
-  if(ret!=0)
-    return ret;
-
-  return 0;
-}
-
-char* key_value_storage_find(key_value_storage_t* stor, const char* key)
-{
-  if(!stor || !key)
-    return NULL;
-
-  string_list_element_t* k = stor->keys_.first_;
-  string_list_element_t* v = stor->values_.first_;
-  while(v && k) {
-    if(!strcmp(k->string_, key))
-      return v->string_;
-
-    k = k->next_;
-    v = v->next_;
-  }
-
-  return NULL;
-}
-
-void key_value_storage_print(key_value_storage_t* stor, const char* head, const char* sep, const char* tail)
-{
-  if(!stor)
-    return;
-
-  string_list_element_t* k = stor->keys_.first_;
-  string_list_element_t* v = stor->values_.first_;
-  while(v && k) {
-    printf("%s%s%s%s%s", head, k->string_, sep, v->string_, tail);
-    k = k->next_;
-    v = v->next_;
-  }
-  printf("\n");
-}
diff --git a/key_value_storage.h b/key_value_storage.h
deleted file mode 100644
index 851897f..0000000
--- a/key_value_storage.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_key_value_storage_h_INCLUDED
-#define RHCTL_key_value_storage_h_INCLUDED
-
-#include "string_list.h"
-
-struct key_value_storage_struct {
-  string_list_t keys_;
-  string_list_t values_;
-};
-typedef struct key_value_storage_struct key_value_storage_t;
-
-void key_value_storage_init(key_value_storage_t* stor);
-void key_value_storage_clear(key_value_storage_t* stor);
-int key_value_storage_add(key_value_storage_t* stor, const char* key, const char* value);
-char* key_value_storage_find(key_value_storage_t* stor, const char* key);
-
-void key_value_storage_print(key_value_storage_t* stor, const char* head, const char* sep, const char* tail);
-
-
-#endif
diff --git a/l_cmd.c b/l_cmd.c
deleted file mode 100644
index 86bc872..0000000
--- a/l_cmd.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <lua.h>
-#include <lauxlib.h>
-
-#include <unistd.h>
-#include <sys/socket.h>
-#include <errno.h>
-
-#include "datatypes.h"
-
-#include "l_cmd.h"
-
-#include "options.h"
-#include "command_queue.h"
-#include "client_list.h"
-#include "log.h"
-#include "utils.h"
-
-int cmd_fd;
-read_buffer_t cmd_buffer;
-
-static int l_cmd_getfd(lua_State *L)
-{
-  if(!lua_istable(L, -1))
-    luaL_error(L, "can't retreive signal fd");
-
-  lua_pushliteral(L, "fd");
-  lua_gettable(L, -2);
-  return 1;
-}
-
-static int l_cmd_dirty(lua_State *L)
-{
-  lua_pushboolean(L, 0);
-  return 1;
-}
-
-static int l_cmd_init(lua_State *L)
-{
-  lua_newtable(L);
-  lua_pushliteral(L, "fd");
-  lua_pushinteger(L, cmd_fd);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "getfd");
-  lua_pushcfunction(L, l_cmd_getfd);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "dirty");
-  lua_pushcfunction(L, l_cmd_dirty);
-  lua_settable(L, -3);
-  return 1;
-}
-
-// gets called by nonblock_recvline for every complete message
-static int cmd_recv_data(lua_State* L)
-{
-  int ret = 0;
-  for(;;) {
-    ret = recv(cmd_fd, &cmd_buffer.buf[cmd_buffer.offset], 1, 0);
-    if(!ret)
-      return 2;
-    if(ret == -1 && errno == EAGAIN)
-      return 0;
-    else if(ret < 0)
-      break;
-
-    if(cmd_buffer.buf[cmd_buffer.offset] == '\n') {
-      cmd_buffer.buf[cmd_buffer.offset] = 0;
-
-      lua_pushstring(L, (char *)(cmd_buffer.buf));
-
-      ret = lua_pcall(L, 1, 1, 0);
-      if(ret) {
-        const char* err_str = luaL_checkstring(L, -1);
-        switch(ret) {
-        case LUA_ERRRUN: log_printf(ERROR, "lua_pcall(cmd_callback) runtime error: %s", err_str); break;
-        case LUA_ERRMEM: log_printf(ERROR, "lua_pcall(cmd_callback) malloc error: %s", err_str); break;
-        case LUA_ERRERR: log_printf(ERROR, "lua_pcall(cmd_callback) error at error handler function: %s", err_str); break;
-        }
-        return -1;
-      }
-
-      ret = lua_tointeger(L, 1);
-      cmd_buffer.offset = 0;
-      break;
-    }
-
-    cmd_buffer.offset++;
-    if(cmd_buffer.offset >= sizeof(cmd_buffer.buf)) {
-      log_printf(DEBUG, "string too long cmd_fd");
-      cmd_buffer.offset = 0;
-      return 0;
-    }
-  }
-
-  return ret;
-}
-
-static int l_cmd_recv_data(lua_State* L)
-{
-  int ret = cmd_recv_data(L);
-  lua_pushinteger(L, ret);
-  return 1;
-}
-
-static int l_cmd_send_string(lua_State* L)
-{
-  int ret = send_string(cmd_fd, luaL_checkstring(L,1));
-  if (ret > 0)
-    do {
-      ret = write(cmd_fd, "\n", 1);
-    } while(!ret || (ret == -1 && errno == EINTR));
-
-  if(ret > 0)
-    ret = 0;
-
-  lua_pushinteger(L, ret);
-  return 1;
-}
-
-static const struct luaL_reg cmd_funcs [] = {
-  { "init", l_cmd_init },
-  { "recv_data", l_cmd_recv_data },
-  { "send_string", l_cmd_send_string },
-  { NULL, NULL }
-};
-
-
-LUALIB_API int luaopen_cmd(lua_State *L)
-{
-  luaL_register(L, LUA_CMDLIBNAME, cmd_funcs);
-  return 1;
-}
diff --git a/l_cmd.h b/l_cmd.h
deleted file mode 100644
index 8c3652c..0000000
--- a/l_cmd.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_l_cmd_h_INCLUDED
-#define RHCTL_l_cmd_h_INCLUDED
-
-#include <lua.h>
-
-extern int cmd_fd;
-
-#define LUA_CMDLIBNAME "cmd"
-LUALIB_API int luaopen_cmd(lua_State *L);
-
-#endif
diff --git a/l_log.c b/l_log.c
deleted file mode 100644
index 4ccf18d..0000000
--- a/l_log.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  anylike
- *
- *  anylike is an IKEv2 Implementation written in Lua and C. It's main
- *  design goal is to provide anytun and uanytun or any other SATP
- *  implementation with a key exchange mechanism but it should also be
- *  possible to use anylike as key exchange daemon for IPSec security
- *  associations. The use of Lua guarantees that anylike is easily
- *  portable to many platforms including very small ones like wireless
- *  routers.
- *
- *
- *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
- *                          Christian Pointner <equinox@anylike.org>
- *
- *  This file is part of anylike.
- *
- *  anylike 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.
- *
- *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <lua.h>
-#include <lauxlib.h>
-
-#include <stdlib.h>
-
-#include "datatypes.h"
-#include "log.h"
-
-#include "l_log.h"
-
-static int l_log_init(lua_State *L)
-{
-  log_init();
-  return 0;
-}
-
-static int l_log_close(lua_State *L)
-{
-  log_close();
-  return 0;
-}
-
-static int l_log_add_target(lua_State *L)
-{
-  int ret = log_add_target(luaL_checkstring(L,1));
-  if(ret) {
-    lua_pushboolean(L, 0);
-    switch(ret) {
-    case -2: lua_pushstring(L, "memory error at log_add_target"); break;
-    case -3: lua_pushstring(L, "unknown log target"); break;
-    case -4: lua_pushstring(L, "this log target is only allowed once"); break;
-    default: lua_pushstring(L, "syntax error"); break;
-    }
-    return 2;
-  }
-
-  lua_pushboolean(L, 1);
-  return 1;
-}
-
-static int l_log_printf(lua_State *L)
-{
-  int numargs = lua_gettop(L);
-  if(numargs < 2)
-    return luaL_error(L, "log.printf too few arguments");
-
-  if(numargs > 2) {
-    lua_getglobal(L, "string");
-    lua_pushliteral(L, "format");
-    lua_gettable(L, -2);
-    lua_insert(L, 2);
-    lua_remove(L, -1);
-    lua_call(L, numargs - 1, 1);
-  }
-
-  log_prio_t prio = luaL_checkint(L,1);
-  log_printf(prio, "%s", luaL_checkstring(L, 2));
-  return 0;
-}
-
-static const struct luaL_reg log_funcs [] = {
-  { "init", l_log_init },
-  { "close", l_log_close },
-  { "add_target", l_log_add_target },
-  { "printf", l_log_printf },
-  { NULL, NULL }
-};
-
-
-LUALIB_API int luaopen_log(lua_State *L)
-{
-  luaL_register(L, LUA_LOGLIBNAME, log_funcs);
-  lua_pushliteral(L, "ERROR");
-  lua_pushinteger(L, ERROR);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "WARNING");
-  lua_pushinteger(L, WARNING);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "NOTICE");
-  lua_pushinteger(L, NOTICE);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "INFO");
-  lua_pushinteger(L, INFO);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "DEBUG");
-  lua_pushinteger(L, DEBUG);
-  lua_settable(L, -3);
-  return 1;
-}
diff --git a/l_log.h b/l_log.h
deleted file mode 100644
index c564c62..0000000
--- a/l_log.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  anylike
- *
- *  anylike is an IKEv2 Implementation written in Lua and C. It's main
- *  design goal is to provide anytun and uanytun or any other SATP
- *  implementation with a key exchange mechanism but it should also be
- *  possible to use anylike as key exchange daemon for IPSec security
- *  associations. The use of Lua guarantees that anylike is easily
- *  portable to many platforms including very small ones like wireless
- *  routers.
- *
- *
- *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
- *                          Christian Pointner <equinox@anylike.org>
- *
- *  This file is part of anylike.
- *
- *  anylike 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.
- *
- *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ANYLIKE_l_log_h_INCLUDED
-#define ANYLIKE_l_log_h_INCLUDED
-
-#include <lua.h>
-
-#define LUA_LOGLIBNAME "log"
-LUALIB_API int luaopen_log(lua_State *L);
-
-#endif
diff --git a/l_sig_handler.c b/l_sig_handler.c
deleted file mode 100644
index 1be30ce..0000000
--- a/l_sig_handler.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- *  anylike
- *
- *  anylike is an IKEv2 Implementation written in Lua and C. It's main
- *  design goal is to provide anytun and uanytun or any other SATP
- *  implementation with a key exchange mechanism but it should also be
- *  possible to use anylike as key exchange daemon for IPSec security
- *  associations. The use of Lua guarantees that anylike is easily
- *  portable to many platforms including very small ones like wireless
- *  routers.
- *
- *
- *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
- *                          Christian Pointner <equinox@anylike.org>
- *
- *  This file is part of anylike.
- *
- *  anylike 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.
- *
- *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <lua.h>
-#include <lauxlib.h>
-
-#include "sig_handler.h"
-
-#include "l_sig_handler.h"
-
-static int l_signal_getfd(lua_State *L)
-{
-  if(!lua_istable(L, -1))
-    luaL_error(L, "can't retreive signal fd");
-
-  lua_pushliteral(L, "fd");
-  lua_gettable(L, -2);
-  return 1;
-}
-
-static int l_signal_dirty(lua_State *L)
-{
-  lua_pushboolean(L, 0);
-  return 1;
-}
-
-static int l_signal_init(lua_State *L)
-{
-  int sig_fd = signal_init();
-  if(sig_fd < 0)
-    luaL_error(L, "error at signal init");
-
-  lua_newtable(L);
-  lua_pushliteral(L, "fd");
-  lua_pushinteger(L, sig_fd);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "getfd");
-  lua_pushcfunction(L, l_signal_getfd);
-  lua_settable(L, -3);
-  lua_pushliteral(L, "dirty");
-  lua_pushcfunction(L, l_signal_dirty);
-  lua_settable(L, -3);
-  return 1;
-}
-
-static int l_signal_stop(lua_State *L)
-{
-  signal_stop();
-  return 0;
-}
-
-static int l_signal_handle(lua_State *L)
-{
-  int ret = signal_handle();
-  lua_pushinteger(L, ret);
-  return 1;
-}
-
-static const struct luaL_reg signal_funcs [] = {
-  { "init", l_signal_init },
-  { "stop", l_signal_stop },
-  { "handle", l_signal_handle },
-  { NULL, NULL }
-};
-
-
-LUALIB_API int luaopen_signal(lua_State *L)
-{
-  luaL_register(L, LUA_SIGNALLIBNAME, signal_funcs);
-  return 1;
-}
diff --git a/l_sig_handler.h b/l_sig_handler.h
deleted file mode 100644
index 447b95e..0000000
--- a/l_sig_handler.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  anylike
- *
- *  anylike is an IKEv2 Implementation written in Lua and C. It's main
- *  design goal is to provide anytun and uanytun or any other SATP
- *  implementation with a key exchange mechanism but it should also be
- *  possible to use anylike as key exchange daemon for IPSec security
- *  associations. The use of Lua guarantees that anylike is easily
- *  portable to many platforms including very small ones like wireless
- *  routers.
- *
- *
- *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
- *                          Christian Pointner <equinox@anylike.org>
- *
- *  This file is part of anylike.
- *
- *  anylike 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.
- *
- *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ANYLIKE_l_signal_handler_h_INCLUDED
-#define ANYLIKE_l_signal_handler_h_INCLUDED
-
-#include <lua.h>
-
-#define LUA_SIGNALLIBNAME "signal"
-LUALIB_API int luaopen_signal(lua_State *L);
-
-#endif
diff --git a/log.c b/log.c
deleted file mode 100644
index 661f9a2..0000000
--- a/log.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- *  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-2010 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 <ctype.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;
-    char* 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/log.h b/log.h
deleted file mode 100644
index e9f35dc..0000000
--- a/log.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *  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-2010 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/log_targets.h b/log_targets.h
deleted file mode 100644
index b6c3ae5..0000000
--- a/log_targets.h
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- *  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-2010 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/luaclient.c b/luaclient.c
deleted file mode 100644
index 901d509..0000000
--- a/luaclient.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "datatypes.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-#include "l_log.h"
-#include "l_sig_handler.h"
-#include "l_cmd.h"
-
-#include "log.h"
-#include "options.h"
-
-#include "daemon.h"
-#include "utils.h"
-
-
-#define LUA_MAIN_LOOP_FUNC "main_loop"
-
-static const luaL_Reg anylike_lualibs[] = {
-  {"", luaopen_base},
-  {LUA_LOADLIBNAME, luaopen_package},
-  {LUA_TABLIBNAME, luaopen_table},
-  {LUA_STRLIBNAME, luaopen_string},
-  {LUA_MATHLIBNAME, luaopen_math},
-  {LUA_IOLIBNAME, luaopen_io},
-  {LUA_OSLIBNAME, luaopen_os},
-  {LUA_LOGLIBNAME, luaopen_log},
-  {LUA_SIGNALLIBNAME, luaopen_signal},
-  {LUA_CMDLIBNAME, luaopen_cmd},
-  {NULL, NULL}
-};
-
-int init_main_loop(lua_State *L, const char* filename)
-{
-  const luaL_Reg *lib = anylike_lualibs;
-  for (; lib->func; lib++) {
-    lua_pushcfunction(L, lib->func);
-    lua_pushstring(L, lib->name);
-    lua_call(L, 1, 0);
-  }
-
-  int ret = luaL_loadfile(L, filename);
-  if(ret) {
-    const char* err_str = luaL_checkstring(L, -1);
-    switch(ret) {
-    case LUA_ERRSYNTAX: log_printf(ERROR, "luaL_loadfile(%s) syntax error: %s", filename, err_str); break;
-    case LUA_ERRMEM: log_printf(ERROR, "luaL_loadfile(%s) malloc error: %s", filename, err_str); break;
-    case LUA_ERRFILE: log_printf(ERROR, "luaL_loadfile(%s) file access error: %s", filename, err_str); break;
-    default: log_printf(ERROR, "luaL_loadfile(%s) unknown error: %s", filename, err_str); break;
-    }
-    return -1;
-  }
-
-  ret = lua_pcall(L, 0, 0, 0);
-  if(ret) {
-    const char* err_str = luaL_checkstring(L, -1);
-    switch(ret) {
-    case LUA_ERRRUN: log_printf(ERROR, "lua_pcall() runtime error: %s", err_str); break;
-    case LUA_ERRMEM: log_printf(ERROR, "lua_pcall() malloc error: %s", err_str); break;
-    case LUA_ERRERR: log_printf(ERROR, "lua_pcall() error at error handler function: %s", err_str); break;
-    }
-    return -1;
-  }
-
-  return 0;
-}
-
-int call_main_loop(lua_State* L, int cmd_fd, options_t* opt)
-{
-  lua_getglobal(L, LUA_MAIN_LOOP_FUNC);
-  if(!lua_isfunction(L, -1)) {
-    log_printf(ERROR, "there is no function '%s' inside the loaded file", LUA_MAIN_LOOP_FUNC);
-    return -1;
-  };
-
-  options_lua_push(opt, L);
-
-  int ret = lua_pcall(L, 1, LUA_MULTRET, 0);
-  if(ret) {
-    const char* err_str = luaL_checkstring(L, -1);
-    switch(ret) {
-    case LUA_ERRRUN: log_printf(ERROR, "lua_pcall(%s) runtime error: %s", LUA_MAIN_LOOP_FUNC, err_str); break;
-    case LUA_ERRMEM: log_printf(ERROR, "lua_pcall(%s) malloc error: %s", LUA_MAIN_LOOP_FUNC, err_str); break;
-    case LUA_ERRERR: log_printf(ERROR, "lua_pcall(%s) error at error handler function: %s", LUA_MAIN_LOOP_FUNC, err_str); break;
-    }
-    return -1;
-  }
-
-  int n = lua_gettop(L);
-  log_printf(DEBUG, "%s returned %d values", LUA_MAIN_LOOP_FUNC, n);
-  int i;
-  for (i = 1; i <= n; i++)
-    log_printf(DEBUG, "return value [%d] = '%s'", i, luaL_checkstring(L, i));
-
-  ret = lua_tointeger(L, 1);
-  return ret;
-}
-
-int main_loop(int cmd_fd, options_t* opt)
-{
-  lua_State *L;
-  L = luaL_newstate();
-  if(!L) {
-    log_printf(ERROR, "error creating lua state");
-    return -1;
-  }
-
-  int ret = init_main_loop(L, opt->lua_file_);
-  if(!ret)
-    ret = call_main_loop(L, cmd_fd, opt);
-
-  lua_close(L);
-  return ret;
-}
-
-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,luaclient,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);
-  }
-
-  for(;;) {
-        // extern global variable defined in l_cmd.c
-    cmd_fd = connect_command_socket(opt.command_sock_);
-    if(cmd_fd < 0)
-      ret = 2;
-    else {
-      ret = main_loop(cmd_fd, &opt);
-    }
-
-    if(ret == 2) {
-      log_printf(ERROR, "socket error, trying to reconnect in 5 seconds..");
-
-      if(cmd_fd > 0)
-        close(cmd_fd);
-      sleep(5);
-    }
-    else
-      break;
-  }
-
-  if(cmd_fd > 0)
-    close(cmd_fd);
-
-  if(!ret)
-    log_printf(NOTICE, "normal shutdown");
-  else if(ret < 0)
-    log_printf(NOTICE, "shutdown after error (code %d)", ret);
-  else
-    log_printf(NOTICE, "shutdown after signal");
-
-  options_clear(&opt);
-  log_close();
-
-  return ret;
-}
diff --git a/mode-tcpserver.lua b/mode-tcpserver.lua
deleted file mode 100644
index 26e42e2..0000000
--- a/mode-tcpserver.lua
+++ /dev/null
@@ -1,175 +0,0 @@
---
---  rhctl
---
---  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
---
---  This file is part of rhctl.
---
---  rhctl 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.
---
---  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
---
-
-package.path = package.path .. ';/usr/share/rhctl/?.lua'
-
-socket = require("socket")
-
-current_mode = nil
-
-function init_server(host, port)
-   local server = assert(socket.tcp())
-
-   assert(server:setoption('reuseaddr', true))
-   assert(server:bind(host, port))
-   assert(server:listen(5))
-
-   return server
-end
-
-local clients = {}
-
-function add_client(hdl)
-   log.printf(log.DEBUG, "new client(" .. hdl:getfd() .. ") from " .. hdl:getpeername())
-   local client = {}
-   if current_mode then
-      client.buffer = current_mode .. "\n"
-   else
-      client.buffer = ""
-   end
-   client.hdl = hdl
-   client.getfd = function() return hdl:getfd() end
-   client.dirty = function() return hdl:dirty() end
-   table.insert(clients, client)
-end
-
-function remove_client(c)
-   local idx = 0
-   local found = false
-   for i, client in ipairs(clients) do
-      if client == c then
-         found = true
-         idx = i
-         break
-      end
-   end
-
-   if found then
-      log.printf(log.DEBUG, "removing client(" .. c.hdl:getfd() .. ")")
-      c.hdl:close()
-      table.remove(clients, idx)
-   end
-end
-
-function cleanup_clients()
-   for _, client in ipairs(clients) do
-      client.hdl:close()
-   end
-end
-
-function clients_get_writeables()
-   local fds = {}
-
-   for _, client in ipairs(clients) do
-      if client.buffer ~= "" then
-         table.insert(fds, client)
-      end
-   end
-
-   return fds
-end
-
-function clients_senddata(data)
-   for _, client in ipairs(clients) do
-      client.buffer  = client.buffer .. data .. "\n"
-   end
-end
-
-function process_cmd(message)
-   log.printf(log.DEBUG, "received message: '%s'", message)
-
-   local new_mode = nil
-   local exps = { "Current Mode: (%a+)", "new Mode: (%a+)" }
-   for _, exp in ipairs(exps) do
-      new_mode = string.match(message, exp)
-      if(new_mode) then
-         new_mode = string.lower(new_mode)
-         break
-      end
-   end
-
-   if(new_mode and new_mode ~= current_mode) then
-      clients_senddata(new_mode)
-      current_mode = new_mode
-   end
-
-   return 0
-end
-
-function main_loop(opt)
-   log.printf(log.NOTICE, "main_loop started")
-   local sig = signal.init()
-   local cmdfd = cmd.init()
-
-   local server = init_server("*", "2345")
-
-   cmd.send_string("listen mode");
-   cmd.send_string("status");
-
-   local return_value = 0
-   while return_value == 0 do
-      local readable, writeable, err = socket.select({ sig , cmdfd , server , unpack(clients) }, clients_get_writeables())
-      if(err) then
-         log.printf(log.ERROR, "select returned with error: %s", err)
-         return_value = -1
-      else
-         for _, input in ipairs(readable) do
-            if input == sig then
-               return_value = signal.handle()
-               if(return_value == 1) then break end
-            elseif input == cmdfd  then
-               return_value = cmd.recv_data(process_cmd)
-               if(return_value ~= 0) then break end
-            elseif input == server then
-               local client = server:accept()
-               if(client == nil) then
-                  return_value =-3
-                  break
-               else
-                  add_client(client)
-               end
-            else
-               if input.hdl then
-                  -- receive is insanely stupid, therefore we must always read one byte only
-                  local _, err = input.hdl:receive(1)
-                  if err then
-                     remove_client(input)
-                  end
-               end
-            end
-         end
-         for _, output in ipairs(writeable) do
-            local ret = output.hdl:send(output.buffer)
-            if(ret == nil) then
-               remove_client(output)
-            else
-               output.buffer = string.sub(output.buffer, ret+1)
-            end
-         end
-      end
-   end
-
-   server:close()
-   cleanup_clients()
-
-   signal.stop()
-   return return_value
-end
diff --git a/mode-watch.lua b/mode-watch.lua
deleted file mode 100644
index cf716cb..0000000
--- a/mode-watch.lua
+++ /dev/null
@@ -1,119 +0,0 @@
---
---  rhctl
---
---  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
---
---  This file is part of rhctl.
---
---  rhctl 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.
---
---  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
---
-
-package.path = package.path .. ';/usr/share/rhctl/?.lua'
-
-socket = require("socket")
-utils = require("utils")
-
-function set_master_led()
-   os.execute("/sbin/led.sh set master")
-   os.execute("/sbin/led.sh clear standby")
-end
-
-function set_standby_led()
-   os.execute("/sbin/led.sh clear master")
-   os.execute("/sbin/led.sh set standby")
-end
-
-function clear_leds()
-   os.execute("/sbin/led.sh clear master")
-   os.execute("/sbin/led.sh clear standby")
-end
-
-current_mode = nil
-
-function process_cmd(message)
-   log.printf(log.DEBUG, "received message: '%s'", message)
-
-   local new_mode = nil
-   local exps = { "Current Mode: (%a+)", "new Mode: (%a+)" }
-   for _, exp in ipairs(exps) do
-      new_mode = string.match(message, exp)
-      if(new_mode) then
-         new_mode = string.lower(new_mode)
-         if(new_mode == "master") then
-            set_master_led()
-            break
-         else
-            if(new_mode == "standby") then
-               set_standby_led()
-               break
-            end
-         end
-      end
-   end
-
-   if(new_mode and new_mode ~= current_mode) then
-      log.printf(log.NOTICE, "mode is now " .. new_mode)
-      if(current_mode == nil) then
-         utils.send_mail("logs@helsinki.at", "[RHCTL] (re)started mode is now " .. new_mode,
-                   "RHCTL just (re)started current mode is " .. new_mode)
-      else
-         utils.send_mail("logs@helsinki.at", "[RHCTL] mode changed to " .. new_mode,
-                   "RHCTL just switched from " .. current_mode .. " to " .. new_mode)
-      end
-      current_mode = new_mode
-   end
-
-   return 0
-end
-
-function main_loop(opt)
-   log.printf(log.NOTICE, "main_loop started")
-   local sig = signal.init()
-   local cmdfd = cmd.init()
-
-   cmd.send_string("listen mode");
-   cmd.send_string("status");
-
-   clear_leds()
-
-   local return_value = 0
-   while return_value == 0 do
-      local readable, _, err = socket.select({ sig , cmdfd }, nil)
-      if(err) then
-         log.printf(log.ERROR, "select returned with error: %s", err)
-         return_value = -1
-      else
-         for _, input in ipairs(readable) do
-            if(input == sig) then
-               return_value = signal.handle()
-               if(return_value == 1) then break end
-            else
-               if(input == cmdfd) then
-                  return_value = cmd.recv_data(process_cmd)
-                  if(return_value ~= 0) then break end
-               else
-                  log.printf(log.ERROR, "select returned invalid handle??")
-                  return_value = -1
-                  break;
-               end
-            end
-         end
-      end
-   end
-
-   clear_leds()
-
-   signal.stop()
-   return return_value
-end
diff --git a/options.c b/options.c
deleted file mode 100644
index 7faa3ce..0000000
--- a/options.c
+++ /dev/null
@@ -1,556 +0,0 @@
-
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. 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--;
-
-  char* mode = NULL;
-  char* channel = NULL;
-  char* baudrate = NULL;
-
-  int i;
-  for(i=1; argc > 0; ++i)
-  {
-    char* str = argv[i];
-    argc--;
-
-    if(!strcmp(str,"-h") || !strcmp(str,"--help"))
-      return -1;
-#ifndef OPT_STDIOCLIENT
-    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_)
-#endif
-    PARSE_STRING_LIST("-L","--log", opt->log_targets_)
-    PARSE_STRING_PARAM("-s","--command-sock", opt->command_sock_)
-#ifndef OPT_STDIOCLIENT
-    PARSE_STRING_PARAM("-b","--baudrate", baudrate)
-#endif
-#ifdef OPT_SWITCHCTL
-    PARSE_STRING_PARAM("-f","--config", opt->conf_file_)
-    PARSE_STRING_PARAM("-d","--device", opt->switch_dev_)
-    PARSE_STRING_PARAM("-m","--mode", mode)
-    PARSE_STRING_PARAM("-c","--channel", channel)
-#endif
-#ifdef OPT_SERIALCLIENT
-    PARSE_STRING_PARAM("-d","--device", opt->serial_dev_)
-    PARSE_STRING_PARAM("-t","--type", opt->type_)
-#endif
-#ifdef OPT_HEARTBEATCLIENT
-    PARSE_STRING_PARAM("-d","--device", opt->serial_dev_)
-    PARSE_INT_PARAM("-t","--timeout", opt->timeout_)
-    PARSE_STRING_PARAM("-l","--led", opt->led_filename_)
-#endif
-#ifdef OPT_LUACLIENT
-    PARSE_STRING_PARAM("-f","--lua-file", opt->lua_file_)
-#endif
-    else
-      return i;
-  }
-
-  if(mode) {
-    if(!strcmp(mode, "master"))
-      opt->mode_ = MODE_MASTER;
-    else if(!strcmp(mode, "standby"))
-      opt->mode_ = MODE_STANDBY;
-    else {
-      free(mode);
-      return -3;
-    }
-    free(mode);
-  }
-
-  if(channel) {
-    if(!strcmp(channel, "main")) {
-      opt->channel_master_ = CHAN_MAIN;
-      opt->channel_standby_ = CHAN_MAIN;
-    }
-    else if(!strcmp(channel, "music")) {
-      opt->channel_master_ = CHAN_MUSIC;
-      opt->channel_standby_ = CHAN_MUSIC;
-    }
-    else {
-      free(channel);
-      return -4;
-    }
-    free(channel);
-  }
-
-  if(baudrate) {
-    int b = atoi(baudrate);
-    free(baudrate);
-    switch(b) {
-    case 1200: opt->baudrate_ = B1200; break;
-    case 2400: opt->baudrate_ = B2400; break;
-    case 4800: opt->baudrate_ = B4800; break;
-    case 9600: opt->baudrate_ = B9600; break;
-    case 19200: opt->baudrate_ = B19200; break;
-    case 38400: opt->baudrate_ = B38400; break;
-    case 57600: opt->baudrate_ = B57600; break;
-    case 115200: opt->baudrate_ = B115200; break;
-    default: return -5;
-    }
-  }
-
-  return 0;
-}
-
-int options_parse_post(options_t* opt)
-{
-  if(!opt)
-    return -1;
-// nothing to do
-
-#ifdef OPT_SWITCHCTL
-  FILE* conf_file = fopen(opt->conf_file_, "r");
-  if(conf_file) {
-    char buf[100];
-    while(fgets(buf, 100, conf_file) != NULL) {
-      char* tmp, *key, *value;
-      for(tmp = buf;*tmp == ' '; ++tmp);
-      if(*(key = tmp) == 0) continue;
-      for(;*tmp != ' ' && *tmp != 0;++tmp);
-      if(*tmp == 0) continue;
-      *tmp=0;
-      ++tmp;
-      for(;*tmp == ' ';++tmp);
-      if(*(value = tmp) == 0) continue;
-      for(;*tmp != ' ' && *tmp != 0 && *tmp != '\n';++tmp);
-      *tmp = 0;
-
-      if(key_value_storage_add(&opt->alias_table_, key, value))
-        return -2;
-    }
-    fclose(conf_file);
-  }
-  else {
-    log_printf(ERROR,"unable to open conf file (%s): %s", opt->conf_file_, strerror(errno));
-    return -1;
-  }
-#endif
-
-  return 0;
-}
-
-void options_default(options_t* opt)
-{
-  if(!opt)
-    return;
-
-#ifdef OPT_SWITCHCTL
-  opt->progname_ = strdup("switchctl");
-#endif
-#ifdef OPT_SERIALCLIENT
-  opt->progname_ = strdup("serialclient");
-#endif
-#ifdef OPT_STDIOCLIENT
-  opt->progname_ = strdup("stdioclient");
-#endif
-#ifdef OPT_HEARTBEATCLIENT
-  opt->progname_ = strdup("heartbeatclient");
-#endif
-#ifdef OPT_LUACLIENT
-  opt->progname_ = strdup("luaclient");
-#endif
-
-/* common */
-  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/rhctl/switchctl.sock");
-  opt->baudrate_ = B19200;
-
-/* switchctl */
-  opt->mode_ = MODE_MASTER;
-  opt->channel_master_ = CHAN_MAIN;
-  opt->channel_standby_ = CHAN_MAIN;
-  opt->conf_file_ = strdup("/etc/rhctl/switchctl.conf");
-  opt->switch_dev_ = strdup("/dev/audioswitch");
-  key_value_storage_init(&opt->alias_table_);
-
-/* serialclient and heartbeatclient */
-  opt->serial_dev_ = strdup("/dev/ttyUSB0");
-
-/* serialclient only */
-  opt->type_ = NULL;
-
-/* heartbeatclient only */
-  opt->timeout_ = 15;
-  opt->led_filename_ = NULL;
-
-/* luaclient only */
-  opt->lua_file_ = strdup("/usr/share/rhctl/mode-leds.lua");
-}
-
-#ifdef OPT_LUACLIENT
-void options_lua_push_string(lua_State* L, const int tidx, const char* key, const char* value)
-{
-  lua_pushstring(L, key);
-  lua_pushstring(L, value);
-  lua_settable(L, tidx);
-}
-
-void options_lua_push_int(lua_State* L, const int tidx, const char* key, const u_int32_t value)
-{
-  lua_pushstring(L, key);
-  lua_pushinteger(L, value);
-  lua_settable(L, tidx);
-}
-
-void options_lua_push_boolean(lua_State* L, const int tidx, const char* key, const u_int32_t value)
-{
-  lua_pushstring(L, key);
-  lua_pushboolean(L, value);
-  lua_settable(L, tidx);
-}
-
-void options_lua_push_string_list(lua_State* L, const int tidx, string_list_t* lst)
-{
-  if(!lst)
-    return;
-
-  string_list_element_t* tmp = lst->first_;
-  if(tmp) {
-    lua_pushstring(L, "log_targets");
-    lua_newtable(L);
-    int i = 1;
-    while(tmp) {
-      lua_pushinteger(L, i++);
-      lua_pushstring(L, tmp->string_);
-      lua_settable(L, -3);
-      tmp = tmp->next_;
-    }
-    lua_settable(L, tidx);
-  }
-}
-
-void options_lua_push(options_t* opt, lua_State* L)
-{
-  lua_newtable(L);
-
-  options_lua_push_string(L, -3, "progname", opt->progname_);
-  options_lua_push_boolean(L, -3, "daemonize", opt->daemonize_);
-  options_lua_push_string(L, -3, "username", opt->username_);
-  options_lua_push_string(L, -3, "groupname", opt->groupname_);
-  options_lua_push_string(L, -3, "chroot_dir", opt->chroot_dir_);
-  options_lua_push_string(L, -3, "pid_file", opt->pid_file_);
-  options_lua_push_string_list(L, -3, &(opt->log_targets_));
-  options_lua_push_string(L, -3, "command_sock", opt->command_sock_);
-  options_lua_push_string(L, -3, "lua_file", opt->lua_file_);
-}
-#endif
-
-void options_clear(options_t* opt)
-{
-  if(!opt)
-    return;
-
-/* common */
-  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_);
-
-/* switchctl */
-  if(opt->conf_file_)
-    free(opt->conf_file_);
-  if(opt->switch_dev_)
-    free(opt->switch_dev_);
-  key_value_storage_clear(&opt->alias_table_);
-
-/* serialclient and heartbeatclient */
-  if(opt->serial_dev_)
-    free(opt->serial_dev_);
-
-/* serialclient only */
-  if(opt->type_)
-    free(opt->type_);
-
-/* heartbeatcleint only */
-  if(opt->led_filename_)
-    free(opt->led_filename_);
-
-/* luaclient only */
-  if(opt->lua_file_)
-    free(opt->lua_file_);
-}
-
-void options_print_usage()
-{
-  printf("USAGE:\n");
-#ifdef OPT_SWITCHCTL
-  printf("switchctl\n");
-#endif
-#ifdef OPT_SERIALCLIENT
-  printf("serialclient\n");
-#endif
-#ifdef OPT_STDIOCLIENT
-  printf("serialclient\n");
-#endif
-#ifdef OPT_HEARTBEATCLIENT
-  printf("heartbeatclient\n");
-#endif
-  printf("  [-h|--help]                         prints this...\n");
-#ifndef OPT_STDIOCLIENT
-  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");
-#endif
-  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/rhctl/switchctl.sock\n");
-#ifndef OPT_STDIOCLIENT
-  printf("  [-b|--baudrate] <baudrate>          the baudrate of the tty to use e.g. 19200\n");
-#endif
-#ifdef OPT_SWITCHCTL
-  printf("  [-d|--device] <tty>                 the tty the audio switch is connected to e.g. /dev/audioswitch\n");
-  printf("  [-f|--config] <file>                the configuration file e.g. /etc/rhctl/switchctl.conf\n");
-  printf("  [-m|--mode] <mode>                  the initial mode to use e.g. master\n");
-  printf("  [-c|--channel] <channel>            the initial channel to use e.g. main\n");
-#endif
-#ifdef OPT_SERIALCLIENT
-  printf("  [-d|--device] <tty>                 the tty to connect to e.g. /dev/ttyUSB0\n");
-  printf("  [-t|--type] <type>                  use this client type\n");
-#endif
-#ifdef OPT_HEARTBEATCLIENT
-  printf("  [-d|--device] <tty>                 the tty to connect to e.g. /dev/ttyUSB0\n");
-  printf("  [-t|--timeout] <timeout>            heartbeat timeout in tenths of a second e.g. 15 -> 1.5s\n");
-  printf("  [-l|--led] <led filename>           sysfs filename of led device to blink\n");
-#endif
-#ifdef OPT_LUACLIENT
-  printf("  [-f|--lua-file] <file>              the configuration file e.g. /usr/share/rhctl/mode-leds.lua\n");
-#endif
-}
-
-void options_print(options_t* opt)
-{
-  if(!opt)
-    return;
-
-  printf("progname: '%s'\n", opt->progname_);
-#ifndef OPT_STDIOCLIENT
-  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_);
-#endif
-  printf("log_targets: \n");
-  string_list_print(&opt->log_targets_, "  '", "'\n");
-
-  printf("command_sock: '%s'\n", opt->command_sock_);
-
-#ifndef OPT_STDIOCLIENT
-  char* br;
-  switch(opt->baudrate_) {
-  case B1200: br = "1200"; break;
-  case B2400: br = "2400"; break;
-  case B4800: br = "4800"; break;
-  case B9600: br = "9600"; break;
-  case B19200: br = "19200"; break;
-  case B38400: br = "38400"; break;
-  case B57600: br = "57600"; break;
-  case B115200: br = "115200"; break;
-  default: br = "invalid"; break;
-  }
-  printf("baudrate: '%s'\n", br);
-#endif
-
-#ifdef OPT_SWITCHCTL
-  printf("mode: '%s'\n", opt->mode_ == MODE_MASTER ? "master" : "standby");
-  printf("channel_master: '%s'\n", opt->channel_master_ == CHAN_MAIN ? "main" : "music");
-  printf("channel_standby: '%s'\n", opt->channel_standby_ == CHAN_MAIN ? "main" : "music");
-  printf("conf_file: '%s'\n", opt->conf_file_);
-  printf("switch_dev: '%s'\n", opt->switch_dev_);
-  printf("alias_table: \n");
-  key_value_storage_print(&opt->alias_table_, "  '", "' -> '", "'\n");
-#endif
-
-#ifdef OPT_SERIALCLIENT
-  printf("serial_dev: '%s'\n", opt->serial_dev_);
-  printf("type: '%s'\n", opt->type_);
-#endif
-
-#ifdef OPT_HEARTBEATCLIENT
-  printf("serial_dev: '%s'\n", opt->serial_dev_);
-  printf("timeout: %d\n", opt->timeout_);
-  printf("led_filename: '%s'\n", opt->led_filename_);
-#endif
-
-#ifdef OPT_LUACLIENT
-  printf("lua_file: '%s'\n", opt->lua_file_);
-#endif
-}
diff --git a/options.h b/options.h
deleted file mode 100644
index 450c8f2..0000000
--- a/options.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_options_h_INCLUDED
-#define RHCTL_options_h_INCLUDED
-
-#include <lua.h>
-
-#include "string_list.h"
-#include "key_value_storage.h"
-#include <termios.h>
-
-enum mode_enum { MODE_MASTER, MODE_STANDBY };
-typedef enum mode_enum switchctl_mode_t;
-
-enum channel_enum { CHAN_MAIN, CHAN_MUSIC };
-typedef enum channel_enum switchctl_channel_t;
-
-struct options_struct {
-/* common */
-  char* progname_;
-  int daemonize_;
-  char* username_;
-  char* groupname_;
-  char* chroot_dir_;
-  char* pid_file_;
-  string_list_t log_targets_;
-
-  char* command_sock_;
-  speed_t baudrate_;
-
-/* switchctl */
-  switchctl_mode_t mode_;
-  switchctl_channel_t channel_master_;
-  switchctl_channel_t channel_standby_;
-  char* conf_file_;
-  char* switch_dev_;
-  key_value_storage_t alias_table_;
-
-/* serialclient and heartbeatclient */
-  char* serial_dev_;
-
-/* serialclient only */
-  char* type_;
-
-/* heartbeatclient only */
-  u_int32_t timeout_;
-  char* led_filename_;
-
-/* luaclient only */
-  char* lua_file_;
-};
-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_lua_push_string(lua_State* L, const int tidx, const char* key, const char* value);
-void options_lua_push_int(lua_State* L, const int tidx, const char* key, const u_int32_t value);
-void options_lua_push_boolean(lua_State* L, const int tidx, const char* key, const u_int32_t value);
-void options_lua_push_string_list(lua_State* L, const int tidx, string_list_t* lst);
-void options_lua_push(options_t* opt, lua_State* L);
-void options_clear(options_t* opt);
-void options_print_usage();
-void options_print(options_t* opt);
-
-#endif
diff --git a/serialclient.c b/serialclient.c
deleted file mode 100644
index 18d62db..0000000
--- a/serialclient.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "datatypes.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include "log.h"
-#include "sig_handler.h"
-#include "options.h"
-
-#include "daemon.h"
-#include "utils.h"
-
-int process_cmd(const char* cmd, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  return 0;
-}
-
-int process_data(int src_fd, int dest_fd)
-{
-  char* buffer[100];
-  int ret = read(src_fd, buffer, 100);
-  if(!ret)
-    return 2;
-  if(ret == -1 && errno == EAGAIN)
-    return 0;
-  if(ret < 0)
-    return ret;
-
-  log_printf(DEBUG, "read %d bytes from fd (%d)", ret, src_fd);
-
-  int len = ret;
-  int offset = 0;
-  for(;;) {
-    ret = write(dest_fd, &buffer[offset], len - offset);
-    if(ret < 0) {
-      if(errno != EINTR)
-        return ret;
-
-      ret = 0;
-    }
-
-    offset += ret;
-    if(offset >= len)
-      break;
-  }
-  log_printf(DEBUG, "wrote %d bytes to fd (%d)", offset, dest_fd);
-  return 0;
-}
-
-int main_loop(int serial_fd, int cmd_fd, options_t* opt)
-{
-  log_printf(NOTICE, "entering main loop");
-
-  fd_set readfds, tmpfds;
-  FD_ZERO(&readfds);
-  FD_SET(serial_fd, &readfds);
-  FD_SET(cmd_fd, &readfds);
-  int max_fd = serial_fd > cmd_fd ? serial_fd : cmd_fd;
-
-  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;
-
-  int return_value = 0;
-  if(opt->type_) {
-    char* tmp;
-    int len = asprintf(&tmp, "type %s\n", opt->type_);
-    if(len < 0) {
-      log_printf(ERROR, "memory error at init");
-      return_value = -2;
-    }
-    else {
-      return_value = send_string(cmd_fd, tmp);
-      free(tmp);
-      if(return_value <= 0) {
-        log_printf(ERROR, "error setting client type");
-        return_value = -1;
-      }
-      else {
-        log_printf(NOTICE, "connecting as type '%s'", opt->type_);
-        return_value = 0;
-      }
-    }
-  }
-
-  while(!return_value) {
-    memcpy(&tmpfds, &readfds, sizeof(tmpfds));
-
-    int ret = select(max_fd+1, &tmpfds, NULL, NULL, NULL);
-    if(ret == -1 && errno != EINTR) {
-      log_printf(ERROR, "select returned with error: %s", strerror(errno));
-      return_value = -1;
-      break;
-    }
-    if(ret == -1 || !ret)
-      continue;
-
-    if(FD_ISSET(sig_fd, &tmpfds))
-      if(signal_handle())
-        return_value = 1;
-
-    if(FD_ISSET(serial_fd, &tmpfds))
-      return_value = process_data(serial_fd, cmd_fd);
-
-    if(FD_ISSET(cmd_fd, &tmpfds)) {
-      return_value = process_data(cmd_fd, serial_fd);
-      if(return_value == 2) return_value = 3;
-    }
-  }
-
-  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 == -5) {
-      fprintf(stderr, "syntax error: invalid baudrate\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,serialclient,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_fd = 0;
-  int serial_fd = 0;
-  for(;;) {
-    cmd_fd = connect_command_socket(opt.command_sock_);
-    if(cmd_fd < 0)
-      ret = 3;
-    else {
-      serial_fd = open(opt.serial_dev_, O_RDWR | O_NOCTTY);
-      if(serial_fd < 0)
-        ret = 2;
-      else {
-        ret = setup_tty(serial_fd, opt.baudrate_);
-        if(ret)
-          ret = 2;
-        else
-          ret = main_loop(serial_fd, cmd_fd, &opt);
-      }
-    }
-
-    if(ret == 2 || ret == 3) {
-      if(ret == 2)
-        log_printf(ERROR, "%s error, trying to reopen in 5 seconds..", opt.serial_dev_);
-      if(ret == 3)
-        log_printf(ERROR, "socket error, trying to reconnect in 5 seconds..");
-
-      if(cmd_fd > 0)
-        close(cmd_fd);
-      if(serial_fd > 0)
-        close(serial_fd);
-      sleep(5);
-    }
-    else
-      break;
-  }
-
-  if(cmd_fd > 0)
-    close(cmd_fd);
-  if(serial_fd > 0)
-    close(serial_fd);
-
-  if(!ret)
-    log_printf(NOTICE, "normal shutdown");
-  else if(ret < 0)
-    log_printf(NOTICE, "shutdown after error (code %d)", ret);
-  else
-    log_printf(NOTICE, "shutdown after signal");
-
-  options_clear(&opt);
-  log_close();
-
-  return ret;
-}
diff --git a/sig_handler.c b/sig_handler.c
deleted file mode 100644
index 02dbcb0..0000000
--- a/sig_handler.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- *  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-2010 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 "sig_handler.h"
-
-#include <signal.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-
-static int sig_pipe_fds[2];
-int reset_flag;
-
-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"); reset_flag = 1; 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(SIGPIPE, &act, NULL);
-  sigaction(SIGCHLD, &act, NULL);
-
-  close(sig_pipe_fds[0]);
-  close(sig_pipe_fds[1]);
-}
diff --git a/sig_handler.h b/sig_handler.h
deleted file mode 100644
index 3acd53a..0000000
--- a/sig_handler.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- *  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-2010 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
-
-extern int reset_flag;
-
-int signal_init();
-int signal_handle();
-void signal_stop();
-
-#endif
diff --git a/silence-watch.lua b/silence-watch.lua
deleted file mode 100644
index 0a2ebe2..0000000
--- a/silence-watch.lua
+++ /dev/null
@@ -1,87 +0,0 @@
---
---  rhctl
---
---  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
---
---  This file is part of rhctl.
---
---  rhctl 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.
---
---  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
---
-
-package.path = package.path .. ';/usr/share/rhctl/?.lua'
-
-socket = require("socket")
-utils = require("utils")
-
-current_state = nil
-
-function process_cmd(message)
-   log.printf(log.DEBUG, "received message: '%s'", message)
-
-   silence_state = string.match(message, "S0S,(%d)")
-
-   if(silence_state and silence_state ~= current_state) then
-      if(silence_state == "0") then
-         log.printf(log.NOTICE, "seen some noise")
-         utils.send_mail("silence@helsinki.at", "[RHCTL] sees some noise",
-                   "There is some noise at output 1 of the audioswitch\nCurrent State is: " .. message)
-      else
-         if (silence_state == "1") then
-            log.printf(log.NOTICE, "silence detected")
-            utils.send_mail("silence@helsinki.at", "[RHCTL] silence detected ",
-                      "Silence detected at output 1 of the audioswitch, make some noise!!\nCurrent State is:  " .. message)
-         end
-      end
-      current_state = silence_state
-   end
-
-   return 0
-end
-
-function main_loop(opt)
-   log.printf(log.NOTICE, "main_loop started")
-   local sig = signal.init()
-   local cmdfd = cmd.init()
-
-   cmd.send_string("listen silence");
-   cmd.send_string("switch *0SS");
-
-   local return_value = 0
-   while return_value == 0 do
-      local readable, _, err = socket.select({ sig , cmdfd }, nil)
-      if(err) then
-         log.printf(log.ERROR, "select returned with error: %s", err)
-         return_value = -1
-      else
-         for _, input in ipairs(readable) do
-            if(input == sig) then
-               return_value = signal.handle()
-               if(return_value == 1) then break end
-            else
-               if(input == cmdfd) then
-                  return_value = cmd.recv_data(process_cmd)
-                  if(return_value ~= 0) then break end
-               else
-                  log.printf(log.ERROR, "select returned invalid handle??")
-                  return_value = -1
-                  break;
-               end
-            end
-         end
-      end
-   end
-
-   signal.stop()
-   return return_value
-end
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..7e7b9dc
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,122 @@
+##
+##  rhctl
+##
+##  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+##
+##  This file is part of rhctl.
+##
+##  rhctl 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.
+##
+##  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+##
+
+ifneq ($(MAKECMDGOALS),distclean)
+include include.mk
+endif
+
+EXE_SWITCHCTL := switchctl
+EXE_SERIALCLIENT := serialclient
+EXE_STDIOCLIENT := stdioclient
+EXE_HEARTBEATCLIENT := heartbeatclient
+EXE_LUACLIENT := luaclient
+
+COMMONOBJ := log.o \
+             sig_handler.o \
+             string_list.o \
+             key_value_storage.o \
+             utils.o
+
+SWITCHCTLOBJ := command_queue.o \
+                client_list.o \
+                opt-switchctl.o \
+                switchctl.o
+
+SERIALCLIENTOBJ := opt-serialclient.o \
+                   serialclient.o
+
+STDIOCLIENTOBJ := opt-stdioclient.o \
+                  stdioclient.o
+
+HEARTBEATCLIENTOBJ := opt-heartbeatclient.o \
+                      heartbeatclient.o
+
+LUACLIENTOBJ := opt-luaclient.o \
+                l_log.o \
+                l_sig_handler.o \
+                l_cmd.o \
+                luaclient.o
+
+
+SRC := $(COMMONOBJ:%.o=%.c) $(SWITCHCTLOBJ:%.o=%.c) $(SERIALCLIENTOBJ:%.o=%.c) $(STDIOCLIENTOBJ:%.o=%.c) $(HEARTBEATCLIENTOBJ:%.o=%.c) $(LUACLIENTOBJ:%.o=%.c) options.c
+
+.PHONY: clean distclean
+
+all: $(EXE_SWITCHCTL) $(EXE_SERIALCLIENT) $(EXE_STDIOCLIENT) $(EXE_HEARTBEATCLIENT) $(EXE_LUACLIENT)
+
+%.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
+
+$(EXE_SWITCHCTL): $(COMMONOBJ) $(SWITCHCTLOBJ)
+	$(CC) $(COMMONOBJ) $(SWITCHCTLOBJ) -o $@ $(LDFLAGS)
+
+$(EXE_SERIALCLIENT): $(COMMONOBJ) $(SERIALCLIENTOBJ)
+	$(CC) $(COMMONOBJ) $(SERIALCLIENTOBJ) -o $@ $(LDFLAGS)
+
+$(EXE_STDIOCLIENT): $(COMMONOBJ) $(STDIOCLIENTOBJ)
+	$(CC) $(COMMONOBJ) $(STDIOCLIENTOBJ) -o $@ $(LDFLAGS)
+
+$(EXE_HEARTBEATCLIENT): $(COMMONOBJ) $(HEARTBEATCLIENTOBJ)
+	$(CC) $(COMMONOBJ) $(HEARTBEATCLIENTOBJ) -o $@ $(LDFLAGS)
+
+$(EXE_LUACLIENT): $(COMMONOBJ) $(LUACLIENTOBJ)
+	$(CC) $(COMMONOBJ) $(LUACLIENTOBJ) -o $@ $(LDFLAGS) $(LUA_LDFLAGS)
+
+opt-switchctl.o: options.c
+	$(CC) $(CFLAGS) -DOPT_SWITCHCTL -o $@ -c $<
+
+opt-serialclient.o: options.c
+	$(CC) $(CFLAGS) -DOPT_SERIALCLIENT -o $@ -c $<
+
+opt-stdioclient.o: options.c
+	$(CC) $(CFLAGS) -DOPT_STDIOCLIENT -o $@ -c $<
+
+opt-heartbeatclient.o: options.c
+	$(CC) $(CFLAGS) -DOPT_HEARTBEATCLIENT -o $@ -c $<
+
+opt-luaclient.o: options.c
+	$(CC) $(CFLAGS) -DOPT_LUACLIENT -o $@ -c $<
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c $<
+
+
+distclean: clean
+	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 $(EXE_SWITCHCTL)
+	rm -f $(EXE_SERIALCLIENT)
+	rm -f $(EXE_STDIOCLIENT)
+	rm -f $(EXE_HEARTBEATCLIENT)
+	rm -f $(EXE_LUACLIENT)
\ No newline at end of file
diff --git a/src/client_list.c b/src/client_list.c
new file mode 100644
index 0000000..141a3a4
--- /dev/null
+++ b/src/client_list.c
@@ -0,0 +1,137 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "client_list.h"
+#include "datatypes.h"
+
+char* client_type_tostring(client_type_t type)
+{
+  switch(type)
+  {
+  case MASTER: return "master";
+  case STANDBY: return "standby";
+  case HB_MASTER: return "hb_master";
+  case HB_STANDBY: return "hb_standby";
+  case DEFAULT: return "unspecified";
+  }
+  return "<invalid>";
+}
+
+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->type = DEFAULT;
+  new_client->request_listener = 0;
+  new_client->mode_listener = 0;
+  new_client->status_listener = 0;
+  new_client->gpi_listener = 0;
+  new_client->oc_listener = 0;
+  new_client->relay_listener = 0;
+  new_client->silence_listener = 0;
+  new_client->health_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..2f3d5f2
--- /dev/null
+++ b/src/client_list.h
@@ -0,0 +1,52 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_client_list_h_INCLUDED
+#define RHCTL_client_list_h_INCLUDED
+
+#include "datatypes.h"
+
+enum client_type_enum { DEFAULT, MASTER, STANDBY, HB_MASTER, HB_STANDBY };
+typedef enum client_type_enum client_type_t;
+char* client_type_tostring(client_type_t);
+
+struct client_struct {
+  int fd;
+  client_type_t type;
+  int request_listener;
+  int mode_listener;
+  int status_listener;
+  int gpi_listener;
+  int oc_listener;
+  int relay_listener;
+  int silence_listener;
+  int health_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/command_queue.c b/src/command_queue.c
new file mode 100644
index 0000000..b72d05e
--- /dev/null
+++ b/src/command_queue.c
@@ -0,0 +1,118 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "command_queue.h"
+#include "datatypes.h"
+
+cmd_t* cmd_get_last(cmd_t* first)
+{
+  if(!first)
+    return NULL;
+
+  while(first->next) {
+    first = first->next;
+  }
+
+  return first;
+}
+
+int cmd_push(cmd_t** first, int fd, cmd_id_t cmd, const char* param)
+{
+  if(!first)
+    return -1;
+
+  cmd_t* new_cmd = malloc(sizeof(cmd_t));
+  if(!new_cmd)
+    return -2;
+
+  new_cmd->fd = fd;
+  new_cmd->cmd = cmd;
+  if(param) {
+    new_cmd->param = strdup(param);
+    if(!new_cmd->param) {
+      free(new_cmd);
+      return -2;
+    }
+  }
+  else
+    new_cmd->param = NULL;
+  new_cmd->sent = 0;
+  new_cmd->tv_start.tv_sec = 0;
+  new_cmd->tv_start.tv_usec = 0;
+  new_cmd->next = NULL;
+
+  if(!(*first)) {
+    *first = new_cmd;
+    return 0;
+  }
+
+  cmd_get_last(*first)->next = new_cmd;
+
+  return 0;
+}
+
+void cmd_sent(cmd_t* cmd)
+{
+  if(!cmd)
+    return;
+
+  cmd->sent = 1;
+  gettimeofday(&cmd->tv_start, NULL);
+}
+
+int cmd_has_expired(cmd_t cmd)
+{
+  struct timeval now;
+  timerclear(&now);
+  gettimeofday(&now, NULL);
+  cmd.tv_start.tv_sec++;
+
+  return timercmp(&cmd.tv_start, &now, <);
+}
+
+void cmd_pop(cmd_t** first)
+{
+  if(!first || !(*first))
+    return;
+
+  cmd_t* deletee = *first;
+  *first = (*first)->next;
+  if(deletee->param)
+    free(deletee->param);
+  free(deletee);
+}
+
+void cmd_clear(cmd_t** first)
+{
+  if(!first || !(*first))
+    return;
+
+  while(*first) {
+    cmd_t* deletee = *first;
+    *first = (*first)->next;
+    if(deletee->param)
+      free(deletee->param);
+    free(deletee);
+  }
+}
diff --git a/src/command_queue.h b/src/command_queue.h
new file mode 100644
index 0000000..4cdcf9f
--- /dev/null
+++ b/src/command_queue.h
@@ -0,0 +1,46 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_command_queue_h_INCLUDED
+#define RHCTL_command_queue_h_INCLUDED
+
+#include <sys/time.h>
+
+enum cmd_id_enum { SWITCH, CHANNEL, TYPE, MODE, HEARTBEAT, STATUS, HEALTH, LOG, LISTEN };
+typedef enum cmd_id_enum cmd_id_t;
+
+struct cmd_struct {
+  int fd;
+  cmd_id_t cmd;
+  char* param;
+  int sent;
+  struct timeval tv_start;
+  struct cmd_struct* next;
+};
+typedef struct cmd_struct cmd_t;
+
+int cmd_push(cmd_t** first, int fd, cmd_id_t cmd, const char* param);
+void cmd_sent(cmd_t* cmd);
+int cmd_has_expired(cmd_t cmd);
+void cmd_pop(cmd_t** first);
+void cmd_clear(cmd_t** first);
+
+#endif
diff --git a/src/configure b/src/configure
new file mode 100755
index 0000000..83fe4d3
--- /dev/null
+++ b/src/configure
@@ -0,0 +1,162 @@
+#!/bin/sh
+#
+#  rhctl
+#
+#  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+#
+#  This file is part of rhctl.
+#
+#  rhctl 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.
+#
+#  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+#
+
+TARGET=`uname -s`
+
+USE_CLANG=0
+
+LUA_LDFLAGS='-lm'
+LUA_DIR=''
+
+print_usage() {
+  echo "configure --help                    print this"
+  echo "          --target=<TARGET>         build target i.e. Linux (default: autodetect)"
+  echo "          --with-lua=<DIR>          use this lua tree instead of system default"
+  echo "          --use-clang               use clang/llvm as compiler/linker"
+}
+
+for arg
+do
+  case $arg in
+  --target=*)
+    TARGET=${arg#--target=}
+  ;;
+  --use-clang)
+    USE_CLANG=1
+  ;;
+  --with-lua=*)
+    LUA_DIR=${arg#--with-lua=}
+  ;;
+  --help)
+    print_usage
+    exit 0
+  ;;
+  *)
+    echo "Unknown argument: $arg"
+    print_usage
+    exit 1
+  ;;
+  esac
+done
+
+if [ $USE_CLANG -eq 0 ]; then
+  CFLAGS='-g -Wall -O2'
+  LDFLAGS='-g -Wall -O2'
+  COMPILER='gcc'
+else
+  CFLAGS='-g -O2'
+  LDFLAGS='-g -O2'
+  COMPILER='clang'
+fi
+
+rm -f include.mk
+case $TARGET in
+  Linux)
+    echo "Linux specific build options"
+    LUA_LDFLAGS=$LUA_LDFLAGS' -ldl'
+  ;;
+  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
+
+test_lua_version()
+{
+  LUA_VERSION=`cat $1 | grep "#define LUA_VERSION[ 	]" | cut -f2- | tr -d '"' | sed -e 's/Lua \([0-9][0-9.]*\)/\1/'`
+  LUA_VERSION_NUM=`cat $1 | grep "#define LUA_VERSION_NUM" | awk '{ print $3 }'`
+  LUA_RELEASE=`cat $1 | grep "#define LUA_RELEASE[ 	]" | cut -f2-`
+
+  if [ $LUA_VERSION_NUM -ge 501 ]; then
+    return 1;
+  else
+    return 0;
+  fi
+}
+
+if [ -z "$LUA_DIR" ]; then
+  for prefix in /usr /usr/local; do
+    if [ -e $prefix/include/lua.h ]; then
+      test_lua_version $prefix/include/lua.h
+      if [ $? -eq 1 ]; then
+        echo "using Lua $LUA_VERSION ($LUA_RELEASE) found at $prefix/include"
+        CFLAGS="$CFLAGS -I$prefix/include"
+        LUA_LDFLAGS="$LUA_LDFLAGS -L$prefix/lib -llua"
+        LUA_DIR=found
+        break
+      fi
+    else
+      for dir in `ls -d $prefix/include/lua* 2> /dev/null`; do
+        if [ -e $dir/lua.h ]; then
+          test_lua_version $dir/lua.h
+          if [ $? -eq 1 ]; then
+            echo "using Lua $LUA_VERSION ($LUA_RELEASE) found at $dir"
+            CFLAGS="$CFLAGS -I$dir"
+            if [ -x "$prefix/bin/lua$LUA_VERSION" ]; then
+              LUA_LDFLAGS="$LUA_LDFLAGS -L$prefix/lib -llua$LUA_VERSION"
+              LUA_DIR=found
+            elif [ -x "$prefix/bin/lua-$LUA_VERSION" ]; then
+              LUA_LDFLAGS="$LUA_LDFLAGS -L$prefix/lib -llua-$LUA_VERSION"
+              LUA_DIR=found
+            else
+              echo "ERROR: found lua.h at $dir/lua.h but no matching lua library"
+              return 1
+            fi
+            break
+          fi
+        fi
+      done
+      if [ -n "$LUA_DIR" ]; then
+        break
+      fi
+    fi
+  done
+
+  if [ -z "$LUA_DIR" ]; then
+    echo "ERROR: no suitable lua found .. please install lua 5.1 or higher or use --with-lua"
+    return 1
+  fi
+
+else
+  CFLAGS="$CFLAGS -I$LUA_DIR/include"
+  LUA_LDFLAGS="$LUA_LDFLAGS $LUA_DIR/lib/liblua.a"
+fi
+
+
+cat >> include.mk <<EOF
+# this file was created automatically
+# do not edit this file directly
+# use ./configure instead
+
+TARGET := $TARGET
+CC := $COMPILER
+CFLAGS := $CFLAGS
+LDFLAGS := $LDFLAGS
+LUA_LDFLAGS := $LUA_LDFLAGS
+EOF
+
+exit 0
diff --git a/src/daemon.h b/src/daemon.h
new file mode 100644
index 0000000..9e6f1b2
--- /dev/null
+++ b/src/daemon.h
@@ -0,0 +1,175 @@
+/*
+ *  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-2010 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, "unknown 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, "unknown 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 program 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;
+  }
+
+  return 0;
+}
+
+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..3a72adc
--- /dev/null
+++ b/src/datatypes.h
@@ -0,0 +1,54 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_datatypes_h_INCLUDED
+#define RHCTL_datatypes_h_INCLUDED
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <arpa/inet.h>
+
+typedef uint8_t bool;
+#define FALSE 0
+#define TRUE 1
+
+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[100];
+};
+typedef struct read_buffer_struct read_buffer_t;
+
+#endif
diff --git a/src/health-watch.lua b/src/health-watch.lua
new file mode 100644
index 0000000..082c19d
--- /dev/null
+++ b/src/health-watch.lua
@@ -0,0 +1,107 @@
+--
+--  rhctl
+--
+--  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+--
+--  This file is part of rhctl.
+--
+--  rhctl 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.
+--
+--  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+--
+
+package.path = package.path .. ';/usr/share/rhctl/?.lua'
+
+socket = require("socket")
+utils = require("utils")
+
+old_state = {}
+current_state = {}
+
+function state_changed()
+   for k,v in pairs(old_state) do
+       if v ~= current_state[k] then
+           return true
+       end
+   end
+   for k,v in pairs(current_state) do
+       if old_state[k] == nil then
+           return true
+       end
+   end
+   return false
+end
+
+function process_cmd(message)
+   local exps = { hs = "^Health: (%a+)$", mc = "^Master: (%a+)$", sc = "^Standby: (%a+)$", hbm = "^Hearbeat Master: (%a+, %a+)$", hbs = "^Hearbeat Standby: (%a+, %a+)$" }
+   for key, exp in pairs(exps) do
+      local state = string.match(message, exp)
+      if(state) then
+         if(key == 'hs') then
+            current_state = {}
+         end
+         current_state[key] = state
+      end
+   end
+
+   if(current_state['hbs'] ~= nil) then
+      if(state_changed()) then
+         body = "RHCTL Health Output:\n\n"
+         body = body .. "Health: " .. current_state['hs'] .. "\n"
+         body = body .. "Master: " .. current_state['mc'] .. "\n"
+         body = body .. "Standby: " .. current_state['sc'] .. "\n"
+         body = body .. "Master Hearbeat: " .. current_state['hbm'] .. "\n"
+         body = body .. "Standby Hearbeat: " .. current_state['hbs'] .. "\n"
+         utils.send_mail("logs@helsinki.at", "[RHCTL] health: " .. current_state['hs'], body)
+         old_state = current_state
+      end
+   end
+
+   return 0
+end
+
+function main_loop(opt)
+   log.printf(log.NOTICE, "main_loop started")
+   local sig = signal.init()
+   local cmdfd = cmd.init()
+
+   cmd.send_string("listen health");
+   cmd.send_string("health");
+
+   local return_value = 0
+   while return_value == 0 do
+      local readable, _, err = socket.select({ sig , cmdfd }, nil)
+      if(err) then
+         log.printf(log.ERROR, "select returned with error: %s", err)
+         return_value = -1
+      else
+         for _, input in ipairs(readable) do
+            if(input == sig) then
+               return_value = signal.handle()
+               if(return_value == 1) then break end
+            else
+               if(input == cmdfd) then
+                  return_value = cmd.recv_data(process_cmd)
+                  if(return_value ~= 0) then break end
+               else
+                  log.printf(log.ERROR, "select returned invalid handle??")
+                  return_value = -1
+                  break;
+               end
+            end
+         end
+      end
+   end
+
+   signal.stop()
+   return return_value
+end
diff --git a/src/heartbeatclient.c b/src/heartbeatclient.c
new file mode 100644
index 0000000..3fe8423
--- /dev/null
+++ b/src/heartbeatclient.c
@@ -0,0 +1,426 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "log.h"
+#include "sig_handler.h"
+#include "options.h"
+
+#include "daemon.h"
+#include "utils.h"
+
+int process_serial(read_buffer_t* buffer, int serial_fd, int led_pipe_fd, bool *state)
+{
+  int ret = 0;
+  struct timeval tv;
+  fd_set fds;
+  FD_ZERO(&fds);
+  FD_SET(serial_fd, &fds);
+
+  for(;;) {
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    ret = select(serial_fd+1, &fds, NULL, NULL, &tv);
+    if(!ret)
+      return 0;
+    else if(ret < 0)
+      return ret;
+
+    ret = read(serial_fd, &buffer->buf[buffer->offset], 1);
+    if(!ret)
+      return 2;
+    if(ret == -1 && errno == EAGAIN)
+      return 0;
+    else if(ret < 0)
+      break;
+
+    if(buffer->buf[buffer->offset] == '\n') {
+      buffer->buf[buffer->offset] = 0;
+
+      if(buffer->offset > 0 && buffer->buf[buffer->offset-1] == '\r')
+        buffer->buf[buffer->offset-1] = 0;
+
+      if(strlen((char *)(buffer->buf))) {
+        *state = TRUE;
+        if(led_pipe_fd >= 0) {
+          char buf = '1';
+          ret = write(led_pipe_fd, &buf, 1);
+          if(ret == -1 && errno != EAGAIN)
+            log_printf(WARNING, "write to led process pipe returned with error: %s", strerror(errno));
+        }
+      }
+
+      buffer->offset = 0;
+      return 0;
+    }
+
+    buffer->offset++;
+    if(buffer->offset >= sizeof(buffer->buf)) {
+      log_printf(DEBUG, "string too long (fd=%d)", serial_fd);
+      buffer->offset = 0;
+      return 0;
+    }
+  }
+
+  return ret;
+}
+
+int main_loop(int serial_fd, int cmd_fd, int led_pipe_fd, options_t* opt)
+{
+  log_printf(NOTICE, "entering main loop");
+
+  fd_set readfds, tmpfds;
+  FD_ZERO(&readfds);
+  FD_SET(serial_fd, &readfds);
+  FD_SET(cmd_fd, &readfds);
+  int max_fd = serial_fd > cmd_fd ? serial_fd : cmd_fd;
+
+  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;
+
+  int return_value = 0;
+  return_value = send_string(cmd_fd, "type heartbeat\n");
+  if(return_value <= 0) {
+    log_printf(ERROR, "error setting client type");
+    return_value = -1;
+  }
+  else {
+    log_printf(NOTICE, "connecting as type heartbeat");
+    return_value = 0;
+  }
+
+  bool state = FALSE;
+  send_string(cmd_fd, "heartbeat 0\n");
+
+  read_buffer_t serial_buffer;
+  serial_buffer.offset = 0;
+  struct timeval timeout;
+  u_int32_t time = 0;
+  while(!return_value) {
+    memcpy(&tmpfds, &readfds, sizeof(tmpfds));
+
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 100000;
+    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) {
+      time++;
+      if(time >= opt->timeout_) {
+        if(state == TRUE) {
+          log_printf(WARNING, "heartbeat timeout");
+          send_string(cmd_fd, "heartbeat 0\n");
+          state = FALSE;
+        }
+        time = 0;
+      }
+      continue;
+    }
+
+    if(FD_ISSET(sig_fd, &tmpfds))
+      if(signal_handle())
+        return_value = 1;
+
+    if(FD_ISSET(serial_fd, &tmpfds)) {
+      bool old_state = state;
+      return_value = process_serial(&serial_buffer, serial_fd, led_pipe_fd, &state);
+      if(return_value)
+        break;
+      if(state) {
+        if(!old_state) {
+          log_printf(WARNING, "heartbeat returned");
+          send_string(cmd_fd, "heartbeat 1\n");
+        }
+        time = 0;
+      }
+    }
+
+    if(FD_ISSET(cmd_fd, &tmpfds)) {
+      u_int8_t buf[100];
+      int ret = recv(cmd_fd, buf, 100, 0);
+      if(!ret)
+        return_value = 3;
+      if(ret == -1 && errno == EAGAIN)
+        return_value = 0;
+      else if(ret < 0)
+        return_value = ret;
+    }
+  }
+
+  signal_stop();
+  return return_value;
+}
+
+int led_process(int pipe_fd, int led_fd)
+{
+  struct timeval timeout;
+  char buf;
+  int ret;
+
+  log_printf(INFO, "led_process: starting up");
+  for(;;) {
+    ret = read(pipe_fd, &buf, 1);
+    if(ret == -1 && errno == EAGAIN)
+      continue;
+    else if(!ret || ret < 0) {
+      log_printf(ERROR, "led_process: read returned with error: %s", strerror(errno));
+      break;
+    }
+
+    buf = '1';
+    ret = write(led_fd, &buf, 1);
+    if(ret == -1 && errno != EAGAIN) {
+      log_printf(ERROR, "led_process: write returned with error: %s", strerror(errno));
+      break;
+    }
+
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 100000;
+    ret = select(0, NULL, NULL, NULL, &timeout);
+    if(ret == -1 && errno != EINTR) {
+      log_printf(ERROR, "led_process: select returned with error: %s", strerror(errno));
+      break;
+    }
+
+    buf = '0';
+    ret = write(led_fd, &buf, 1);
+    if(ret == -1 && errno != EAGAIN) {
+      log_printf(ERROR, "led_process: write returned with error: %s", strerror(errno));
+      break;
+    }
+  }
+  log_printf(INFO, "led_process: stopped");
+  return -1;
+}
+
+int start_led_process(options_t* opt)
+{
+  int pipefd[2];
+  int led_fd;
+  pid_t cpid;
+
+  led_fd = open(opt->led_filename_, O_WRONLY);
+  if(led_fd < 0) {
+    log_printf(ERROR, "led_process: open() failed: %s", strerror(errno));
+    return -2;
+  }
+
+  if (pipe(pipefd) == -1) {
+    log_printf(ERROR, "led_process: pipe() failed: %s", strerror(errno));
+    close(led_fd);
+    return -2;
+  }
+
+  cpid = fork();
+  if (cpid == -1) {
+    log_printf(ERROR, "led_process: fork() failed: %s", strerror(errno));
+    close(pipefd[0]);
+    close(pipefd[1]);
+    close(led_fd);
+    return -2;
+  }
+
+  if (cpid == 0) {
+    close(pipefd[1]);
+    return led_process(pipefd[0], led_fd);
+  }
+
+  close(pipefd[0]);
+  close(led_fd);
+  return pipefd[1];
+}
+
+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 == -5) {
+      fprintf(stderr, "syntax error: invalid baudrate\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,heartbeatclient,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 led_pipe_fd = -1;
+  if(opt.led_filename_) {
+    log_printf(NOTICE, "starting led blink process");
+    led_pipe_fd = start_led_process(&opt);
+    if(led_pipe_fd == -1) {
+      options_clear(&opt);
+      log_close();
+      exit(0);
+    }
+    if(led_pipe_fd < -1) {
+      options_clear(&opt);
+      log_close();
+      exit(-1);
+    }
+  }
+
+  int cmd_fd = 0;
+  int serial_fd = 0;
+  for(;;) {
+    cmd_fd = connect_command_socket(opt.command_sock_);
+    if(cmd_fd < 0)
+      ret = 3;
+    else {
+      serial_fd = open(opt.serial_dev_, O_RDWR | O_NOCTTY);
+      if(serial_fd < 0)
+        ret = 2;
+      else {
+        ret = setup_tty(serial_fd, opt.baudrate_);
+        if(ret)
+          ret = 2;
+        else
+          ret = main_loop(serial_fd, cmd_fd, led_pipe_fd, &opt);
+      }
+    }
+
+    if(ret == 2 || ret == 3) {
+      if(ret == 2)
+        log_printf(ERROR, "%s error, trying to reopen in 5 seconds..", opt.serial_dev_);
+      if(ret == 3)
+        log_printf(ERROR, "socket error, trying to reconnect in 5 seconds..");
+
+      if(cmd_fd > 0)
+        close(cmd_fd);
+      if(serial_fd > 0)
+        close(serial_fd);
+      sleep(5);
+    }
+    else
+      break;
+  }
+
+  if(cmd_fd > 0)
+    close(cmd_fd);
+  if(serial_fd > 0)
+    close(serial_fd);
+
+  if(!ret)
+    log_printf(NOTICE, "normal shutdown");
+  else if(ret < 0)
+    log_printf(NOTICE, "shutdown after error (code %d)", ret);
+  else
+    log_printf(NOTICE, "shutdown after signal");
+
+  options_clear(&opt);
+  log_close();
+
+  return ret;
+}
diff --git a/src/key_value_storage.c b/src/key_value_storage.c
new file mode 100644
index 0000000..c6341e6
--- /dev/null
+++ b/src/key_value_storage.c
@@ -0,0 +1,92 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "key_value_storage.h"
+
+void key_value_storage_init(key_value_storage_t* stor)
+{
+  if(!stor)
+    return;
+
+  string_list_init(&(stor->keys_));
+  string_list_init(&(stor->values_));
+}
+
+void key_value_storage_clear(key_value_storage_t* stor)
+{
+  if(!stor)
+    return;
+
+  string_list_clear(&(stor->keys_));
+  string_list_clear(&(stor->values_));
+}
+
+int key_value_storage_add(key_value_storage_t* stor, const char* key, const char* value)
+{
+  if(!stor || !key || !value)
+    return -1;
+
+  int ret = string_list_add(&(stor->keys_), key);
+  if(ret!=0)
+    return ret;
+
+  ret = string_list_add(&(stor->values_), value);
+  if(ret!=0)
+    return ret;
+
+  return 0;
+}
+
+char* key_value_storage_find(key_value_storage_t* stor, const char* key)
+{
+  if(!stor || !key)
+    return NULL;
+
+  string_list_element_t* k = stor->keys_.first_;
+  string_list_element_t* v = stor->values_.first_;
+  while(v && k) {
+    if(!strcmp(k->string_, key))
+      return v->string_;
+
+    k = k->next_;
+    v = v->next_;
+  }
+
+  return NULL;
+}
+
+void key_value_storage_print(key_value_storage_t* stor, const char* head, const char* sep, const char* tail)
+{
+  if(!stor)
+    return;
+
+  string_list_element_t* k = stor->keys_.first_;
+  string_list_element_t* v = stor->values_.first_;
+  while(v && k) {
+    printf("%s%s%s%s%s", head, k->string_, sep, v->string_, tail);
+    k = k->next_;
+    v = v->next_;
+  }
+  printf("\n");
+}
diff --git a/src/key_value_storage.h b/src/key_value_storage.h
new file mode 100644
index 0000000..851897f
--- /dev/null
+++ b/src/key_value_storage.h
@@ -0,0 +1,41 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_key_value_storage_h_INCLUDED
+#define RHCTL_key_value_storage_h_INCLUDED
+
+#include "string_list.h"
+
+struct key_value_storage_struct {
+  string_list_t keys_;
+  string_list_t values_;
+};
+typedef struct key_value_storage_struct key_value_storage_t;
+
+void key_value_storage_init(key_value_storage_t* stor);
+void key_value_storage_clear(key_value_storage_t* stor);
+int key_value_storage_add(key_value_storage_t* stor, const char* key, const char* value);
+char* key_value_storage_find(key_value_storage_t* stor, const char* key);
+
+void key_value_storage_print(key_value_storage_t* stor, const char* head, const char* sep, const char* tail);
+
+
+#endif
diff --git a/src/l_cmd.c b/src/l_cmd.c
new file mode 100644
index 0000000..86bc872
--- /dev/null
+++ b/src/l_cmd.c
@@ -0,0 +1,152 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include "datatypes.h"
+
+#include "l_cmd.h"
+
+#include "options.h"
+#include "command_queue.h"
+#include "client_list.h"
+#include "log.h"
+#include "utils.h"
+
+int cmd_fd;
+read_buffer_t cmd_buffer;
+
+static int l_cmd_getfd(lua_State *L)
+{
+  if(!lua_istable(L, -1))
+    luaL_error(L, "can't retreive signal fd");
+
+  lua_pushliteral(L, "fd");
+  lua_gettable(L, -2);
+  return 1;
+}
+
+static int l_cmd_dirty(lua_State *L)
+{
+  lua_pushboolean(L, 0);
+  return 1;
+}
+
+static int l_cmd_init(lua_State *L)
+{
+  lua_newtable(L);
+  lua_pushliteral(L, "fd");
+  lua_pushinteger(L, cmd_fd);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "getfd");
+  lua_pushcfunction(L, l_cmd_getfd);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "dirty");
+  lua_pushcfunction(L, l_cmd_dirty);
+  lua_settable(L, -3);
+  return 1;
+}
+
+// gets called by nonblock_recvline for every complete message
+static int cmd_recv_data(lua_State* L)
+{
+  int ret = 0;
+  for(;;) {
+    ret = recv(cmd_fd, &cmd_buffer.buf[cmd_buffer.offset], 1, 0);
+    if(!ret)
+      return 2;
+    if(ret == -1 && errno == EAGAIN)
+      return 0;
+    else if(ret < 0)
+      break;
+
+    if(cmd_buffer.buf[cmd_buffer.offset] == '\n') {
+      cmd_buffer.buf[cmd_buffer.offset] = 0;
+
+      lua_pushstring(L, (char *)(cmd_buffer.buf));
+
+      ret = lua_pcall(L, 1, 1, 0);
+      if(ret) {
+        const char* err_str = luaL_checkstring(L, -1);
+        switch(ret) {
+        case LUA_ERRRUN: log_printf(ERROR, "lua_pcall(cmd_callback) runtime error: %s", err_str); break;
+        case LUA_ERRMEM: log_printf(ERROR, "lua_pcall(cmd_callback) malloc error: %s", err_str); break;
+        case LUA_ERRERR: log_printf(ERROR, "lua_pcall(cmd_callback) error at error handler function: %s", err_str); break;
+        }
+        return -1;
+      }
+
+      ret = lua_tointeger(L, 1);
+      cmd_buffer.offset = 0;
+      break;
+    }
+
+    cmd_buffer.offset++;
+    if(cmd_buffer.offset >= sizeof(cmd_buffer.buf)) {
+      log_printf(DEBUG, "string too long cmd_fd");
+      cmd_buffer.offset = 0;
+      return 0;
+    }
+  }
+
+  return ret;
+}
+
+static int l_cmd_recv_data(lua_State* L)
+{
+  int ret = cmd_recv_data(L);
+  lua_pushinteger(L, ret);
+  return 1;
+}
+
+static int l_cmd_send_string(lua_State* L)
+{
+  int ret = send_string(cmd_fd, luaL_checkstring(L,1));
+  if (ret > 0)
+    do {
+      ret = write(cmd_fd, "\n", 1);
+    } while(!ret || (ret == -1 && errno == EINTR));
+
+  if(ret > 0)
+    ret = 0;
+
+  lua_pushinteger(L, ret);
+  return 1;
+}
+
+static const struct luaL_reg cmd_funcs [] = {
+  { "init", l_cmd_init },
+  { "recv_data", l_cmd_recv_data },
+  { "send_string", l_cmd_send_string },
+  { NULL, NULL }
+};
+
+
+LUALIB_API int luaopen_cmd(lua_State *L)
+{
+  luaL_register(L, LUA_CMDLIBNAME, cmd_funcs);
+  return 1;
+}
diff --git a/src/l_cmd.h b/src/l_cmd.h
new file mode 100644
index 0000000..8c3652c
--- /dev/null
+++ b/src/l_cmd.h
@@ -0,0 +1,32 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_l_cmd_h_INCLUDED
+#define RHCTL_l_cmd_h_INCLUDED
+
+#include <lua.h>
+
+extern int cmd_fd;
+
+#define LUA_CMDLIBNAME "cmd"
+LUALIB_API int luaopen_cmd(lua_State *L);
+
+#endif
diff --git a/src/l_log.c b/src/l_log.c
new file mode 100644
index 0000000..4ccf18d
--- /dev/null
+++ b/src/l_log.c
@@ -0,0 +1,120 @@
+/*
+ *  anylike
+ *
+ *  anylike is an IKEv2 Implementation written in Lua and C. It's main
+ *  design goal is to provide anytun and uanytun or any other SATP
+ *  implementation with a key exchange mechanism but it should also be
+ *  possible to use anylike as key exchange daemon for IPSec security
+ *  associations. The use of Lua guarantees that anylike is easily
+ *  portable to many platforms including very small ones like wireless
+ *  routers.
+ *
+ *
+ *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
+ *                          Christian Pointner <equinox@anylike.org>
+ *
+ *  This file is part of anylike.
+ *
+ *  anylike 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.
+ *
+ *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include <stdlib.h>
+
+#include "datatypes.h"
+#include "log.h"
+
+#include "l_log.h"
+
+static int l_log_init(lua_State *L)
+{
+  log_init();
+  return 0;
+}
+
+static int l_log_close(lua_State *L)
+{
+  log_close();
+  return 0;
+}
+
+static int l_log_add_target(lua_State *L)
+{
+  int ret = log_add_target(luaL_checkstring(L,1));
+  if(ret) {
+    lua_pushboolean(L, 0);
+    switch(ret) {
+    case -2: lua_pushstring(L, "memory error at log_add_target"); break;
+    case -3: lua_pushstring(L, "unknown log target"); break;
+    case -4: lua_pushstring(L, "this log target is only allowed once"); break;
+    default: lua_pushstring(L, "syntax error"); break;
+    }
+    return 2;
+  }
+
+  lua_pushboolean(L, 1);
+  return 1;
+}
+
+static int l_log_printf(lua_State *L)
+{
+  int numargs = lua_gettop(L);
+  if(numargs < 2)
+    return luaL_error(L, "log.printf too few arguments");
+
+  if(numargs > 2) {
+    lua_getglobal(L, "string");
+    lua_pushliteral(L, "format");
+    lua_gettable(L, -2);
+    lua_insert(L, 2);
+    lua_remove(L, -1);
+    lua_call(L, numargs - 1, 1);
+  }
+
+  log_prio_t prio = luaL_checkint(L,1);
+  log_printf(prio, "%s", luaL_checkstring(L, 2));
+  return 0;
+}
+
+static const struct luaL_reg log_funcs [] = {
+  { "init", l_log_init },
+  { "close", l_log_close },
+  { "add_target", l_log_add_target },
+  { "printf", l_log_printf },
+  { NULL, NULL }
+};
+
+
+LUALIB_API int luaopen_log(lua_State *L)
+{
+  luaL_register(L, LUA_LOGLIBNAME, log_funcs);
+  lua_pushliteral(L, "ERROR");
+  lua_pushinteger(L, ERROR);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "WARNING");
+  lua_pushinteger(L, WARNING);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "NOTICE");
+  lua_pushinteger(L, NOTICE);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "INFO");
+  lua_pushinteger(L, INFO);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "DEBUG");
+  lua_pushinteger(L, DEBUG);
+  lua_settable(L, -3);
+  return 1;
+}
diff --git a/src/l_log.h b/src/l_log.h
new file mode 100644
index 0000000..c564c62
--- /dev/null
+++ b/src/l_log.h
@@ -0,0 +1,40 @@
+/*
+ *  anylike
+ *
+ *  anylike is an IKEv2 Implementation written in Lua and C. It's main
+ *  design goal is to provide anytun and uanytun or any other SATP
+ *  implementation with a key exchange mechanism but it should also be
+ *  possible to use anylike as key exchange daemon for IPSec security
+ *  associations. The use of Lua guarantees that anylike is easily
+ *  portable to many platforms including very small ones like wireless
+ *  routers.
+ *
+ *
+ *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
+ *                          Christian Pointner <equinox@anylike.org>
+ *
+ *  This file is part of anylike.
+ *
+ *  anylike 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.
+ *
+ *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ANYLIKE_l_log_h_INCLUDED
+#define ANYLIKE_l_log_h_INCLUDED
+
+#include <lua.h>
+
+#define LUA_LOGLIBNAME "log"
+LUALIB_API int luaopen_log(lua_State *L);
+
+#endif
diff --git a/src/l_sig_handler.c b/src/l_sig_handler.c
new file mode 100644
index 0000000..1be30ce
--- /dev/null
+++ b/src/l_sig_handler.c
@@ -0,0 +1,99 @@
+/*
+ *  anylike
+ *
+ *  anylike is an IKEv2 Implementation written in Lua and C. It's main
+ *  design goal is to provide anytun and uanytun or any other SATP
+ *  implementation with a key exchange mechanism but it should also be
+ *  possible to use anylike as key exchange daemon for IPSec security
+ *  associations. The use of Lua guarantees that anylike is easily
+ *  portable to many platforms including very small ones like wireless
+ *  routers.
+ *
+ *
+ *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
+ *                          Christian Pointner <equinox@anylike.org>
+ *
+ *  This file is part of anylike.
+ *
+ *  anylike 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.
+ *
+ *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "sig_handler.h"
+
+#include "l_sig_handler.h"
+
+static int l_signal_getfd(lua_State *L)
+{
+  if(!lua_istable(L, -1))
+    luaL_error(L, "can't retreive signal fd");
+
+  lua_pushliteral(L, "fd");
+  lua_gettable(L, -2);
+  return 1;
+}
+
+static int l_signal_dirty(lua_State *L)
+{
+  lua_pushboolean(L, 0);
+  return 1;
+}
+
+static int l_signal_init(lua_State *L)
+{
+  int sig_fd = signal_init();
+  if(sig_fd < 0)
+    luaL_error(L, "error at signal init");
+
+  lua_newtable(L);
+  lua_pushliteral(L, "fd");
+  lua_pushinteger(L, sig_fd);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "getfd");
+  lua_pushcfunction(L, l_signal_getfd);
+  lua_settable(L, -3);
+  lua_pushliteral(L, "dirty");
+  lua_pushcfunction(L, l_signal_dirty);
+  lua_settable(L, -3);
+  return 1;
+}
+
+static int l_signal_stop(lua_State *L)
+{
+  signal_stop();
+  return 0;
+}
+
+static int l_signal_handle(lua_State *L)
+{
+  int ret = signal_handle();
+  lua_pushinteger(L, ret);
+  return 1;
+}
+
+static const struct luaL_reg signal_funcs [] = {
+  { "init", l_signal_init },
+  { "stop", l_signal_stop },
+  { "handle", l_signal_handle },
+  { NULL, NULL }
+};
+
+
+LUALIB_API int luaopen_signal(lua_State *L)
+{
+  luaL_register(L, LUA_SIGNALLIBNAME, signal_funcs);
+  return 1;
+}
diff --git a/src/l_sig_handler.h b/src/l_sig_handler.h
new file mode 100644
index 0000000..447b95e
--- /dev/null
+++ b/src/l_sig_handler.h
@@ -0,0 +1,40 @@
+/*
+ *  anylike
+ *
+ *  anylike is an IKEv2 Implementation written in Lua and C. It's main
+ *  design goal is to provide anytun and uanytun or any other SATP
+ *  implementation with a key exchange mechanism but it should also be
+ *  possible to use anylike as key exchange daemon for IPSec security
+ *  associations. The use of Lua guarantees that anylike is easily
+ *  portable to many platforms including very small ones like wireless
+ *  routers.
+ *
+ *
+ *  Copyright (C) 2009-2010 Markus Grueneis <gimpf@anylike.org>
+ *                          Christian Pointner <equinox@anylike.org>
+ *
+ *  This file is part of anylike.
+ *
+ *  anylike 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.
+ *
+ *  anylike 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 anylike. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ANYLIKE_l_signal_handler_h_INCLUDED
+#define ANYLIKE_l_signal_handler_h_INCLUDED
+
+#include <lua.h>
+
+#define LUA_SIGNALLIBNAME "signal"
+LUALIB_API int luaopen_signal(lua_State *L);
+
+#endif
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..661f9a2
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,260 @@
+/*
+ *  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-2010 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 <ctype.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;
+    char* 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..e9f35dc
--- /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-2010 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..b6c3ae5
--- /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-2010 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/luaclient.c b/src/luaclient.c
new file mode 100644
index 0000000..901d509
--- /dev/null
+++ b/src/luaclient.c
@@ -0,0 +1,268 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "l_log.h"
+#include "l_sig_handler.h"
+#include "l_cmd.h"
+
+#include "log.h"
+#include "options.h"
+
+#include "daemon.h"
+#include "utils.h"
+
+
+#define LUA_MAIN_LOOP_FUNC "main_loop"
+
+static const luaL_Reg anylike_lualibs[] = {
+  {"", luaopen_base},
+  {LUA_LOADLIBNAME, luaopen_package},
+  {LUA_TABLIBNAME, luaopen_table},
+  {LUA_STRLIBNAME, luaopen_string},
+  {LUA_MATHLIBNAME, luaopen_math},
+  {LUA_IOLIBNAME, luaopen_io},
+  {LUA_OSLIBNAME, luaopen_os},
+  {LUA_LOGLIBNAME, luaopen_log},
+  {LUA_SIGNALLIBNAME, luaopen_signal},
+  {LUA_CMDLIBNAME, luaopen_cmd},
+  {NULL, NULL}
+};
+
+int init_main_loop(lua_State *L, const char* filename)
+{
+  const luaL_Reg *lib = anylike_lualibs;
+  for (; lib->func; lib++) {
+    lua_pushcfunction(L, lib->func);
+    lua_pushstring(L, lib->name);
+    lua_call(L, 1, 0);
+  }
+
+  int ret = luaL_loadfile(L, filename);
+  if(ret) {
+    const char* err_str = luaL_checkstring(L, -1);
+    switch(ret) {
+    case LUA_ERRSYNTAX: log_printf(ERROR, "luaL_loadfile(%s) syntax error: %s", filename, err_str); break;
+    case LUA_ERRMEM: log_printf(ERROR, "luaL_loadfile(%s) malloc error: %s", filename, err_str); break;
+    case LUA_ERRFILE: log_printf(ERROR, "luaL_loadfile(%s) file access error: %s", filename, err_str); break;
+    default: log_printf(ERROR, "luaL_loadfile(%s) unknown error: %s", filename, err_str); break;
+    }
+    return -1;
+  }
+
+  ret = lua_pcall(L, 0, 0, 0);
+  if(ret) {
+    const char* err_str = luaL_checkstring(L, -1);
+    switch(ret) {
+    case LUA_ERRRUN: log_printf(ERROR, "lua_pcall() runtime error: %s", err_str); break;
+    case LUA_ERRMEM: log_printf(ERROR, "lua_pcall() malloc error: %s", err_str); break;
+    case LUA_ERRERR: log_printf(ERROR, "lua_pcall() error at error handler function: %s", err_str); break;
+    }
+    return -1;
+  }
+
+  return 0;
+}
+
+int call_main_loop(lua_State* L, int cmd_fd, options_t* opt)
+{
+  lua_getglobal(L, LUA_MAIN_LOOP_FUNC);
+  if(!lua_isfunction(L, -1)) {
+    log_printf(ERROR, "there is no function '%s' inside the loaded file", LUA_MAIN_LOOP_FUNC);
+    return -1;
+  };
+
+  options_lua_push(opt, L);
+
+  int ret = lua_pcall(L, 1, LUA_MULTRET, 0);
+  if(ret) {
+    const char* err_str = luaL_checkstring(L, -1);
+    switch(ret) {
+    case LUA_ERRRUN: log_printf(ERROR, "lua_pcall(%s) runtime error: %s", LUA_MAIN_LOOP_FUNC, err_str); break;
+    case LUA_ERRMEM: log_printf(ERROR, "lua_pcall(%s) malloc error: %s", LUA_MAIN_LOOP_FUNC, err_str); break;
+    case LUA_ERRERR: log_printf(ERROR, "lua_pcall(%s) error at error handler function: %s", LUA_MAIN_LOOP_FUNC, err_str); break;
+    }
+    return -1;
+  }
+
+  int n = lua_gettop(L);
+  log_printf(DEBUG, "%s returned %d values", LUA_MAIN_LOOP_FUNC, n);
+  int i;
+  for (i = 1; i <= n; i++)
+    log_printf(DEBUG, "return value [%d] = '%s'", i, luaL_checkstring(L, i));
+
+  ret = lua_tointeger(L, 1);
+  return ret;
+}
+
+int main_loop(int cmd_fd, options_t* opt)
+{
+  lua_State *L;
+  L = luaL_newstate();
+  if(!L) {
+    log_printf(ERROR, "error creating lua state");
+    return -1;
+  }
+
+  int ret = init_main_loop(L, opt->lua_file_);
+  if(!ret)
+    ret = call_main_loop(L, cmd_fd, opt);
+
+  lua_close(L);
+  return ret;
+}
+
+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,luaclient,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);
+  }
+
+  for(;;) {
+        // extern global variable defined in l_cmd.c
+    cmd_fd = connect_command_socket(opt.command_sock_);
+    if(cmd_fd < 0)
+      ret = 2;
+    else {
+      ret = main_loop(cmd_fd, &opt);
+    }
+
+    if(ret == 2) {
+      log_printf(ERROR, "socket error, trying to reconnect in 5 seconds..");
+
+      if(cmd_fd > 0)
+        close(cmd_fd);
+      sleep(5);
+    }
+    else
+      break;
+  }
+
+  if(cmd_fd > 0)
+    close(cmd_fd);
+
+  if(!ret)
+    log_printf(NOTICE, "normal shutdown");
+  else if(ret < 0)
+    log_printf(NOTICE, "shutdown after error (code %d)", ret);
+  else
+    log_printf(NOTICE, "shutdown after signal");
+
+  options_clear(&opt);
+  log_close();
+
+  return ret;
+}
diff --git a/src/mode-tcpserver.lua b/src/mode-tcpserver.lua
new file mode 100644
index 0000000..26e42e2
--- /dev/null
+++ b/src/mode-tcpserver.lua
@@ -0,0 +1,175 @@
+--
+--  rhctl
+--
+--  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+--
+--  This file is part of rhctl.
+--
+--  rhctl 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.
+--
+--  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+--
+
+package.path = package.path .. ';/usr/share/rhctl/?.lua'
+
+socket = require("socket")
+
+current_mode = nil
+
+function init_server(host, port)
+   local server = assert(socket.tcp())
+
+   assert(server:setoption('reuseaddr', true))
+   assert(server:bind(host, port))
+   assert(server:listen(5))
+
+   return server
+end
+
+local clients = {}
+
+function add_client(hdl)
+   log.printf(log.DEBUG, "new client(" .. hdl:getfd() .. ") from " .. hdl:getpeername())
+   local client = {}
+   if current_mode then
+      client.buffer = current_mode .. "\n"
+   else
+      client.buffer = ""
+   end
+   client.hdl = hdl
+   client.getfd = function() return hdl:getfd() end
+   client.dirty = function() return hdl:dirty() end
+   table.insert(clients, client)
+end
+
+function remove_client(c)
+   local idx = 0
+   local found = false
+   for i, client in ipairs(clients) do
+      if client == c then
+         found = true
+         idx = i
+         break
+      end
+   end
+
+   if found then
+      log.printf(log.DEBUG, "removing client(" .. c.hdl:getfd() .. ")")
+      c.hdl:close()
+      table.remove(clients, idx)
+   end
+end
+
+function cleanup_clients()
+   for _, client in ipairs(clients) do
+      client.hdl:close()
+   end
+end
+
+function clients_get_writeables()
+   local fds = {}
+
+   for _, client in ipairs(clients) do
+      if client.buffer ~= "" then
+         table.insert(fds, client)
+      end
+   end
+
+   return fds
+end
+
+function clients_senddata(data)
+   for _, client in ipairs(clients) do
+      client.buffer  = client.buffer .. data .. "\n"
+   end
+end
+
+function process_cmd(message)
+   log.printf(log.DEBUG, "received message: '%s'", message)
+
+   local new_mode = nil
+   local exps = { "Current Mode: (%a+)", "new Mode: (%a+)" }
+   for _, exp in ipairs(exps) do
+      new_mode = string.match(message, exp)
+      if(new_mode) then
+         new_mode = string.lower(new_mode)
+         break
+      end
+   end
+
+   if(new_mode and new_mode ~= current_mode) then
+      clients_senddata(new_mode)
+      current_mode = new_mode
+   end
+
+   return 0
+end
+
+function main_loop(opt)
+   log.printf(log.NOTICE, "main_loop started")
+   local sig = signal.init()
+   local cmdfd = cmd.init()
+
+   local server = init_server("*", "2345")
+
+   cmd.send_string("listen mode");
+   cmd.send_string("status");
+
+   local return_value = 0
+   while return_value == 0 do
+      local readable, writeable, err = socket.select({ sig , cmdfd , server , unpack(clients) }, clients_get_writeables())
+      if(err) then
+         log.printf(log.ERROR, "select returned with error: %s", err)
+         return_value = -1
+      else
+         for _, input in ipairs(readable) do
+            if input == sig then
+               return_value = signal.handle()
+               if(return_value == 1) then break end
+            elseif input == cmdfd  then
+               return_value = cmd.recv_data(process_cmd)
+               if(return_value ~= 0) then break end
+            elseif input == server then
+               local client = server:accept()
+               if(client == nil) then
+                  return_value =-3
+                  break
+               else
+                  add_client(client)
+               end
+            else
+               if input.hdl then
+                  -- receive is insanely stupid, therefore we must always read one byte only
+                  local _, err = input.hdl:receive(1)
+                  if err then
+                     remove_client(input)
+                  end
+               end
+            end
+         end
+         for _, output in ipairs(writeable) do
+            local ret = output.hdl:send(output.buffer)
+            if(ret == nil) then
+               remove_client(output)
+            else
+               output.buffer = string.sub(output.buffer, ret+1)
+            end
+         end
+      end
+   end
+
+   server:close()
+   cleanup_clients()
+
+   signal.stop()
+   return return_value
+end
diff --git a/src/mode-watch.lua b/src/mode-watch.lua
new file mode 100644
index 0000000..cf716cb
--- /dev/null
+++ b/src/mode-watch.lua
@@ -0,0 +1,119 @@
+--
+--  rhctl
+--
+--  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+--
+--  This file is part of rhctl.
+--
+--  rhctl 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.
+--
+--  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+--
+
+package.path = package.path .. ';/usr/share/rhctl/?.lua'
+
+socket = require("socket")
+utils = require("utils")
+
+function set_master_led()
+   os.execute("/sbin/led.sh set master")
+   os.execute("/sbin/led.sh clear standby")
+end
+
+function set_standby_led()
+   os.execute("/sbin/led.sh clear master")
+   os.execute("/sbin/led.sh set standby")
+end
+
+function clear_leds()
+   os.execute("/sbin/led.sh clear master")
+   os.execute("/sbin/led.sh clear standby")
+end
+
+current_mode = nil
+
+function process_cmd(message)
+   log.printf(log.DEBUG, "received message: '%s'", message)
+
+   local new_mode = nil
+   local exps = { "Current Mode: (%a+)", "new Mode: (%a+)" }
+   for _, exp in ipairs(exps) do
+      new_mode = string.match(message, exp)
+      if(new_mode) then
+         new_mode = string.lower(new_mode)
+         if(new_mode == "master") then
+            set_master_led()
+            break
+         else
+            if(new_mode == "standby") then
+               set_standby_led()
+               break
+            end
+         end
+      end
+   end
+
+   if(new_mode and new_mode ~= current_mode) then
+      log.printf(log.NOTICE, "mode is now " .. new_mode)
+      if(current_mode == nil) then
+         utils.send_mail("logs@helsinki.at", "[RHCTL] (re)started mode is now " .. new_mode,
+                   "RHCTL just (re)started current mode is " .. new_mode)
+      else
+         utils.send_mail("logs@helsinki.at", "[RHCTL] mode changed to " .. new_mode,
+                   "RHCTL just switched from " .. current_mode .. " to " .. new_mode)
+      end
+      current_mode = new_mode
+   end
+
+   return 0
+end
+
+function main_loop(opt)
+   log.printf(log.NOTICE, "main_loop started")
+   local sig = signal.init()
+   local cmdfd = cmd.init()
+
+   cmd.send_string("listen mode");
+   cmd.send_string("status");
+
+   clear_leds()
+
+   local return_value = 0
+   while return_value == 0 do
+      local readable, _, err = socket.select({ sig , cmdfd }, nil)
+      if(err) then
+         log.printf(log.ERROR, "select returned with error: %s", err)
+         return_value = -1
+      else
+         for _, input in ipairs(readable) do
+            if(input == sig) then
+               return_value = signal.handle()
+               if(return_value == 1) then break end
+            else
+               if(input == cmdfd) then
+                  return_value = cmd.recv_data(process_cmd)
+                  if(return_value ~= 0) then break end
+               else
+                  log.printf(log.ERROR, "select returned invalid handle??")
+                  return_value = -1
+                  break;
+               end
+            end
+         end
+      end
+   end
+
+   clear_leds()
+
+   signal.stop()
+   return return_value
+end
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..7faa3ce
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,556 @@
+
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. 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--;
+
+  char* mode = NULL;
+  char* channel = NULL;
+  char* baudrate = NULL;
+
+  int i;
+  for(i=1; argc > 0; ++i)
+  {
+    char* str = argv[i];
+    argc--;
+
+    if(!strcmp(str,"-h") || !strcmp(str,"--help"))
+      return -1;
+#ifndef OPT_STDIOCLIENT
+    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_)
+#endif
+    PARSE_STRING_LIST("-L","--log", opt->log_targets_)
+    PARSE_STRING_PARAM("-s","--command-sock", opt->command_sock_)
+#ifndef OPT_STDIOCLIENT
+    PARSE_STRING_PARAM("-b","--baudrate", baudrate)
+#endif
+#ifdef OPT_SWITCHCTL
+    PARSE_STRING_PARAM("-f","--config", opt->conf_file_)
+    PARSE_STRING_PARAM("-d","--device", opt->switch_dev_)
+    PARSE_STRING_PARAM("-m","--mode", mode)
+    PARSE_STRING_PARAM("-c","--channel", channel)
+#endif
+#ifdef OPT_SERIALCLIENT
+    PARSE_STRING_PARAM("-d","--device", opt->serial_dev_)
+    PARSE_STRING_PARAM("-t","--type", opt->type_)
+#endif
+#ifdef OPT_HEARTBEATCLIENT
+    PARSE_STRING_PARAM("-d","--device", opt->serial_dev_)
+    PARSE_INT_PARAM("-t","--timeout", opt->timeout_)
+    PARSE_STRING_PARAM("-l","--led", opt->led_filename_)
+#endif
+#ifdef OPT_LUACLIENT
+    PARSE_STRING_PARAM("-f","--lua-file", opt->lua_file_)
+#endif
+    else
+      return i;
+  }
+
+  if(mode) {
+    if(!strcmp(mode, "master"))
+      opt->mode_ = MODE_MASTER;
+    else if(!strcmp(mode, "standby"))
+      opt->mode_ = MODE_STANDBY;
+    else {
+      free(mode);
+      return -3;
+    }
+    free(mode);
+  }
+
+  if(channel) {
+    if(!strcmp(channel, "main")) {
+      opt->channel_master_ = CHAN_MAIN;
+      opt->channel_standby_ = CHAN_MAIN;
+    }
+    else if(!strcmp(channel, "music")) {
+      opt->channel_master_ = CHAN_MUSIC;
+      opt->channel_standby_ = CHAN_MUSIC;
+    }
+    else {
+      free(channel);
+      return -4;
+    }
+    free(channel);
+  }
+
+  if(baudrate) {
+    int b = atoi(baudrate);
+    free(baudrate);
+    switch(b) {
+    case 1200: opt->baudrate_ = B1200; break;
+    case 2400: opt->baudrate_ = B2400; break;
+    case 4800: opt->baudrate_ = B4800; break;
+    case 9600: opt->baudrate_ = B9600; break;
+    case 19200: opt->baudrate_ = B19200; break;
+    case 38400: opt->baudrate_ = B38400; break;
+    case 57600: opt->baudrate_ = B57600; break;
+    case 115200: opt->baudrate_ = B115200; break;
+    default: return -5;
+    }
+  }
+
+  return 0;
+}
+
+int options_parse_post(options_t* opt)
+{
+  if(!opt)
+    return -1;
+// nothing to do
+
+#ifdef OPT_SWITCHCTL
+  FILE* conf_file = fopen(opt->conf_file_, "r");
+  if(conf_file) {
+    char buf[100];
+    while(fgets(buf, 100, conf_file) != NULL) {
+      char* tmp, *key, *value;
+      for(tmp = buf;*tmp == ' '; ++tmp);
+      if(*(key = tmp) == 0) continue;
+      for(;*tmp != ' ' && *tmp != 0;++tmp);
+      if(*tmp == 0) continue;
+      *tmp=0;
+      ++tmp;
+      for(;*tmp == ' ';++tmp);
+      if(*(value = tmp) == 0) continue;
+      for(;*tmp != ' ' && *tmp != 0 && *tmp != '\n';++tmp);
+      *tmp = 0;
+
+      if(key_value_storage_add(&opt->alias_table_, key, value))
+        return -2;
+    }
+    fclose(conf_file);
+  }
+  else {
+    log_printf(ERROR,"unable to open conf file (%s): %s", opt->conf_file_, strerror(errno));
+    return -1;
+  }
+#endif
+
+  return 0;
+}
+
+void options_default(options_t* opt)
+{
+  if(!opt)
+    return;
+
+#ifdef OPT_SWITCHCTL
+  opt->progname_ = strdup("switchctl");
+#endif
+#ifdef OPT_SERIALCLIENT
+  opt->progname_ = strdup("serialclient");
+#endif
+#ifdef OPT_STDIOCLIENT
+  opt->progname_ = strdup("stdioclient");
+#endif
+#ifdef OPT_HEARTBEATCLIENT
+  opt->progname_ = strdup("heartbeatclient");
+#endif
+#ifdef OPT_LUACLIENT
+  opt->progname_ = strdup("luaclient");
+#endif
+
+/* common */
+  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/rhctl/switchctl.sock");
+  opt->baudrate_ = B19200;
+
+/* switchctl */
+  opt->mode_ = MODE_MASTER;
+  opt->channel_master_ = CHAN_MAIN;
+  opt->channel_standby_ = CHAN_MAIN;
+  opt->conf_file_ = strdup("/etc/rhctl/switchctl.conf");
+  opt->switch_dev_ = strdup("/dev/audioswitch");
+  key_value_storage_init(&opt->alias_table_);
+
+/* serialclient and heartbeatclient */
+  opt->serial_dev_ = strdup("/dev/ttyUSB0");
+
+/* serialclient only */
+  opt->type_ = NULL;
+
+/* heartbeatclient only */
+  opt->timeout_ = 15;
+  opt->led_filename_ = NULL;
+
+/* luaclient only */
+  opt->lua_file_ = strdup("/usr/share/rhctl/mode-leds.lua");
+}
+
+#ifdef OPT_LUACLIENT
+void options_lua_push_string(lua_State* L, const int tidx, const char* key, const char* value)
+{
+  lua_pushstring(L, key);
+  lua_pushstring(L, value);
+  lua_settable(L, tidx);
+}
+
+void options_lua_push_int(lua_State* L, const int tidx, const char* key, const u_int32_t value)
+{
+  lua_pushstring(L, key);
+  lua_pushinteger(L, value);
+  lua_settable(L, tidx);
+}
+
+void options_lua_push_boolean(lua_State* L, const int tidx, const char* key, const u_int32_t value)
+{
+  lua_pushstring(L, key);
+  lua_pushboolean(L, value);
+  lua_settable(L, tidx);
+}
+
+void options_lua_push_string_list(lua_State* L, const int tidx, string_list_t* lst)
+{
+  if(!lst)
+    return;
+
+  string_list_element_t* tmp = lst->first_;
+  if(tmp) {
+    lua_pushstring(L, "log_targets");
+    lua_newtable(L);
+    int i = 1;
+    while(tmp) {
+      lua_pushinteger(L, i++);
+      lua_pushstring(L, tmp->string_);
+      lua_settable(L, -3);
+      tmp = tmp->next_;
+    }
+    lua_settable(L, tidx);
+  }
+}
+
+void options_lua_push(options_t* opt, lua_State* L)
+{
+  lua_newtable(L);
+
+  options_lua_push_string(L, -3, "progname", opt->progname_);
+  options_lua_push_boolean(L, -3, "daemonize", opt->daemonize_);
+  options_lua_push_string(L, -3, "username", opt->username_);
+  options_lua_push_string(L, -3, "groupname", opt->groupname_);
+  options_lua_push_string(L, -3, "chroot_dir", opt->chroot_dir_);
+  options_lua_push_string(L, -3, "pid_file", opt->pid_file_);
+  options_lua_push_string_list(L, -3, &(opt->log_targets_));
+  options_lua_push_string(L, -3, "command_sock", opt->command_sock_);
+  options_lua_push_string(L, -3, "lua_file", opt->lua_file_);
+}
+#endif
+
+void options_clear(options_t* opt)
+{
+  if(!opt)
+    return;
+
+/* common */
+  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_);
+
+/* switchctl */
+  if(opt->conf_file_)
+    free(opt->conf_file_);
+  if(opt->switch_dev_)
+    free(opt->switch_dev_);
+  key_value_storage_clear(&opt->alias_table_);
+
+/* serialclient and heartbeatclient */
+  if(opt->serial_dev_)
+    free(opt->serial_dev_);
+
+/* serialclient only */
+  if(opt->type_)
+    free(opt->type_);
+
+/* heartbeatcleint only */
+  if(opt->led_filename_)
+    free(opt->led_filename_);
+
+/* luaclient only */
+  if(opt->lua_file_)
+    free(opt->lua_file_);
+}
+
+void options_print_usage()
+{
+  printf("USAGE:\n");
+#ifdef OPT_SWITCHCTL
+  printf("switchctl\n");
+#endif
+#ifdef OPT_SERIALCLIENT
+  printf("serialclient\n");
+#endif
+#ifdef OPT_STDIOCLIENT
+  printf("serialclient\n");
+#endif
+#ifdef OPT_HEARTBEATCLIENT
+  printf("heartbeatclient\n");
+#endif
+  printf("  [-h|--help]                         prints this...\n");
+#ifndef OPT_STDIOCLIENT
+  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");
+#endif
+  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/rhctl/switchctl.sock\n");
+#ifndef OPT_STDIOCLIENT
+  printf("  [-b|--baudrate] <baudrate>          the baudrate of the tty to use e.g. 19200\n");
+#endif
+#ifdef OPT_SWITCHCTL
+  printf("  [-d|--device] <tty>                 the tty the audio switch is connected to e.g. /dev/audioswitch\n");
+  printf("  [-f|--config] <file>                the configuration file e.g. /etc/rhctl/switchctl.conf\n");
+  printf("  [-m|--mode] <mode>                  the initial mode to use e.g. master\n");
+  printf("  [-c|--channel] <channel>            the initial channel to use e.g. main\n");
+#endif
+#ifdef OPT_SERIALCLIENT
+  printf("  [-d|--device] <tty>                 the tty to connect to e.g. /dev/ttyUSB0\n");
+  printf("  [-t|--type] <type>                  use this client type\n");
+#endif
+#ifdef OPT_HEARTBEATCLIENT
+  printf("  [-d|--device] <tty>                 the tty to connect to e.g. /dev/ttyUSB0\n");
+  printf("  [-t|--timeout] <timeout>            heartbeat timeout in tenths of a second e.g. 15 -> 1.5s\n");
+  printf("  [-l|--led] <led filename>           sysfs filename of led device to blink\n");
+#endif
+#ifdef OPT_LUACLIENT
+  printf("  [-f|--lua-file] <file>              the configuration file e.g. /usr/share/rhctl/mode-leds.lua\n");
+#endif
+}
+
+void options_print(options_t* opt)
+{
+  if(!opt)
+    return;
+
+  printf("progname: '%s'\n", opt->progname_);
+#ifndef OPT_STDIOCLIENT
+  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_);
+#endif
+  printf("log_targets: \n");
+  string_list_print(&opt->log_targets_, "  '", "'\n");
+
+  printf("command_sock: '%s'\n", opt->command_sock_);
+
+#ifndef OPT_STDIOCLIENT
+  char* br;
+  switch(opt->baudrate_) {
+  case B1200: br = "1200"; break;
+  case B2400: br = "2400"; break;
+  case B4800: br = "4800"; break;
+  case B9600: br = "9600"; break;
+  case B19200: br = "19200"; break;
+  case B38400: br = "38400"; break;
+  case B57600: br = "57600"; break;
+  case B115200: br = "115200"; break;
+  default: br = "invalid"; break;
+  }
+  printf("baudrate: '%s'\n", br);
+#endif
+
+#ifdef OPT_SWITCHCTL
+  printf("mode: '%s'\n", opt->mode_ == MODE_MASTER ? "master" : "standby");
+  printf("channel_master: '%s'\n", opt->channel_master_ == CHAN_MAIN ? "main" : "music");
+  printf("channel_standby: '%s'\n", opt->channel_standby_ == CHAN_MAIN ? "main" : "music");
+  printf("conf_file: '%s'\n", opt->conf_file_);
+  printf("switch_dev: '%s'\n", opt->switch_dev_);
+  printf("alias_table: \n");
+  key_value_storage_print(&opt->alias_table_, "  '", "' -> '", "'\n");
+#endif
+
+#ifdef OPT_SERIALCLIENT
+  printf("serial_dev: '%s'\n", opt->serial_dev_);
+  printf("type: '%s'\n", opt->type_);
+#endif
+
+#ifdef OPT_HEARTBEATCLIENT
+  printf("serial_dev: '%s'\n", opt->serial_dev_);
+  printf("timeout: %d\n", opt->timeout_);
+  printf("led_filename: '%s'\n", opt->led_filename_);
+#endif
+
+#ifdef OPT_LUACLIENT
+  printf("lua_file: '%s'\n", opt->lua_file_);
+#endif
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644
index 0000000..450c8f2
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,87 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_options_h_INCLUDED
+#define RHCTL_options_h_INCLUDED
+
+#include <lua.h>
+
+#include "string_list.h"
+#include "key_value_storage.h"
+#include <termios.h>
+
+enum mode_enum { MODE_MASTER, MODE_STANDBY };
+typedef enum mode_enum switchctl_mode_t;
+
+enum channel_enum { CHAN_MAIN, CHAN_MUSIC };
+typedef enum channel_enum switchctl_channel_t;
+
+struct options_struct {
+/* common */
+  char* progname_;
+  int daemonize_;
+  char* username_;
+  char* groupname_;
+  char* chroot_dir_;
+  char* pid_file_;
+  string_list_t log_targets_;
+
+  char* command_sock_;
+  speed_t baudrate_;
+
+/* switchctl */
+  switchctl_mode_t mode_;
+  switchctl_channel_t channel_master_;
+  switchctl_channel_t channel_standby_;
+  char* conf_file_;
+  char* switch_dev_;
+  key_value_storage_t alias_table_;
+
+/* serialclient and heartbeatclient */
+  char* serial_dev_;
+
+/* serialclient only */
+  char* type_;
+
+/* heartbeatclient only */
+  u_int32_t timeout_;
+  char* led_filename_;
+
+/* luaclient only */
+  char* lua_file_;
+};
+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_lua_push_string(lua_State* L, const int tidx, const char* key, const char* value);
+void options_lua_push_int(lua_State* L, const int tidx, const char* key, const u_int32_t value);
+void options_lua_push_boolean(lua_State* L, const int tidx, const char* key, const u_int32_t value);
+void options_lua_push_string_list(lua_State* L, const int tidx, string_list_t* lst);
+void options_lua_push(options_t* opt, lua_State* L);
+void options_clear(options_t* opt);
+void options_print_usage();
+void options_print(options_t* opt);
+
+#endif
diff --git a/src/serialclient.c b/src/serialclient.c
new file mode 100644
index 0000000..18d62db
--- /dev/null
+++ b/src/serialclient.c
@@ -0,0 +1,285 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "log.h"
+#include "sig_handler.h"
+#include "options.h"
+
+#include "daemon.h"
+#include "utils.h"
+
+int process_cmd(const char* cmd, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  return 0;
+}
+
+int process_data(int src_fd, int dest_fd)
+{
+  char* buffer[100];
+  int ret = read(src_fd, buffer, 100);
+  if(!ret)
+    return 2;
+  if(ret == -1 && errno == EAGAIN)
+    return 0;
+  if(ret < 0)
+    return ret;
+
+  log_printf(DEBUG, "read %d bytes from fd (%d)", ret, src_fd);
+
+  int len = ret;
+  int offset = 0;
+  for(;;) {
+    ret = write(dest_fd, &buffer[offset], len - offset);
+    if(ret < 0) {
+      if(errno != EINTR)
+        return ret;
+
+      ret = 0;
+    }
+
+    offset += ret;
+    if(offset >= len)
+      break;
+  }
+  log_printf(DEBUG, "wrote %d bytes to fd (%d)", offset, dest_fd);
+  return 0;
+}
+
+int main_loop(int serial_fd, int cmd_fd, options_t* opt)
+{
+  log_printf(NOTICE, "entering main loop");
+
+  fd_set readfds, tmpfds;
+  FD_ZERO(&readfds);
+  FD_SET(serial_fd, &readfds);
+  FD_SET(cmd_fd, &readfds);
+  int max_fd = serial_fd > cmd_fd ? serial_fd : cmd_fd;
+
+  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;
+
+  int return_value = 0;
+  if(opt->type_) {
+    char* tmp;
+    int len = asprintf(&tmp, "type %s\n", opt->type_);
+    if(len < 0) {
+      log_printf(ERROR, "memory error at init");
+      return_value = -2;
+    }
+    else {
+      return_value = send_string(cmd_fd, tmp);
+      free(tmp);
+      if(return_value <= 0) {
+        log_printf(ERROR, "error setting client type");
+        return_value = -1;
+      }
+      else {
+        log_printf(NOTICE, "connecting as type '%s'", opt->type_);
+        return_value = 0;
+      }
+    }
+  }
+
+  while(!return_value) {
+    memcpy(&tmpfds, &readfds, sizeof(tmpfds));
+
+    int ret = select(max_fd+1, &tmpfds, NULL, NULL, NULL);
+    if(ret == -1 && errno != EINTR) {
+      log_printf(ERROR, "select returned with error: %s", strerror(errno));
+      return_value = -1;
+      break;
+    }
+    if(ret == -1 || !ret)
+      continue;
+
+    if(FD_ISSET(sig_fd, &tmpfds))
+      if(signal_handle())
+        return_value = 1;
+
+    if(FD_ISSET(serial_fd, &tmpfds))
+      return_value = process_data(serial_fd, cmd_fd);
+
+    if(FD_ISSET(cmd_fd, &tmpfds)) {
+      return_value = process_data(cmd_fd, serial_fd);
+      if(return_value == 2) return_value = 3;
+    }
+  }
+
+  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 == -5) {
+      fprintf(stderr, "syntax error: invalid baudrate\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,serialclient,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_fd = 0;
+  int serial_fd = 0;
+  for(;;) {
+    cmd_fd = connect_command_socket(opt.command_sock_);
+    if(cmd_fd < 0)
+      ret = 3;
+    else {
+      serial_fd = open(opt.serial_dev_, O_RDWR | O_NOCTTY);
+      if(serial_fd < 0)
+        ret = 2;
+      else {
+        ret = setup_tty(serial_fd, opt.baudrate_);
+        if(ret)
+          ret = 2;
+        else
+          ret = main_loop(serial_fd, cmd_fd, &opt);
+      }
+    }
+
+    if(ret == 2 || ret == 3) {
+      if(ret == 2)
+        log_printf(ERROR, "%s error, trying to reopen in 5 seconds..", opt.serial_dev_);
+      if(ret == 3)
+        log_printf(ERROR, "socket error, trying to reconnect in 5 seconds..");
+
+      if(cmd_fd > 0)
+        close(cmd_fd);
+      if(serial_fd > 0)
+        close(serial_fd);
+      sleep(5);
+    }
+    else
+      break;
+  }
+
+  if(cmd_fd > 0)
+    close(cmd_fd);
+  if(serial_fd > 0)
+    close(serial_fd);
+
+  if(!ret)
+    log_printf(NOTICE, "normal shutdown");
+  else if(ret < 0)
+    log_printf(NOTICE, "shutdown after error (code %d)", ret);
+  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..02dbcb0
--- /dev/null
+++ b/src/sig_handler.c
@@ -0,0 +1,163 @@
+/*
+ *  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-2010 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 "sig_handler.h"
+
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+static int sig_pipe_fds[2];
+int reset_flag;
+
+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"); reset_flag = 1; 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(SIGPIPE, &act, NULL);
+  sigaction(SIGCHLD, &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..3acd53a
--- /dev/null
+++ b/src/sig_handler.h
@@ -0,0 +1,45 @@
+/*
+ *  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-2010 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
+
+extern int reset_flag;
+
+int signal_init();
+int signal_handle();
+void signal_stop();
+
+#endif
diff --git a/src/silence-watch.lua b/src/silence-watch.lua
new file mode 100644
index 0000000..0a2ebe2
--- /dev/null
+++ b/src/silence-watch.lua
@@ -0,0 +1,87 @@
+--
+--  rhctl
+--
+--  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+--
+--  This file is part of rhctl.
+--
+--  rhctl 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.
+--
+--  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+--
+
+package.path = package.path .. ';/usr/share/rhctl/?.lua'
+
+socket = require("socket")
+utils = require("utils")
+
+current_state = nil
+
+function process_cmd(message)
+   log.printf(log.DEBUG, "received message: '%s'", message)
+
+   silence_state = string.match(message, "S0S,(%d)")
+
+   if(silence_state and silence_state ~= current_state) then
+      if(silence_state == "0") then
+         log.printf(log.NOTICE, "seen some noise")
+         utils.send_mail("silence@helsinki.at", "[RHCTL] sees some noise",
+                   "There is some noise at output 1 of the audioswitch\nCurrent State is: " .. message)
+      else
+         if (silence_state == "1") then
+            log.printf(log.NOTICE, "silence detected")
+            utils.send_mail("silence@helsinki.at", "[RHCTL] silence detected ",
+                      "Silence detected at output 1 of the audioswitch, make some noise!!\nCurrent State is:  " .. message)
+         end
+      end
+      current_state = silence_state
+   end
+
+   return 0
+end
+
+function main_loop(opt)
+   log.printf(log.NOTICE, "main_loop started")
+   local sig = signal.init()
+   local cmdfd = cmd.init()
+
+   cmd.send_string("listen silence");
+   cmd.send_string("switch *0SS");
+
+   local return_value = 0
+   while return_value == 0 do
+      local readable, _, err = socket.select({ sig , cmdfd }, nil)
+      if(err) then
+         log.printf(log.ERROR, "select returned with error: %s", err)
+         return_value = -1
+      else
+         for _, input in ipairs(readable) do
+            if(input == sig) then
+               return_value = signal.handle()
+               if(return_value == 1) then break end
+            else
+               if(input == cmdfd) then
+                  return_value = cmd.recv_data(process_cmd)
+                  if(return_value ~= 0) then break end
+               else
+                  log.printf(log.ERROR, "select returned invalid handle??")
+                  return_value = -1
+                  break;
+               end
+            end
+         end
+      end
+   end
+
+   signal.stop()
+   return return_value
+end
diff --git a/src/stdioclient.c b/src/stdioclient.c
new file mode 100644
index 0000000..670758a
--- /dev/null
+++ b/src/stdioclient.c
@@ -0,0 +1,190 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "log.h"
+#include "sig_handler.h"
+#include "options.h"
+
+#include "daemon.h"
+#include "utils.h"
+
+int process_cmd(const char* cmd, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  return 0;
+}
+
+int process_data(int src_fd, int dest_fd)
+{
+  char* buffer[100];
+  int ret = read(src_fd, buffer, 100);
+  if(!ret)
+    return 2;
+  if(ret == -1 && errno == EAGAIN)
+    return 0;
+  if(ret < 0)
+    return ret;
+
+  log_printf(DEBUG, "read %d bytes from fd (%d)", ret, src_fd);
+
+  int len = ret;
+  int offset = 0;
+  for(;;) {
+    ret = write(dest_fd, &buffer[offset], len - offset);
+    if(ret < 0) {
+
+      if(errno != EINTR)
+        return ret;
+
+      ret = 0;
+    }
+
+    offset += ret;
+    if(offset+1 >= len)
+      break;
+  }
+  return 0;
+}
+
+int main_loop(int cmd_fd, options_t* opt)
+{
+  log_printf(NOTICE, "entering main loop");
+
+  fd_set readfds, tmpfds;
+  FD_ZERO(&readfds);
+  FD_SET(0, &readfds);
+  FD_SET(cmd_fd, &readfds);
+  int max_fd = cmd_fd;
+
+  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;
+
+  int return_value = 0;
+
+  while(!return_value) {
+    memcpy(&tmpfds, &readfds, sizeof(tmpfds));
+
+    int ret = select(max_fd+1, &tmpfds, NULL, NULL, NULL);
+    if(ret == -1 && errno != EINTR) {
+      log_printf(ERROR, "select returned with error: %s", strerror(errno));
+      return_value = -1;
+      break;
+    }
+    if(ret == -1 || !ret)
+      continue;
+
+    if(FD_ISSET(sig_fd, &tmpfds))
+      if(signal_handle())
+        return_value = 1;
+
+    if(FD_ISSET(0, &tmpfds))
+      return_value = process_data(0, cmd_fd);
+
+    if(FD_ISSET(cmd_fd, &tmpfds))
+      return_value = process_data(cmd_fd, 1);
+  }
+
+  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("stderr:2");
+  }
+  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);
+  }
+
+  int cmd_fd = connect_command_socket(opt.command_sock_);
+  if(cmd_fd < 0) {
+    options_clear(&opt);
+    log_close();
+    exit(-1);
+  }
+
+  ret = main_loop( cmd_fd, &opt);
+
+  close(cmd_fd);
+
+  if(!ret)
+    log_printf(NOTICE, "normal shutdown");
+  else if(ret < 0)
+    log_printf(NOTICE, "shutdown after error (code %d)", ret);
+  else
+    log_printf(NOTICE, "shutdown after signal");
+
+  options_clear(&opt);
+  log_close();
+
+  return ret;
+}
diff --git a/src/string_list.c b/src/string_list.c
new file mode 100644
index 0000000..5e0ddf8
--- /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-2010 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..9e210ae
--- /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-2010 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/switchctl.c b/src/switchctl.c
new file mode 100644
index 0000000..f3daab0
--- /dev/null
+++ b/src/switchctl.c
@@ -0,0 +1,1158 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "log.h"
+#include "sig_handler.h"
+#include "options.h"
+
+#include "command_queue.h"
+#include "client_list.h"
+#include "key_value_storage.h"
+
+#include "daemon.h"
+#include "utils.h"
+
+struct state_struct {
+  switchctl_mode_t mode_;
+  switchctl_channel_t channel_master_;
+  switchctl_channel_t channel_standby_;
+  bool hb_state_master_;
+  bool hb_state_standby_;
+};
+typedef struct state_struct state_t;
+
+state_t state_;
+
+int send_command(int switch_fd, cmd_t* cmd)
+{
+  if(!cmd)
+    return -1;
+
+  char* c = NULL;
+  switch(cmd->cmd) {
+  case SWITCH:
+  case CHANNEL: c = cmd->param; break;
+  case STATUS: c = "*0SL"; break;
+  default: break;
+  }
+
+  if(c == NULL)
+    return 0;
+
+  int ret = send_string(switch_fd, c);
+  if(ret > 0) {
+    cmd_sent(cmd);
+    return 0;
+  }
+
+  return ret;
+}
+
+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;
+}
+
+void send_usage(int fd)
+{
+  if(fd < 0)
+    return;
+
+  send_response(fd, "Usage: ");
+  send_response(fd, "  help       prints this");
+  send_response(fd, "  quit       close connection");
+  send_response(fd, "  type       set client type, one of: master, standby, hb_master, hb_standby");
+  send_response(fd, "  channel    switch to channel main or music");
+  send_response(fd, "             client type master and standby only");
+  send_response(fd, "  mode       switch to mode master or standby");
+  send_response(fd, "  heartbeat  update heartbeat status for master or standby");
+  send_response(fd, "  status     get actual status from switch");
+  send_response(fd, "  health     get health info");
+  send_response(fd, "  listen     register for events, no parameter for all");
+  send_response(fd, "             one of: request, mode, status, health, gpi, oc, relay, silence, none");
+  send_response(fd, "  log        add line to daemons log file");
+  send_response(fd, "  switch     send raw commands to the switch");
+}
+
+int process_cmd_request(const char* cmd, cmd_id_t cmd_id, const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  client_t* c = client_find(client_lst, fd);
+  if(!c) {
+    log_printf(WARNING, "ignoring request from unknown client");
+    send_response(fd, "EEE: switch: client not found in client list?!");
+    return 0;
+  }
+
+  char* cmd_param = NULL;
+  if(cmd_id == SWITCH) {
+    if((state_.mode_ == MODE_MASTER && c->type == STANDBY )||
+       (state_.mode_ == MODE_STANDBY && c->type == MASTER ))
+    {
+      log_printf(INFO, "silently ignoring request from inactive system (%s)", c->type == MASTER ? "master" : "standby");
+      return 0;
+    }
+
+    if(!param) {
+      log_printf(INFO, "ignoring switch command without parameter");
+      send_response(fd, "EEE: switch: missing parameter");
+      return 0;
+    }
+
+    if(param[0] == '*') {
+      cmd_param = strdup(param);
+    }
+    else {
+      const char* ch_name = NULL;
+      char* ch_nr = NULL;
+      if(!strncmp(param, "up ", 3)) {
+        cmd_param = strdup("*0FUii");
+        ch_name = &(param[3]);
+        ch_nr = &(cmd_param[4]);
+      }
+      else if(!strncmp(param, "down ", 5)) {
+        cmd_param = strdup("*0FDii");
+        ch_name = &(param[5]);
+        ch_nr = &(cmd_param[4]);
+      }
+      else if(!strncmp(param, "select ", 7)) {
+        cmd_param = strdup("*0ii1");
+        ch_name = &(param[7]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "add ", 4)) {
+        cmd_param = strdup("*0ii3");
+        ch_name = &(param[4]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "rm ", 3)) {
+        cmd_param = strdup("*0ii5");
+        ch_name = &(param[3]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "mute ", 5)) {
+        cmd_param = strdup("*0iiM1");
+        ch_name = &(param[5]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "select2 ", 8)) {
+        cmd_param = strdup("*0ii2");
+        ch_name = &(param[8]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "add2 ", 5)) {
+        cmd_param = strdup("*0ii4");
+        ch_name = &(param[5]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "rm2 ", 4)) {
+        cmd_param = strdup("*0ii6");
+        ch_name = &(param[4]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else if(!strncmp(param, "mute2 ", 6)) {
+        cmd_param = strdup("*0iiM2");
+        ch_name = &(param[6]);
+        ch_nr = &(cmd_param[2]);
+      }
+      else {
+        log_printf(INFO, "ignoring invalid switch command: '%s'", param);
+        send_response(fd, "EEE: switch: invalid command");
+        return 0;
+      }
+      char* ch_tmp = key_value_storage_find(&opt->alias_table_, ch_name);
+      if(!ch_tmp || ch_tmp[0] == 0 || ch_tmp[1] == 0 || ch_tmp[2] != 0) {
+        log_printf(ERROR, "invalid channel name or number: %s", ch_name);
+        send_response(fd, "EEE: switch: invalid channel name or number");
+        free(cmd_param);
+        return 0;
+      }
+
+      ch_nr[0] = ch_tmp[0];
+      ch_nr[1] = ch_tmp[1];
+    }
+    log_printf(DEBUG, "enqueing command to switch: '%s' (request was: '%s')", cmd_param, param);
+  }
+
+  int ret = cmd_push(cmd_q, fd, cmd_id, cmd_param);
+  if(cmd_param)
+    free(cmd_param);
+  if(ret)
+    return ret;
+
+  if(cmd_id == STATUS) {
+    char buf[3][30];
+    snprintf(buf[0], 30, "Current Mode: %s", state_.mode_ == MODE_MASTER ? "Master" : "Standby");
+    send_response(fd, buf[0]);
+    snprintf(buf[1], 30, "Master Channel: %s", state_.channel_master_ == CHAN_MAIN ? "Main" : "Music");
+    send_response(fd, buf[1]);
+    snprintf(buf[2], 30, "Standby Channel: %s", state_.channel_standby_ == CHAN_MAIN ? "Main" : "Music");
+    send_response(fd, buf[2]);
+    client_t* client;
+    int listener_cnt = 0;
+    for(client = client_lst; client; client = client->next)
+      if(client->status_listener && client->fd != fd) {
+        send_response(client->fd, buf[0]);
+        send_response(client->fd, buf[1]);
+        send_response(client->fd, buf[2]);
+        listener_cnt++;
+      }
+    log_printf(DEBUG, "sent status to %d additional listeners", listener_cnt);
+  }
+
+  log_printf(NOTICE, "command: %s", cmd);
+
+  return 0;
+}
+
+int crossfade(const char* ch_from, const char* ch_to, int fd, cmd_t **cmd_q, options_t* opt)
+{
+  char* cmd_param = strdup("*0FDii*0FUii");
+
+  char* ch_nr_from = key_value_storage_find(&opt->alias_table_, ch_from);
+  if(!ch_nr_from || ch_nr_from[0] == 0 || ch_nr_from[1] == 0 || ch_nr_from[2] != 0) {
+    log_printf(ERROR, "invalid channel name or number: %s", ch_from);
+    send_response(fd, "EEE: channel: invalid channel name or number");
+    free(cmd_param);
+    return 1;
+  }
+
+  char* ch_nr_to = key_value_storage_find(&opt->alias_table_, ch_to);
+  if(!ch_nr_to || ch_nr_to[0] == 0 || ch_nr_to[1] == 0 || ch_nr_to[2] != 0) {
+    log_printf(ERROR, "invalid channel name or number: %s", ch_to);
+    send_response(fd, "EEE: channel: invalid channel name or number");
+    free(cmd_param);
+    return 1;
+  }
+
+  cmd_param[4] = ch_nr_from[0];
+  cmd_param[5] = ch_nr_from[1];
+  cmd_param[10] = ch_nr_to[0];
+  cmd_param[11] = ch_nr_to[1];
+
+  log_printf(DEBUG, "enqueing command to switch: '%s'", cmd_param);
+  int ret = cmd_push(cmd_q, fd, CHANNEL, cmd_param);
+  free(cmd_param);
+
+  return ret;
+}
+
+int process_cmd_channel(const char* cmd, const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  client_t* c = client_find(client_lst, fd);
+  if(!c) {
+    log_printf(WARNING, "ignoring request from unknown client");
+    send_response(fd, "EEE: channel: client not found in client list?!");
+    return 0;
+  }
+
+  if(c->type != MASTER && c->type != STANDBY) {
+    log_printf(WARNING, "ignoring request from client of wrong type");
+    send_response(fd, "EEE: channel: client type doesn't fit");
+    return 0;
+  }
+
+  if(!param) {
+    log_printf(INFO, "ignoring channel command without parameter");
+    send_response(fd, "EEE: channel: missing parameter");
+    return 0;
+  }
+
+  switchctl_channel_t old_channel;
+  if(c->type == MASTER)
+    old_channel = state_.channel_master_;
+  else
+    old_channel = state_.channel_standby_;
+
+  char* ch_from = NULL;
+  char* ch_to = NULL;
+  if(!strcmp(param, "main")) {
+    if(state_.mode_ == MODE_MASTER) {
+      ch_from = "master_music";
+      ch_to = "master_main";
+    }
+    else {
+      ch_from = "standby_music";
+      ch_to = "standby_main";
+    }
+    if(c->type == MASTER)
+      state_.channel_master_ = CHAN_MAIN;
+    else
+      state_.channel_standby_ = CHAN_MAIN;
+  }
+  else if(!strcmp(param, "music")) {
+    if(state_.mode_ == MODE_MASTER) {
+      ch_from = "master_main";
+      ch_to = "master_music";
+    }
+    else {
+      ch_from = "standby_main";
+      ch_to = "standby_music";
+    }
+    if(c->type == MASTER)
+      state_.channel_master_ = CHAN_MUSIC;
+    else
+      state_.channel_standby_ = CHAN_MUSIC;
+  }
+
+  if((state_.mode_ == MODE_MASTER && c->type == STANDBY )||
+     (state_.mode_ == MODE_STANDBY && c->type == MASTER ))
+  {
+    log_printf(INFO, "no crossfade for inactive system (%s), just updated channel info", c->type == MASTER ? "master" : "standby");
+    return 0;
+  }
+
+  int ret = crossfade(ch_from, ch_to, fd, cmd_q, opt);
+  if(ret) {
+    if(c->type == MASTER)
+      state_.channel_master_ = old_channel;
+    else
+      state_.channel_standby_ = old_channel;
+
+    if(ret > 0)
+      return 0;
+
+    return ret;
+  }
+
+  log_printf(NOTICE, "command: %s", cmd);
+
+  return 0;
+}
+
+int change_mode(switchctl_mode_t old_mode, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  char* ch_from = NULL;
+  if(old_mode == MODE_MASTER && state_.channel_master_ == CHAN_MAIN)
+    ch_from = "master_main";
+  else if(old_mode == MODE_MASTER && state_.channel_master_ == CHAN_MUSIC)
+    ch_from = "master_music";
+  else if(old_mode == MODE_STANDBY && state_.channel_standby_ == CHAN_MAIN)
+    ch_from = "standby_main";
+  else if(old_mode == MODE_STANDBY && state_.channel_standby_ == CHAN_MUSIC)
+    ch_from = "standby_music";
+  else {
+    state_.mode_ = old_mode;
+    log_printf(ERROR, "EEE: mode: old config is illegal?!");
+    return 0;
+  }
+
+  char* ch_to = NULL;
+  if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MAIN)
+    ch_to = "master_main";
+  else if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MUSIC)
+    ch_to = "master_music";
+  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MAIN)
+    ch_to = "standby_main";
+  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MUSIC)
+    ch_to = "standby_music";
+  else {
+    state_.mode_ = old_mode;
+    log_printf(ERROR, "EEE: mode: current config is illegal?!");
+    return 0;
+  }
+
+  int ret = crossfade(ch_from, ch_to, fd, cmd_q, opt);
+  if(ret) {
+    state_.mode_ = old_mode;
+    if(ret > 0)
+      return 0;
+
+    return ret;
+  }
+
+  char* mode_str;
+  int len = asprintf(&mode_str, "new Mode: %s", state_.mode_ == MODE_MASTER ? "master" : "standby");
+  if(len > 0) {
+    log_printf(NOTICE, "%s", mode_str);
+    client_t* client;
+    int listener_cnt = 0;
+    for(client = client_lst; client; client = client->next)
+      if(client->mode_listener && client->fd != fd) {
+        send_response(client->fd, mode_str);
+        listener_cnt++;
+      }
+    free(mode_str);
+    log_printf(DEBUG, "sent new mode to %d additional listeners", listener_cnt);
+  }
+
+  return 0;
+}
+
+int process_cmd_mode(const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  switchctl_mode_t old_mode = state_.mode_;
+
+  if(param) {
+    if(!strncmp(param, "master", 6))
+      state_.mode_ = MODE_MASTER;
+    else if(!strncmp(param, "standby", 7))
+      state_.mode_ = MODE_STANDBY;
+    else {
+      log_printf(DEBUG, "unkown mode '%s'", param);
+      send_response(fd, "EEE: mode: unknown mode");
+      return 0;
+    }
+
+        // swap master with standby channels only when mode has changed
+    if(old_mode != state_.mode_)
+      return change_mode(old_mode, fd, cmd_q, client_lst, opt);
+  }
+  else {
+    log_printf(ERROR, "unable to set mode: empty parameter");
+    send_response(fd, "EEE: mode: missing parameter");
+  }
+
+  return 0;
+}
+
+int send_health_status(int fd, client_t* client_lst)
+{
+  bool mc, sc, hmc, hsc;
+  mc = sc = hmc = hsc = 0;
+
+  client_t* client;
+  for(client = client_lst; client; client = client->next) {
+    switch(client->type) {
+      case MASTER: mc=1; break;
+      case STANDBY: sc=1; break;
+      case HB_MASTER: hmc=1; break;
+      case HB_STANDBY: hsc=1; break;
+      default: break;
+    }
+  }
+
+  char buf[5][50];
+  snprintf(buf[0], 50, "Health: %s", (mc && sc && hmc && hsc && state_.hb_state_master_ && state_.hb_state_standby_) ? "ok" : "error");
+  snprintf(buf[1], 50, "Master: %s", (mc) ? "connected" : "offline");
+  snprintf(buf[2], 50, "Standby: %s", (sc) ? "connected" : "offline");
+  snprintf(buf[3], 50, "Hearbeat Master: %s, %s", (hmc) ? "connected" : "offline", (state_.hb_state_master_) ? "present" : "timeout");
+  snprintf(buf[4], 50, "Hearbeat Standby: %s, %s", (hsc) ? "connected" : "offline", (state_.hb_state_standby_) ? "present" : "timeout");
+
+  if(fd >= 0) {
+    send_response(fd, buf[0]);
+    send_response(fd, buf[1]);
+    send_response(fd, buf[2]);
+    send_response(fd, buf[3]);
+    send_response(fd, buf[4]);
+  }
+
+  int listener_cnt = 0;
+  for(client = client_lst; client; client = client->next)
+    if(client->health_listener && client->fd != fd) {
+      send_response(client->fd, buf[0]);
+      send_response(client->fd, buf[1]);
+      send_response(client->fd, buf[2]);
+      send_response(client->fd, buf[3]);
+      send_response(client->fd, buf[4]);
+      listener_cnt++;
+    }
+  log_printf(DEBUG, "sent health info to %d additional listeners", listener_cnt);
+  return 0;
+}
+
+int update_health_status(int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  bool hmc, hsc;
+  hmc = hsc = 0;
+
+  client_t* client;
+  for(client = client_lst; client; client = client->next) {
+    switch(client->type) {
+      case HB_MASTER: hmc=1; break;
+      case HB_STANDBY: hsc=1; break;
+      default: break;
+    }
+  }
+
+  if(!hmc) state_.hb_state_master_ = 0;
+  if(!hsc) state_.hb_state_standby_ = 0;
+
+  switchctl_mode_t old_mode = state_.mode_;
+  if(state_.mode_ == MODE_MASTER) {
+    if(!state_.hb_state_master_) {
+      if(state_.hb_state_standby_) {
+        state_.mode_ = MODE_STANDBY;
+        return change_mode(old_mode, fd, cmd_q, client_lst, opt);
+      }
+    }
+  } else {
+    if(!state_.hb_state_standby_) {
+      if(state_.hb_state_master_) {
+        state_.mode_ = MODE_MASTER;
+        return change_mode(old_mode, fd, cmd_q, client_lst, opt);
+      }
+    }
+  }
+
+  send_health_status(fd, client_lst);
+
+  return 0;
+}
+
+void process_cmd_type(const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  if(param) {
+    client_t* client = client_find(client_lst, fd);
+    if(client) {
+      if(client->type == DEFAULT) {
+        if(!strncmp(param, "master", 6)) {
+          client->type = MASTER;
+          client->gpi_listener = 1;
+        }
+        else if(!strncmp(param, "standby", 7)) {
+          client->type = STANDBY;
+          client->gpi_listener = 1;
+        }
+        else if(!strncmp(param, "hb_master", 9))
+          client->type = HB_MASTER;
+        else if(!strncmp(param, "hb_standby", 10))
+          client->type = HB_STANDBY;
+        else {
+          log_printf(DEBUG, "unkown client type '%s'", param);
+          send_response(fd, "EEE: type: unknown client type");
+          return;
+        }
+        log_printf(DEBUG, "client %d type set to %s", fd, param);
+        update_health_status(-1, cmd_q, client_lst, opt);
+      }
+      else {
+        log_printf(ERROR, "unable to set client type for %d: type already set to %s", fd, client_type_tostring(client->type));
+        send_response(fd, "EEE: type: type already set");
+      }
+    }
+    else {
+      log_printf(ERROR, "unable to set client type for %d: client not found", fd);
+      send_response(fd, "EEE: type: client not found in client list?!");
+    }
+  }
+  else {
+    log_printf(ERROR, "unable to set client type for %d: empty parameter", fd);
+    send_response(fd, "EEE: type: missing parameter");
+  }
+}
+
+int process_cmd_heartbeat(const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  if(param) {
+    client_t* client = client_find(client_lst, fd);
+    if(client) {
+      switch(client->type) {
+        case HB_MASTER: {
+          state_.hb_state_master_ = (param[0] == '1') ? TRUE : FALSE;
+          break;
+        }
+        case HB_STANDBY: {
+          state_.hb_state_standby_ = (param[0] == '1') ? TRUE : FALSE;
+          break;
+        }
+        default: {
+          log_printf(ERROR, "unable to update heartbeat status: wrong client type");
+          send_response(fd, "EEE: heartbeat: wrong client type");
+          break;
+        }
+      }
+      update_health_status(-1, cmd_q, client_lst, opt);
+    }
+    else {
+      log_printf(ERROR, "unable to update heartbeat status: client not found");
+      send_response(fd, "EEE: heartbeat: client not found in client list?!");
+    }
+  }
+  else {
+    log_printf(ERROR, "unable to update heartbeat status: empty parameter");
+    send_response(fd, "EEE: heartbeat: missing parameter");
+  }
+  return 0;
+}
+
+int process_cmd_health(const char* param, int fd, client_t* client_lst)
+{
+  return send_health_status(fd, 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->request_listener = 1;
+      listener->mode_listener = 1;
+      listener->status_listener = 1;
+      listener->gpi_listener = 1;
+      listener->oc_listener = 1;
+      listener->relay_listener = 1;
+      listener->silence_listener = 1;
+      listener->health_listener = 1;
+    }
+    else {
+      if(!strncmp(param, "request", 7))
+        listener->request_listener = 1;
+      else if(!strncmp(param, "mode", 6))
+        listener->mode_listener = 1;
+      else if(!strncmp(param, "status", 6))
+        listener->status_listener = 1;
+      else if(!strncmp(param, "gpi", 3))
+        listener->gpi_listener = 1;
+      else if(!strncmp(param, "oc", 2))
+        listener->oc_listener = 1;
+      else if(!strncmp(param, "relay", 5))
+        listener->relay_listener = 1;
+      else if(!strncmp(param, "silence", 7))
+        listener->silence_listener = 1;
+      else if(!strncmp(param, "health", 6))
+        listener->health_listener = 1;
+      else if(!strncmp(param, "none", 4)) {
+        listener->request_listener = 0;
+        listener->mode_listener = 0;
+        listener->status_listener = 0;
+        listener->gpi_listener = 0;
+        listener->oc_listener = 0;
+        listener->relay_listener = 0;
+        listener->silence_listener = 0;
+        listener->health_listener = 0;
+      }
+      else {
+        log_printf(DEBUG, "unkown listener type '%s'", param);
+        send_response(fd, "EEE: 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, "EEE: listen: client not found in client list?!");
+  }
+}
+
+int process_cmd(const char* cmd, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
+{
+  log_printf(DEBUG, "processing command from %d", fd);
+
+  if(!cmd_q || !cmd)
+    return -1;
+
+  cmd_id_t cmd_id;
+  if(!strncmp(cmd, "switch", 6))
+    cmd_id = SWITCH;
+  else if(!strncmp(cmd, "channel", 7))
+    cmd_id = CHANNEL;
+  else if(!strncmp(cmd, "type", 4))
+    cmd_id = TYPE;
+  else if(!strncmp(cmd, "mode", 4))
+    cmd_id = MODE;
+  else if(!strncmp(cmd, "heartbeat", 5))
+    cmd_id = HEARTBEAT;
+  else if(!strncmp(cmd, "status", 6))
+    cmd_id = STATUS;
+  else if(!strncmp(cmd, "health", 6))
+    cmd_id = HEALTH;
+  else if(!strncmp(cmd, "log", 3))
+    cmd_id = LOG;
+  else if(!strncmp(cmd, "listen", 6))
+    cmd_id = LISTEN;
+  else if(!strncmp(cmd, "quit", 4))
+    return 2;
+  else {
+    if(!strncmp(cmd, "help", 4)) {
+      send_usage(fd);
+    } else {
+      log_printf(WARNING, "unknown command '%s'", cmd);
+      send_response(fd, "EEE: unknown command");
+    }
+    return 0;
+  }
+  char* param = strchr(cmd, ' ');
+  if(param)
+    param++;
+
+  if(cmd_id == SWITCH || cmd_id == CHANNEL || cmd_id == MODE) {
+    char* resp;
+    int len = asprintf(&resp, "Request: %s", cmd);
+    if(len > 0) {
+      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 SWITCH:
+  case STATUS: {
+    int ret = process_cmd_request(cmd, cmd_id, param, fd, cmd_q, client_lst, opt);
+    if(ret)
+      return ret;
+    break;
+  }
+  case CHANNEL: {
+    int ret = process_cmd_channel(cmd, param, fd, cmd_q, client_lst, opt);
+    if(ret)
+      return ret;
+    break;
+  }
+  case TYPE: process_cmd_type(param, fd, cmd_q, client_lst, opt); break;
+  case MODE: {
+    int ret = process_cmd_mode(param, fd, cmd_q, client_lst, opt);
+    if(ret)
+      return ret;
+    break;
+  }
+  case HEARTBEAT: {
+    int ret = process_cmd_heartbeat(param, fd, cmd_q, client_lst, opt);
+    if(ret)
+      return ret;
+    break;
+  }
+  case HEALTH: {
+    int ret = process_cmd_health(param, fd, client_lst);
+    if(ret)
+      return ret;
+    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 nonblock_recvline(read_buffer_t* buffer, int fd, cmd_t** cmd_q, 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)
+      break;
+
+    if(buffer->buf[buffer->offset] == '\n') {
+      buffer->buf[buffer->offset] = 0;
+      ret = process_cmd((char *)(buffer->buf), fd, cmd_q, 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;
+}
+
+#define SEND_TO_LISTENER(STRING, LEN, FLAG)                                            \
+        if(!strncmp((char *)(buffer->buf), STRING, LEN)) {                             \
+          client_t* client;                                                            \
+          int listener_cnt = 0;                                                        \
+          for(client = client_lst; client; client = client->next)                      \
+            if(client->FLAG && client->fd != cmd_fd) {                                 \
+              send_response(client->fd, (char *)(buffer->buf));                        \
+              listener_cnt++;                                                          \
+            }                                                                          \
+          log_printf(DEBUG, "sent message to %d additional listeners", listener_cnt);  \
+        }                                                                              \
+
+int process_switch(read_buffer_t* buffer, int switch_fd, cmd_t **cmd_q, client_t* client_lst)
+{
+  int ret = 0;
+  struct timeval tv;
+  fd_set fds;
+  FD_ZERO(&fds);
+  FD_SET(switch_fd, &fds);
+
+  for(;;) {
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    ret = select(switch_fd+1, &fds, NULL, NULL, &tv);
+    if(!ret)
+      return 0;
+    else if(ret < 0)
+      return ret;
+
+    ret = read(switch_fd, &buffer->buf[buffer->offset], 1);
+    if(!ret)
+      return 2;
+    if(ret == -1 && errno == EAGAIN)
+      return 0;
+    else if(ret < 0)
+      break;
+
+    if(buffer->buf[buffer->offset] == '\n') {
+      buffer->buf[buffer->offset] = 0;
+
+      if(buffer->offset > 0 && buffer->buf[buffer->offset-1] == '\r')
+        buffer->buf[buffer->offset-1] = 0;
+
+      if(strlen((char *)(buffer->buf))) {
+        log_printf(NOTICE, "switch-firmware: '%s'", (char *)(buffer->buf));
+
+        int cmd_fd = -1;
+        if(cmd_q && (*cmd_q)) {
+          cmd_fd = (*cmd_q)->fd;
+          send_response(cmd_fd, (char *)(buffer->buf));
+        }
+
+        SEND_TO_LISTENER("S0L", 3, status_listener);
+        SEND_TO_LISTENER("S0P", 3, gpi_listener);
+        SEND_TO_LISTENER("S0O", 3, oc_listener);
+        SEND_TO_LISTENER("S0R", 3, relay_listener);
+        SEND_TO_LISTENER("S0S", 3, silence_listener);
+
+        if((!strncmp((char *)(buffer->buf), "RRR", 3)) ||
+           (!strncmp((char *)(buffer->buf), "EEE", 3)))
+          cmd_pop(cmd_q);
+      }
+
+      buffer->offset = 0;
+      return 0;
+    }
+
+    buffer->offset++;
+    if(buffer->offset >= sizeof(buffer->buf)) {
+      log_printf(DEBUG, "string too long (fd=%d)", switch_fd);
+      buffer->offset = 0;
+      return 0;
+    }
+  }
+
+  return ret;
+}
+
+int main_loop(int switch_fd, int cmd_listen_fd, options_t* opt)
+{
+  log_printf(NOTICE, "entering main loop");
+
+  fd_set readfds, tmpfds;
+  FD_ZERO(&readfds);
+  FD_SET(switch_fd, &readfds);
+  FD_SET(cmd_listen_fd, &readfds);
+  int max_fd = switch_fd > cmd_listen_fd ? switch_fd : cmd_listen_fd;
+  cmd_t* cmd_q = NULL;
+  client_t* client_lst = NULL;
+
+  read_buffer_t switch_buffer;
+  switch_buffer.offset = 0;
+
+  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;
+
+  int return_value = 0;
+
+  char* channel = "unknown";
+  if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MAIN) channel = "master_main";
+  else if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MUSIC) channel = "master_music";
+  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MAIN) channel = "standby_main";
+  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MUSIC) channel = "standby_music";
+
+  char* cmd_param = strdup("*0M1*0ii1");
+  char* ch_nr = key_value_storage_find(&opt->alias_table_, channel);
+  if(!ch_nr || ch_nr[0] == 0 || ch_nr[1] == 0 || ch_nr[2] != 0) {
+    log_printf(ERROR, "invalid channel name or number: %s", channel);
+    free(cmd_param);
+    return_value = -1;
+  }
+  else {
+    cmd_param[6] = ch_nr[0];
+    cmd_param[7] = ch_nr[1];
+    return_value = cmd_push(&cmd_q, -1, SWITCH, cmd_param);
+    free(cmd_param);
+    send_command(switch_fd, cmd_q);
+  }
+
+  struct timeval timeout;
+  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) {
+      if(cmd_q && cmd_has_expired(*cmd_q)) {
+        log_printf(ERROR, "last command expired");
+        cmd_pop(&cmd_q);
+      }
+      else
+        continue;
+    }
+
+    if(FD_ISSET(sig_fd, &tmpfds)) {
+      if(signal_handle()) {
+        return_value = 1;
+        break;
+      }
+    }
+
+    if(FD_ISSET(switch_fd, &tmpfds)) {
+      return_value = process_switch(&switch_buffer, switch_fd, &cmd_q, client_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);
+      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, &cmd_q, 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);
+          update_health_status(-1, &cmd_q, client_lst, opt);
+          return_value = 0;
+          continue;
+        }
+        if(return_value)
+          break;
+
+      }
+      if(lst)
+        lst = lst->next;
+    }
+
+    if(cmd_q && !cmd_q->sent)
+      send_command(switch_fd, cmd_q);
+  }
+
+  cmd_clear(&cmd_q);
+  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 == -3) {
+      fprintf(stderr, "syntax error: mode name must be either master or standby\n");
+    }
+    if(ret == -4) {
+      fprintf(stderr, "syntax error: channel name must be either main or music\n");
+    }
+    if(ret == -5) {
+      fprintf(stderr, "syntax error: invalid baudrate\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,switchctl,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);
+  }
+
+  state_.mode_ = opt.mode_;
+  state_.channel_master_ = opt.channel_master_;
+  state_.channel_standby_ = opt.channel_standby_;
+  state_.hb_state_master_ = FALSE;
+  state_.hb_state_standby_ = FALSE;
+
+  int switch_fd = 0;
+  for(;;) {
+    switch_fd = open(opt.switch_dev_, O_RDWR | O_NOCTTY);
+    if(switch_fd < 0)
+      ret = 2;
+    else {
+      ret = setup_tty(switch_fd, opt.baudrate_);
+      if(ret)
+        ret = 2;
+      else
+        ret = main_loop(switch_fd, cmd_listen_fd, &opt);
+    }
+
+    if(ret == 2) {
+      log_printf(ERROR, "%s error, trying to reopen in 5 seconds..", opt.switch_dev_);
+      if(switch_fd > 0)
+        close(switch_fd);
+      sleep(5);
+    }
+    else
+      break;
+  }
+
+  close(cmd_listen_fd);
+  if(switch_fd > 0)
+    close(switch_fd);
+
+  if(!ret)
+    log_printf(NOTICE, "normal shutdown");
+  else if(ret < 0)
+    log_printf(NOTICE, "shutdown after error (code %d)", ret);
+  else
+    log_printf(NOTICE, "shutdown after signal");
+
+  options_clear(&opt);
+  log_close();
+
+  return ret;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..5ffbd5b
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,172 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "datatypes.h"
+
+#include <sys/un.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.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 setup_tty(int fd, speed_t speed)
+{
+  struct termios tmio;
+
+  int ret = tcgetattr(fd, &tmio);
+  if(ret) {
+    log_printf(ERROR, "Error on tcgetattr(): %s", strerror(errno));
+    return ret;
+  }
+
+  tmio.c_iflag &= ~(INLCR | ICRNL | IGNCR | IXON | IXOFF);
+  tmio.c_oflag &= ~(ONLCR | OCRNL | ONOCR | ONLRET);
+  tmio.c_cflag |= CS8 | CLOCAL | CREAD;
+  tmio.c_cflag &= ~(CSTOPB | PARENB);
+  tmio.c_lflag &= ~(ICANON | ECHO);
+  tmio.c_cc[VTIME]    = 0;
+  tmio.c_cc[VMIN]     = 1;
+
+  ret = cfsetospeed(&tmio, speed);
+  if(ret) {
+    log_printf(ERROR, "Error on cfsetospeed(): %s", strerror(errno));
+    return ret;
+  }
+
+  ret = cfsetispeed(&tmio, speed);
+  if(ret) {
+    log_printf(ERROR, "Error on cfsetispeed(): %s", strerror(errno));
+    return ret;
+  }
+
+  ret = tcsetattr(fd, TCSANOW, &tmio);
+  if(ret) {
+    log_printf(ERROR, "Error on tcsetattr(): %s", strerror(errno));
+    return ret;
+  }
+
+  ret = tcflush(fd, TCIFLUSH);
+  if(ret) {
+    log_printf(ERROR, "Error on tcflush(): %s", strerror(errno));
+    return ret;
+  }
+
+  fd_set fds;
+  struct timeval tv;
+  FD_ZERO(&fds);
+  FD_SET(fd, &fds);
+  tv.tv_sec = 0;
+  tv.tv_usec = 50000;
+  for(;;) {
+    ret = select(fd+1, &fds, NULL, NULL, &tv);
+    if(ret > 0) {
+      char buffer[100];
+      ret = read(fd, buffer, sizeof(buffer));
+    }
+    else
+      break;
+  }
+
+  return 0;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..f83bc84
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,35 @@
+/*
+ *  rhctl
+ *
+ *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+ *
+ *  This file is part of rhctl.
+ *
+ *  rhctl 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.
+ *
+ *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RHCTL_utils_h_INCLUDED
+#define RHCTL_utils_h_INCLUDED
+
+#include "command_queue.h"
+#include "client_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 setup_tty(int fd, speed_t speed);
+
+#endif
diff --git a/src/utils.lua b/src/utils.lua
new file mode 100644
index 0000000..6d1e3a0
--- /dev/null
+++ b/src/utils.lua
@@ -0,0 +1,35 @@
+--
+--  rhctl
+--
+--  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
+--
+--  This file is part of rhctl.
+--
+--  rhctl 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.
+--
+--  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
+--
+
+local utils = {}
+
+function utils.send_mail(address, subject, bodytext)
+   local fp = assert(io.popen("/usr/bin/msmtp " .. address, "w"))
+
+   fp:write("Subject: " .. subject .. "\n")
+   fp:write("To: " .. address .. "\n")
+   fp:write("\n")
+   fp:write(bodytext)
+   fp:write("\n")
+   fp:close()
+end
+
+return utils
diff --git a/stdioclient.c b/stdioclient.c
deleted file mode 100644
index 670758a..0000000
--- a/stdioclient.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "datatypes.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include "log.h"
-#include "sig_handler.h"
-#include "options.h"
-
-#include "daemon.h"
-#include "utils.h"
-
-int process_cmd(const char* cmd, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  return 0;
-}
-
-int process_data(int src_fd, int dest_fd)
-{
-  char* buffer[100];
-  int ret = read(src_fd, buffer, 100);
-  if(!ret)
-    return 2;
-  if(ret == -1 && errno == EAGAIN)
-    return 0;
-  if(ret < 0)
-    return ret;
-
-  log_printf(DEBUG, "read %d bytes from fd (%d)", ret, src_fd);
-
-  int len = ret;
-  int offset = 0;
-  for(;;) {
-    ret = write(dest_fd, &buffer[offset], len - offset);
-    if(ret < 0) {
-
-      if(errno != EINTR)
-        return ret;
-
-      ret = 0;
-    }
-
-    offset += ret;
-    if(offset+1 >= len)
-      break;
-  }
-  return 0;
-}
-
-int main_loop(int cmd_fd, options_t* opt)
-{
-  log_printf(NOTICE, "entering main loop");
-
-  fd_set readfds, tmpfds;
-  FD_ZERO(&readfds);
-  FD_SET(0, &readfds);
-  FD_SET(cmd_fd, &readfds);
-  int max_fd = cmd_fd;
-
-  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;
-
-  int return_value = 0;
-
-  while(!return_value) {
-    memcpy(&tmpfds, &readfds, sizeof(tmpfds));
-
-    int ret = select(max_fd+1, &tmpfds, NULL, NULL, NULL);
-    if(ret == -1 && errno != EINTR) {
-      log_printf(ERROR, "select returned with error: %s", strerror(errno));
-      return_value = -1;
-      break;
-    }
-    if(ret == -1 || !ret)
-      continue;
-
-    if(FD_ISSET(sig_fd, &tmpfds))
-      if(signal_handle())
-        return_value = 1;
-
-    if(FD_ISSET(0, &tmpfds))
-      return_value = process_data(0, cmd_fd);
-
-    if(FD_ISSET(cmd_fd, &tmpfds))
-      return_value = process_data(cmd_fd, 1);
-  }
-
-  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("stderr:2");
-  }
-  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);
-  }
-
-  int cmd_fd = connect_command_socket(opt.command_sock_);
-  if(cmd_fd < 0) {
-    options_clear(&opt);
-    log_close();
-    exit(-1);
-  }
-
-  ret = main_loop( cmd_fd, &opt);
-
-  close(cmd_fd);
-
-  if(!ret)
-    log_printf(NOTICE, "normal shutdown");
-  else if(ret < 0)
-    log_printf(NOTICE, "shutdown after error (code %d)", ret);
-  else
-    log_printf(NOTICE, "shutdown after signal");
-
-  options_clear(&opt);
-  log_close();
-
-  return ret;
-}
diff --git a/string_list.c b/string_list.c
deleted file mode 100644
index 5e0ddf8..0000000
--- a/string_list.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- *  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-2010 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/string_list.h b/string_list.h
deleted file mode 100644
index 9e210ae..0000000
--- a/string_list.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *  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-2010 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/switchctl.c b/switchctl.c
deleted file mode 100644
index f3daab0..0000000
--- a/switchctl.c
+++ /dev/null
@@ -1,1158 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "datatypes.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include "log.h"
-#include "sig_handler.h"
-#include "options.h"
-
-#include "command_queue.h"
-#include "client_list.h"
-#include "key_value_storage.h"
-
-#include "daemon.h"
-#include "utils.h"
-
-struct state_struct {
-  switchctl_mode_t mode_;
-  switchctl_channel_t channel_master_;
-  switchctl_channel_t channel_standby_;
-  bool hb_state_master_;
-  bool hb_state_standby_;
-};
-typedef struct state_struct state_t;
-
-state_t state_;
-
-int send_command(int switch_fd, cmd_t* cmd)
-{
-  if(!cmd)
-    return -1;
-
-  char* c = NULL;
-  switch(cmd->cmd) {
-  case SWITCH:
-  case CHANNEL: c = cmd->param; break;
-  case STATUS: c = "*0SL"; break;
-  default: break;
-  }
-
-  if(c == NULL)
-    return 0;
-
-  int ret = send_string(switch_fd, c);
-  if(ret > 0) {
-    cmd_sent(cmd);
-    return 0;
-  }
-
-  return ret;
-}
-
-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;
-}
-
-void send_usage(int fd)
-{
-  if(fd < 0)
-    return;
-
-  send_response(fd, "Usage: ");
-  send_response(fd, "  help       prints this");
-  send_response(fd, "  quit       close connection");
-  send_response(fd, "  type       set client type, one of: master, standby, hb_master, hb_standby");
-  send_response(fd, "  channel    switch to channel main or music");
-  send_response(fd, "             client type master and standby only");
-  send_response(fd, "  mode       switch to mode master or standby");
-  send_response(fd, "  heartbeat  update heartbeat status for master or standby");
-  send_response(fd, "  status     get actual status from switch");
-  send_response(fd, "  health     get health info");
-  send_response(fd, "  listen     register for events, no parameter for all");
-  send_response(fd, "             one of: request, mode, status, health, gpi, oc, relay, silence, none");
-  send_response(fd, "  log        add line to daemons log file");
-  send_response(fd, "  switch     send raw commands to the switch");
-}
-
-int process_cmd_request(const char* cmd, cmd_id_t cmd_id, const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  client_t* c = client_find(client_lst, fd);
-  if(!c) {
-    log_printf(WARNING, "ignoring request from unknown client");
-    send_response(fd, "EEE: switch: client not found in client list?!");
-    return 0;
-  }
-
-  char* cmd_param = NULL;
-  if(cmd_id == SWITCH) {
-    if((state_.mode_ == MODE_MASTER && c->type == STANDBY )||
-       (state_.mode_ == MODE_STANDBY && c->type == MASTER ))
-    {
-      log_printf(INFO, "silently ignoring request from inactive system (%s)", c->type == MASTER ? "master" : "standby");
-      return 0;
-    }
-
-    if(!param) {
-      log_printf(INFO, "ignoring switch command without parameter");
-      send_response(fd, "EEE: switch: missing parameter");
-      return 0;
-    }
-
-    if(param[0] == '*') {
-      cmd_param = strdup(param);
-    }
-    else {
-      const char* ch_name = NULL;
-      char* ch_nr = NULL;
-      if(!strncmp(param, "up ", 3)) {
-        cmd_param = strdup("*0FUii");
-        ch_name = &(param[3]);
-        ch_nr = &(cmd_param[4]);
-      }
-      else if(!strncmp(param, "down ", 5)) {
-        cmd_param = strdup("*0FDii");
-        ch_name = &(param[5]);
-        ch_nr = &(cmd_param[4]);
-      }
-      else if(!strncmp(param, "select ", 7)) {
-        cmd_param = strdup("*0ii1");
-        ch_name = &(param[7]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "add ", 4)) {
-        cmd_param = strdup("*0ii3");
-        ch_name = &(param[4]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "rm ", 3)) {
-        cmd_param = strdup("*0ii5");
-        ch_name = &(param[3]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "mute ", 5)) {
-        cmd_param = strdup("*0iiM1");
-        ch_name = &(param[5]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "select2 ", 8)) {
-        cmd_param = strdup("*0ii2");
-        ch_name = &(param[8]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "add2 ", 5)) {
-        cmd_param = strdup("*0ii4");
-        ch_name = &(param[5]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "rm2 ", 4)) {
-        cmd_param = strdup("*0ii6");
-        ch_name = &(param[4]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else if(!strncmp(param, "mute2 ", 6)) {
-        cmd_param = strdup("*0iiM2");
-        ch_name = &(param[6]);
-        ch_nr = &(cmd_param[2]);
-      }
-      else {
-        log_printf(INFO, "ignoring invalid switch command: '%s'", param);
-        send_response(fd, "EEE: switch: invalid command");
-        return 0;
-      }
-      char* ch_tmp = key_value_storage_find(&opt->alias_table_, ch_name);
-      if(!ch_tmp || ch_tmp[0] == 0 || ch_tmp[1] == 0 || ch_tmp[2] != 0) {
-        log_printf(ERROR, "invalid channel name or number: %s", ch_name);
-        send_response(fd, "EEE: switch: invalid channel name or number");
-        free(cmd_param);
-        return 0;
-      }
-
-      ch_nr[0] = ch_tmp[0];
-      ch_nr[1] = ch_tmp[1];
-    }
-    log_printf(DEBUG, "enqueing command to switch: '%s' (request was: '%s')", cmd_param, param);
-  }
-
-  int ret = cmd_push(cmd_q, fd, cmd_id, cmd_param);
-  if(cmd_param)
-    free(cmd_param);
-  if(ret)
-    return ret;
-
-  if(cmd_id == STATUS) {
-    char buf[3][30];
-    snprintf(buf[0], 30, "Current Mode: %s", state_.mode_ == MODE_MASTER ? "Master" : "Standby");
-    send_response(fd, buf[0]);
-    snprintf(buf[1], 30, "Master Channel: %s", state_.channel_master_ == CHAN_MAIN ? "Main" : "Music");
-    send_response(fd, buf[1]);
-    snprintf(buf[2], 30, "Standby Channel: %s", state_.channel_standby_ == CHAN_MAIN ? "Main" : "Music");
-    send_response(fd, buf[2]);
-    client_t* client;
-    int listener_cnt = 0;
-    for(client = client_lst; client; client = client->next)
-      if(client->status_listener && client->fd != fd) {
-        send_response(client->fd, buf[0]);
-        send_response(client->fd, buf[1]);
-        send_response(client->fd, buf[2]);
-        listener_cnt++;
-      }
-    log_printf(DEBUG, "sent status to %d additional listeners", listener_cnt);
-  }
-
-  log_printf(NOTICE, "command: %s", cmd);
-
-  return 0;
-}
-
-int crossfade(const char* ch_from, const char* ch_to, int fd, cmd_t **cmd_q, options_t* opt)
-{
-  char* cmd_param = strdup("*0FDii*0FUii");
-
-  char* ch_nr_from = key_value_storage_find(&opt->alias_table_, ch_from);
-  if(!ch_nr_from || ch_nr_from[0] == 0 || ch_nr_from[1] == 0 || ch_nr_from[2] != 0) {
-    log_printf(ERROR, "invalid channel name or number: %s", ch_from);
-    send_response(fd, "EEE: channel: invalid channel name or number");
-    free(cmd_param);
-    return 1;
-  }
-
-  char* ch_nr_to = key_value_storage_find(&opt->alias_table_, ch_to);
-  if(!ch_nr_to || ch_nr_to[0] == 0 || ch_nr_to[1] == 0 || ch_nr_to[2] != 0) {
-    log_printf(ERROR, "invalid channel name or number: %s", ch_to);
-    send_response(fd, "EEE: channel: invalid channel name or number");
-    free(cmd_param);
-    return 1;
-  }
-
-  cmd_param[4] = ch_nr_from[0];
-  cmd_param[5] = ch_nr_from[1];
-  cmd_param[10] = ch_nr_to[0];
-  cmd_param[11] = ch_nr_to[1];
-
-  log_printf(DEBUG, "enqueing command to switch: '%s'", cmd_param);
-  int ret = cmd_push(cmd_q, fd, CHANNEL, cmd_param);
-  free(cmd_param);
-
-  return ret;
-}
-
-int process_cmd_channel(const char* cmd, const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  client_t* c = client_find(client_lst, fd);
-  if(!c) {
-    log_printf(WARNING, "ignoring request from unknown client");
-    send_response(fd, "EEE: channel: client not found in client list?!");
-    return 0;
-  }
-
-  if(c->type != MASTER && c->type != STANDBY) {
-    log_printf(WARNING, "ignoring request from client of wrong type");
-    send_response(fd, "EEE: channel: client type doesn't fit");
-    return 0;
-  }
-
-  if(!param) {
-    log_printf(INFO, "ignoring channel command without parameter");
-    send_response(fd, "EEE: channel: missing parameter");
-    return 0;
-  }
-
-  switchctl_channel_t old_channel;
-  if(c->type == MASTER)
-    old_channel = state_.channel_master_;
-  else
-    old_channel = state_.channel_standby_;
-
-  char* ch_from = NULL;
-  char* ch_to = NULL;
-  if(!strcmp(param, "main")) {
-    if(state_.mode_ == MODE_MASTER) {
-      ch_from = "master_music";
-      ch_to = "master_main";
-    }
-    else {
-      ch_from = "standby_music";
-      ch_to = "standby_main";
-    }
-    if(c->type == MASTER)
-      state_.channel_master_ = CHAN_MAIN;
-    else
-      state_.channel_standby_ = CHAN_MAIN;
-  }
-  else if(!strcmp(param, "music")) {
-    if(state_.mode_ == MODE_MASTER) {
-      ch_from = "master_main";
-      ch_to = "master_music";
-    }
-    else {
-      ch_from = "standby_main";
-      ch_to = "standby_music";
-    }
-    if(c->type == MASTER)
-      state_.channel_master_ = CHAN_MUSIC;
-    else
-      state_.channel_standby_ = CHAN_MUSIC;
-  }
-
-  if((state_.mode_ == MODE_MASTER && c->type == STANDBY )||
-     (state_.mode_ == MODE_STANDBY && c->type == MASTER ))
-  {
-    log_printf(INFO, "no crossfade for inactive system (%s), just updated channel info", c->type == MASTER ? "master" : "standby");
-    return 0;
-  }
-
-  int ret = crossfade(ch_from, ch_to, fd, cmd_q, opt);
-  if(ret) {
-    if(c->type == MASTER)
-      state_.channel_master_ = old_channel;
-    else
-      state_.channel_standby_ = old_channel;
-
-    if(ret > 0)
-      return 0;
-
-    return ret;
-  }
-
-  log_printf(NOTICE, "command: %s", cmd);
-
-  return 0;
-}
-
-int change_mode(switchctl_mode_t old_mode, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  char* ch_from = NULL;
-  if(old_mode == MODE_MASTER && state_.channel_master_ == CHAN_MAIN)
-    ch_from = "master_main";
-  else if(old_mode == MODE_MASTER && state_.channel_master_ == CHAN_MUSIC)
-    ch_from = "master_music";
-  else if(old_mode == MODE_STANDBY && state_.channel_standby_ == CHAN_MAIN)
-    ch_from = "standby_main";
-  else if(old_mode == MODE_STANDBY && state_.channel_standby_ == CHAN_MUSIC)
-    ch_from = "standby_music";
-  else {
-    state_.mode_ = old_mode;
-    log_printf(ERROR, "EEE: mode: old config is illegal?!");
-    return 0;
-  }
-
-  char* ch_to = NULL;
-  if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MAIN)
-    ch_to = "master_main";
-  else if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MUSIC)
-    ch_to = "master_music";
-  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MAIN)
-    ch_to = "standby_main";
-  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MUSIC)
-    ch_to = "standby_music";
-  else {
-    state_.mode_ = old_mode;
-    log_printf(ERROR, "EEE: mode: current config is illegal?!");
-    return 0;
-  }
-
-  int ret = crossfade(ch_from, ch_to, fd, cmd_q, opt);
-  if(ret) {
-    state_.mode_ = old_mode;
-    if(ret > 0)
-      return 0;
-
-    return ret;
-  }
-
-  char* mode_str;
-  int len = asprintf(&mode_str, "new Mode: %s", state_.mode_ == MODE_MASTER ? "master" : "standby");
-  if(len > 0) {
-    log_printf(NOTICE, "%s", mode_str);
-    client_t* client;
-    int listener_cnt = 0;
-    for(client = client_lst; client; client = client->next)
-      if(client->mode_listener && client->fd != fd) {
-        send_response(client->fd, mode_str);
-        listener_cnt++;
-      }
-    free(mode_str);
-    log_printf(DEBUG, "sent new mode to %d additional listeners", listener_cnt);
-  }
-
-  return 0;
-}
-
-int process_cmd_mode(const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  switchctl_mode_t old_mode = state_.mode_;
-
-  if(param) {
-    if(!strncmp(param, "master", 6))
-      state_.mode_ = MODE_MASTER;
-    else if(!strncmp(param, "standby", 7))
-      state_.mode_ = MODE_STANDBY;
-    else {
-      log_printf(DEBUG, "unkown mode '%s'", param);
-      send_response(fd, "EEE: mode: unknown mode");
-      return 0;
-    }
-
-        // swap master with standby channels only when mode has changed
-    if(old_mode != state_.mode_)
-      return change_mode(old_mode, fd, cmd_q, client_lst, opt);
-  }
-  else {
-    log_printf(ERROR, "unable to set mode: empty parameter");
-    send_response(fd, "EEE: mode: missing parameter");
-  }
-
-  return 0;
-}
-
-int send_health_status(int fd, client_t* client_lst)
-{
-  bool mc, sc, hmc, hsc;
-  mc = sc = hmc = hsc = 0;
-
-  client_t* client;
-  for(client = client_lst; client; client = client->next) {
-    switch(client->type) {
-      case MASTER: mc=1; break;
-      case STANDBY: sc=1; break;
-      case HB_MASTER: hmc=1; break;
-      case HB_STANDBY: hsc=1; break;
-      default: break;
-    }
-  }
-
-  char buf[5][50];
-  snprintf(buf[0], 50, "Health: %s", (mc && sc && hmc && hsc && state_.hb_state_master_ && state_.hb_state_standby_) ? "ok" : "error");
-  snprintf(buf[1], 50, "Master: %s", (mc) ? "connected" : "offline");
-  snprintf(buf[2], 50, "Standby: %s", (sc) ? "connected" : "offline");
-  snprintf(buf[3], 50, "Hearbeat Master: %s, %s", (hmc) ? "connected" : "offline", (state_.hb_state_master_) ? "present" : "timeout");
-  snprintf(buf[4], 50, "Hearbeat Standby: %s, %s", (hsc) ? "connected" : "offline", (state_.hb_state_standby_) ? "present" : "timeout");
-
-  if(fd >= 0) {
-    send_response(fd, buf[0]);
-    send_response(fd, buf[1]);
-    send_response(fd, buf[2]);
-    send_response(fd, buf[3]);
-    send_response(fd, buf[4]);
-  }
-
-  int listener_cnt = 0;
-  for(client = client_lst; client; client = client->next)
-    if(client->health_listener && client->fd != fd) {
-      send_response(client->fd, buf[0]);
-      send_response(client->fd, buf[1]);
-      send_response(client->fd, buf[2]);
-      send_response(client->fd, buf[3]);
-      send_response(client->fd, buf[4]);
-      listener_cnt++;
-    }
-  log_printf(DEBUG, "sent health info to %d additional listeners", listener_cnt);
-  return 0;
-}
-
-int update_health_status(int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  bool hmc, hsc;
-  hmc = hsc = 0;
-
-  client_t* client;
-  for(client = client_lst; client; client = client->next) {
-    switch(client->type) {
-      case HB_MASTER: hmc=1; break;
-      case HB_STANDBY: hsc=1; break;
-      default: break;
-    }
-  }
-
-  if(!hmc) state_.hb_state_master_ = 0;
-  if(!hsc) state_.hb_state_standby_ = 0;
-
-  switchctl_mode_t old_mode = state_.mode_;
-  if(state_.mode_ == MODE_MASTER) {
-    if(!state_.hb_state_master_) {
-      if(state_.hb_state_standby_) {
-        state_.mode_ = MODE_STANDBY;
-        return change_mode(old_mode, fd, cmd_q, client_lst, opt);
-      }
-    }
-  } else {
-    if(!state_.hb_state_standby_) {
-      if(state_.hb_state_master_) {
-        state_.mode_ = MODE_MASTER;
-        return change_mode(old_mode, fd, cmd_q, client_lst, opt);
-      }
-    }
-  }
-
-  send_health_status(fd, client_lst);
-
-  return 0;
-}
-
-void process_cmd_type(const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  if(param) {
-    client_t* client = client_find(client_lst, fd);
-    if(client) {
-      if(client->type == DEFAULT) {
-        if(!strncmp(param, "master", 6)) {
-          client->type = MASTER;
-          client->gpi_listener = 1;
-        }
-        else if(!strncmp(param, "standby", 7)) {
-          client->type = STANDBY;
-          client->gpi_listener = 1;
-        }
-        else if(!strncmp(param, "hb_master", 9))
-          client->type = HB_MASTER;
-        else if(!strncmp(param, "hb_standby", 10))
-          client->type = HB_STANDBY;
-        else {
-          log_printf(DEBUG, "unkown client type '%s'", param);
-          send_response(fd, "EEE: type: unknown client type");
-          return;
-        }
-        log_printf(DEBUG, "client %d type set to %s", fd, param);
-        update_health_status(-1, cmd_q, client_lst, opt);
-      }
-      else {
-        log_printf(ERROR, "unable to set client type for %d: type already set to %s", fd, client_type_tostring(client->type));
-        send_response(fd, "EEE: type: type already set");
-      }
-    }
-    else {
-      log_printf(ERROR, "unable to set client type for %d: client not found", fd);
-      send_response(fd, "EEE: type: client not found in client list?!");
-    }
-  }
-  else {
-    log_printf(ERROR, "unable to set client type for %d: empty parameter", fd);
-    send_response(fd, "EEE: type: missing parameter");
-  }
-}
-
-int process_cmd_heartbeat(const char* param, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  if(param) {
-    client_t* client = client_find(client_lst, fd);
-    if(client) {
-      switch(client->type) {
-        case HB_MASTER: {
-          state_.hb_state_master_ = (param[0] == '1') ? TRUE : FALSE;
-          break;
-        }
-        case HB_STANDBY: {
-          state_.hb_state_standby_ = (param[0] == '1') ? TRUE : FALSE;
-          break;
-        }
-        default: {
-          log_printf(ERROR, "unable to update heartbeat status: wrong client type");
-          send_response(fd, "EEE: heartbeat: wrong client type");
-          break;
-        }
-      }
-      update_health_status(-1, cmd_q, client_lst, opt);
-    }
-    else {
-      log_printf(ERROR, "unable to update heartbeat status: client not found");
-      send_response(fd, "EEE: heartbeat: client not found in client list?!");
-    }
-  }
-  else {
-    log_printf(ERROR, "unable to update heartbeat status: empty parameter");
-    send_response(fd, "EEE: heartbeat: missing parameter");
-  }
-  return 0;
-}
-
-int process_cmd_health(const char* param, int fd, client_t* client_lst)
-{
-  return send_health_status(fd, 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->request_listener = 1;
-      listener->mode_listener = 1;
-      listener->status_listener = 1;
-      listener->gpi_listener = 1;
-      listener->oc_listener = 1;
-      listener->relay_listener = 1;
-      listener->silence_listener = 1;
-      listener->health_listener = 1;
-    }
-    else {
-      if(!strncmp(param, "request", 7))
-        listener->request_listener = 1;
-      else if(!strncmp(param, "mode", 6))
-        listener->mode_listener = 1;
-      else if(!strncmp(param, "status", 6))
-        listener->status_listener = 1;
-      else if(!strncmp(param, "gpi", 3))
-        listener->gpi_listener = 1;
-      else if(!strncmp(param, "oc", 2))
-        listener->oc_listener = 1;
-      else if(!strncmp(param, "relay", 5))
-        listener->relay_listener = 1;
-      else if(!strncmp(param, "silence", 7))
-        listener->silence_listener = 1;
-      else if(!strncmp(param, "health", 6))
-        listener->health_listener = 1;
-      else if(!strncmp(param, "none", 4)) {
-        listener->request_listener = 0;
-        listener->mode_listener = 0;
-        listener->status_listener = 0;
-        listener->gpi_listener = 0;
-        listener->oc_listener = 0;
-        listener->relay_listener = 0;
-        listener->silence_listener = 0;
-        listener->health_listener = 0;
-      }
-      else {
-        log_printf(DEBUG, "unkown listener type '%s'", param);
-        send_response(fd, "EEE: 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, "EEE: listen: client not found in client list?!");
-  }
-}
-
-int process_cmd(const char* cmd, int fd, cmd_t **cmd_q, client_t* client_lst, options_t* opt)
-{
-  log_printf(DEBUG, "processing command from %d", fd);
-
-  if(!cmd_q || !cmd)
-    return -1;
-
-  cmd_id_t cmd_id;
-  if(!strncmp(cmd, "switch", 6))
-    cmd_id = SWITCH;
-  else if(!strncmp(cmd, "channel", 7))
-    cmd_id = CHANNEL;
-  else if(!strncmp(cmd, "type", 4))
-    cmd_id = TYPE;
-  else if(!strncmp(cmd, "mode", 4))
-    cmd_id = MODE;
-  else if(!strncmp(cmd, "heartbeat", 5))
-    cmd_id = HEARTBEAT;
-  else if(!strncmp(cmd, "status", 6))
-    cmd_id = STATUS;
-  else if(!strncmp(cmd, "health", 6))
-    cmd_id = HEALTH;
-  else if(!strncmp(cmd, "log", 3))
-    cmd_id = LOG;
-  else if(!strncmp(cmd, "listen", 6))
-    cmd_id = LISTEN;
-  else if(!strncmp(cmd, "quit", 4))
-    return 2;
-  else {
-    if(!strncmp(cmd, "help", 4)) {
-      send_usage(fd);
-    } else {
-      log_printf(WARNING, "unknown command '%s'", cmd);
-      send_response(fd, "EEE: unknown command");
-    }
-    return 0;
-  }
-  char* param = strchr(cmd, ' ');
-  if(param)
-    param++;
-
-  if(cmd_id == SWITCH || cmd_id == CHANNEL || cmd_id == MODE) {
-    char* resp;
-    int len = asprintf(&resp, "Request: %s", cmd);
-    if(len > 0) {
-      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 SWITCH:
-  case STATUS: {
-    int ret = process_cmd_request(cmd, cmd_id, param, fd, cmd_q, client_lst, opt);
-    if(ret)
-      return ret;
-    break;
-  }
-  case CHANNEL: {
-    int ret = process_cmd_channel(cmd, param, fd, cmd_q, client_lst, opt);
-    if(ret)
-      return ret;
-    break;
-  }
-  case TYPE: process_cmd_type(param, fd, cmd_q, client_lst, opt); break;
-  case MODE: {
-    int ret = process_cmd_mode(param, fd, cmd_q, client_lst, opt);
-    if(ret)
-      return ret;
-    break;
-  }
-  case HEARTBEAT: {
-    int ret = process_cmd_heartbeat(param, fd, cmd_q, client_lst, opt);
-    if(ret)
-      return ret;
-    break;
-  }
-  case HEALTH: {
-    int ret = process_cmd_health(param, fd, client_lst);
-    if(ret)
-      return ret;
-    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 nonblock_recvline(read_buffer_t* buffer, int fd, cmd_t** cmd_q, 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)
-      break;
-
-    if(buffer->buf[buffer->offset] == '\n') {
-      buffer->buf[buffer->offset] = 0;
-      ret = process_cmd((char *)(buffer->buf), fd, cmd_q, 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;
-}
-
-#define SEND_TO_LISTENER(STRING, LEN, FLAG)                                            \
-        if(!strncmp((char *)(buffer->buf), STRING, LEN)) {                             \
-          client_t* client;                                                            \
-          int listener_cnt = 0;                                                        \
-          for(client = client_lst; client; client = client->next)                      \
-            if(client->FLAG && client->fd != cmd_fd) {                                 \
-              send_response(client->fd, (char *)(buffer->buf));                        \
-              listener_cnt++;                                                          \
-            }                                                                          \
-          log_printf(DEBUG, "sent message to %d additional listeners", listener_cnt);  \
-        }                                                                              \
-
-int process_switch(read_buffer_t* buffer, int switch_fd, cmd_t **cmd_q, client_t* client_lst)
-{
-  int ret = 0;
-  struct timeval tv;
-  fd_set fds;
-  FD_ZERO(&fds);
-  FD_SET(switch_fd, &fds);
-
-  for(;;) {
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-    ret = select(switch_fd+1, &fds, NULL, NULL, &tv);
-    if(!ret)
-      return 0;
-    else if(ret < 0)
-      return ret;
-
-    ret = read(switch_fd, &buffer->buf[buffer->offset], 1);
-    if(!ret)
-      return 2;
-    if(ret == -1 && errno == EAGAIN)
-      return 0;
-    else if(ret < 0)
-      break;
-
-    if(buffer->buf[buffer->offset] == '\n') {
-      buffer->buf[buffer->offset] = 0;
-
-      if(buffer->offset > 0 && buffer->buf[buffer->offset-1] == '\r')
-        buffer->buf[buffer->offset-1] = 0;
-
-      if(strlen((char *)(buffer->buf))) {
-        log_printf(NOTICE, "switch-firmware: '%s'", (char *)(buffer->buf));
-
-        int cmd_fd = -1;
-        if(cmd_q && (*cmd_q)) {
-          cmd_fd = (*cmd_q)->fd;
-          send_response(cmd_fd, (char *)(buffer->buf));
-        }
-
-        SEND_TO_LISTENER("S0L", 3, status_listener);
-        SEND_TO_LISTENER("S0P", 3, gpi_listener);
-        SEND_TO_LISTENER("S0O", 3, oc_listener);
-        SEND_TO_LISTENER("S0R", 3, relay_listener);
-        SEND_TO_LISTENER("S0S", 3, silence_listener);
-
-        if((!strncmp((char *)(buffer->buf), "RRR", 3)) ||
-           (!strncmp((char *)(buffer->buf), "EEE", 3)))
-          cmd_pop(cmd_q);
-      }
-
-      buffer->offset = 0;
-      return 0;
-    }
-
-    buffer->offset++;
-    if(buffer->offset >= sizeof(buffer->buf)) {
-      log_printf(DEBUG, "string too long (fd=%d)", switch_fd);
-      buffer->offset = 0;
-      return 0;
-    }
-  }
-
-  return ret;
-}
-
-int main_loop(int switch_fd, int cmd_listen_fd, options_t* opt)
-{
-  log_printf(NOTICE, "entering main loop");
-
-  fd_set readfds, tmpfds;
-  FD_ZERO(&readfds);
-  FD_SET(switch_fd, &readfds);
-  FD_SET(cmd_listen_fd, &readfds);
-  int max_fd = switch_fd > cmd_listen_fd ? switch_fd : cmd_listen_fd;
-  cmd_t* cmd_q = NULL;
-  client_t* client_lst = NULL;
-
-  read_buffer_t switch_buffer;
-  switch_buffer.offset = 0;
-
-  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;
-
-  int return_value = 0;
-
-  char* channel = "unknown";
-  if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MAIN) channel = "master_main";
-  else if(state_.mode_ == MODE_MASTER && state_.channel_master_ == CHAN_MUSIC) channel = "master_music";
-  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MAIN) channel = "standby_main";
-  else if(state_.mode_ == MODE_STANDBY && state_.channel_standby_ == CHAN_MUSIC) channel = "standby_music";
-
-  char* cmd_param = strdup("*0M1*0ii1");
-  char* ch_nr = key_value_storage_find(&opt->alias_table_, channel);
-  if(!ch_nr || ch_nr[0] == 0 || ch_nr[1] == 0 || ch_nr[2] != 0) {
-    log_printf(ERROR, "invalid channel name or number: %s", channel);
-    free(cmd_param);
-    return_value = -1;
-  }
-  else {
-    cmd_param[6] = ch_nr[0];
-    cmd_param[7] = ch_nr[1];
-    return_value = cmd_push(&cmd_q, -1, SWITCH, cmd_param);
-    free(cmd_param);
-    send_command(switch_fd, cmd_q);
-  }
-
-  struct timeval timeout;
-  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) {
-      if(cmd_q && cmd_has_expired(*cmd_q)) {
-        log_printf(ERROR, "last command expired");
-        cmd_pop(&cmd_q);
-      }
-      else
-        continue;
-    }
-
-    if(FD_ISSET(sig_fd, &tmpfds)) {
-      if(signal_handle()) {
-        return_value = 1;
-        break;
-      }
-    }
-
-    if(FD_ISSET(switch_fd, &tmpfds)) {
-      return_value = process_switch(&switch_buffer, switch_fd, &cmd_q, client_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);
-      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, &cmd_q, 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);
-          update_health_status(-1, &cmd_q, client_lst, opt);
-          return_value = 0;
-          continue;
-        }
-        if(return_value)
-          break;
-
-      }
-      if(lst)
-        lst = lst->next;
-    }
-
-    if(cmd_q && !cmd_q->sent)
-      send_command(switch_fd, cmd_q);
-  }
-
-  cmd_clear(&cmd_q);
-  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 == -3) {
-      fprintf(stderr, "syntax error: mode name must be either master or standby\n");
-    }
-    if(ret == -4) {
-      fprintf(stderr, "syntax error: channel name must be either main or music\n");
-    }
-    if(ret == -5) {
-      fprintf(stderr, "syntax error: invalid baudrate\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,switchctl,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);
-  }
-
-  state_.mode_ = opt.mode_;
-  state_.channel_master_ = opt.channel_master_;
-  state_.channel_standby_ = opt.channel_standby_;
-  state_.hb_state_master_ = FALSE;
-  state_.hb_state_standby_ = FALSE;
-
-  int switch_fd = 0;
-  for(;;) {
-    switch_fd = open(opt.switch_dev_, O_RDWR | O_NOCTTY);
-    if(switch_fd < 0)
-      ret = 2;
-    else {
-      ret = setup_tty(switch_fd, opt.baudrate_);
-      if(ret)
-        ret = 2;
-      else
-        ret = main_loop(switch_fd, cmd_listen_fd, &opt);
-    }
-
-    if(ret == 2) {
-      log_printf(ERROR, "%s error, trying to reopen in 5 seconds..", opt.switch_dev_);
-      if(switch_fd > 0)
-        close(switch_fd);
-      sleep(5);
-    }
-    else
-      break;
-  }
-
-  close(cmd_listen_fd);
-  if(switch_fd > 0)
-    close(switch_fd);
-
-  if(!ret)
-    log_printf(NOTICE, "normal shutdown");
-  else if(ret < 0)
-    log_printf(NOTICE, "shutdown after error (code %d)", ret);
-  else
-    log_printf(NOTICE, "shutdown after signal");
-
-  options_clear(&opt);
-  log_close();
-
-  return ret;
-}
diff --git a/utils.c b/utils.c
deleted file mode 100644
index 5ffbd5b..0000000
--- a/utils.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "datatypes.h"
-
-#include <sys/un.h>
-#include <termios.h>
-#include <unistd.h>
-#include <errno.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 setup_tty(int fd, speed_t speed)
-{
-  struct termios tmio;
-
-  int ret = tcgetattr(fd, &tmio);
-  if(ret) {
-    log_printf(ERROR, "Error on tcgetattr(): %s", strerror(errno));
-    return ret;
-  }
-
-  tmio.c_iflag &= ~(INLCR | ICRNL | IGNCR | IXON | IXOFF);
-  tmio.c_oflag &= ~(ONLCR | OCRNL | ONOCR | ONLRET);
-  tmio.c_cflag |= CS8 | CLOCAL | CREAD;
-  tmio.c_cflag &= ~(CSTOPB | PARENB);
-  tmio.c_lflag &= ~(ICANON | ECHO);
-  tmio.c_cc[VTIME]    = 0;
-  tmio.c_cc[VMIN]     = 1;
-
-  ret = cfsetospeed(&tmio, speed);
-  if(ret) {
-    log_printf(ERROR, "Error on cfsetospeed(): %s", strerror(errno));
-    return ret;
-  }
-
-  ret = cfsetispeed(&tmio, speed);
-  if(ret) {
-    log_printf(ERROR, "Error on cfsetispeed(): %s", strerror(errno));
-    return ret;
-  }
-
-  ret = tcsetattr(fd, TCSANOW, &tmio);
-  if(ret) {
-    log_printf(ERROR, "Error on tcsetattr(): %s", strerror(errno));
-    return ret;
-  }
-
-  ret = tcflush(fd, TCIFLUSH);
-  if(ret) {
-    log_printf(ERROR, "Error on tcflush(): %s", strerror(errno));
-    return ret;
-  }
-
-  fd_set fds;
-  struct timeval tv;
-  FD_ZERO(&fds);
-  FD_SET(fd, &fds);
-  tv.tv_sec = 0;
-  tv.tv_usec = 50000;
-  for(;;) {
-    ret = select(fd+1, &fds, NULL, NULL, &tv);
-    if(ret > 0) {
-      char buffer[100];
-      ret = read(fd, buffer, sizeof(buffer));
-    }
-    else
-      break;
-  }
-
-  return 0;
-}
diff --git a/utils.h b/utils.h
deleted file mode 100644
index f83bc84..0000000
--- a/utils.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *  rhctl
- *
- *  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
- *
- *  This file is part of rhctl.
- *
- *  rhctl 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.
- *
- *  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RHCTL_utils_h_INCLUDED
-#define RHCTL_utils_h_INCLUDED
-
-#include "command_queue.h"
-#include "client_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 setup_tty(int fd, speed_t speed);
-
-#endif
diff --git a/utils.lua b/utils.lua
deleted file mode 100644
index 6d1e3a0..0000000
--- a/utils.lua
+++ /dev/null
@@ -1,35 +0,0 @@
---
---  rhctl
---
---  Copyright (C) 2009-2014 Christian Pointner <equinox@helsinki.at>
---
---  This file is part of rhctl.
---
---  rhctl 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.
---
---  rhctl 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 rhctl. If not, see <http://www.gnu.org/licenses/>.
---
-
-local utils = {}
-
-function utils.send_mail(address, subject, bodytext)
-   local fp = assert(io.popen("/usr/bin/msmtp " .. address, "w"))
-
-   fp:write("Subject: " .. subject .. "\n")
-   fp:write("To: " .. address .. "\n")
-   fp:write("\n")
-   fp:write(bodytext)
-   fp:write("\n")
-   fp:close()
-end
-
-return utils
-- 
cgit v0.10.2