Add the liblcfg to handle config file
authorNeutron Soutmun <neo.neutron@gmail.com>
Wed, 26 Nov 2008 19:11:40 +0000 (02:11 +0700)
committerNeutron Soutmun <neo.neutron@gmail.com>
Wed, 26 Nov 2008 19:11:40 +0000 (02:11 +0700)
2008-11-27  Neutron Soutmun <neo.neutron@gmail.com>

[ Suriya Soutmun ]
* +lcfg/lcfg_static.{h,c}, +lcfg/Makefile.am, +src/rh-config.c,
  src/rahunasd.{h,c}:
  Add liblcfg and their implementation to satisfy RahuNAS requirement.
* Makefile.am, configure.ac, src/Makefile.am:
  Add the lcfg and their implementation to build config and makefile.

[ Neutron Soutmun ]
* +example/rahunas.conf: Add example config file.
* -example/rahunas.firewall, -example/rahunas.bandwidth,
  +example/firewall.sh, +example/bandwidth.sh:
  - Rename the wrapper script.
  - Adjust the firewall.sh to satisfy the config implementation.
* example/rahunas.default:
  Remove the parts the moved to the config file.
* src/rh-ipset.c, src/rh-task-ipset.c, src/rh-xmlrpc-cmd.c:
  Update accordingly to new config implementation.

18 files changed:
ChangeLog
Makefile.am
configure.ac
example/bandwidth.sh [moved from example/rahunas.bandwidth with 100% similarity]
example/firewall.sh [moved from example/rahunas.firewall with 85% similarity]
example/rahunas.conf [new file with mode: 0644]
example/rahunas.default
lcfg/Makefile.am [new file with mode: 0644]
lcfg/lcfg_static.c [new file with mode: 0755]
lcfg/lcfg_static.h [new file with mode: 0755]
src/Makefile.am
src/rahunasd.c
src/rahunasd.h
src/rh-config.c [new file with mode: 0755]
src/rh-config.h
src/rh-ipset.c
src/rh-task-ipset.c
src/rh-xmlrpc-cmd.c

index 1ea7069..a1d3ef0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2008-11-27  Neutron Soutmun <neo.neutron@gmail.com>
+
+       [ Suriya Soutmun ]
+       * +lcfg/lcfg_static.{h,c}, +lcfg/Makefile.am, +src/rh-config.c,
+         src/rahunasd.{h,c}:
+         Add liblcfg and their implementation to satisfy RahuNAS requirement.
+       * Makefile.am, configure.ac, src/Makefile.am: 
+         Add the lcfg and their implementation to build config and makefile.
+
+       [ Neutron Soutmun ]
+       * +example/rahunas.conf: Add example config file.
+       * -example/rahunas.firewall, -example/rahunas.bandwidth,
+         +example/firewall.sh, +example/bandwidth.sh:
+         - Rename the wrapper script.
+         - Adjust the firewall.sh to satisfy the config implementation.
+       * example/rahunas.default: 
+         Remove the parts the moved to the config file.
+       * src/rh-ipset.c, src/rh-task-ipset.c, src/rh-xmlrpc-cmd.c:
+         Update accordingly to new config implementation.
+
 2008-11-26  Neutron Soutmun <neo.neutron@gmail.com>
 
        * example/rahunas.bandwidth: Fix the script delete all filters rather than 
index 615d8c8..bb2018e 100644 (file)
@@ -1 +1 @@
-SUBDIRS = xmlrpc src data
+SUBDIRS = xmlrpc lcfg src data
index d795889..8e4e8cb 100644 (file)
@@ -55,6 +55,7 @@ AC_CHECK_FUNCS([dup2])
 AC_CONFIG_FILES([
        Makefile
        xmlrpc/Makefile
+  lcfg/Makefile
        src/Makefile
        data/Makefile
   src/include/linux/netfilter_ipv4/Makefile
similarity index 85%
rename from example/rahunas.firewall
rename to example/firewall.sh
index e1a28f5..0506617 100755 (executable)
@@ -3,34 +3,49 @@
 # the clients and redirect them to the login page to get the authorization to 
 # access the network.
 
+set -e
+
 IPTABLES=/sbin/iptables
 IPSET=/usr/sbin/ipset
 
 NAME="rahunas"
+INIT=/etc/default/rahunas
 RUN=/var/run/rahunas-firewall
 
-# These values are overriden in /etc/default/rahunas if they exist
 RUN_DAEMON=no
-DEV_WAN="eth0"
-DEV_LAN="eth1"
-BRIDGE=""
-CLIENTS_RANGE_START="192.168.0.2"
-CLIENTS_RANGE_END="192.168.0.254"
-CLIENTS_NETWORK=""
-CLIENTS_IGNORE_MAC=no
-EXCLUDED=""
 
-# Get configuration
-if [ -r /etc/default/rahunas ]; then
-  . /etc/default/rahunas
-fi
+test -f $INIT || exit 0 
+. $INIT
 
-if [ "$RUN_DAEMON" = "no" ]; then
-  exit 0
-fi
+test "$RUN_DAEMON" = "yes" || exit 0
+test -f $RAHUNAS_CONFIG || exit 1
 
-# Bridge config
+get_config_value () {
+  key=$1
+  cat $RAHUNAS_CONFIG | grep -v ^/ | grep -w "$key" | cut -d= -f2 | sed 's: ::g' | sed 's:"::g'
+}
 
+# Get configuration
+DEV_WAN=`get_config_value dev_wan`
+DEV_LAN=`get_config_value dev_lan`
+BRIDGE=`get_config_value bridge`
+MASQUERADE=`get_config_value masquerade`
+SERVER=`get_config_value server`
+CLIENTS_RANGE_START=`get_config_value clients_range_start`
+CLIENTS_RANGE_END=`get_config_value clients_range_end`
+CLIENTS_NETWORK=`get_config_value clients_network`
+EXCLUDED=`get_config_value excluded`
+IGNORE_MAC=`get_config_value ignore_mac`
+BANDWIDTH_SHAPE_IMQ=`get_config_value bandwidth_shape_imq`
+FORWARD_DHCP=`get_config_value forward_dhcp`
+FORWARD_DNS=`get_config_value forward_dns`
+PROXY_PORT=`get_config_value proxy_port`
+PROXY_HOST=`get_config_value proxy_host`
+TRANSPARENT_PROXY=`get_config_value transparent_proxy`
+SSH=`get_config_value ssh`
+BITTORRENT_BLOCK=`get_config_value bittorrent_block`
+
+# Bridge config
 if [ "$BRIDGE" = "yes" ]; then
   DEV_IN_PARAM="-m physdev --physdev-in"
   DEV_OUT_PARAM="-m physdev --physdev-out"
@@ -66,7 +81,7 @@ add_set () {
     ipset_opt="--from $CLIENTS_RANGE_START --to $CLIENTS_RANGE_END"
   fi
   
-  if [ "$CLIENTS_IGNORE_MAC" = "yes" ]; then
+  if [ "$IGNORE_MAC" = "yes" ]; then
     ipset_ignoremac="--ignoremac"
   fi
   
@@ -221,6 +236,16 @@ rules () {
       -p udp --dport 67:68 -j DROP
   fi
 
+  ##
+  # Bittorrent Blocking (layer7 module in kernel is needed)
+  # Note: 
+  #   bittorrent-announce is custom pattern defined may warning with official
+  #   pattern downloaded from l7-filter site.
+  ##
+  if [ "$BITTORRENT_BLOCK" = "yes" ]; then
+    $IPTABLES -t mangle -A $CHAIN_MANGLE_PREROUTING -m layer7 --l7proto bittorrent-announce -j DROP
+    $IPTABLES -t mangle -A $CHAIN_MANGLE_PREROUTING -m layer7 --l7proto bittorrent -j DROP
+  fi
   
   ##
   # Mark the connections that have been authorized to save rule check time
diff --git a/example/rahunas.conf b/example/rahunas.conf
new file mode 100644 (file)
index 0000000..db8a31b
--- /dev/null
@@ -0,0 +1,100 @@
+//////////////////////////////////////////////////////////
+// RahuNAS Configuration                                //
+// Author: Neutron Soutmun <neo.neutron@gmail.com>      //
+// Date: 2008-11-26                                     //
+//////////////////////////////////////////////////////////
+
+/////////////////////////
+// Daemon              //
+/////////////////////////
+
+//// XML-RPC Server hostname or IP address
+//// default: localhost
+// xml_serv_host = "localhost"
+
+//// XML-RPC Server port
+//// default: 8888
+// xml_serv_port = "8888"
+
+//// XML-RPC URL
+//// default: /xmlrpc_service.php
+// xml_serv_url = "/xmlrpc_service.php"
+
+//// Polling interval (seconds)
+//// default: 60
+// polling_interval = "60"
+
+//// Idle threshold
+//// default: 600 (600 seconds = 10 minutes)
+// idle_threshold = "600"
+
+//// Log file
+log_file = "/var/log/rahunas/rahunas.log"
+
+/////////////////////////
+// Start-Up            //
+/////////////////////////
+
+//// External Interface
+dev_wan = "eth0"
+
+//// Internal Interface
+dev_lan = "eth1"
+
+//// Bridging the External and Internal interface (Transparent Inline)
+bridge = "no"
+
+//// MASQUERADE (automatic source NAT) for the packets going outside (External)
+masquerade = "yes"
+
+//// RahuNAS Server's IP address or hostname
+server = "172.30.0.1"
+
+//// Clients IP 
+//// clients_range_start and clients_range_end are single ip, eg. 172.30.0.1.
+//// clients_network is network address/netmask, eg 172.30.0.0/22.
+////
+//// Note: The clients_network will overide the clients_range_start and 
+////       clients_range_end.
+
+clients_range_start = ""
+clients_range_end = ""
+clients_network = "172.30.0.0/22"
+
+//// Excluded IP address
+//// space separated IP address, it can be single ip or network address/netmask
+//// example: excluded = "172.30.0.100 202.28.92.0/24"
+excluded = ""
+
+//// Ignore MAC address
+//// no  - RahuNAS connected to the clients network locally and can grap MAC.
+//// yes - RahuNAS can not grap MAC, use only IP for identification. (Remote/L3)
+ignore_mac = "no"
+
+//// Enable bandwidth shaping using IMQ and iproute2
+bandwidth_shape_imq = "yes"
+
+//// Enable DHCP forwarding
+//// yes - The DHCP server is not setup on RahuNAS box, forwarding the request 
+////       out of the box.
+forward_dhcp = "no"
+
+//// Enable DNS forwarding 
+//// yes - The DNS server is not setup on RahuNAS box, forwarding the request 
+////       out of the box.
+forward_dns = "no"
+
+//// Enable SQUID Cache/Proxy 
+//// proxy_port = "0" to disable.
+proxy_port = "0"
+proxy_host = "localhost"
+
+//// Enable transparent proxy
+//// Do the http request interception and redirect to the proxy_host:proxy_port
+transparent_proxy = "no"
+
+//// Enable SSH - Secure Shell
+ssh = "yes"
+
+//// Enable Bittorrent blocking
+bittorrent_block = "no"
index 907489d..aed7022 100644 (file)
@@ -4,56 +4,5 @@
 # Set to "yes" to have the init script start rahunas.
 RUN_DAEMON=no
 
-# External Interface
-DEV_WAN="eth0"
-
-# Internal Interface
-DEV_LAN="eth1"
-
-# Running in bridge mode - [yes|no]
-BRIDGE=no
-
-# MASQUERADE
-# MASQUERADE=[yes|no]
-MASQUERADE=yes
-
-# Server
-# SERVER=[hostname|IP]
-SERVER=172.30.0.1
-
-# Clients IP 
-# CLIENTS_RANGE_START and CLENTS_RANGE_END are [single ip].
-# CLIENTS_NETWORK is [network_address/netmask].
-# CLIENTS_IGNORE_MAC is [yes|no].
-#
-# Note: The CLIENTS_NETWORK will be used and overide 
-# the CLIENTS_RANGE_{START,STOP}.
-
-CLIENTS_RANGE_START=""
-CLIENTS_RANGE_END=""
-CLIENTS_NETWORK="172.30.0.0/22"
-CLIENTS_IGNORE_MAC=no
-
-# Enable Bandwidth shaping using IMQ and iproute2
-# BANDWIDTH_SHAPE_IMQ=[yes|no]
-BANDWIDTH_SHAPE_IMQ=yes
-
-# Enable DHCP Forwarding
-# FORWARD_DHCP=[yes|no]
-FORWARD_DHCP=no
-
-# Enable DNS Forwarding 
-# FORWARD_DNS=[yes|no]
-FORWARD_DNS=no
-
-# Enable SQUID Cache-Proxy 
-# PROXY_PORT=[0 = disable]
-# PROXY_HOST=[hostname/IP]
-# TRANSPARENT_PROXY=[yes|no]
-
-PROXY_PORT=3128
-PROXY_HOST=localhost
-TRANSPARENT_PROXY=no
-
-# SSH - Secure Shell for maintenance
-SSH=yes
+# RahuNAS config file
+RAHUNAS_CONFIG=/etc/rahunas/rahunas.conf
diff --git a/lcfg/Makefile.am b/lcfg/Makefile.am
new file mode 100644 (file)
index 0000000..5a7d706
--- /dev/null
@@ -0,0 +1,3 @@
+noinst_LIBRARIES = liblcfg.a
+
+liblcfg_a_SOURCES = lcfg_static.c lcfg_static.h 
diff --git a/lcfg/lcfg_static.c b/lcfg/lcfg_static.c
new file mode 100755 (executable)
index 0000000..11c8813
--- /dev/null
@@ -0,0 +1,1209 @@
+/* This file is an autogenerated single-file version of liblcfg.
+ * The revision used to create this file is 997. It is recommended
+ * that you update this file on a regulary basis from the original
+ * liblcfg distribution package.
+ *
+ * The most recent version of liblcfg is available at
+ *   <http://liblcfg.mwcollect.org>
+ */
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "lcfg_static.h"
+/*** begin file include/lcfg/lcfg_string.h ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_string.h 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+#ifndef LCFG_STRING_H
+#define LCFG_STRING_H
+
+struct lcfg_string *  lcfg_string_new();
+struct lcfg_string *  lcfg_string_new_copy(struct lcfg_string *);
+int                   lcfg_string_set(struct lcfg_string *, const char *);
+int                   lcfg_string_cat_char(struct lcfg_string *, char);
+int                   lcfg_string_cat_cstr(struct lcfg_string *, const char *);
+int                   lcfg_string_cat_uint(struct lcfg_string *, unsigned int);
+int                   lcfg_string_find(struct lcfg_string *, char);
+int                   lcfg_string_rfind(struct lcfg_string *, char);
+void                  lcfg_string_trunc(struct lcfg_string *, unsigned int);
+inline const char *   lcfg_string_cstr(struct lcfg_string *);
+inline unsigned int   lcfg_string_len(struct lcfg_string *);
+void                  lcfg_string_delete(struct lcfg_string *);
+
+#endif
+/*** end file include/lcfg/lcfg_string.h ***/
+/*** begin file include/lcfg/lcfg_token.h ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_token.h 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+#ifndef LCFG_TOKEN_H
+#define LCFG_TOKEN_H
+
+
+enum lcfg_token_type {
+       lcfg_null_token = 0,
+       lcfg_identifier,
+       lcfg_equals,
+       lcfg_string,
+       lcfg_sbracket_open,
+       lcfg_sbracket_close,
+       lcfg_comma,
+       lcfg_brace_open,
+       lcfg_brace_close
+};
+
+extern const char *lcfg_token_map[];
+
+struct lcfg_token
+{
+       enum lcfg_token_type type;
+       struct lcfg_string *string;
+       short line;
+       short col;
+};
+
+
+#endif
+/*** end file include/lcfg/lcfg_token.h ***/
+/*** begin file include/lcfg/lcfg_scanner.h ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_scanner.h 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+#ifndef LCFG_SCANNER_H
+#define LCFG_SCANNER_H
+
+
+struct lcfg_scanner;
+struct lcfg_token;
+
+struct lcfg_scanner *    lcfg_scanner_new(struct lcfg *, int fd);
+enum lcfg_status         lcfg_scanner_init(struct lcfg_scanner *);
+enum lcfg_status         lcfg_scanner_next_token(struct lcfg_scanner *, struct lcfg_token *);
+int                      lcfg_scanner_has_next(struct lcfg_scanner *);
+void                     lcfg_scanner_delete(struct lcfg_scanner *);
+
+#endif
+/*** end file include/lcfg/lcfg_scanner.h ***/
+/*** begin file include/lcfg/lcfg_parser.h ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_parser.h 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+#ifndef LCFG_PARSER_H
+#define LCFG_PARSER_H
+
+
+struct lcfg_parser;
+
+struct lcfg_parser *  lcfg_parser_new(struct lcfg *, const char *);
+enum lcfg_status      lcfg_parser_run(struct lcfg_parser *);
+enum lcfg_status      lcfg_parser_accept(struct lcfg_parser *, lcfg_visitor_function, void *);
+void                  lcfg_parser_delete(struct lcfg_parser *);
+enum lcfg_status      lcfg_parser_get(struct lcfg_parser *, const char *, void **, size_t *);
+
+#endif
+/*** end file include/lcfg/lcfg_parser.h ***/
+/*** begin file src/lcfg_string.c ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_string.c 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+
+
+struct lcfg_string
+{
+       char *str;
+       unsigned int size;
+       unsigned int capacity;
+};
+
+int lcfg_string_set(struct lcfg_string *s, const char *cstr)
+{
+       lcfg_string_trunc(s, 0);
+       return lcfg_string_cat_cstr(s, cstr);
+}
+
+
+/* make sure new_size bytes fit into the string */
+inline static void lcfg_string_grow(struct lcfg_string *s, unsigned int new_size)
+{
+       /* always allocate one byte more than needed
+        * to make _cstr() working in any case without realloc. */
+       while( (new_size + 1) > s->capacity )
+       {
+               s->capacity *= 2;
+               s->str = realloc(s->str, s->capacity);
+       }
+}
+
+struct lcfg_string *lcfg_string_new()
+{
+       struct lcfg_string *s = malloc(sizeof(struct lcfg_string));
+       assert(s);
+       
+       s->capacity = 8;
+       s->size = 0;
+       s->str = malloc(s->capacity);
+       
+       assert(s->str);
+       
+       return s;
+}
+
+struct lcfg_string *lcfg_string_new_copy(struct lcfg_string *s)
+{
+       struct lcfg_string *s_new = malloc(sizeof(struct lcfg_string));
+       assert(s_new);
+       
+       s_new->capacity = s->capacity;
+       s_new->size = s->size;
+       s_new->str = malloc(s_new->capacity);
+       
+       memcpy(s_new->str, s->str, s_new->size);
+       
+       return s_new;
+}
+
+int lcfg_string_cat_uint(struct lcfg_string *s, unsigned int i)
+{
+       unsigned int size_needed = 1;
+       unsigned int ii = i;
+       char c;
+       
+       while( ii > 10 )
+       {
+               size_needed++;
+               ii /= 10;
+       }
+       
+       lcfg_string_grow(s, s->size + size_needed);
+       
+       ii = size_needed - 1;
+       do
+       {
+               c = '0' + i % 10;
+               s->str[s->size + ii--] = c;
+               i /= 10;
+       } while( i != 0 );
+       
+       s->size += size_needed;
+       
+       return s->size;
+}
+
+int lcfg_string_find(struct lcfg_string *s, char c)
+{
+       int i;
+       
+       for( i = 0; i < s->size; i++ )
+       {
+               if( s->str[i] == c )
+               {
+                       return i;
+               }
+       }
+       
+       return -1;
+}
+
+int lcfg_string_rfind(struct lcfg_string *s, char c)
+{
+       int i;
+       
+       for( i = s->size - 1; i >= 0; i-- )
+       {
+               if( s->str[i] == c )
+               {
+                       return i;
+               }
+       }
+       
+       return -1;
+}
+
+void lcfg_string_trunc(struct lcfg_string *s, unsigned int max_size)
+{
+       if( max_size < s->size )
+       {
+               s->size = max_size;
+       }
+}
+
+int lcfg_string_cat_cstr(struct lcfg_string *s, const char *cstr)
+{
+       size_t len = strlen(cstr);
+       
+       lcfg_string_grow(s, s->size + len);
+       
+       memcpy(s->str + s->size, cstr, len);
+       
+       s->size += len;
+       
+       return s->size;
+}
+
+int lcfg_string_cat_char(struct lcfg_string *s, char c)
+{
+       lcfg_string_grow(s, s->size + 1);
+       
+       s->str[s->size++] = c;
+       
+       return s->size;
+}
+
+inline const char *lcfg_string_cstr(struct lcfg_string *s)
+{
+       s->str[s->size] = '\0';
+       return s->str;
+}
+
+inline unsigned int lcfg_string_len(struct lcfg_string *s)
+{
+       return s->size;
+}
+
+void lcfg_string_delete(struct lcfg_string *s)
+{
+       free(s->str);
+       free(s);
+}
+/*** end file src/lcfg_string.c ***/
+/*** begin file src/lcfg_token.c ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_token.c 997 2007-05-07 02:11:53Z dp $
+ * 
+ */
+
+
+const char *lcfg_token_map[] = {
+       "null_token",
+       "T_IDENTIFIER",
+       "`='",
+       "T_STRING",
+       "`['",
+       "`]'",
+       "`,'",
+       "`{'",
+       "`}'"
+ };
+/*** end file src/lcfg_token.c ***/
+/*** begin file src/lcfg_scanner.c ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_scanner.c 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+
+
+#define BUFFER_SIZE 0xff
+
+struct lcfg_scanner
+{
+       struct lcfg *lcfg;
+       
+       int fd;
+       char buffer[BUFFER_SIZE];
+       int offset;
+       int size;
+       int eof;
+       
+       short line;
+       short col;
+       
+       struct lcfg_token prepared_token;
+       int token_eof;
+};
+
+
+static enum lcfg_status lcfg_scanner_buffer_fill(struct lcfg_scanner *s)
+{
+       if( (s->size = read(s->fd, s->buffer, BUFFER_SIZE)) < 0 )
+       {
+               lcfg_error_set(s->lcfg, "read(): %s", strerror(errno));
+               return lcfg_status_error;
+       }
+       else if( s->size == 0 )
+       {
+               s->eof = !0;
+       }
+       else
+       {
+               s->offset = 0;
+       }
+       
+       return lcfg_status_ok;
+}
+
+static inline int lcfg_scanner_char_eof(struct lcfg_scanner *s)
+{
+       if( s->eof )
+       {
+               return !0;
+       }
+       else
+       {
+               if( s->size == 0 || s->offset == BUFFER_SIZE )
+               {
+                       lcfg_scanner_buffer_fill(s);
+               }
+               if( s->size < BUFFER_SIZE && s->offset == s->size )
+               {
+                       s->eof = !0;
+               }
+       
+               return s->eof;
+       }
+}
+
+static enum lcfg_status lcfg_scanner_char_read(struct lcfg_scanner *s, char *c)
+{
+       if( lcfg_scanner_char_eof(s) )
+       {
+               lcfg_error_set(s->lcfg, "%s", "cannot read beyond eof");
+               return lcfg_status_error;
+       }
+       
+       *c = s->buffer[s->offset++];
+
+       return lcfg_status_ok;
+}
+
+static enum lcfg_status lcfg_scanner_char_peek(struct lcfg_scanner *s, char *c)
+{
+       if( lcfg_scanner_char_eof(s) )
+       {
+               lcfg_error_set(s->lcfg, "%s", "cannot peek beyond eof");
+               return lcfg_status_error;
+       }
+       
+       *c = s->buffer[s->offset];
+
+       return lcfg_status_ok;
+}
+
+/* the beautiful lowlevel fsm */
+static enum lcfg_status lcfg_scanner_token_read(struct lcfg_scanner *s)
+{
+       enum scanner_state { start = 0, comm_start, in_oneline, in_multiline, multiline_end, in_identifier, in_str, in_esc, esc_hex_exp_first, esc_hex_exp_second, invalid };
+       enum scanner_state state = start;
+       char c = '\0';
+       char hex[3];
+       
+       s->prepared_token.type = lcfg_null_token;
+       
+       while( !lcfg_scanner_char_eof(s) )
+       {
+               int consume = !0;
+               lcfg_scanner_char_peek(s, &c);
+               
+               switch( state )
+               {
+                       case start:
+                               switch( c )
+                               {
+                                       case ' ':
+                                       case '\t':
+                                       case '\r':
+                                       case '\n':
+                                               break;
+                                       case '=':
+                                               s->prepared_token.type = lcfg_equals;
+                                               break;
+                                       case '[':
+                                               s->prepared_token.type = lcfg_sbracket_open;
+                                               break;
+                                       case ']':
+                                               s->prepared_token.type = lcfg_sbracket_close;
+                                               break;
+                                       case '{':
+                                               s->prepared_token.type = lcfg_brace_open;
+                                               break;
+                                       case '}':
+                                               s->prepared_token.type = lcfg_brace_close;
+                                               break;
+                                       case ',':
+                                               s->prepared_token.type = lcfg_comma;
+                                               break;
+                                       case '/':
+                                               state = comm_start;
+                                               break;
+                                       case '"':
+                                               state = in_str;
+                                               lcfg_string_trunc(s->prepared_token.string, 0);
+                                               break;
+                                       default:
+                                               if( isalpha(c) )
+                                               {
+                                                       lcfg_string_trunc(s->prepared_token.string, 0);
+                                                       lcfg_string_cat_char(s->prepared_token.string, c);
+                                                       state = in_identifier;
+                                               }
+                                               else
+                                               {
+                                                       lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col);
+                                                       state = invalid;
+                                               }
+                               }
+                               break;
+                       case comm_start:
+                               if( c == '/' )
+                               {
+                                       state = in_oneline;
+                               }
+                               else if( c == '*' )
+                               {
+                                       state = in_multiline;
+                               }
+                               else
+                               {
+                                       lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col);
+                                       state = invalid;
+                               }
+                               break;
+                       case in_oneline:
+                               if( c == '\n' )
+                               {
+                                       state = start;
+                               }
+                               break;
+                       case in_multiline:
+                               if( c == '*' )
+                               {
+                                       state = multiline_end;
+                               }
+                               break;
+                       case multiline_end:
+                               if( c == '/' )
+                               {
+                                       state = start;
+                               }
+                               else if( c != '*' )
+                               {
+                                       state = in_multiline;
+                               }
+                               break;
+                       case in_identifier:
+                               if( isalnum(c) || c == '-' || c == '_' )
+                               {
+                                       lcfg_string_cat_char(s->prepared_token.string, c);
+                               }
+                               else
+                               {
+                                       s->prepared_token.type = lcfg_identifier;
+                                       consume = 0;
+                                       state = start;
+                               }
+                               break;
+                       case in_str:
+                               if( c == '"' )
+                               {
+                                       s->prepared_token.type = lcfg_string;
+                                       state = start;
+                               }
+                               else if( c == '\\' )
+                               {
+                                       state = in_esc;
+                               }
+                               else
+                               {
+                                       lcfg_string_cat_char(s->prepared_token.string, c);
+                               }
+                               break;
+                       case in_esc:
+                               state = in_str;
+                               switch( c )
+                               {
+                                       case '"':
+                                               lcfg_string_cat_char(s->prepared_token.string, '"');
+                                               break;
+                                       case 'n':
+                                               lcfg_string_cat_char(s->prepared_token.string, '\n');
+                                               break;
+                                       case 't':
+                                               lcfg_string_cat_char(s->prepared_token.string, '\t');
+                                               break;
+                                       case 'r':
+                                               lcfg_string_cat_char(s->prepared_token.string, '\r');
+                                               break;
+                                       case '0':
+                                               lcfg_string_cat_char(s->prepared_token.string, '\0');
+                                               break;
+                                       case 'x':
+                                               state = esc_hex_exp_first;
+                                               break;
+                                       default:
+                                               lcfg_error_set(s->lcfg, "invalid string escape sequence `%c' near line %d, col %d", c, s->line, s->col);
+                                               state = invalid;
+                               }
+                               break;
+                       case esc_hex_exp_first:
+                               if( !isxdigit(c) )
+                               {
+                                       lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col);
+                                       state = invalid;
+                               }
+                               hex[0] = c;
+                               state = esc_hex_exp_second;
+                               break;
+                       case esc_hex_exp_second:
+                               if( !isxdigit(c) )
+                               {
+                                       lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col);
+                                       state = invalid;
+                               }
+                               hex[1] = c;
+                               hex[2] = '\0';
+                               lcfg_string_cat_char(s->prepared_token.string, strtoul(hex, NULL, 16));
+                               state = in_str;
+                               break;
+                       case invalid:
+                               break;
+               }
+               /*#include <stdio.h>
+               printf("read %c at line %d column %d, new state is %d\n", isprint(c) ? c : '.', s->line, s->col, state);*/
+               
+               /* this is technically not optimal (token position identified by last char), but it will suffice for now */
+               s->prepared_token.line = s->line;
+               s->prepared_token.col = s->col;
+       
+               if( consume )
+               {
+                       lcfg_scanner_char_read(s, &c);
+                       if( c == '\n' )
+                       {
+                               s->line++;
+                               s->col = 1;
+                       }
+                       else
+                       {
+                               s->col++;
+                       }
+               }
+               
+               if( s->prepared_token.type != lcfg_null_token || state == invalid )
+               {
+                       break;
+               }
+       }
+       
+       if( state != start )
+       {
+               if( state != invalid )
+               {
+                       lcfg_error_set(s->lcfg, "parse error: premature end of file near line %d, col %d", s->line, s->col);
+               }
+               
+               return lcfg_status_error;
+       }
+       
+       return lcfg_status_ok;
+}
+
+enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *s)
+{
+       /* prepare the first token */
+       return lcfg_scanner_token_read(s); 
+}
+
+int lcfg_scanner_has_next(struct lcfg_scanner *s)
+{
+       return s->prepared_token.type != lcfg_null_token;
+}
+
+enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *s, struct lcfg_token *t)
+{
+       if( !lcfg_scanner_has_next(s) )
+       {
+               lcfg_error_set(s->lcfg, "%s", "cannot access tokenstream beyond eof");
+               return lcfg_status_error;
+       }
+       
+       memcpy(t, &s->prepared_token, sizeof(struct lcfg_token));
+       t->string = lcfg_string_new_copy(s->prepared_token.string);
+       
+       /* prepare the next token */
+       return lcfg_scanner_token_read(s); 
+}
+
+struct lcfg_scanner *lcfg_scanner_new(struct lcfg *c, int fd)
+{
+       struct lcfg_scanner *s = malloc(sizeof(struct lcfg_scanner));
+       assert(s);
+       
+       memset(s, 0, sizeof(struct lcfg_scanner));
+       
+       s->lcfg = c;
+       s->fd = fd;
+       
+       s->line = s->col = 1;
+       
+       s->prepared_token.string = lcfg_string_new();
+       
+       return s;
+}
+
+void lcfg_scanner_delete(struct lcfg_scanner *s)
+{
+       lcfg_string_delete(s->prepared_token.string);
+       free(s);
+}
+
+/*** end file src/lcfg_scanner.c ***/
+/*** begin file src/lcfg_parser.c ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg_parser.c 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+
+
+#ifndef strdup
+char *strdup(const char *s)
+{
+       char *sdup;
+       
+       size_t len = strlen(s) + 1;
+       sdup = malloc(len);
+
+       if( sdup != NULL )
+       {
+               memcpy(sdup, s, len);
+       }
+       
+       return sdup;
+}
+#endif
+
+struct lcfg_parser_value_pair
+{
+       char *key;
+       struct lcfg_string *value;
+};
+
+
+struct lcfg_parser
+{
+       struct lcfg *lcfg;
+       char *filename;
+       struct lcfg_scanner *scanner;
+       
+       struct lcfg_parser_value_pair *values;
+       unsigned int value_length;
+       unsigned int value_capacity;
+};
+
+static int lcfg_parser_add_value(struct lcfg_parser *p, const char *key, struct lcfg_string *value)
+{
+       if( p->value_length == p->value_capacity )
+       {
+               p->value_capacity *= 2;
+               p->values = realloc(p->values, sizeof(struct lcfg_parser_value_pair) * p->value_capacity);
+               assert(p->values);
+       }
+       
+       p->values[p->value_length].key = strdup(key);
+       p->values[p->value_length].value = lcfg_string_new_copy(value);
+       
+       return ++p->value_length;
+}
+
+struct lcfg_parser *lcfg_parser_new(struct lcfg *c, const char *filename)
+{
+       struct lcfg_parser *p = malloc(sizeof(struct lcfg_parser));
+       assert(p);
+       
+       memset(p, 0, sizeof(struct lcfg_parser));
+       
+       p->filename = strdup(filename);
+       p->lcfg = c;
+       
+       p->value_length = 0;
+       p->value_capacity = 8;
+       p->values = malloc(sizeof(struct lcfg_parser_value_pair) * p->value_capacity);
+       assert(p->values);
+       
+       return p;
+}
+
+/* this is a basic push down automata */
+static enum lcfg_status lcfg_parser_parse(struct lcfg_parser *p)
+{
+       enum state { top_level = 0, exp_equals, exp_value, in_list, in_map, invalid };
+       /*const char *state_map[] = { "top_level", "exp_equals", "exp_value", "in_list", "in_map", "invalid" };*/
+
+       struct state_element
+       {
+               enum state s;
+               int list_counter;
+       };
+
+       /* start of ugly preproc stuff */
+       #define STATE_STACK_PUSH(t) \
+       if( ssi + 1 == state_stack_size ) \
+       { \
+               state_stack_size *= 2; \
+               state_stack = realloc(state_stack, state_stack_size * sizeof(struct state_element)); \
+       } \
+       state_stack[++ssi].s = t; \
+       state_stack[ssi].list_counter = 0
+       #define STATE_STACK_POP() ssi--
+       #define PATH_PUSH_STR(s) \
+       if( lcfg_string_len(current_path) != 0 ) \
+       { \
+               lcfg_string_cat_char(current_path, '.'); \
+       } \
+       lcfg_string_cat_cstr(current_path, s);
+       #define PATH_PUSH_INT(i) \
+       if( lcfg_string_len(current_path) != 0 ) \
+       { \
+               lcfg_string_cat_char(current_path, '.'); \
+       } \
+       lcfg_string_cat_uint(current_path, i);
+       #define PATH_POP() \
+       if( lcfg_string_rfind(current_path, '.') != -1 ) \
+       { \
+               lcfg_string_trunc(current_path, lcfg_string_rfind(current_path, '.')); \
+       } \
+       else \
+       { \
+               lcfg_string_trunc(current_path, 0); \
+       }
+       /* end of ugly preproc stuff */
+
+       if( lcfg_scanner_init(p->scanner) != lcfg_status_ok )
+       {
+               return lcfg_status_error;
+       }
+       
+       int state_stack_size = 8;
+       int ssi = 0; /* ssi = state stack index */
+       struct state_element *state_stack = malloc(sizeof(struct state_element) * state_stack_size);
+       
+       state_stack[ssi].s = top_level;
+       state_stack[ssi].list_counter = 0;
+       
+       struct lcfg_token t;
+       struct lcfg_string *current_path = lcfg_string_new();
+       
+       while( lcfg_scanner_has_next(p->scanner) && state_stack[ssi].s != invalid )
+       {
+               if( lcfg_scanner_next_token(p->scanner, &t) != lcfg_status_ok )
+               {
+                       free(state_stack);
+                       lcfg_string_delete(t.string);
+                       lcfg_string_delete(current_path);
+                       return lcfg_status_error;
+               }
+               
+               switch( state_stack[ssi].s )
+               {
+                       case top_level:
+                       case in_map:
+                               if( t.type == lcfg_identifier )
+                               {
+                                       PATH_PUSH_STR(lcfg_string_cstr(t.string));
+                                       STATE_STACK_PUSH(exp_equals);
+                               }
+                               else if( state_stack[ssi].s == in_map && t.type == lcfg_brace_close )
+                               {
+                                       STATE_STACK_POP();
+                                       PATH_POP();
+                               }
+                               else
+                               {
+                                       lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected identifier%s", lcfg_token_map[t.type], t.line, t.col, state_stack[ssi].s == in_map ? " or `}'" : ""); 
+                                       state_stack[ssi].s = invalid;
+                               }
+                               break;
+                       case exp_equals:
+                               if( t.type == lcfg_equals )
+                                       state_stack[ssi].s = exp_value;
+                               else
+                               {
+                                       lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected `='", lcfg_token_map[t.type], t.line, t.col); 
+                                       state_stack[ssi].s = invalid;
+                               }
+                               break;
+                       case exp_value:
+                               if( t.type == lcfg_string )
+                               {
+                                       lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string);
+                                       /*printf("adding string value for single statement\n");*/
+                                       STATE_STACK_POP();
+                                       PATH_POP();
+                               }
+                               else if( t.type == lcfg_sbracket_open )
+                               {
+                                       state_stack[ssi].s = in_list;
+                               }
+                               else if( t.type == lcfg_brace_open )
+                               {
+                                       state_stack[ssi].s = in_map;
+                               }
+                               else
+                               {
+                                       lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[' or `{'", lcfg_token_map[t.type], t.line, t.col); 
+                                       state_stack[ssi].s = invalid;
+                               }
+                               break;
+                       case in_list:
+                               if( t.type == lcfg_comma ); /* ignore comma */
+                               else if( t.type == lcfg_string )
+                               {
+                                       PATH_PUSH_INT(state_stack[ssi].list_counter);
+                                       lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string);
+                                       PATH_POP();
+                                       /*printf("adding string to list pos %d\n", state_stack[ssi].list_counter);*/
+                                       state_stack[ssi].list_counter++;
+                               }
+                               else if( t.type == lcfg_sbracket_open )
+                               {
+                                       PATH_PUSH_INT(state_stack[ssi].list_counter);
+                                       /*printf("adding list to list pos %d\n", state_stack[ssi].list_counter);*/
+                                       state_stack[ssi].list_counter++;
+                                       STATE_STACK_PUSH(in_list);
+                               }
+                               else if( t.type == lcfg_brace_open )
+                               {
+                                       PATH_PUSH_INT(state_stack[ssi].list_counter);
+                                       /*printf("adding map to list pos %d\n", state_stack[ssi].list_counter);*/
+                                       state_stack[ssi].list_counter++;
+                                       STATE_STACK_PUSH(in_map);
+                               }
+                               else if( t.type == lcfg_sbracket_close )
+                               {
+                                       PATH_POP();
+                                       STATE_STACK_POP();
+                               }
+                               else
+                               {
+                                       lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[', `{', `,' or `]'", lcfg_token_map[t.type], t.line, t.col); 
+                                       state_stack[ssi].s = invalid;
+                               }
+                               break;
+                       case invalid: /* unreachable */
+                               assert(0);
+                               break;
+               }
+               
+               lcfg_string_delete(t.string);
+               
+               /*printf(" *** pda: read %s, state is now %s\n", lcfg_token_map[t.type], state_map[state_stack[ssi].s]);*/
+       }
+
+       lcfg_string_delete(current_path);       
+       
+       if( state_stack[ssi].s == top_level && ssi == 0 )
+       {
+               free(state_stack);
+               return lcfg_status_ok;
+       }
+       else
+       {
+               free(state_stack);
+               return lcfg_status_error;
+       }
+}
+
+enum lcfg_status lcfg_parser_run(struct lcfg_parser *p)
+{      
+       int fd = open(p->filename, 0);
+       enum lcfg_status status;
+       
+       if( fd < 0 )
+       {
+               lcfg_error_set(p->lcfg, "open(): %s", strerror(errno));
+               return lcfg_status_error;
+       }
+       
+       p->scanner = lcfg_scanner_new(p->lcfg, fd);
+       
+       status = lcfg_parser_parse(p);  
+       
+       close(fd);
+       
+       return status;
+}
+enum lcfg_status lcfg_parser_accept(struct lcfg_parser *p, lcfg_visitor_function fn, void *user_data)
+{
+       int i;
+       
+       for( i = 0; i < p->value_length; i++ )
+       {
+               if( fn(p->values[i].key, (void *)lcfg_string_cstr(p->values[i].value), lcfg_string_len(p->values[i].value), user_data) != lcfg_status_ok )
+               {
+                       lcfg_error_set(p->lcfg, "%s", "configuration value traversal aborted upon user request");
+                       return lcfg_status_error;
+               }
+       }
+       
+       return lcfg_status_ok;
+}
+
+enum lcfg_status lcfg_parser_get(struct lcfg_parser *p, const char *key, void **data, size_t *len)
+{
+       int i;
+
+       for( i = 0; i < p->value_length; i++ )
+       {
+               if( !strcmp(p->values[i].key, key) )
+               {
+                       *data = (void *)lcfg_string_cstr(p->values[i].value);
+                       *len = lcfg_string_len(p->values[i].value);
+                       return lcfg_status_ok;
+               }
+       }
+       
+       return lcfg_status_error;
+}
+
+
+void lcfg_parser_delete(struct lcfg_parser *p)
+{
+       if( p->scanner != NULL )
+       {
+               lcfg_scanner_delete(p->scanner);
+       }
+       
+       int i;
+       
+       for( i = 0; i < p->value_length; i++ )
+       {
+               free(p->values[i].key);
+               lcfg_string_delete(p->values[i].value);
+       }
+       free(p->values);
+       free(p->filename);
+       free(p);
+}
+/*** end file src/lcfg_parser.c ***/
+/*** begin file src/lcfg.c ***/
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg.c 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+
+
+struct lcfg
+{
+       char error[0xff];
+       struct lcfg_parser *parser;
+};
+
+struct lcfg *lcfg_new(const char *filename)
+{
+       struct lcfg *c = malloc(sizeof(struct lcfg));
+       assert(c);
+       memset(c, 0, sizeof(struct lcfg));
+       
+       c->parser = lcfg_parser_new(c, filename);
+       assert(c->parser);
+       
+       return c;
+}
+
+void lcfg_delete(struct lcfg *c)
+{
+       lcfg_parser_delete(c->parser);
+       free(c);
+}
+
+const char *lcfg_error_get(struct lcfg *c)
+{
+       return c->error;
+}
+
+enum lcfg_status lcfg_parse(struct lcfg *c)
+{
+       return lcfg_parser_run(c->parser);
+}
+
+enum lcfg_status lcfg_accept(struct lcfg *c, lcfg_visitor_function fn, void *user_data)
+{
+       return lcfg_parser_accept(c->parser, fn, user_data);
+}
+
+enum lcfg_status lcfg_value_get(struct lcfg *c, const char *key, void **data, size_t *len)
+{
+       return lcfg_parser_get(c->parser, key, data, len);
+}
+
+void lcfg_error_set(struct lcfg *c, const char *fmt, ...)
+{      
+       va_list ap;
+       va_start(ap, fmt);
+       vsnprintf(c->error, sizeof(c->error), fmt, ap);
+       va_end(ap);
+}
+
+/*** end file src/lcfg.c ***/
diff --git a/lcfg/lcfg_static.h b/lcfg/lcfg_static.h
new file mode 100755 (executable)
index 0000000..bb670cc
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * liblcfg - lightweight configuration file library
+ * Copyright (C) 2007  Paul Baecher
+ * 
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *    $Id: lcfg.h 995 2007-05-07 01:49:42Z dp $
+ * 
+ */
+
+#ifndef LCFG_H
+#define LCFG_H
+
+struct lcfg;
+
+enum lcfg_status { lcfg_status_ok, lcfg_status_error };
+
+typedef enum lcfg_status (*lcfg_visitor_function)(const char *key, void *data, size_t size, void *user_data);
+
+
+/* open a new config file */
+struct lcfg *        lcfg_new(const char *filename);
+
+/* parse config into memory */
+enum lcfg_status     lcfg_parse(struct lcfg *);
+
+/* visit all configuration elements */
+enum lcfg_status     lcfg_accept(struct lcfg *, lcfg_visitor_function, void *);
+
+/* access a value by path */
+enum lcfg_status     lcfg_value_get(struct lcfg *, const char *, void **, size_t *);
+
+/* return the last error message */
+const char *         lcfg_error_get(struct lcfg *);
+
+/* set error */
+void                 lcfg_error_set(struct lcfg *, const char *fmt, ...);
+
+/* destroy lcfg context */
+void                 lcfg_delete(struct lcfg *);
+
+
+#endif
index c628982..fa9a2bd 100644 (file)
@@ -11,7 +11,9 @@ AM_CFLAGS = \
        -DRAHUNAS_VERSION=\"$(RAHUNAS_VERSION)\" \
        -DPROGRAM=\"$(PROGRAM)\" \
        -DIPSET_VERSION=\"$(IPSET_VERSION)\" \
-       -DRAHUNAS_CONF_DIR=\"$(sysconfdir)/rahunas/\"
+       -DRAHUNAS_CONF_DIR=\"$(sysconfdir)/rahunas/\" \
+  -DRAHUNAS_LOG_DIR=\"$(localstatedir)/log/rahunas/\" \
+  -DRAHUNAS_RUN_DIR=\"$(localstatedir)/run/\"
 
 rahunasd_SOURCES = \
        rahunasd.c \
@@ -35,9 +37,11 @@ rahunasd_SOURCES = \
   rh-task-bandwidth.c \
   rh-task-bandwidth.h \
   rh-radius.h \
+  rh-config.c \
        rh-config.h
 
 rahunasd_LDADD =  \
        $(top_builddir)/xmlrpc/libgnetxmlrpc.a \
+  $(top_builddir)/lcfg/liblcfg.a \
        $(LIBGNET_LIBS) \
        $(LIBGDA_LIBS)
index 8615497..9fe89ef 100644 (file)
@@ -31,6 +31,7 @@ int getline(int fd, char *buf, size_t size);
 size_t expired_check(void *data);
 
 /* Declaration */
+struct rahunas_config rh_config;
 struct rahunas_map *map = NULL;
 struct set *rahunas_set = NULL;
 
@@ -216,7 +217,7 @@ size_t expired_check(void *data)
  
   for (i = 0; i < map->size; i++) {
     if (test_bit(IPSET_RAHUNAS_ISSET, (void *)&table[i].flags)) {
-      if ((time(NULL) - table[i].timestamp) > IDLE_THRESHOLD) {
+      if ((time(NULL) - table[i].timestamp) > rh_config.idle_threshold) {
         // Idle Timeout
         DP("Found IP: %s idle timeout", idtoip(map, i));
         req.id = i;
@@ -243,7 +244,7 @@ void rh_exit()
   syslog(LOG_ALERT, "Child Exiting ..");
   rh_task_stopservice(map);
   rh_task_cleanup();
-  rh_closelog(DEFAULT_LOG);
+  rh_closelog(rh_config.log_file);
 }
 
 static void
@@ -353,14 +354,22 @@ int main(int argc, char **argv)
        GNetXmlRpcServer *server = NULL;
        GMainLoop* main_loop     = NULL;
 
+   
+
   signal(SIGTERM, rh_sighandler);
   signal(SIGKILL, rh_sighandler);
 
        watch_child(argv);
 
+  /* Get configuration from config file */
+  if (config_init(&rh_config) < 0) {
+         syslog(LOG_ERR, "Could not open config file %s", CONFIG_FILE);
+    exit(EXIT_FAILURE);
+  }
+
   /* Open log file */
-       if ((fd_log = rh_openlog(DEFAULT_LOG)) == (-1)) {
-    syslog(LOG_ERR, "Could not open log file %s", DEFAULT_LOG);
+       if ((fd_log = rh_openlog(rh_config.log_file)) < 0) {
+    syslog(LOG_ERR, "Could not open log file %s", rh_config.log_file);
     exit(EXIT_FAILURE);
   }
 
@@ -399,7 +408,7 @@ int main(int argc, char **argv)
                                                                                                                                           do_getsessioninfo, 
                                                                                                                                                 map);
 
-  g_timeout_add_seconds (POLLING, polling, map);
+  g_timeout_add_seconds (rh_config.polling_interval, polling, map);
 
   rh_task_startservice(map);
 
index 9a2c7c9..4322ce7 100644 (file)
@@ -23,6 +23,7 @@
 
 #define DEFAULT_MAC "00:00:00:00:00:00"
 
+extern struct rahunas_config rh_config; 
 extern struct rahunas_map *map;
 extern struct set *rahunas_set;
 extern const char *termstring; 
diff --git a/src/rh-config.c b/src/rh-config.c
new file mode 100755 (executable)
index 0000000..42379d7
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * RahuNAS configuration
+ * Author: Suriya Soutmun <darksolar@gmail.com>
+ * Date:   2008-11-26
+ */
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "rh-config.h"
+
+void config_default(struct rahunas_config *config) {
+  config->polling_interval = POLLING;
+  config->idle_threshold = IDLE_THRESHOLD;
+  config->xml_serv_port = XMLSERVICE_PORT;
+  config->xml_serv_host = strdup(XMLSERVICE_HOST);
+  config->xml_serv_url = strdup(XMLSERVICE_URL);
+  config->set_name = strdup(SET_NAME);
+  config->log_file = strdup(DEFAULT_LOG);
+}
+
+enum lcfg_status rahunas_visitor(const char *key, void *data, size_t size, 
+                                 void *user_data) {
+  struct rahunas_config *config = (struct rahunas_config *)user_data;
+
+  if(config == NULL)
+    return lcfg_status_error;
+
+  char *value = strndup(data, size);
+
+  if(value == NULL)
+    return lcfg_status_error;
+
+  if(!strcmp(key, "log_file")) {
+    config->log_file = value;
+  } else if (!strcmp(key, "idle_threshold")) {
+    config->idle_threshold = atoi(value);
+  } else if (!strcmp(key, "polling")) {
+    config->polling_interval = atoi(value);
+  } else if (!strcmp(key, "set_name")) {
+    config->set_name = value;
+  } else if (!strcmp(key, "xml_service_host")) {
+    config->xml_serv_host = value;
+  } else if (!strcmp(key, "xml_service_port")) {
+    config->xml_serv_port = atoi(value);
+  } else if (!strcmp(key, "xml_service_url")) {
+    config->xml_serv_url = value; 
+  }
+
+  return lcfg_status_ok;
+}
+
+int config_init(struct rahunas_config *config) {
+
+  config_default(config);
+
+  lcfg_visitor_function visitor_func = rahunas_visitor;
+  struct lcfg *c = lcfg_new(CONFIG_FILE);
+  
+  if (lcfg_parse(c) != lcfg_status_ok) {
+    syslog(LOG_ERR, "config error: %s", lcfg_error_get(c));
+    lcfg_delete(c);
+    return -1;
+  }
+
+  if (lcfg_accept(c, visitor_func, config) != lcfg_status_ok) {
+    syslog(LOG_ERR, "config error: %s", lcfg_error_get(c));
+    lcfg_delete(c);
+    return -1;
+  }
+
+  if (c != NULL)
+    lcfg_delete(c);
+
+  return 0;
+}
index e2fad77..b09daa0 100644 (file)
@@ -1,29 +1,36 @@
 /**
- * RahuNASd config file 
- * Author: Neutron Soutmun <neo.neutron@gmail.com>
- * Date:   2008-08-07
+ * RahuNAS configuration header
+ * Author: Suriya Soutmun <darksolar@gmail.com>
+ * Date:   2008-11-26
  */
+#ifndef __RH_CONFIG_H 
+#define __RH_CONFIG_H 
 
-#ifndef __RH_CONFIG_H
-#define __RH_CONFIG_H
-
-/* Configuration */
-#define DEFAULT_LOG "/var/log/rahunas/rahunas.log"
-#define DEFAULT_PID "/var/run/rahunasd.pid"
-
-#ifdef RH_DEBUG
-#  define IDLE_THRESHOLD 30
-#  define POLLING 15 
-#else
-#  define IDLE_THRESHOLD 600
-#  define POLLING 60 
-#endif
+#include "../lcfg/lcfg_static.h"
 
+#define DEFAULT_LOG RAHUNAS_LOG_DIR "rahunas.log"
+#define IDLE_THRESHOLD 600
+#define POLLING 60 
 #define SET_NAME "rahunas_set"
 #define XMLSERVICE_HOST        "localhost"
 #define XMLSERVICE_PORT        8888
 #define XMLSERVICE_URL "/xmlrpc_service.php"
 
+#define CONFIG_FILE RAHUNAS_CONF_DIR "rahunas.conf"
+#define DEFAULT_PID RAHUNAS_RUN_DIR "rahunasd.pid"
 #define DB_NAME "rahunas"
 
-#endif // __RH_CONFIG_H
+struct rahunas_config {
+  int  polling_interval;
+  int  idle_threshold;
+  int  xml_serv_port;
+  char *xml_serv_host;
+  char *xml_serv_url;
+  char *set_name;
+  char *log_file;
+};
+
+int config_init(struct rahunas_config *config);
+enum lcfg_status rahunas_visitor(const char *key, void *data, size_t size, 
+                                 void *user_data);
+#endif // __RH_CONFIG_H 
index 9e9a892..a49806a 100644 (file)
@@ -357,7 +357,7 @@ int get_header_from_set (struct rahunas_map *map)
        in_addr_t first_ip;
        in_addr_t last_ip;
 
-  size = req_size = load_set_list(SET_NAME, &idx, 
+  size = req_size = load_set_list(rh_config.set_name, &idx, 
                                   IP_SET_OP_LIST_SIZE, CMD_LIST); 
 
   DP("Get Set Size: %d", size);
@@ -401,7 +401,7 @@ int walk_through_set (int (*callback)(void *))
   socklen_t size, req_size;
   int res = 0;
 
-  size = req_size = load_set_list(SET_NAME, &idx, 
+  size = req_size = load_set_list(rh_config.set_name, &idx, 
                                   IP_SET_OP_LIST_SIZE, CMD_LIST); 
 
   DP("Get Set Size: %d", size);
index 1b09b5d..0455a35 100644 (file)
@@ -46,7 +46,7 @@ static void init (void)
 {
   logmsg(RH_LOG_NORMAL, "Task IPSET init..");  
 
-  rahunas_set = set_adt_get(SET_NAME);
+  rahunas_set = set_adt_get(rh_config.set_name);
 
   DP("getsetname: %s", rahunas_set->name);
   DP("getsetid: %d", rahunas_set->id);
@@ -57,7 +57,7 @@ static void init (void)
 static void cleanup (void)
 {
   logmsg(RH_LOG_NORMAL, "Task IPSET cleanup..");  
-  set_flush(SET_NAME);
+  set_flush(rh_config.set_name);
 
   rh_free(&rahunas_set);
 }
@@ -66,7 +66,7 @@ static void cleanup (void)
 static int startservice (struct rahunas_map *map)
 {
   /* Ensure the set is empty */
-  set_flush(SET_NAME);
+  set_flush(rh_config.set_name);
 }
 
 /* Stop service task */
index a8378db..556eadd 100644 (file)
@@ -36,8 +36,9 @@ int send_xmlrpc_stopacct(struct rahunas_map *map, uint32_t id, int cause) {
   if (params == NULL)
     return (-1);
 
-  client = gnet_xmlrpc_client_new(XMLSERVICE_HOST, XMLSERVICE_URL, 
-                                       XMLSERVICE_PORT);
+  client = gnet_xmlrpc_client_new(rh_config.xml_serv_host, 
+                                  rh_config.xml_serv_url, 
+                                       rh_config.xml_serv_port);
 
   if (!client) {
     logmsg(RH_LOG_ERROR, "Could not connect to XML-RPC service");