diff --git a/Makefile b/Makefile
index eba3e9c..0f64874 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ MANDIR  = $(PREFIX)/share/man
 
 CFLAGS  = -O2 -g -std=c99 -fno-strict-aliasing -Wall -W -D_GNU_SOURCE -I/usr/local/include
 LDFLAGS = -lssl -lcrypto -lev -L/usr/local/lib
-OBJS    = stud.o ringbuffer.o configuration.o
+OBJS    = stud.o ringbuffer.o configuration.o log.o
 
 all: realall
 
diff --git a/configuration.c b/configuration.c
index ff3409f..05d7369 100644
--- a/configuration.c
+++ b/configuration.c
@@ -9,6 +9,7 @@
 #include <errno.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#include <libgen.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <getopt.h>
@@ -19,8 +20,10 @@
 #include <sys/stat.h>
 #include <syslog.h>
 
+#include "stud.h"
 #include "configuration.h"
 #include "version.h"
+#include "log.h"
 
 #define ADDR_LEN 150
 #define PORT_LEN 6
@@ -42,10 +45,15 @@
 #define CFG_SYSLOG "syslog"
 #define CFG_SYSLOG_FACILITY "syslog-facility"
 #define CFG_PARAM_SYSLOG_FACILITY 11015
+#define CFG_LOG_LEVEL "log-level"
+#define CFG_PARAM_LOG_LEVEL 11016
+#define CFG_PARAM_INSTANCE_NAME 11017
 #define CFG_DAEMON "daemon"
 #define CFG_WRITE_IP "write-ip"
 #define CFG_WRITE_PROXY "write-proxy"
 #define CFG_PEM_FILE "pem-file"
+#define CFG_INSTANCE_NAME "instance-name"
+#define CFG_PID_FILE "pid-file"
 
 #ifdef USE_SHARED_CACHE
   #define CFG_SHARED_CACHE "shared-cache"
@@ -69,6 +77,9 @@
 #endif
 // END: configuration parameters
 
+#define PID_DIR "/var/run/stud"
+#define PID_DIR_NONPRIVILEGED "/tmp"
+
 static char var_buf[CONFIG_BUF_SIZE];
 static char val_buf[CONFIG_BUF_SIZE];
 static char error_buf[CONFIG_BUF_SIZE];
@@ -90,14 +101,28 @@ char * config_error_get (void) {
   return error_buf;
 }
 
-void config_die (char *fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  vfprintf(stderr, fmt, args);
-  va_end(args);
-  fprintf(stderr, "\n");
+char * pid_dir_get () {
+    if (getuid() == 0) {
+        return PID_DIR;
+    } else {
+        char *td = getenv("TMPDIR");
+    return (td == NULL || strlen(td) < 1) ?
+        PID_DIR_NONPRIVILEGED :
+        td;
+  }
+}
 
-  exit(1);
+int config_param_val_pid_file (char *str, char *dst) {
+  int len = (str == NULL) ? 0 : strlen(str);
+  memset(dst, '\0', PATH_MAX);
+  if (len < 1) {
+    char *name = stud_instance_name(NULL);
+    if (name == NULL || strlen(name) < 1) name = "stud";
+    snprintf(dst, PATH_MAX, "%s/%s.pid", pid_dir_get(), name);
+  } else {
+    strncat(dst, str, PATH_MAX);
+  }
+  return 1;
 }
 
 stud_config * config_new (void) {
@@ -138,13 +163,15 @@ stud_config * config_new (void) {
   r->SHCUPD_MCASTTTL    = NULL;
 #endif
 
-  r->QUIET              = 0;
   r->SYSLOG             = 0;
   r->SYSLOG_FACILITY    = LOG_DAEMON;
+  r->LOG_LEVEL          = log_str2level(NULL);
   r->TCP_KEEPALIVE_TIME = 3600;
   r->DAEMONIZE          = 0;
   r->PREFER_SERVER_CIPHERS = 0;
 
+  config_param_val_pid_file(NULL, r->PID_FILE);
+
   return r;
 }
 
@@ -528,10 +555,10 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int
   struct stat st;
       
   if (strcmp(k, "tls") == 0) {
-    //cfg->ENC_TLS = 1;
+    cfg->ETYPE = ENC_TLS;
   }
   else if (strcmp(k, "ssl") == 0) {
-    //cfg->ENC_TLS = 0;
+    cfg->ETYPE = ENC_SSL;
   }
   else if (strcmp(k, CFG_CIPHERS) == 0) {
     if (v != NULL && strlen(v) > 0) {
@@ -619,7 +646,7 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int
     }
   }
   else if (strcmp(k, CFG_QUIET) == 0) {
-    r = config_param_val_bool(v, &cfg->QUIET);
+    cfg->LOG_LEVEL = LOG_ERR;
   }
   else if (strcmp(k, CFG_SYSLOG) == 0) {
     r = config_param_val_bool(v, &cfg->SYSLOG);
@@ -665,6 +692,9 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int
       r = 0;
     }
   }
+  else if (strcmp(k, CFG_LOG_LEVEL) == 0) {
+    cfg->LOG_LEVEL = log_str2level(v);
+  }
   else if (strcmp(k, CFG_DAEMON) == 0) {
     r = config_param_val_bool(v, &cfg->DAEMONIZE);
   }
@@ -687,6 +717,12 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int
         config_assign_str(&cfg->CERT_FILE, v);
     }
   }
+  else if (strcmp(k, CFG_INSTANCE_NAME) == 0) {
+    stud_instance_name(v);
+  }
+  else if (strcmp(k, CFG_PID_FILE) == 0) {
+    r = config_param_val_pid_file(v, cfg->PID_FILE);
+  }
   else {
     fprintf(
       stderr,
@@ -696,17 +732,19 @@ void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int
   }
   
   if (! r) {
-    if (file != NULL)
-      config_die("Error in configuration file '%s', line %d: %s\n", file, line, config_error_get()); 
-    else
-      config_die("Invalid parameter '%s': %s", k, config_error_get());
+    if (file != NULL) {
+      die("Error in configuration file '%s', line %d: %s\n", file, line, config_error_get()); 
+    } else {
+      die("Invalid parameter '%s': %s", k, config_error_get());
+    }
   }
 }
 
 #ifndef NO_CONFIG_FILE
 int config_file_parse (char *file, stud_config *cfg) {
-  if (cfg == NULL)
-    config_die("Undefined stud options; THIS IS A BUG!\n");
+  if (cfg == NULL) {
+    die("Undefined stud options; THIS IS A BUG!\n");
+  }
   
   char line[CONFIG_BUF_SIZE];
   FILE *fd = NULL;
@@ -717,8 +755,26 @@ int config_file_parse (char *file, stud_config *cfg) {
   } else {
     fd = fopen(file, "r");
   }
-  if (fd == NULL)
-      config_die("Unable to open configuration file '%s': %s\n", file, strerror(errno));
+  if (fd == NULL) {
+      die("Unable to open configuration file '%s': %s\n", file, strerror(errno));
+  }
+
+  // assign stud instance name
+  char *bn = strdup(basename(file));
+  if (bn != NULL) {
+    char *ptr = bn + strlen(bn);
+    while (ptr > bn) {
+      if (*ptr == '.') {
+        *ptr = '\0';
+        break;
+      }
+      ptr--;
+    }
+    str_trim(bn);
+    stud_instance_name(bn);
+    config_param_val_pid_file(NULL, cfg->PID_FILE);
+    free(bn);
+  }
 
   // read config
   int i = 0;
@@ -737,8 +793,6 @@ int config_file_parse (char *file, stud_config *cfg) {
     if (val == NULL) continue;
     str_trim(val);
     
-    // printf("File '%s', line %d, key: '%s', value: '%s'\n", file, i, key, val);
-    
     // validate configuration key => value
     config_param_validate(key, val, cfg, file, i);
   }
@@ -894,9 +948,13 @@ void config_print_usage_fd (char *prog, stud_config *cfg, FILE *out) {
   fprintf(out, "  -q  --quiet                Be quiet; emit only error messages\n");
   fprintf(out, "  -s  --syslog               Send log message to syslog in addition to stderr/stdout\n");
   fprintf(out, "  --syslog-facility=FACILITY Syslog facility to use (Default: \"%s\")\n", config_disp_log_facility(cfg->SYSLOG_FACILITY));
+  fprintf(out, "      --log-level=NAME       Log level (Default: \"%s\")\n", config_disp_str(log_level2str(cfg->LOG_LEVEL)));
+  fprintf(out, "      --instance-name        Instance name (Default: \"%s\")\n", config_disp_str(stud_instance_name(NULL)));
   fprintf(out, "\n");
   fprintf(out, "OTHER OPTIONS:\n");
   fprintf(out, "      --daemon               Fork into background and become a daemon (Default: %s)\n", config_disp_bool(cfg->DAEMONIZE));
+  fprintf(out, "  -p  --pid-file=FILE        Save daemon's pid to specified file (Default: \"%s\")\n", config_disp_str(cfg->PID_FILE));
+  fprintf(out, "\n");
   fprintf(out, "      --write-ip             Write 1 octet with the IP family followed by the IP\n");
   fprintf(out, "                             address in 4 (IPv4) or 16 (IPv6) octets little-endian\n");
   fprintf(out, "                             to backend before the actual data\n");
@@ -1044,12 +1102,6 @@ void config_print_default (FILE *fd, stud_config *cfg) {
   fprintf(fd, FMT_QSTR, CFG_GROUP, config_disp_gid(cfg->GID));
   fprintf(fd, "\n");
 
-  fprintf(fd, "# Quiet execution, report only error messages\n");
-  fprintf(fd, "#\n");
-  fprintf(fd, "# type: boolean\n");
-  fprintf(fd, FMT_STR, CFG_QUIET, config_disp_bool(cfg->QUIET));
-  fprintf(fd, "\n");
-
   fprintf(fd, "# Use syslog for logging\n");
   fprintf(fd, "#\n");
   fprintf(fd, "# type: boolean\n");
@@ -1062,12 +1114,30 @@ void config_print_default (FILE *fd, stud_config *cfg) {
   fprintf(fd, FMT_QSTR, CFG_SYSLOG_FACILITY, config_disp_log_facility(cfg->SYSLOG_FACILITY));
   fprintf(fd, "\n");
 
+  fprintf(fd, "# Log level\n");
+  fprintf(fd, "# Available levels: fatal, error, warning, notice, info, debug\n");
+  fprintf(fd, "# type: string\n");
+  fprintf(fd, FMT_QSTR, CFG_LOG_LEVEL, config_disp_str(log_level2str(cfg->LOG_LEVEL)));
+  fprintf(fd, "\n");
+
+  fprintf(fd, "# Instance name (used for syslog ident)\n");
+  fprintf(fd, "# type: string\n");
+  fprintf(fd, FMT_QSTR, CFG_INSTANCE_NAME, config_disp_str(stud_instance_name(NULL)));
+  fprintf(fd, "\n");
+
   fprintf(fd, "# Run as daemon\n");
   fprintf(fd, "#\n");
   fprintf(fd, "# type: boolean\n");
   fprintf(fd, FMT_STR, CFG_DAEMON, config_disp_bool(cfg->DAEMONIZE));
   fprintf(fd, "\n");
 
+  fprintf(fd, "# Pid file\n");
+  fprintf(fd, "# NOTE: Pid file is automatically computed from instance name.\n");
+  fprintf(fd, "#\n");
+  fprintf(fd, "# type: string\n");
+  fprintf(fd, "# %s = \"%s\"\n", CFG_PID_FILE, config_disp_str(cfg->PID_FILE));
+  fprintf(fd, "\n");
+
   fprintf(fd, "# Report client address by writing IP before sending data\n");
   fprintf(fd, "#\n");
   fprintf(fd, "# NOTE: This option is mutually exclusive with option %s.\n", CFG_WRITE_PROXY);
@@ -1127,9 +1197,12 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
     { CFG_QUIET, 0, NULL, 'q' },
     { CFG_SYSLOG, 0, NULL, 's' },
     { CFG_SYSLOG_FACILITY, 1, NULL, CFG_PARAM_SYSLOG_FACILITY },
+    { CFG_LOG_LEVEL, 1, NULL, CFG_PARAM_LOG_LEVEL },
+    { CFG_INSTANCE_NAME, 1, NULL, CFG_PARAM_INSTANCE_NAME },
     { CFG_DAEMON, 0, &cfg->DAEMONIZE, 1 },
     { CFG_WRITE_IP, 0, &cfg->WRITE_IP_OCTET, 1 },
     { CFG_WRITE_PROXY, 0, &cfg->WRITE_PROXY_LINE, 1 },
+    { CFG_PID_FILE, 1, NULL, 'p' },
  
     { "test", 0, NULL, 't' },
     { "version", 0, NULL, 'V' },
@@ -1141,7 +1214,7 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
     int option_index = 0;
     c = getopt_long(
       argc, argv,
-      "c:e:Ob:f:n:B:C:U:P:M:k:r:u:g:qstVh",
+      "c:e:Ob:f:n:B:C:U:P:M:k:r:u:g:qsp:tVh",
       long_options, &option_index
     );
 
@@ -1153,8 +1226,9 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
         break;
 #ifndef NO_CONFIG_FILE
       case CFG_PARAM_CFGFILE:
-        if (!config_file_parse(optarg, cfg))
-          config_die("%s", config_error_get());
+        if (!config_file_parse(optarg, cfg)) {
+          die("%s", config_error_get());
+        }
         break;
       case CFG_PARAM_DEFCFG:
         config_print_default(stdout, cfg);
@@ -1164,6 +1238,9 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
       case CFG_PARAM_SYSLOG_FACILITY:
         config_param_validate(CFG_SYSLOG_FACILITY, optarg, cfg, NULL, 0);
         break;
+      case CFG_PARAM_LOG_LEVEL:
+        config_param_validate(CFG_LOG_LEVEL, optarg, cfg, NULL, 0);
+        break;
       case 'c':
         config_param_validate(CFG_CIPHERS, optarg, cfg, NULL, 0);
         break;
@@ -1212,11 +1289,17 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
         config_param_validate(CFG_GROUP, optarg, cfg, NULL, 0);
         break;
       case 'q':
-        config_param_validate(CFG_QUIET, CFG_BOOL_ON, cfg, NULL, 0);
+        config_param_validate(CFG_LOG_LEVEL, LSTR_ERR, cfg, NULL, 0);
+        break;
+      case CFG_PARAM_INSTANCE_NAME:
+        config_param_validate(CFG_INSTANCE_NAME, optarg, cfg, NULL, 0);
         break;
       case 's':
         config_param_validate(CFG_SYSLOG, CFG_BOOL_ON, cfg, NULL, 0);
         break;
+      case 'p':
+        config_param_validate(CFG_PID_FILE, optarg, cfg, NULL, 0);
+        break;
       case 't':
         test_only = 1;
         break;
@@ -1230,14 +1313,15 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
         break;
 
       default:
-        config_die("Invalid command line parameters. Run %s --help for instructions.", basename(argv[0]));
+        die("Invalid command line parameters. Run %s --help for instructions.", basename(argv[0]));
     }
   }
   
   prog = argv[0];
 
-  if (tls && ssl)
-    config_die("Options --tls and --ssl are mutually exclusive.");
+  if (tls && ssl) {
+    die("Options --tls and --ssl are mutually exclusive.");
+  }
   else {
     if (ssl)
       cfg->ETYPE = ENC_SSL;
@@ -1245,17 +1329,18 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
       cfg->ETYPE = ENC_TLS;
   }
 
-  if (cfg->WRITE_IP_OCTET && cfg->WRITE_PROXY_LINE)
-    config_die("Options --write-ip and --write-proxy are mutually exclusive.");
+  if (cfg->WRITE_IP_OCTET && cfg->WRITE_PROXY_LINE) {
+    die("Options --write-ip and --write-proxy are mutually exclusive.");
+  }
 
   if (cfg->DAEMONIZE) {
     cfg->SYSLOG = 1;
-    cfg->QUIET = 1;
   }
 
 #ifdef USE_SHARED_CACHE
-  if (cfg->SHCUPD_IP != NULL && ! cfg->SHARED_CACHE)
-    config_die("Shared cache update listener is defined, but shared cache is disabled.");
+  if (cfg->SHCUPD_IP != NULL && ! cfg->SHARED_CACHE) {
+    die("Shared cache update listener is defined, but shared cache is disabled.");
+  }
 #endif
   
   // argv leftovers, do we have pem file as an argument?
@@ -1263,14 +1348,15 @@ void config_parse_cli(int argc, char **argv, stud_config *cfg) {
   argv += optind;
   if (argv != NULL && argv[0] != NULL)
     config_param_validate(CFG_PEM_FILE, argv[0], cfg, NULL, 0);
-  else if (cfg->CERT_FILE == NULL || strlen(cfg->CERT_FILE) < 1)
-    config_die("No x509 certificate PEM file specified!");
+  else if (cfg->CERT_FILE == NULL || strlen(cfg->CERT_FILE) < 1) {
+    fail("No x509 certificate PEM file specified!");
+  }
   
   // was this only a test?
   if (test_only) {
     fprintf(stderr, "Trying to initialize SSL context with certificate '%s'\n", cfg->CERT_FILE);
     if (! init_openssl())
-      config_die("Error initializing OpenSSL.");
+      die("Error initializing OpenSSL.");
     printf("%s configuration looks ok.\n", basename(prog));
     exit(0);
   }
diff --git a/configuration.h b/configuration.h
index 44be7f6..e60f08b 100644
--- a/configuration.h
+++ b/configuration.h
@@ -6,6 +6,7 @@
  */
 
 #include <sys/types.h>
+#include <limits.h>
 
 #ifdef USE_SHARED_CACHE
   #include "shctx.h"
@@ -51,12 +52,13 @@ struct __stud_config {
     char *SHCUPD_MCASTIF;
     char *SHCUPD_MCASTTTL;
 #endif
-    int QUIET;
     int SYSLOG;
     int SYSLOG_FACILITY;
+    int LOG_LEVEL;
     int TCP_KEEPALIVE_TIME;
     int DAEMONIZE;
     int PREFER_SERVER_CIPHERS;
+    char PID_FILE[PATH_MAX];
 };
 
 typedef struct __stud_config stud_config;
diff --git a/debian/changelog b/debian/changelog
index c4d45a5..2c25113 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,25 @@
+stud (0.3~0.2012061401) unstable; urgency=low
+
+  * fixes --write-proxy
+
+ -- Lars van de Kerkhof <lars@permanentmarkers.nl>  Thu, 14 Jun 2012 9:39:41 +0100
+
+stud (0.3~0.2012052301) unstable; urgency=low
+
+  * changed default pid file location
+
+ -- Charlie Root <root@example.com>  Wed, 23 May 2012 14:15:41 +0100
+
+stud (0.3~0.2012051501) unstable; urgency=low
+
+  * Merged native configuration file support
+  * Added init script :)
+
+ -- Charlie Root <root@example.com>  Tue, 15 May 2012 23:03:41 +0200
+
 stud (0.3~0.20111212) unstable; urgency=low
 
   * Initial release.
 
- -- Charlie Root <root@interseek.com>  Mon, 12 Dec 2011 13:24:18 +0100
+ -- Charlie Root <root@example.com>  Mon, 12 Dec 2011 13:24:18 +0100
 
diff --git a/debian/rules b/debian/rules
index aa6b51e..29cc8cb 100755
--- a/debian/rules
+++ b/debian/rules
@@ -27,6 +27,11 @@ override_dh_auto_install:
 	chmod 700 $(CURDIR)/debian/stud/etc/stud
 	echo "Run /etc/init.d/stud --sample-config > /etc/stud/something.conf for default instance configuration" >> "$(CURDIR)/debian/stud/etc/stud/README.TXT"
 
+	# add init script
+	mkdir -p $(CURDIR)/debian/stud/etc/init.d
+	cp -a $(CURDIR)/init.stud $(CURDIR)/debian/stud/etc/init.d/stud
+	chmod 755 $(CURDIR)/debian/stud/etc/init.d/stud
+
 %:
 	dh $@
 
diff --git a/init.stud b/init.stud
index 0dba4c5..4c0d2eb 100755
--- a/init.stud
+++ b/init.stud
@@ -21,139 +21,17 @@ CONFIG_DIR="/etc/stud"
 # Runtime directory data
 RUNTIME_DIR="/var/run/stud"
 
-#######################################################
+# filedescriptor limit
+ULIMIT_N=100000
 
 #######################################################
 
-stud_single_instance_config_reset() {
-#######################################################
-#            stud instance configuration              #
-#######################################################
-
-# stud listening address
-FRONTEND_ADDRESS="*,8443"
-
-# upstream service address
-BACKEND_ADDRESS="127.0.0.1,80"
-
-# x509 certificate file
-CERT_FILE=""
-
-# TLS only service? Don't set this to 1 if you're
-# offloading HTTPS.
-TLS_ONLY="0"
-
-# cipher suite (run openssl ciphers for full list)
-CIPHER_SUITE="HIGH"
-
-# OpenSSL engine
-ENGINE=""
-
-# Number of worker processes
-WORKERS="1"
-
-# Listen backlog
-BACKLOG=""
-
-# Chroot directory
-CHROOT_DIR=""
-
-# drop privileges and run as specified
-# user if set
-SETUID_USER=""
-
-# use shared cache with specified number of sessions
-# WARNING: stud must be compiled with USE_SHARED_CACHE=1
-SHARED_CACHE_SESSIONS="0"
-
-# Accept cache updates on specified address
-#
-# syntax: HOST,PORT
-#
-# WARNING: stud must be compiled with USE_SHARED_CACHE=1
-#          SHARED_CACHE_SESSIONS must be >= 1
-CACHE_UPDATE_ACCEPT=""
-
-# Send cache updates to specified list space separated peers
-#
-# syntax: HOST1,PORT HOST2,PORT
-#
-# WARNING: stud must be compiled with USE_SHARED_CACHE=1
-#          and CACHE_UPDATE_ACCEPT must be defined
-CACHE_UPDATE_SEND=""
-
-# Force network interface and ttl to receive and send multicast
-# cache updates
-#
-# syntax: IFACE[,TTL]
-#
-# WARNING: stud must be compiled with USE_SHARED_CACHE=1
-#          and CACHE_UPDATE_ACCEPT must be defined
-CACHE_UPDATE_IFACE=""
-
-# default tcp keepalive on client socket in seconds
-CLIENT_TCP_KEEPALIVE_SEC=""
-
-# log to syslog?
-SYSLOG="1"
-
-# Enable write-ip?
-WRITE_IP="0"
-
-# Enable SENDPROXY protocol; see
-# http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
-# for additional info
-WRITE_PROXY="0"
-
-# Alternative OpenSSL library dir
-# Use this if you'd like to run stud with different
-# version of OpenSSL library
-OPENSSL_LIB_DIR=""
-
-# Semicolon separated list of process affinities; requires
-# taskset(8) utility.
-#
-# SYNTAX:
-#   "<process_number>:<affinity>;<process_number2>:<affinity2>;..."
-#
-# <process_number>:      stud worker process number, starting with 1
-# <affinity>:            process affinity, see taskset(8) for details
-#
-# EXAMPLES:
-#
-#   "1:0"                => bind first process to CPU0
-#
-#   "1:0;2:3-4;3:5;4:7"  => bind first worker process to CPU0,
-#                        second worker process to CPU3 and CPU4,
-#                        third worker process to CPU5 and fourth
-#                        worker process to CPU7
-PROCESS_AFFINITY=""
-
-# Process priority (integer between -19 to 19)
-# lower value means higher priority
-#
-PROCESS_PRIORITY=""
-
-# ulimit -n value before starting single stud instance
-#
-# Comment out or set to 0 to disable ulimit -n
-# setup.
-#
-ULIMIT_N=""
-
-# Additional stud command line options
-#
-# NOTE: set this only if you really know what your're
-#       doing
-# 
-# ADDITIONAL_STUD_OPT=""
-
-# EOF
-}
-
 PATH="${PATH}:."
 INSTANCE_NAME=""
 STUD=`which stud 2>/dev/null`
+uid=`id -u`
+
+test "${uid}" != "0" && RUNTIME_DIR="/tmp"
 
 die() {
   msg_log "FATAL: $@"
@@ -173,90 +51,22 @@ msg_err() {
 
 
 _real_single_instance_start() {
+  cfg="${1}"
+  instance_name=""
+  if [ ! -f "${cfg}" ] || [ ! -r "${cfg}" ]; then
+    instance_name="${cfg}"
+    cfg="${CONFIG_DIR}/${cfg}.conf"
+    if [ ! -f "${cfg}" ] || [ ! -r "${cfg}" ]; then
+      die "Invalid configuration file '${cfg}'."
+    fi
+  fi
   # check stud binary
   if [ -z "${STUD}" ] || [ ! -f "${STUD}" ] || [ ! -x "${STUD}" ]; then
     die "Invalid stud binary: '${STUD}'"
   fi
 
-  # generate stud command line options
-  opts="-f ${FRONTEND_ADDRESS}"
-  opts="${opts} -b ${BACKEND_ADDRESS}"
-  
-  if [ "${TLS_ONLY}" = "1" ]; then
-    opts="${opts} --tls"
-  else
-    opts="${opts} --ssl"
-  fi
-
-  test ! -z "${CIPHER_SUITE}" && opts="${opts} -c ${CIPHER_SUITE}"
-  test ! -z "${ENGINE}" && opts="${opts} -e ${ENGINE}"
-
-  if [ ! -z "${WORKERS}" ] && [ ${WORKERS} -gt 0 ]; then
-    opts="${opts} -n ${WORKERS}"
-  fi
-
-  if [ ! -z "${BACKLOG}" ] && [ ${BACKLOG} -gt 0 ]; then
-    opts="${opts} -B ${BACKLOG}"
-  fi
-
-  if [ ! -z "${CLIENT_TCP_KEEPALIVE_SEC}" ] && [ ${CLIENT_TCP_KEEPALIVE_SEC} -gt 0 ]; then
-    opts="${opts} -k ${CLIENT_TCP_KEEPALIVE_SEC}"
-  fi
-
-  # shared cache sessions...
-  if [ ! -z "${SHARED_CACHE_SESSIONS}" ] && [ ${SHARED_CACHE_SESSIONS} -gt 0 ]; then
-    opts="${opts} -C ${SHARED_CACHE_SESSIONS}"
-
-    # shared cache stuff
-    if [ ! -z "${CACHE_UPDATE_ACCEPT}" ]; then
-      opts="${opts} -U ${CACHE_UPDATE_ACCEPT}"
-
-      c_u=0
-      for h in ${CACHE_UPDATE_SEND}; do
-        test ! -z "${h}" || continue
-        opts="${opts} -P ${h}"
-        c_u=$((c_u + 1))
-      done
-      if [ ${c_u} -lt 1 ]; then
-        die "Cache updates are enabled but CACHE_UPDATE_SEND option seems to be empty."
-      fi
-
-      if [ ! -z "${CACHE_UPDATE_IFACE}" ]; then
-        opts="${opts} -M ${CACHE_UPDATE_IFACE}"
-      fi
-    fi
-    
-  fi
-
-  # chroot?
-  test ! -z "${CHROOT_DIR}" && opts="${opts} -r ${CHROOT_DIR}"
-  test ! -z "${SETUID_USER}" && opts="${opts} -u ${SETUID_USER}"
-
-  opts="${opts} -q"
-  test "${SYSLOG}" = "1" && opts="${opts} -s"
-
-  test "${WRITE_IP}" = "1" && opts="${opts} --write-ip"
-  test "${WRITE_PROXY}" = "1" && opts="${opts} --write-proxy"
-
-  if [ ! -z "${CERT_FILE}" ] && [ -f "${CERT_FILE}" ] && [ -r "${CERT_FILE}" ]; then
-    opts="${opts} ${CERT_FILE}"
-  else
-    die "Invalid or unreadable certificate file '${CERT_FILE}'."
-  fi
-
-  # additional command line options?!
-  if [ ! -z "${ADDITIONAL_STUD_OPT}" ]; then
-    opts="${opts} ${ADDITIONAL_STUD_OPT}"
-  fi
-
-  # priority?!
-  prefix=""
-  if [ ! -z "${PROCESS_PRIORITY}" ]; then
-    prefix="nice -n ${PROCESS_PRIORITY}"
-  fi
-
   # we want to start stud in a daemon mode...
-  opts="${opts} --daemon"
+  opts="--config ${cfg} --daemon"
 
   # set ulimits!
   ulimit_set || die "Unable to set stud runtime limits."
@@ -265,13 +75,13 @@ _real_single_instance_start() {
   unset LD_LIBRARY_PATH
 
   # set new lib path if requested
-  if [ ! -z "${OPENSSL_LIB_DIR}" -a -d "${OPENSSL_LIB_DIR}" ]; then
+  if [ ! -z "${OPENSSL_LIB_DIR}" ] && [ -d "${OPENSSL_LIB_DIR}" ]; then
     LD_LIBRARY_PATH="${OPENSSL_LIB_DIR}"
     export LD_LIBRARY_PATH
   fi
 
   # start stud!
-  msg_log "Starting instance '${INSTANCE_NAME}': ${STUD} ${opts}"
+  msg_log "Starting instance '${instance_name}': ${STUD} ${opts}"
   out=`${prefix} ${STUD} ${opts} 2>&1`
 
   # remove lib path
@@ -283,17 +93,12 @@ _real_single_instance_start() {
      die "Empty stud pid. This is extremely weird."
   fi
 
-  # wait a little bit, check if pid is still alive
-  sleep 0.2 >/dev/null 2>&1
-  if ! kill -0 "${stud_pid}" >/dev/null 2>&1; then
-    die "Stud started successfully as pid ${stud_pid} but died shortly after startup.";
-  fi
-
-  # write pid file!
-  pid_file_write "${INSTANCE_NAME}" "${stud_pid}" || msg_warn "${Error}"
+  file="${RUNTIME_DIR}/${1}.pid"
+  echo "${stud_pid}" > "${file}"
 
+  return 0
   # set affinity!
-  stud_affinity_set "${stud_pid}" || die "${Error}"
+  # stud_affinity_set "${stud_pid}" || die "${Error}"
 }
 
 stud_single_instance_start() {
@@ -311,7 +116,7 @@ stud_single_instance_start() {
 
   # do the real stuff...
   Error=""
-  out=`_real_single_instance_start 2>&1`
+  out=`_real_single_instance_start "${name}" 2>&1`
   rv=$?
   if [ "${rv}" != "0" ]; then
     Error="${out}"
@@ -354,7 +159,7 @@ stud_single_instance_stop() {
     sleep 0.1 >/dev/null 2>&1
   done
 
-  # not ok?! try to with headshot...
+  # not ok?! shoot it in the head
   if [ "${ok}" != "1" ]; then
     msg_log "Gentle stop of instance ${name} failed, trying to stop it with SIGKILL."
     if ! kill -9 "${pid}"; then
@@ -412,21 +217,6 @@ pid_file_read() {
   head -n 1 "${file}"
 }
 
-# writes pid file for specific instance
-pid_file_write() {
-  test -z "${1}" && return 1
-  test -z "${2}" && return 1
-
-  # check runtime directory
-  if [ ! -e "${RUNTIME_DIR}" ] || [ ! -d "${RUNTIME_DIR}" ] || [ ! -w "${RUNTIME_DIR}" ]; then
-    # try to create directory
-    mkdir -p "${RUNTIME_DIR}" || die "Unable to create missing runtime directory '${RUNTIME_DIR}'"
-  fi
-
-  file="${RUNTIME_DIR}/${1}.pid"
-  echo "${2}" > "${file}"
-}
-
 # lists running instances
 running_instance_list() {
   list=""
@@ -468,17 +258,18 @@ stud_instances_start() {
     die "No stud instances configured in directory '${CONFIG_DIR}'."
   fi
 
+  # create runtime directory if necessary
+  if [ ! -d "${RUNTIME_DIR}" ]; then
+    mkdir -p "${RUNTIME_DIR}" || die "Unable to create runtime/pid directory ${RUNTIME_DIR}"
+  fi
+
   echo "Starting stud instances:"
   num_ok=0
   num_failed=0
   for instance in ${list}; do
     echo -n "  ${instance}: "
-    # load configuration
-    if ! stud_single_instance_config_load "${instance}"; then
-      echo "failed: ${Error}"
-      return 1
     # start instance
-    elif stud_single_instance_start "${instance}"; then
+    if stud_single_instance_start "${instance}"; then
       echo "done."
       msg_log "Instance ${name} successfully started."
       num_ok=$((num_ok + 1))
@@ -540,12 +331,8 @@ stud_instances_restart() {
   for instance in ${list}; do
     echo -n "  ${instance}: ";
 
-    # load configuration
-    if ! stud_single_instance_config_load "${instance}"; then
-      echo "failed: ${Error}"
-      return 1
     # restart instance
-    elif stud_single_instance_restart "${instance}"; then
+    if stud_single_instance_restart "${instance}"; then
       echo "done."
       num_ok=$((num_ok + 1))
       msg_log "Instance ${instance} successfully restarted."
@@ -578,7 +365,7 @@ stud_instances_status() {
   fi
 
   for instance in ${list_all}; do
-    echo -n "  ${instance}: "
+    printf "  %-55s" "${instance}"
     if stud_single_instance_status "${instance}"; then
       echo "running as pid $Error"
       i=$((i + 1))
@@ -599,7 +386,7 @@ stud_config_instances_list() {
   for file in ${CONFIG_DIR}/*.conf; do
     test -f "${file}" -a -r "${file}" || continue
     fileb=`basename "${file}"`
-    name=`echo "${fileb}" | cut -d. -f1`
+    name=`echo "${fileb}" | sed -e 's/\.conf$//g'`
     test ! -z "${name}" || continue
     list="${list} ${name}"
   done
@@ -608,28 +395,7 @@ stud_config_instances_list() {
 }
 
 stud_single_instance_config_print() {
-  head -n 151 "$0" | tail -n 123
-}
-
-stud_single_instance_config_load() {
-  file="${CONFIG_DIR}/${1}.conf"
-  INSTANCE_NAME=""
-
-  # reset configuration
-  stud_single_instance_config_reset
-  Error=''
-
-  if [ -f "${file}" -a -r "${file}" ]; then
-    . "${file}" >/dev/null || Error="Unable to load instance configuration file '${file}'."
-  else
-    Error="Invalid or unreadable instance configuration file '${file}'."
-    return 1
-  fi
-
-  # set instance name...
-  INSTANCE_NAME="${1}"
-
-  return 0
+  ${STUD} --default-config
 }
 
 stud_instance_worker_pids() {
@@ -705,6 +471,9 @@ stud_affinity_set() {
 }
 
 ulimit_n_set() {
+  if [ "$uid" != "0" ]; then
+    return 0
+  fi
   if [ -z "$ULIMIT_N" ] || [ "$ULIMIT_N" = "0" ]; then
     return 0
   fi
@@ -765,8 +534,19 @@ OPTIONS:
 EOF
 }
 
+############################################################
+#                          MAIN                            #
+############################################################
+
+# try to load script configuration file
+for dir in /etc/default /etc/sysconfig /usr/local/etc/default /usr/local/etc/sysconfig; do
+  f="${dir}/stud"
+  test -f "${f}" -a -r "${f}" || continue
+  . "${f}" >/dev/null 2>&1 && break
+done
+
 # parse command line...
-TEMP=`getopt -o C:R:Vh --long config-dir:,runtime-dir:,sample-config,version,help -n "$MYNAME" -- "$@"`
+TEMP=`getopt -o C:R:Vh --long config-dir:,runtime-dir:,sample-config,default-config,version,help -n "$MYNAME" -- "$@"`
 test "$?" != "0" && die "Invalid command line arguments. Run $MYNAME --help for instructions."
 eval set -- "$TEMP"
 while true; do
@@ -779,7 +559,7 @@ while true; do
       RUNTIME_DIR="${2}"
       shift 2
       ;;
-    --sample-config)
+    --sample-config|--default-config)
       stud_single_instance_config_print
       exit 0
       ;;
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..e90bb8c
--- /dev/null
+++ b/log.c
@@ -0,0 +1,285 @@
+/**
+ * log.c
+ *
+ * Author: Brane F. Gracnar
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <ev.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/engine.h>
+#include <errno.h>
+#include <syslog.h>
+
+
+#include "ringbuffer.h"
+#include "stud.h"
+#include "configuration.h"
+#include "log.h"
+
+
+#define TIMESTR_BUF_LEN         40
+#define TIMESTR_FMT             "%Y/%m/%d %H:%M:%S.%%d"
+#define SOCKSTR_BUF_LEN         48
+#define PSSTR_BUF_LEN           100
+#define LOG_BUF_LEN             1024
+
+#define DOLOG(l) (l <= CONFIG->LOG_LEVEL) ? 1 : 0
+
+
+char buf_timestr[TIMESTR_BUF_LEN];
+char sockstr_buf[SOCKSTR_BUF_LEN];
+char psstr_buf[PSSTR_BUF_LEN];
+
+static int logging_initialized = 0;
+
+extern stud_config *CONFIG;
+extern int child_num;
+
+char * ts_as_str (char *dst) {
+  if (dst == NULL) dst = buf_timestr;
+
+  char fmtbuf[TIMESTR_BUF_LEN];
+  memset(fmtbuf, '\0', sizeof(fmtbuf));
+  memset(dst, '\0', TIMESTR_BUF_LEN);
+
+  // get cached loop time...
+  ev_tstamp t_ev = ev_now(EV_DEFAULT);
+
+  time_t t = (time_t) t_ev;
+  struct tm *lt = localtime(&t);
+  strftime(fmtbuf, sizeof(fmtbuf), TIMESTR_FMT, lt);
+
+  // add microtime
+  int frac = (t_ev - (long) t_ev) * 1000000;
+  snprintf(dst, TIMESTR_BUF_LEN, fmtbuf, frac);
+
+  return dst;
+}
+
+char * sock_as_str (int sock, char *dst) {
+  if (dst == NULL) dst = sockstr_buf;
+  memset(dst, '\0', SOCKSTR_BUF_LEN);
+
+  struct sockaddr_storage addr;
+  char ipstr[INET6_ADDRSTRLEN];
+  int port = 0;
+
+  socklen_t len = sizeof(addr);
+  getpeername(sock, (struct sockaddr*)&addr, &len);
+  
+  // IPv4 socket?
+  if (addr.ss_family == AF_INET) {
+    struct sockaddr_in *s = (struct sockaddr_in *)&addr;
+    port = ntohs(s->sin_port);
+    inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr));
+  }
+  // IPv6 socket?
+  else if (addr.ss_family == AF_INET6) {
+    struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
+    port = ntohs(s->sin6_port);
+    inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr));
+  }
+  // UNIX domain socket?
+  else if (addr.ss_family == AF_UNIX) {
+    
+  }
+
+  snprintf(dst, SOCKSTR_BUF_LEN, "[%s]:%d", ipstr, port);
+  return dst;
+}
+
+char * ps_as_str (proxystate *ps, char *dst) {
+  char up_buf[SOCKSTR_BUF_LEN];
+  char down_buf[SOCKSTR_BUF_LEN];
+
+  if (dst == NULL) dst = psstr_buf;
+  memset(dst, '\0', PSSTR_BUF_LEN);
+    
+  sock_as_str(ps->fd_up, up_buf);
+  sock_as_str(ps->fd_down, down_buf);
+  
+  snprintf(dst, PSSTR_BUF_LEN, "%s => %s", up_buf, down_buf);
+  
+  return dst;
+}
+
+char buf_pcsig[10];
+char * proc_signature (char *dst) {
+  if (child_num < 0 || child_num >= CONFIG->NCORES)
+    return "master";
+  else {
+    if (dst == NULL) dst = buf_pcsig;
+    
+    // already cached stuff?
+    if (dst == buf_pcsig && buf_pcsig[0] != '\0') return dst;
+    
+    memset(dst, '\0', sizeof(buf_pcsig));
+    snprintf(dst, sizeof(buf_pcsig), "worker #%d", child_num);
+  }
+  
+  return dst;
+}
+
+char * log_level2str (int level) {
+  switch (level) {
+    case LOG_EMERG:
+      return LSTR_FATAL;
+      break;
+    case LOG_ERR:
+      return LSTR_ERR;
+      break;
+    case LOG_WARNING:
+      return LSTR_WARN;
+      break;
+    case LOG_NOTICE:
+      return LSTR_NOTICE;
+      break;
+    case LOG_INFO:
+      return LSTR_INFO;
+      break;
+    case LOG_DEBUG:
+      return LSTR_DEBUG;
+      break;
+    default:
+      return LSTR_UNKNOWN;
+  }
+}
+
+int log_str2level (char *str) {
+  int r = LOG_NOTICE;
+  if (str == NULL || strlen(str) < 1) return r;
+  
+  if (strcasecmp(str, LSTR_FATAL) == 0)
+    r = LOG_EMERG;
+  else if (strcasecmp(str, LSTR_ERR) == 0)
+    r = LOG_ERR;
+  else if (strcasecmp(str, LSTR_WARN) == 0 || strcasecmp(str, "warn") == 0)
+    r = LOG_WARNING;
+  else if (strcasecmp(str, LSTR_NOTICE) == 0)
+    r = LOG_NOTICE;
+  else if (strcasecmp(str, LSTR_INFO) == 0)
+    r = LOG_INFO;
+  else if (strcasecmp(str, LSTR_DEBUG) == 0)
+    r = LOG_DEBUG;
+
+  return r;
+}
+
+char buf_syslog_ident[128];
+char * log_syslog_ident (void) {
+  char *n = stud_instance_name(NULL);
+  if (n == NULL || strlen(n) < 1)
+    return "stud";
+  
+  snprintf(buf_syslog_ident, sizeof(buf_syslog_ident), "stud/%s", n);
+  return buf_syslog_ident;
+}
+
+void log_init (void) {
+  if (logging_initialized) return;
+  if (CONFIG->SYSLOG) {
+    openlog(log_syslog_ident(), LOG_CONS | LOG_PID | LOG_NDELAY, CONFIG->SYSLOG_FACILITY);
+  }
+}
+
+void log_msg (int level, char *fmt, ...) {
+  va_list args;
+  char buf[LOG_BUF_LEN];
+  
+  //printf("LCFG LLEVEL: %d, LLEVEL: %d\n", CONFIG->LOG_LEVEL, level);
+  if (! DOLOG(level)) return;
+  //printf("DAEMONIZE: %d\n", CONFIG->DAEMONIZE);
+  //printf("SYSLOG: %d\n", CONFIG->SYSLOG);
+  if (CONFIG->DAEMONIZE && ! CONFIG->SYSLOG) return;
+
+  // format log message
+  memset(buf, '\0', LOG_BUF_LEN);
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+
+  if (! CONFIG->DAEMONIZE) {
+    fprintf(stderr, "[%s] %-10s %-7s %s\n", ts_as_str(NULL), proc_signature(NULL), log_level2str(level), buf);
+  }
+  
+  if (CONFIG->SYSLOG) {
+    syslog(level, "%s %s", proc_signature(NULL), buf);
+  }
+}
+
+
+void log_msg_ps (int level, proxystate *ps, char *fmt, ...) {
+  va_list args;
+  char buf[LOG_BUF_LEN];
+  
+  if (! DOLOG(level)) return;
+
+  // format log message
+  memset(buf, '\0', LOG_BUF_LEN);
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+  
+  log_msg(level, "%s %s", ps_as_str(ps, NULL), buf);
+}
+
+void log_msg_sock (int level, int sock, char *fmt, ...) {
+  va_list args;
+  char buf[LOG_BUF_LEN];
+  
+  if (! DOLOG(level)) return;
+
+  // format log message
+  memset(buf, '\0', LOG_BUF_LEN);
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+  
+  log_msg(level, "%s %s", sock_as_str(sock, NULL), buf);
+}
+
+void log_msg_lps (int level, int listener, proxystate *ps, char *fmt, ...) {
+  va_list args;
+  char buf[LOG_BUF_LEN];
+  
+  if (! DOLOG(level)) return;
+
+  // format log message
+  memset(buf, '\0', LOG_BUF_LEN);
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+  
+  log_msg(
+    level, "[%s %s] %s", sock_as_str(listener, NULL),
+    ps_as_str(ps, NULL), buf
+  );  
+}
+
+void fail (char *fmt, ...) {
+  va_list args;
+  char buf[LOG_BUF_LEN];
+
+  // format log message
+  memset(buf, '\0', LOG_BUF_LEN);
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+  
+  die("%s %s", buf, strerror(errno));
+}
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..ffcff59
--- /dev/null
+++ b/log.h
@@ -0,0 +1,107 @@
+/**
+ * log.h
+ *
+ * Author: Brane F. Gracnar
+ *
+ */
+
+// "Initializes" logging subsystem
+void log_init (void);
+
+// Writes timestamp (including microseconds) to dst.
+// If dst == NULL uses it's own buffer. 
+char * ts_as_str (char *dst);
+
+// Writes string representation of proxystate structure to dst.
+// If dst == NULL uses it's own buffer.
+char * ps_as_str (proxystate *ps, char *dst);
+
+// Writes string representation of a socket to dst.
+// If dst == NULL uses it's own buffer.
+char * sock_as_str (int sock, char *dst);
+
+char * log_level2str (int level);
+int log_str2level (char *str);
+
+/**
+ * Log level names
+ */
+
+#define LSTR_FATAL      "FATAL"
+#define LSTR_ERR        "ERROR"
+#define LSTR_WARN       "WARNING"
+#define LSTR_NOTICE     "NOTICE"
+#define LSTR_INFO       "INFO"
+#define LSTR_DEBUG      "DEBUG"
+#define LSTR_UNKNOWN    "UNKNOWN"
+
+/**
+ * General purpose logging functions
+ */
+
+void log_msg (int level, char *fmt, ...);
+
+#define log_fatal(...)          log_msg(LOG_EMERG, __VA_ARGS__)
+#define log_err(...)            log_msg(LOG_ERR, __VA_ARGS__)
+#define log_warn(...)           log_msg(LOG_WARNING, __VA_ARGS__)
+#define log_notice(...)         log_msg(LOG_NOTICE, __VA_ARGS__)
+#define log_info(...)           log_msg(LOG_INFO, __VA_ARGS__)
+#define log_debug(...)          log_msg(LOG_DEBUG, __VA_ARGS__)
+
+/**
+ * Proxy state logging "functions"
+ */
+
+void log_msg_ps (int level, proxystate *ps, char *fmt, ...);
+
+#define log_fatal_ps(...)        log_msg_ps(LOG_EMERG, __VA_ARGS__)
+#define log_err_ps(...)          log_msg_ps(LOG_ERR, __VA_ARGS__)
+#define log_warn_ps(...)         log_msg_ps(LOG_WARNING, __VA_ARGS__)
+#define log_notice_ps(...)       log_msg_ps(LOG_NOTICE, __VA_ARGS__)
+#define log_info_ps(...)         log_msg_ps(LOG_INFO, __VA_ARGS__)
+#define log_debug_ps(...)        log_msg_ps(LOG_DEBUG, __VA_ARGS__)
+
+/**
+ * Socket logging "functions"
+ */
+
+void log_msg_sock (int level, int sock, char *fmt, ...);
+
+#define log_fatal_sock(...)      log_msg_sock(LOG_EMERG, __VA_ARGS__)
+#define log_err_sock(...)        log_msg_sock(LOG_ERR, __VA_ARGS__)
+#define log_warn_sock(...)       log_msg_sock(LOG_WARNING, __VA_ARGS__)
+#define log_notice_sock(...)     log_msg_sock(LOG_NOTICE, __VA_ARGS__)
+#define log_info_sock(...)       log_msg_sock(LOG_INFO, __VA_ARGS__)
+#define log_debug_sock(...)      log_msg_sock(LOG_DEBUG, __VA_ARGS__)
+
+/**
+ * Socket and proxy state logging functions
+ */
+
+void log_msg_lps (int level, int listener, proxystate *ps, char *fmt, ...);
+
+#define log_fatal_lps(...)      log_msg_lps(LOG_EMERG, __VA_ARGS__)
+#define log_err_lps(...)        log_msg_lps(LOG_ERR, __VA_ARGS__)
+#define log_warn_lps(...)       log_msg_lps(LOG_WARNING, __VA_ARGS__)
+#define log_notice_lps(...)     log_msg_lps(LOG_NOTICE, __VA_ARGS__)
+#define log_info_lps(...)       log_msg_lps(LOG_INFO, __VA_ARGS__)
+#define log_debug_lps(...)      log_msg_lps(LOG_DEBUG, __VA_ARGS__)
+
+
+/**
+ * Fatal logging "functions"
+ */
+
+void fail (char *fmt, ...);
+
+#define die(...)                                        \
+  log_msg(LOG_CRIT, __VA_ARGS__);                      \
+  exit(1)
+
+#define die_ps(...)                                     \
+  log_msg_ps(LOG_CRIT, __VA_ARGS__);                   \
+  exit(1)
+
+#define die_sock(...)                                   \
+  log_msg_sock(LOG_CRIT, __VA_ARGS__);                 \
+  exit(1)
diff --git a/stud.c b/stud.c
index 0ed6581..cdb97b2 100644
--- a/stud.c
+++ b/stud.c
@@ -59,9 +59,11 @@
 #include <openssl/engine.h>
 #include <ev.h>
 
+#include "stud.h"
 #include "ringbuffer.h"
 #include "shctx.h"
 #include "configuration.h"
+#include "log.h"
 
 #ifndef MSG_NOSIGNAL
 # define MSG_NOSIGNAL 0
@@ -86,7 +88,7 @@ static struct addrinfo *backaddr;
 static pid_t master_pid;
 static ev_io listener;
 static int listener_socket;
-static int child_num;
+int child_num = -1;
 static pid_t *child_pids;
 static SSL_CTX *ssl_ctx;
 
@@ -109,58 +111,6 @@ stud_config *CONFIG;
 
 static char tcp_proxy_line[128] = "";
 
-/* What agent/state requests the shutdown--for proper half-closed
- * handling */
-typedef enum _SHUTDOWN_REQUESTOR {
-    SHUTDOWN_HARD,
-    SHUTDOWN_DOWN,
-    SHUTDOWN_UP
-} SHUTDOWN_REQUESTOR;
-
-/*
- * Proxied State
- *
- * All state associated with one proxied connection
- */
-typedef struct proxystate {
-    ringbuffer ring_down; /* pushing bytes from client to backend */
-    ringbuffer ring_up;   /* pushing bytes from backend to client */
-
-    ev_io ev_r_up;        /* Upstream write event */
-    ev_io ev_w_up;        /* Upstream read event */
-
-    ev_io ev_r_handshake; /* Downstream write event */
-    ev_io ev_w_handshake; /* Downstream read event */
-
-    ev_io ev_r_down;      /* Downstream write event */
-    ev_io ev_w_down;      /* Downstream read event */
-
-    int fd_up;            /* Upstream (client) socket */
-    int fd_down;          /* Downstream (backend) socket */
-
-    int want_shutdown:1;  /* Connection is half-shutdown */
-    int handshaked:1;     /* Initial handshake happened */
-    int renegotiation:1;  /* Renegotation is occuring */
-
-    SSL *ssl;             /* OpenSSL SSL state */
-
-    struct sockaddr_storage remote_ip;  /* Remote ip returned from `accept` */
-} proxystate;
-
-#define LOG(...)                                        \
-    do {                                                \
-      if (!CONFIG->QUIET) fprintf(stdout, __VA_ARGS__); \
-      if (CONFIG->SYSLOG) syslog(LOG_INFO, __VA_ARGS__);                    \
-    } while(0)
-
-#define ERR(...)                    \
-    do {                            \
-      fprintf(stderr, __VA_ARGS__); \
-      if (CONFIG->SYSLOG) syslog(LOG_ERR, __VA_ARGS__); \
-    } while(0)
-
-#define NULL_DEV "/dev/null"
-
 /* set a file descriptor (socket) to non-blocking mode */
 static void setnonblocking(int fd) {
     int flag = 1;
@@ -174,32 +124,18 @@ static void settcpkeepalive(int fd) {
     socklen_t optlen = sizeof(optval);
 
     if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
-        ERR("Error activating SO_KEEPALIVE on client socket: %s", strerror(errno));
+        log_err("Error activating SO_KEEPALIVE on client socket: %s", strerror(errno));
     }
 
     optval = CONFIG->TCP_KEEPALIVE_TIME;
     optlen = sizeof(optval);
 #ifdef TCP_KEEPIDLE
     if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen) < 0) {
-        ERR("Error setting TCP_KEEPIDLE on client socket: %s", strerror(errno));
+        log_err("Error setting TCP_KEEPIDLE on client socket: %s", strerror(errno));
     }
 #endif
 }
 
-static void fail(const char* s) {
-    perror(s);
-    exit(1);
-}
-
-void die (char *fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  vfprintf(stderr, fmt, args);
-  va_end(args);
-
-  exit(1);
-}
-
 #ifndef OPENSSL_NO_DH
 static int init_dh(SSL_CTX *ctx, const char *cert) {
     DH *dh;
@@ -216,13 +152,13 @@ static int init_dh(SSL_CTX *ctx, const char *cert) {
     dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
     BIO_free(bio);
     if (!dh) {
-        ERR("{core} Note: no DH parameters found in %s\n", cert);
+        log_err("{core} Note: no DH parameters found in %s", cert);
         return -1;
     }
 
-    LOG("{core} Using DH parameters from %s\n", cert);
+    log_notice("{core} Using DH parameters from %s", cert);
     SSL_CTX_set_tmp_dh(ctx, dh);
-    LOG("{core} DH initialized with %d bit key\n", 8*DH_size(dh));
+    log_notice("{core} DH initialized with %d bit key", 8*DH_size(dh));
     DH_free(dh);
 
 #ifndef OPENSSL_NO_EC
@@ -231,7 +167,7 @@ static int init_dh(SSL_CTX *ctx, const char *cert) {
     ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
     SSL_CTX_set_tmp_ecdh(ctx,ecdh);
     EC_KEY_free(ecdh);
-    LOG("{core} ECDH Initialized with NIST P-256\n");
+    log_notice("{core} ECDH Initialized with NIST P-256");
 #endif /* NID_X9_62_prime256v1 */
 #endif /* OPENSSL_NO_EC */
 
@@ -249,7 +185,7 @@ static void info_callback(const SSL *ssl, int where, int ret) {
         proxystate *ps = (proxystate *)SSL_get_app_data(ssl);
         if (ps->handshaked) {
             ps->renegotiation = 1;
-            LOG("{core} SSL renegotiation asked by client\n");
+            log_info_ps(ps, "SSL renegotiation asked by client.");
         }
     }
 }
@@ -352,23 +288,32 @@ static int create_shcupd_socket() {
     const int gai_err = getaddrinfo(CONFIG->SHCUPD_IP, CONFIG->SHCUPD_PORT,
                                     &hints, &ai);
     if (gai_err != 0) {
-        ERR("{getaddrinfo}: [%s]\n", gai_strerror(gai_err));
-        exit(1);
+        die(
+          "Error creating shared cache update listening socket [%s]:%s: getaddrinfo: %s",
+          CONFIG->SHCUPD_IP, CONFIG->SHCUPD_PORT,
+          gai_strerror(gai_err)
+        );
     }
 
     /* check if peers inet family addresses match */
     while (*pai) {
         if ((*pai)->ai_family != ai->ai_family) {
-            ERR("Share host and peers inet family differs\n");
-            exit(1);
+            die(
+              "Error creating shared cache update listening socket [%s]:%s: share host and peers inet family differs.",
+              CONFIG->SHCUPD_IP, CONFIG->SHCUPD_PORT
+            );
         }
         pai++;
     }
 
     int s = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
     
-    if (s == -1)
-      fail("{socket: shared cache updates}");
+    if (s == -1) {
+      fail(
+        "Error creating shared cache update listening socket [%s]:%s:",
+        CONFIG->SHCUPD_IP, CONFIG->SHCUPD_PORT
+      );
+    }
 
     int t = 1;
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int));
@@ -390,8 +335,7 @@ static int create_shcupd_socket() {
 
                 memset(&ifr, 0, sizeof(ifr));
                 if (strlen(CONFIG->SHCUPD_MCASTIF) > IFNAMSIZ) {
-                    ERR("Error iface name is too long [%s]\n",CONFIG->SHCUPD_MCASTIF);
-                    exit(1);
+                    die("Error iface name is too long [%s].", CONFIG->SHCUPD_MCASTIF);
                 }
 
                 memcpy(ifr.ifr_name, CONFIG->SHCUPD_MCASTIF, strlen(CONFIG->SHCUPD_MCASTIF));
@@ -453,8 +397,7 @@ static int create_shcupd_socket() {
 
                 memset(&ifr, 0, sizeof(ifr));
                 if (strlen(CONFIG->SHCUPD_MCASTIF) > IFNAMSIZ) {
-                    ERR("Error iface name is too long [%s]\n",CONFIG->SHCUPD_MCASTIF);
-                    exit(1);
+                    die("Error iface name is too long [%s].", CONFIG->SHCUPD_MCASTIF);
                 }
 
                 memcpy(ifr.ifr_name, CONFIG->SHCUPD_MCASTIF, strlen(CONFIG->SHCUPD_MCASTIF));
@@ -501,7 +444,7 @@ static int create_shcupd_socket() {
 #endif /* IPV6_ADD_MEMBERSHIP */
 
     if (bind(s, ai->ai_addr, ai->ai_addrlen)) {
-        fail("{bind-socket}");
+        fail("{bind-socket}:");
     }
 
     freeaddrinfo(ai);
@@ -561,8 +504,7 @@ SSL_CTX * init_openssl() {
 
     rsa = load_rsa_privatekey(ctx, CONFIG->CERT_FILE);
     if(!rsa) {
-       ERR("Error loading rsa private key\n");
-       exit(1);
+       fail("Error loading RSA private key:");
     }
 
     if (SSL_CTX_use_RSAPrivateKey(ctx,rsa) <= 0) {
@@ -586,7 +528,7 @@ SSL_CTX * init_openssl() {
                 ERR_print_errors_fp(stderr);
                 exit(1);
             }
-            LOG("{core} will use OpenSSL engine %s.\n", ENGINE_get_id(e));
+            log_notice("{core} will use OpenSSL engine %s.", ENGINE_get_id(e));
             ENGINE_finish(e);
             ENGINE_free(e);
         }
@@ -602,13 +544,11 @@ SSL_CTX * init_openssl() {
 #ifdef USE_SHARED_CACHE
     if (CONFIG->SHARED_CACHE) {
         if (shared_context_init(ctx, CONFIG->SHARED_CACHE) < 0) {
-            ERR("Unable to alloc memory for shared cache.\n");
-            exit(1);
+            fail("Unable to alloc memory for shared cache:");
         }
 	if (CONFIG->SHCUPD_PORT) {
             if (compute_secret(rsa, shared_secret) < 0) {
-                ERR("Unable to compute shared secret.\n");
-                exit(1);
+                die("Unable to compute shared secret.");
             }
 
             /* Force tls tickets cause keys differs */
@@ -649,8 +589,7 @@ static void prepare_proxy_line(struct sockaddr* ai_addr) {
       assert(res < sizeof(tcp_proxy_line));
     }
     else {
-        ERR("The --write-proxy mode is not implemented for this address family.\n");
-        exit(1);
+        die("The --write-proxy mode is not implemented for this address family.");
     }
 }
 
@@ -664,14 +603,21 @@ static int create_main_socket() {
     const int gai_err = getaddrinfo(CONFIG->FRONT_IP, CONFIG->FRONT_PORT,
                                     &hints, &ai);
     if (gai_err != 0) {
-        ERR("{getaddrinfo}: [%s]\n", gai_strerror(gai_err));
-        exit(1);
+        die(
+          "Error creating listening socket [%s]:%s {getaddrinfo}: %s",
+          CONFIG->FRONT_IP, CONFIG->FRONT_PORT,
+          gai_strerror(gai_err)
+        );
     }
 
     int s = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
     
-    if (s == -1)
-      fail("{socket: main}");
+    if (s == -1) {
+      fail(
+        "Error creating listening socket [%s]:%s",
+        CONFIG->FRONT_IP, CONFIG->FRONT_PORT
+      );
+    }
 
     int t = 1;
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int));
@@ -681,7 +627,10 @@ static int create_main_socket() {
     setnonblocking(s);
 
     if (bind(s, ai->ai_addr, ai->ai_addrlen)) {
-        fail("{bind-socket}");
+        fail(
+          "Error binding listening socket [%s]:%s",
+          CONFIG->FRONT_IP, CONFIG->FRONT_PORT
+        );
     }
 
 #ifndef NO_DEFER_ACCEPT
@@ -710,7 +659,7 @@ static int create_back_socket() {
     int flag = 1;
     int ret = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
     if (ret == -1) {
-      perror("Couldn't setsockopt to backend (TCP_NODELAY)\n");
+      log_err_sock(s, "Couldn't setsockopt to backend (TCP_NODELAY): %s", strerror(errno));
     }
 
     int t = 1;
@@ -719,7 +668,7 @@ static int create_back_socket() {
     if (t == 0 || errno == EINPROGRESS || errno == EINTR)
         return s;
 
-    perror("{backend-connect}");
+    log_err_sock(s, "Unable to connect to backend: %s", strerror(errno));
 
     return -1;
 }
@@ -765,11 +714,11 @@ static void handle_socket_errno(proxystate *ps) {
         return;
 
     if (errno == ECONNRESET)
-        ERR("{backend} Connection reset by peer\n");
+        log_err("{backend} Connection reset by peer");
     else if (errno == ETIMEDOUT)
-        ERR("{backend} Connection to backend timed out\n");
+        log_err("{backend} Connection to backend timed out");
     else if (errno == EPIPE)
-        ERR("{backend} Broken pipe to backend (EPIPE)\n");
+        log_err("{backend} Broken pipe to backend (EPIPE)");
     else
         perror("{backend} [errno]");
     shutdown_proxy(ps, SHUTDOWN_DOWN);
@@ -798,7 +747,7 @@ static void back_read(struct ev_loop *loop, ev_io *w, int revents) {
             safe_enable_io(ps, &ps->ev_w_up);
     }
     else if (t == 0) {
-        LOG("{backend} Connection closed\n");
+        log_info_ps(ps, "{backend} Connection closed");
         shutdown_proxy(ps, SHUTDOWN_DOWN);
     }
     else {
@@ -806,6 +755,7 @@ static void back_read(struct ev_loop *loop, ev_io *w, int revents) {
         handle_socket_errno(ps);
     }
 }
+
 /* Write some data, previously received on the secure upstream socket,
  * out of the downstream buffer and onto the backend socket */
 static void back_write(struct ev_loop *loop, ev_io *w, int revents) {
@@ -908,7 +858,7 @@ static void handle_connect(struct ev_loop *loop, ev_io *w, int revents) {
         /* do nothing, we'll get phoned home again... */
     }
     else {
-        perror("{backend-connect}");
+        log_err_ps(ps, "Error connecting to backend: %s", strerror(errno));
         shutdown_proxy(ps, SHUTDOWN_HARD);
     }
 }
@@ -972,11 +922,11 @@ static void client_handshake(struct ev_loop *loop, ev_io *w, int revents) {
             ev_io_start(loop, &ps->ev_w_handshake);
         }
         else if (err == SSL_ERROR_ZERO_RETURN) {
-            LOG("{client} Connection closed (in handshake)\n");
+            log_info_ps(ps, "Client closed connection in TLS handshake.");
             shutdown_proxy(ps, SHUTDOWN_UP);
         }
         else {
-            LOG("{client} Unexpected SSL error (in handshake): %d\n", err);
+            log_err_ps(ps, "Unexpected SSL error in handshake: %d", err);
             shutdown_proxy(ps, SHUTDOWN_UP);
         }
     }
@@ -985,14 +935,14 @@ static void client_handshake(struct ev_loop *loop, ev_io *w, int revents) {
 /* Handle a socket error condition passed to us from OpenSSL */
 static void handle_fatal_ssl_error(proxystate *ps, int err) {
     if (err == SSL_ERROR_ZERO_RETURN)
-        ERR("{client} Connection closed (in data)\n");
+        log_err_ps(ps, "Client closed connection closed in data.");
     else if (err == SSL_ERROR_SYSCALL)
         if (errno == 0)
-            ERR("{client} Connection closed (in data)\n");
+            log_debug_ps(ps, "Client closed connection in data.");
         else
-            perror("{client} [errno] ");
+            log_err_ps(ps, "Client error: %s", strerror(errno));
     else
-        ERR("{client} Unexpected SSL_read error: %d\n", err);
+        log_err_ps(ps, "Unexpected SSL_read error: %d", err);
     shutdown_proxy(ps, SHUTDOWN_UP);
 }
 
@@ -1078,32 +1028,25 @@ static void handle_accept(struct ev_loop *loop, ev_io *w, int revents) {
     socklen_t sl = sizeof(addr);
     int client = accept(w->fd, (struct sockaddr *) &addr, &sl);
     if (client == -1) {
-        switch (errno) {
-        case EMFILE:
-            ERR("{client} accept() failed; too many open files for this process\n");
-            break;
-
-        case ENFILE:
-            ERR("{client} accept() failed; too many open files for this system\n");
-            break;
-
-        default:
-            assert(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN);
-            break;
-        }
+        if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
+          return;
+        
+        log_err("Error accepting connection on %s: %s", sock_as_str(w->fd, NULL), strerror(errno));
         return;
     }
 
     int flag = 1;
     int ret = setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );
     if (ret == -1) {
-      perror("Couldn't setsockopt on client (TCP_NODELAY)\n");
+      int e = errno;
+      log_err("[%s] Couldn't setsockopt TCP_NODELAY: %s", sock_as_str(client, NULL), strerror(e));
     }
 #ifdef TCP_CWND
     int cwnd = 10;
     ret = setsockopt(client, IPPROTO_TCP, TCP_CWND, &cwnd, sizeof(cwnd));
     if (ret == -1) {
-      perror("Couldn't setsockopt on client (TCP_CWND)\n");
+      int e = errno;
+      log_err("[%s] Couldn't setsockopt TCP_CWND: %s", sock_as_str(client, NULL), strerror(e));
     }
 #endif
 
@@ -1113,8 +1056,9 @@ static void handle_accept(struct ev_loop *loop, ev_io *w, int revents) {
     int back = create_back_socket();
 
     if (back == -1) {
+        int e = errno;
+        log_err("[client %s] Unable to create backend socket: %s", sock_as_str(client, NULL), strerror(e));
         close(client);
-        perror("{backend-connect}");
         return;
     }
 
@@ -1129,6 +1073,9 @@ static void handle_accept(struct ev_loop *loop, ev_io *w, int revents) {
     SSL_set_fd(ssl, client);
 
     proxystate *ps = (proxystate *)malloc(sizeof(proxystate));
+    if (ps == NULL) {
+      fail("Unable to allocate memory for new proxy state:");
+    }
 
     ps->fd_up = client;
     ps->fd_down = back;
@@ -1168,7 +1115,7 @@ static void check_ppid(struct ev_loop *loop, ev_timer *w, int revents) {
     (void) revents;
     pid_t ppid = getppid();
     if (ppid != master_pid) {
-        ERR("{core} Process %d detected parent death, closing listener socket.\n", child_num);
+        log_err("Process %d detected parent death, closing listener socket.", child_num);
         ev_timer_stop(loop, w);
         ev_io_stop(loop, &listener);
         close(listener_socket);
@@ -1180,7 +1127,7 @@ static void check_ppid(struct ev_loop *loop, ev_timer *w, int revents) {
 /* Set up the child (worker) process including libev event loop, read event
  * on the bound socket, etc */
 static void handle_connections() {
-    LOG("{core} Process %d online\n", child_num);
+    log_notice("Process %d online", child_num);
 
     /* child cannot create new children... */
     create_workers = 0;
@@ -1193,9 +1140,9 @@ static void handle_connections() {
 
     int res = sched_setaffinity(0, sizeof(cpus), &cpus);
     if (!res)
-        LOG("{core} Successfully attached to CPU #%d\n", child_num);
+        log_notice("Successfully attached to CPU #%d", child_num);
     else
-        ERR("{core-warning} Unable to attach to CPU #%d; do you have that many cores?\n", child_num);
+        log_warn("Unable to attach to CPU #%d: %s (Do you have that many cores?)", child_num, strerror(errno));
 #endif
 
     loop = ev_default_loop(EVFLAG_AUTO);
@@ -1209,22 +1156,35 @@ static void handle_connections() {
     ev_io_start(loop, &listener);
 
     ev_loop(loop, 0);
-    ERR("{core} Child %d exiting.\n", child_num);
+    log_err("Child %d exiting.", child_num);
     exit(1);
 }
 
 void change_root() {
-    if (chroot(CONFIG->CHROOT) == -1)
-        fail("chroot");
-    if (chdir("/"))
-        fail("chdir");
+    if (chroot(CONFIG->CHROOT) == -1) {
+        fail("Unable to chroot to %s", CONFIG->CHROOT);
+    }
+    if (chdir("/")) {
+        fail("Unable to chdir to / inside chroot %s", CONFIG->CHROOT);
+    }
 }
 
 void drop_privileges() {
-    if (setgid(CONFIG->GID))
-        fail("setgid failed");
-    if (setuid(CONFIG->UID))
-        fail("setuid failed");
+    if (setgid(CONFIG->GID)) {
+        fail("Unable to drop privileges: setgid to %d failed:", CONFIG->GID);
+    }
+    if (setuid(CONFIG->UID)) {
+        fail("Unable to drop privileges: setuid to %d failed:", CONFIG->UID);
+    }
+}
+
+char instance_name[128];
+char * stud_instance_name (char *name) {
+  if (name != NULL) {
+    memset(instance_name, '\0', sizeof(instance_name));
+    strncpy(instance_name, name, (sizeof(instance_name) - 1));
+  }
+  return instance_name;
 }
 
 
@@ -1238,8 +1198,12 @@ void init_globals() {
     const int gai_err = getaddrinfo(CONFIG->BACK_IP, CONFIG->BACK_PORT,
                                     &hints, &backaddr);
     if (gai_err != 0) {
-        ERR("{getaddrinfo}: [%s]", gai_strerror(gai_err));
-        exit(1);
+        die(
+          "Error creating backend address [%s]:%d: {getaddrinfo}: %s",
+          CONFIG->BACK_IP,
+          CONFIG->BACK_PORT,
+          gai_strerror(gai_err)
+        );
     }
 
 #ifdef USE_SHARED_CACHE
@@ -1256,8 +1220,11 @@ void init_globals() {
             const int gai_err = getaddrinfo(spo->ip,
                                 spo->port ? spo->port : CONFIG->SHCUPD_PORT, &hints, pai);
             if (gai_err != 0) {
-                ERR("{getaddrinfo}: [%s]", gai_strerror(gai_err));
-                exit(1);
+                die(
+                  "Error creating shared cache update peer socket [%s]:%s getaddrinfo: %s",
+                  spo->ip, CONFIG->SHCUPD_PORT,
+                  gai_strerror(gai_err)
+                );
             }
             spo++;
             pai++;
@@ -1265,11 +1232,11 @@ void init_globals() {
     }
 #endif
     /* child_pids */
-    if ((child_pids = calloc(CONFIG->NCORES, sizeof(pid_t))) == NULL)
-        fail("calloc");
+    if ((child_pids = calloc(CONFIG->NCORES, sizeof(pid_t))) == NULL) {
+        fail("Unable to allocate memory for child pids:");
+    }
 
-    if (CONFIG->SYSLOG)
-        openlog("stud", LOG_CONS | LOG_PID | LOG_NDELAY, CONFIG->SYSLOG_FACILITY);
+    log_init();
 }
 
 /* Forks COUNT children starting with START_INDEX.
@@ -1282,8 +1249,7 @@ void start_children(int start_index, int count) {
     for (child_num = start_index; child_num < start_index + count; child_num++) {
         int pid = fork();
         if (pid == -1) {
-            ERR("{core} fork() failed: %s; Goodbye cruel world!\n", strerror(errno));
-            exit(1);
+            die("Unable to start children, fork() failed: %s; Goodbye cruel world!", strerror(errno));
         }
         else if (pid == 0) { /* child */
             handle_connections();
@@ -1307,7 +1273,7 @@ void replace_child_with_pid(pid_t pid) {
         }
     }
 
-    ERR("Cannot find index for child pid %d", pid);
+    log_err("Cannot find index for child pid %d", pid);
 }
 
 /* Manage status changes in child processes */
@@ -1318,23 +1284,23 @@ static void do_wait(int __attribute__ ((unused)) signo) {
 
     if (pid == -1) {
         if (errno == ECHILD) {
-            ERR("{core} All children have exited! Restarting...\n");
+            log_err("All children have exited! Restarting.");
             start_children(0, CONFIG->NCORES);
         }
         else if (errno == EINTR) {
-            ERR("{core} Interrupted wait\n");
+            log_warn("Interrupted wait");
         }
         else {
-            fail("wait");
+            fail("Error wait(2) for children:");
         }
     }
     else {
         if (WIFEXITED(status)) {
-            ERR("{core} Child %d exited with status %d. Replacing...\n", pid, WEXITSTATUS(status));
+            log_err("Child %d exited with status %d, replacing.", pid, WEXITSTATUS(status));
             replace_child_with_pid(pid);
         }
         else if (WIFSIGNALED(status)) {
-            ERR("{core} Child %d was terminated by signal %d. Replacing...\n", pid, WTERMSIG(status));
+            log_err("Child %d was terminated by signal %d, replacing.", pid, WTERMSIG(status));
             replace_child_with_pid(pid);
         }
     }
@@ -1346,17 +1312,22 @@ static void sigh_terminate (int __attribute__ ((unused)) signo) {
 
     /* are we the master? */
     if (getpid() == master_pid) {
-        LOG("{core} Received signal %d, shutting down.\n", signo);
+        log_notice("Received signal %d, shutting down.", signo);
 
         /* kill all children */
         int i;
         for (i = 0; i < CONFIG->NCORES; i++) {
-            /* LOG("Stopping worker pid %d.\n", child_pids[i]); */
+            /* log_notice("Stopping worker pid %d.", child_pids[i]); */
             if (child_pids[i] > 1 && kill(child_pids[i], SIGTERM) != 0) {
-                ERR("{core} Unable to send SIGTERM to worker pid %d: %s\n", child_pids[i], strerror(errno));
+                log_err("Unable to send SIGTERM to worker pid %d: %s", child_pids[i], strerror(errno));
             }
         }
-        /* LOG("Shutdown complete.\n"); */
+        /* log_notice("Shutdown complete."); */
+
+	// remove pid file
+	if (CONFIG->DAEMONIZE && strlen(CONFIG->PID_FILE) > 0) {
+            unlink(CONFIG->PID_FILE);
+	}
     }
 
     /* this is it, we're done... */
@@ -1371,8 +1342,9 @@ void init_signals() {
     act.sa_handler = SIG_IGN;
 
     /* Avoid getting PIPE signal when writing to a closed file descriptor */
-    if (sigaction(SIGPIPE, &act, NULL) < 0)
-        fail("sigaction - sigpipe");
+    if (sigaction(SIGPIPE, &act, NULL) < 0) {
+        fail("Unable to install SIGPIPE signal handler:");
+    }
 
     /* We don't care if someone stops and starts a child process with kill (1) */
     act.sa_flags = SA_NOCLDSTOP;
@@ -1380,38 +1352,50 @@ void init_signals() {
     act.sa_handler = do_wait;
 
     /* We do care when child processes change status */
-    if (sigaction(SIGCHLD, &act, NULL) < 0)
-        fail("sigaction - sigchld");
+    if (sigaction(SIGCHLD, &act, NULL) < 0) {
+        fail("Unable to install SIGCHLD signal handler:");
+    }
 
     /* catch INT and TERM signals */
     act.sa_flags = 0;
     act.sa_handler = sigh_terminate;
     if (sigaction(SIGINT, &act, NULL) < 0) {
-        ERR("Unable to register SIGINT signal handler: %s\n", strerror(errno));
-        exit(1);
+        fail("Unable to register SIGINT signal handler:");
     }
     if (sigaction(SIGTERM, &act, NULL) < 0) {
-        ERR("Unable to register SIGTERM signal handler: %s\n", strerror(errno));
-        exit(1);
+        fail("Unable to register SIGTERM signal handler:");
     }
 }
 
+int pid_file_write (char *path, pid_t pid) {
+    FILE *fd = fopen(path, "w");
+    if (fd == NULL) return 0;
+    if (fprintf(fd, "%d\n", pid) < 1) return 0;
+    if (fclose(fd) != 0) return 0;
+    return 1;
+}
+
 void daemonize () {
     /* go to root directory */
     if (chdir("/") != 0) {
-        ERR("Unable change directory to /: %s\n", strerror(errno));
-        exit(1);
+        die("Unable change directory to /: %s", strerror(errno));
     }
 
     /* let's make some children, baby :) */
     pid_t pid = fork();
     if (pid < 0) {
-        ERR("Unable to daemonize: fork failed: %s\n", strerror(errno));
-        exit(1);
+        die("Unable to daemonize: fork failed: %s", strerror(errno));
     }
 
     /* am i the parent? */
     if (pid != 0) {
+        // write pid file
+        if (! pid_file_write(CONFIG->PID_FILE, pid)) {
+            int e = errno;
+            kill(pid, SIGKILL); // kill the child
+            fprintf(stderr, "Unable to write pid file %s: %s\n", CONFIG->PID_FILE, strerror(e));
+            exit(1);
+        }
         printf("{core} Daemonized as pid %d.\n", pid);
         exit(0);
     }
@@ -1424,28 +1408,24 @@ void daemonize () {
     /* reopen standard streams to null device */
     stdin = fopen(NULL_DEV, "r");
     if (stdin == NULL) {
-        ERR("Unable to reopen stdin to %s: %s\n", NULL_DEV, strerror(errno));
-        exit(1);
+        die("Unable to reopen stdin to %s: %s", NULL_DEV, strerror(errno));
     }
     stdout = fopen(NULL_DEV, "w");
     if (stdout == NULL) {
-        ERR("Unable to reopen stdout to %s: %s\n", NULL_DEV, strerror(errno));
-        exit(1);
+        die("Unable to reopen stdout to %s: %s", NULL_DEV, strerror(errno));
     }
     stderr = fopen(NULL_DEV, "w");
     if (stderr == NULL) {
-        ERR("Unable to reopen stderr to %s: %s\n", NULL_DEV, strerror(errno));
-        exit(1);
+        die("Unable to reopen stderr to %s: %s", NULL_DEV, strerror(errno));
     }
 
     /* this is child, the new master */
     pid_t s = setsid();
     if (s < 0) {
-        ERR("Unable to create new session, setsid(2) failed: %s :: %d\n", strerror(errno), s);
-        exit(1);
+        die("Unable to create new session, setsid(2) failed: %s :: %d", strerror(errno), s);
     }
 
-    LOG("Successfully daemonized as pid %d.\n", getpid());
+    log_notice("Successfully daemonized as pid %d.", getpid());
 }
 
 void openssl_check_version() {
@@ -1455,8 +1435,8 @@ void openssl_check_version() {
     /* check if we're running the same openssl that we were */
     /* compiled with */
     if ((openssl_version ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) {
-        ERR(
-	    "WARNING: {core} OpenSSL version mismatch; stud was compiled with %lx, now using %lx.\n",
+        log_warn(
+	    "OpenSSL version mismatch; stud was compiled with %lx, now using %lx.",
 	    (unsigned long int) OPENSSL_VERSION_NUMBER,
 	    (unsigned long int) openssl_version
 	);
@@ -1464,7 +1444,7 @@ void openssl_check_version() {
 	/* exit(1); */
     }
 
-    LOG("{core} Using OpenSSL version %lx.\n", (unsigned long int) openssl_version);
+      log_debug("Using OpenSSL version %lx.", (unsigned long int) openssl_version);
 }
 
 /* Process command line args, create the bound socket,
@@ -1472,7 +1452,7 @@ void openssl_check_version() {
 int main(int argc, char **argv) {
     // initialize configuration
     CONFIG = config_new();
-    
+
     // parse command line
     config_parse_cli(argc, argv, CONFIG);
     
@@ -1505,8 +1485,7 @@ int main(int argc, char **argv) {
 
     /* should we daemonize ?*/
     if (CONFIG->DAEMONIZE) {
-        /* disable logging to stderr */
-        CONFIG->QUIET = 1;
+        // enable syslog logging
         CONFIG->SYSLOG = 1;
 
         /* become a daemon */
diff --git a/stud.h b/stud.h
new file mode 100644
index 0000000..7405d7a
--- /dev/null
+++ b/stud.h
@@ -0,0 +1,76 @@
+/**
+  * Copyright 2011 Bump Technologies, Inc. All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without modification, are
+  * permitted provided that the following conditions are met:
+  *
+  *    1. Redistributions of source code must retain the above copyright notice, this list of
+  *       conditions and the following disclaimer.
+  *
+  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+  *       of conditions and the following disclaimer in the documentation and/or other materials
+  *       provided with the distribution.
+  *
+  * THIS SOFTWARE IS PROVIDED BY BUMP TECHNOLOGIES, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BUMP TECHNOLOGIES, INC. OR
+  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  *
+  * The views and conclusions contained in the software and documentation are those of the
+  * authors and should not be interpreted as representing official policies, either expressed
+  * or implied, of Bump Technologies, Inc.
+  *
+  **/
+
+#include <sys/socket.h>
+#include <openssl/ssl.h>
+#include <ev.h>
+
+#include "ringbuffer.h"
+
+/* What agent/state requests the shutdown--for proper half-closed
+ * handling */
+typedef enum _SHUTDOWN_REQUESTOR {
+    SHUTDOWN_HARD,
+    SHUTDOWN_DOWN,
+    SHUTDOWN_UP
+} SHUTDOWN_REQUESTOR;
+
+/*
+ * Proxied State
+ *
+ * All state associated with one proxied connection
+ */
+typedef struct proxystate {
+    ringbuffer ring_down; /* pushing bytes from client to backend */
+    ringbuffer ring_up;   /* pushing bytes from backend to client */
+
+    ev_io ev_r_up;        /* Upstream write event */
+    ev_io ev_w_up;        /* Upstream read event */
+
+    ev_io ev_r_handshake; /* Downstream write event */
+    ev_io ev_w_handshake; /* Downstream read event */
+
+    ev_io ev_r_down;      /* Downstream write event */
+    ev_io ev_w_down;      /* Downstream read event */
+
+    int fd_up;            /* Upstream (client) socket */
+    int fd_down;          /* Downstream (backend) socket */
+
+    int want_shutdown:1;  /* Connection is half-shutdown */
+    int handshaked:1;     /* Initial handshake happened */
+    int renegotiation:1;  /* Renegotation is occuring */
+
+    SSL *ssl;             /* OpenSSL SSL state */
+
+    struct sockaddr_storage remote_ip;  /* Remote ip returned from `accept` */
+} proxystate;
+
+#define NULL_DEV "/dev/null"
+
+char * stud_instance_name (char *name);