summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2015-07-26 17:13:42 (GMT)
committerChristian Pointner <equinox@spreadspace.org>2015-07-26 17:13:42 (GMT)
commit1324242685b63a511be3b3625f78e1ebf2dafc76 (patch)
treeb83d4947415757fba945b7a91a25230c9d044144 /src
parentc6bbdfbdde366bfbb37858e7dc9e0fc351595023 (diff)
moved sources to src/
Diffstat (limited to 'src')
-rw-r--r--src/Makefile122
-rw-r--r--src/client_list.c137
-rw-r--r--src/client_list.h52
-rw-r--r--src/command_queue.c118
-rw-r--r--src/command_queue.h46
-rwxr-xr-xsrc/configure162
-rw-r--r--src/daemon.h175
-rw-r--r--src/datatypes.h54
-rw-r--r--src/health-watch.lua107
-rw-r--r--src/heartbeatclient.c426
-rw-r--r--src/key_value_storage.c92
-rw-r--r--src/key_value_storage.h41
-rw-r--r--src/l_cmd.c152
-rw-r--r--src/l_cmd.h32
-rw-r--r--src/l_log.c120
-rw-r--r--src/l_log.h40
-rw-r--r--src/l_sig_handler.c99
-rw-r--r--src/l_sig_handler.h40
-rw-r--r--src/log.c260
-rw-r--r--src/log.h90
-rw-r--r--src/log_targets.h363
-rw-r--r--src/luaclient.c268
-rw-r--r--src/mode-tcpserver.lua175
-rw-r--r--src/mode-watch.lua119
-rw-r--r--src/options.c556
-rw-r--r--src/options.h87
-rw-r--r--src/serialclient.c285
-rw-r--r--src/sig_handler.c163
-rw-r--r--src/sig_handler.h45
-rw-r--r--src/silence-watch.lua87
-rw-r--r--src/stdioclient.c190
-rw-r--r--src/string_list.c113
-rw-r--r--src/string_list.h56
-rw-r--r--src/switchctl.c1158
-rw-r--r--src/utils.c172
-rw-r--r--src/utils.h35
-rw-r--r--src/utils.lua35
37 files changed, 6272 insertions, 0 deletions
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