]> Kevux Git Server - koopa/commitdiff
Update: add custom/experimental sessionize accounts and postgresql account auto-creat...
authorKevin Day <thekevinday@gmail.com>
Sat, 18 Feb 2017 23:00:09 +0000 (17:00 -0600)
committerKevin Day <thekevinday@gmail.com>
Sat, 18 Feb 2017 23:00:09 +0000 (17:00 -0600)
This adds some experimental helpers for either creating postgresql accounts via ldap or providing session management independent of PHP.

Some of the C code is not using threads but likely should.
I will eventually come back and make a threaded version.

program/autocreate_ldap_accounts_in_postgresql/readme.txt [new file with mode: 0644]
program/autocreate_ldap_accounts_in_postgresql/settings/example.settings [new file with mode: 0644]
program/autocreate_ldap_accounts_in_postgresql/settings/systems.settings [new file with mode: 0644]
program/autocreate_ldap_accounts_in_postgresql/source/bash/autocreate_ldap_accounts_in_postgresql.sh [new file with mode: 0644]
program/autocreate_ldap_accounts_in_postgresql/source/c/autocreate_ldap_accounts_in_postgresql.c [new file with mode: 0644]
program/autocreate_ldap_accounts_in_postgresql/source/php/autocreate_ldap_accounts_in_postgresql-client.php [new file with mode: 0644]
program/sessionize_accounts/readme.txt [new file with mode: 0644]
program/sessionize_accounts/settings/systems.settings [new file with mode: 0644]
program/sessionize_accounts/source/bash/sessionize_accounts.sh [new file with mode: 0644]
program/sessionize_accounts/source/php/sessionize_accounts-client.php [new file with mode: 0644]
program/sessionize_accounts/source/php/sessionize_accounts-server.php [new file with mode: 0644]

diff --git a/program/autocreate_ldap_accounts_in_postgresql/readme.txt b/program/autocreate_ldap_accounts_in_postgresql/readme.txt
new file mode 100644 (file)
index 0000000..f7f08d4
--- /dev/null
@@ -0,0 +1,23 @@
+Installation
+============
+This assumes that the /programs/ paths are being used.
+
+Compile the source code:
+  gcc -g -lldap -lpq source/c/autocreate_ldap_accounts_in_postgresql.c -o /programs/bin/autocreate_ldap_accounts_in_postgresql
+
+Add and enable the init script:
+  cp -v source/bash/autocreate_ldap_accounts_in_postgresql.sh /etc/init.d/autocreate_ldap_accounts_in_postgresql
+  chkconfig --add autocreate_ldap_accounts_in_postgresql
+  chkconfig autocreate_ldap_accounts_in_postgresql on
+
+Configure the settings (assuming system called "example"):
+  mkdir -vp /programs/settings/autocreate_ldap_accounts_in_postgresql/
+  cp -v settings/{example,systems}.settings /programs/settings/autocreate_ldap_accounts_in_postgresql/
+
+Note: rename 'example.settings' to the name of the system as defined in 'systems.settings'.
+
+Start the service
+  service autocreate_ldap_accounts_in_postgresql start
+
+For most users, the /programs/ path needs to be changed to a custom path for your system.
+The source code and bash scripts will need to be updated with these hardcoded paths.
diff --git a/program/autocreate_ldap_accounts_in_postgresql/settings/example.settings b/program/autocreate_ldap_accounts_in_postgresql/settings/example.settings
new file mode 100644 (file)
index 0000000..cdd685c
--- /dev/null
@@ -0,0 +1,6 @@
+# fss-0000
+
+alap_name_system example
+alap_name_group example_users
+alap_name_database example_database
+alap_port 1234
diff --git a/program/autocreate_ldap_accounts_in_postgresql/settings/systems.settings b/program/autocreate_ldap_accounts_in_postgresql/settings/systems.settings
new file mode 100644 (file)
index 0000000..bcc2f9e
--- /dev/null
@@ -0,0 +1,3 @@
+# fss-0000
+
+alap_systems example
diff --git a/program/autocreate_ldap_accounts_in_postgresql/source/bash/autocreate_ldap_accounts_in_postgresql.sh b/program/autocreate_ldap_accounts_in_postgresql/source/bash/autocreate_ldap_accounts_in_postgresql.sh
new file mode 100644 (file)
index 0000000..a20217c
--- /dev/null
@@ -0,0 +1,471 @@
+#!/bin/bash
+#
+# autocreate_ldap_accounts_in_postgresql      Helper service for auto-populating ldap accounts in postgresql.
+#
+# chkconfig: 345 40 60
+# description: Provide a per-database/per-role way to auto-create ldap accounts and auto assign a single role.
+
+### BEGIN INIT INFO
+# Provides: autocreate_ldap_accounts_in_postgresql
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Default-Start: 3 4 5
+# Default-Stop: 0 1 2 6
+# Short-Description: Auto-create ldap-based accounts in postgresql.
+# Description: Provide a per-database/per-role way to auto-create ldap accounts and auto assign a single role.
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+main() {
+  local process_owner="alap"
+  local process_group="alap"
+  local path_programs="/programs/"
+  local path_service="${path_programs}bin/autocreate_ldap_accounts_in_postgresql"
+  local path_settings="${path_programs}settings/autocreate_ldap_accounts_in_postgresql/"
+  local path_systems="${path_settings}systems.settings"
+  local path_pids="/var/run/autocreate_ldap_accounts_in_postgresql/"
+  local parameter_system=$2
+  local alap_systems=
+  local i=
+  local j=
+
+  # when process_owner is defined, make sure that the binary has the following set:
+  # setcap cap_net_bind_service=ep /programs/bin/autocreate_ldap_accounts_in_postgresql
+
+  if [[ ! -f $path_systems ]] ; then
+    echo "No valid path_systems file defined at: $path_systems"
+    exit -1
+  fi
+
+  if [[ ! -d $path_pids ]] ; then
+    mkdir -p $path_pids
+  fi
+
+  if [[ $process_owner != "" ]] ; then
+    chown $process_owner $path_pids
+  fi
+
+  alap_systems=$(grep -o '^alap_systems[[:space:]][[:space:]]*.*$' $path_systems | sed -e 's|^alap_systems[[:space:]][[:space:]]*||')
+
+  if [[ $alap_systems == "" ]] ; then
+    echo "No valid systems defined by setting 'alap_systems' in file: $path_systems"
+    exit -1
+  fi
+
+  if [[ $parameter_system != "" ]] ; then
+    j=$alap_systems
+    alap_systems=
+
+    for i in $j ; do
+      if [[ $i == $parameter_system ]] ; then
+        alap_systems=$i
+        break;
+      fi
+    done
+
+    i=
+    j=
+
+    if [[ $alap_systems == "" ]] ; then
+      echo "System '$parameter_system' is not a valid system defined by setting 'alap_systems' in file: $path_systems"
+      exit -1
+    fi
+  fi
+
+  j=$alap_systems
+  alap_systems=
+  for i in $j ; do
+    if [[ -f $path_settings${i}.settings ]] ; then
+      alap_systems="$alap_systems$i "
+    else
+      echo "Skipping system '$i' because it does not have a settings file defined here: '$path_settings${i}.settings'"
+    fi
+  done
+
+  i=
+  j=
+
+  case "$1" in
+    start)
+      start
+      ;;
+    stop)
+      stop
+      ;;
+    restart)
+      restart
+      ;;
+    status)
+      status
+      ;;
+    *)
+      echo "Usage: autocreate_ldap_accounts_in_postgresql {start|stop|restart|status}"
+      return 2
+  esac
+
+  return $?
+}
+
+start() {
+  local alap_name_system=
+  local alap_name_group=
+  local alap_name_database=
+  local alap_connect_user=
+  local alap_connect_password=
+  local alap_port=
+  local alap_system=
+  local result=
+  local any_success=0
+  local any_failure=0
+
+  for alap_system in $alap_systems ; do
+    load_system_settings
+    check_pid
+
+    if [[ $result -eq -1 ]] ; then
+      continue
+    elif [[ $result -gt 0 ]] ; then
+      echo "Not starting process for $alap_system, it is already running with pid=$pid."
+      continue
+    fi
+
+    start_command
+
+    if [[ $result -eq 0 ]] ; then
+      wait_pid
+
+      if [[ $result -eq 0 ]] ; then
+        get_pid
+      fi
+
+      if [[ $pid == "" ]] ; then
+        echo "Started process for $alap_system but was unable to determine pid, command: $path_service $alap_name_system $alap_name_group $alap_name_database $alap_port."
+      else
+        echo "Successfully started process for $alap_system, pid=$pid, command: $path_service $alap_name_system $alap_name_group $alap_name_database $alap_port."
+      fi
+    fi
+  done
+
+  if [[ $any_success -ne 0 || $any_failure -eq 1 ]] ; then
+    exit -1
+  fi
+
+  return 0
+}
+
+stop() {
+  local alap_name_system=
+  local alap_name_group=
+  local alap_name_database=
+  local alap_port=
+  local alap_system=
+  local result=
+  local any_success=0
+  local any_failure=0
+  local original_pid=
+
+  for alap_system in $alap_systems ; do
+    load_system_settings
+    get_pid
+
+    if [[ $pid == "" ]] ; then
+      continue
+    fi
+
+    stop_command
+
+    if [[ $result -eq 0 ]] ; then
+      original_pid=$pid
+      check_pid
+
+      if [[ $result -eq -2 ]] ; then
+        echo "Successfully stopped process for $alap_system, pid=$original_pid."
+      else
+        echo "Sent stop command for $alap_system, pid=$pid, but pid file ($pid_file) still exists."
+      fi
+    fi
+  done
+
+  if [[ $any_success -ne 0 || $any_failure -eq 1 ]] ; then
+    exit -1
+  fi
+
+  return 0
+}
+
+restart() {
+  local alap_name_system=
+  local alap_name_group=
+  local alap_name_database=
+  local alap_port=
+  local alap_system=
+  local result=
+  local any_success=0
+  local any_failure=0
+  local original_pid=
+
+  for alap_system in $alap_systems ; do
+    load_system_settings
+    check_pid
+
+    if [[ $result -lt 0 ]] ; then
+      continue
+    elif [[ $result -gt 0 ]] ; then
+      stop_command
+
+      if [[ $result -ne 0 ]] ; then
+        continue
+      fi
+
+      original_pid=$pid
+      check_pid
+
+      if [[ $result -eq -2 ]] ; then
+        echo "Successfully stopped process for $alap_system, pid=$original_pid."
+      else
+        echo "Sent stop command for $alap_system, pid=$original_pid, but pid file ($pid_file) still exists (cannot start process, skipping)."
+        continue
+      fi
+    fi
+
+    start_command
+
+    if [[ $result -eq 0 ]] ; then
+      wait_pid
+
+      if [[ $result -eq 0 ]] ; then
+        get_pid
+      fi
+
+      if [[ $pid == "" ]] ; then
+        echo "Started process for $alap_system but was unable to determine pid, command: $path_service $alap_name_system $alap_name_group $alap_name_database $alap_port."
+      else
+        echo "Successfully started process for $alap_system, pid=$pid, command: $path_service $alap_name_system $alap_name_group $alap_name_database $alap_port."
+      fi
+    fi
+  done
+
+  if [[ $any_success -ne 0 ]] ; then
+    exit -1
+  fi
+
+  return 0
+}
+
+status() {
+  local alap_name_system=
+  local alap_name_group=
+  local alap_name_database=
+  local alap_port=
+  local alap_system=
+  local pid_file=
+  local pid=
+  local result=
+
+  for alap_system in $alap_systems ; do
+    load_system_settings
+    get_pid
+
+    if [[ $pid == "" ]] ; then
+      continue
+    fi
+
+    echo "The system '$alap_system' appears to be running as process $pid."
+  done
+
+  return 0
+}
+
+load_system_settings() {
+  local path_system=$path_settings${alap_system}.settings
+  alap_name_system=
+  alap_name_group=
+  alap_name_database=
+  alap_connect_user=
+  alap_connect_password=
+  alap_port=
+
+  if [[ $alap_system == "" || ! -f $path_system ]] ; then
+    echo "No valid path_systems file defined at: $path_system"
+    exit -1
+  fi
+
+  alap_name_system=$(grep -o '^alap_name_system[[:space:]][[:space:]]*.*$' $path_system | sed -e 's|^alap_name_system[[:space:]][[:space:]]*||')
+  alap_name_group=$(grep -o '^alap_name_group[[:space:]][[:space:]]*.*$' $path_system | sed -e 's|^alap_name_group[[:space:]][[:space:]]*||')
+  alap_name_database=$(grep -o '^alap_name_database[[:space:]][[:space:]]*.*$' $path_system | sed -e 's|^alap_name_database[[:space:]][[:space:]]*||')
+  alap_connect_user=$(grep -o '^alap_connect_user[[:space:]][[:space:]]*.*$' $path_system | sed -e 's|^alap_connect_user[[:space:]][[:space:]]*||')
+  alap_connect_password=$(grep -o '^alap_connect_password[[:space:]][[:space:]]*.*$' $path_system | sed -e 's|^alap_connect_password[[:space:]][[:space:]]*||')
+  alap_port=$(grep -o '^alap_port[[:space:]][[:space:]]*.*$' $path_system | sed -e 's|^alap_port[[:space:]][[:space:]]*||')
+
+  if [[ $alap_name_system == "" ]] ; then
+    echo "No valid alap_name_system setting defined in file: $path_system"
+    exit -1
+  fi
+
+  if [[ $alap_name_group == "" ]] ; then
+    echo "No valid alap_name_group setting defined in file: $path_system"
+    exit -1
+  fi
+
+  if [[ $alap_name_database == "" ]] ; then
+    echo "No valid alap_name_database setting defined in file: $path_system"
+    exit -1
+  fi
+
+  if [[ $alap_connect_user == "" ]] ; then
+    echo "No valid alap_connect_user setting defined in file: $path_system"
+    exit -1
+  fi
+
+  if [[ $alap_port == "" ]] ; then
+    echo "No valid alap_port setting defined in file: $path_system"
+    exit -1
+  fi
+}
+
+start_command() {
+  export alap_connect_user="$alap_connect_user"
+  export alap_connect_password="$alap_connect_password"
+
+  if [[ $process_owner == "" ]] ; then
+    $path_service $alap_name_system $alap_name_group $alap_name_database $alap_port
+    result=$?
+  else
+    su $process_owner -m -c "$path_service $alap_name_system $alap_name_group $alap_name_database $alap_port"
+    result=$?
+  fi
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Failed to start process, command: $path_service $alap_name_system $alap_name_group $alap_name_database $alap_port."
+    any_failure=1
+  else
+    any_success=1
+  fi
+}
+
+stop_command() {
+  # -3 = SIGQUIT, -15 = SIGTERM, -9 = SIGKILL
+  kill -3 $pid
+  result=$?
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Signal to quit failed, command: kill -3 $pid."
+    any_failure=1
+  else
+    any_success=1
+
+    # pause and give the process time to close down.
+    sleep 0.1
+  fi
+}
+
+wait_pid() {
+  local k=
+  local max=32
+
+  pid=
+  pid_file=$path_pids$alap_system.pid
+  result=-1
+
+  # the started process will go into the background, so wait until the pid file is created, but only wait for so long.
+  let k=0
+  while [[ $k -lt $max ]] ; do
+    if [[ -f $pid_file ]] ; then
+      result=0
+      break
+    fi
+
+    sleep 0.05
+
+    let k=$k+1
+  done
+
+  return 0
+}
+
+get_pid() {
+  pid=
+  pid_file=$path_pids$alap_system.pid
+
+  if [[ ! -f $pid_file ]] ; then
+    echo "No pid file ($pid_file) found for system '$alap_system', it must not be running."
+    return 0
+  fi
+
+  pid=$(cat $pid_file)
+  result=$?
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Failed to read the pid file ($pid_file) for system '$alap_system', command: cat $pid_file."
+    pid=
+    return 0
+  fi
+
+  if [[ $pid == "" ]] ; then
+    echo "The pid file ($pid_file) for system '$alap_system' is empty."
+    pid=
+    return 0
+  fi
+
+  result=$(ps --no-headers -o pid -p $pid)
+  if [[ $? -lt 0 ]] ; then
+    echo "An error occured while searching for the process for system '$alap_system', command: ps --no-headers -o pid -p $pid."
+    pid=
+    return 0
+  fi
+
+  if [[ $result == "" ]] ; then
+    echo "No process $pid was found for the system '$alap_system', the pid file might be stale or inaccurate."
+    pid=
+    return 0
+  fi
+}
+
+check_pid() {
+  pid=
+  pid_file=$path_pids$alap_system.pid
+
+  if [[ ! -f $pid_file ]] ; then
+    result=-2
+    return 0
+  fi
+
+  pid=$(cat $pid_file)
+  result=$?
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Failed to read the pid file ($pid_file) for system '$alap_system', command: cat $pid_file."
+    result=-1
+    return 0
+  fi
+
+  if [[ $pid == "" ]] ; then
+    result=0
+    return 0
+  fi
+
+  result=$(ps --no-headers -o pid -p $pid)
+  if [[ $? -lt 0 ]] ; then
+    echo "An error occured while searching for the process for system '$alap_system', command: ps --no-headers -o pid -p $pid."
+    result=-1
+    return 0
+  fi
+
+  if [[ $result == "" ]] ; then
+    result=
+
+    # the pid file is invalid, so remove the pid file.
+    rm -f $pid_file
+
+    # return 0 to allow for starting a new process.
+    result=0
+    return 0
+  fi
+
+  result=1
+  return 0
+}
+
+main "$1" "$2"
diff --git a/program/autocreate_ldap_accounts_in_postgresql/source/c/autocreate_ldap_accounts_in_postgresql.c b/program/autocreate_ldap_accounts_in_postgresql/source/c/autocreate_ldap_accounts_in_postgresql.c
new file mode 100644 (file)
index 0000000..8767105
--- /dev/null
@@ -0,0 +1,1551 @@
+/**
+ * Helper program for auto-creating ldap accounts.
+ *
+ * This was written originally using sockets, but it makes more sense to run this on the database server (for security reasons).
+ * - The original socket code is left alone, but is not used.
+ *
+ * The program expects the following parameters: [user_name] [group_name] [database_name] [listen_port].
+ *
+ * The system will listen on the socket waiting on a valid username to create.
+ * This only accept usernames with alphanumeric, '-', or '_' in their name.
+ * - All other characters will result in an error.
+ * - Some username patterns will be blacklisted. (@todo: implement this via regex.)
+ *
+ * A packet size of PACKET_SIZE_INPUT is defined to ensure that the string is operated on only after all data is received.
+ * - A NULL byte before the PACKET_SIZE_INPUT is reached will also terminate the packet.
+ *
+ * @todo: review this functionality "http://www.postgresql.org/docs/current/static/libpq-notice-processing.html".
+ *
+ * Compiled with:
+ *   gcc  -lpq -lldap autocreate_ldap_accounts_in_postgresql.c -o autocreate_ldap_accounts_in_postgresql
+ *
+ * Role created with:
+ *   create role create_ldap_users createrole;
+ *   alter role create_ldap_users login;
+ *   grant fcs_users to create_ldap_users with admin option;
+ *
+ *   Alternatively, roles can be granted CREATEROLE, such as:
+ *     grant createrole to create_ldap_users
+ *
+ * @todo: when implementing the init script, the pid can be created via: ps --no-headers -o pid -p $(cat fcs.pid)
+ *
+ * Copyright Kevin Day, lgpl v2.1 or later.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <sched.h>
+#include <netdb.h>
+#include <limits.h>
+#include <syslog.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/signalfd.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <ldap.h>
+#include <libpq-fe.h>
+
+// select either socket or network method.
+//#define USE_SOCKET     1
+#define USE_NETWORK    1
+//#define DEBUG_ENABLED  1
+
+#define LOG_ID    "autocreate_ldap_accounts_in_postgresql: "
+#define PATH_PID  "/var/run/autocreate_ldap_accounts_in_postgresql/%s.pid"
+
+// by granting a postgresql user the same access as a specified role, one can easily manage access by only setting permissions on the role.
+// for consistency purposes, I suggest individual users have something like 'fcs_user' while the role/group should be something like 'fcs_users'.
+// with this design admin users need to be manually updated on the database to have access to create users as well.
+// admin users would then need something like this: "grant fcs_users to kday with admin option;".
+#define PSQL_SELECT             "select rolname from pg_roles where rolname = '%s';"
+#define PSQL_SELECT_LENGTH      48
+#define PSQL_CREATE             "create role %s with login inherit;"
+#define PSQL_CREATE_LENGTH      32
+#define PSQL_GRANT              "grant %s to %s;"
+#define PSQL_GRANT_LENGTH       11
+//#define PSQL_CONNECTION         "host=127.0.0.1 port=5433 dbname=%s connect_timeout=2 sslmode=require user= password="
+#define PSQL_CONNECTION         "port=5433 dbname=%s connect_timeout=2 sslmode=disable user=%s password=%s"
+#define PSQL_CONNECTION_LENGTH  73
+
+#define PARAMETER_LENGTH_MAX 96
+
+#define LDAP_SERVER            "ldaps://ldap.example.com:1636"
+#define LDAP_SEARCH_DN         "uid=%s,ou=users,ou=People"
+#define LDAP_SEARCH_DN_LENGTH  47
+
+#define LDAP_RETRY_BIND_RETRY      4
+#define LDAP_RETRY_BIND_TIMEOUT    200000 // (microseconds) 0.2 second timeout.
+#define LDAP_RETRY_SEARCH_RETRY    4
+#define LDAP_RETRY_SEARCH_TIMEOUT  200000 // (microseconds) 0.2 second timeout.
+
+#define PACKET_SIZE_INPUT   63
+#define PACKET_SIZE_OUTPUT  1
+
+// if stack size is too small, then on some systems (generally glibc based ones) will segfault/illegal-instruction under certain circumstances.
+// in this case it the circumstance happens with clone(), ldap_initialize(), and glibc.
+//#define STACK_SIZE 8192
+#define STACK_SIZE 65536
+
+#define PROTOCOL_NULL    0
+#define PROTOCOL_SOCKET  SOL_SOCKET
+#define PROTOCOL_TCP     6
+#define PROTOCOL_UDP     17
+
+#define SOCKET_TYPE     SOCK_STREAM
+#define SOCKET_BACKLOG  15
+
+#ifdef USE_NETWORK
+  #define SOCKET_FAMILY    AF_INET // 'family' is also called 'domain' in this case.
+  #define SOCKET_PROTOCOL  PROTOCOL_TCP
+  #define SOCKET_TIMEOUT   160000 // 0.16 seconds.
+#elif defined USE_SOCKET
+  #define SOCKET_FAMILY       AF_UNIX // 'family' is also called 'domain' in this case.
+  #define SOCKET_PATH         "/var/www/sockets/autocreate_ldap_accounts_in_postgresql/%s/%s.socket"
+  #define SOCKET_PATH_LENGTH  64
+  #define SOCKET_PROTOCOL     PROTOCOL_NULL
+  #define SOCKET_PORT         0
+  #define SOCKET_TIMEOUT      10000 // (microseconds) 0.01 seconds.
+#endif // USE_SOCKET
+
+#define FLAGS_RECEIVE  0
+#define FLAGS_SEND     MSG_NOSIGNAL
+#define FLAGS_CLONE    CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_VM | CLONE_SIGHAND | CLONE_THREAD
+
+
+// environment variables used.
+#define ENVIRONMENT_CONNECT_USER      "alap_connect_user"
+#define ENVIRONMENT_CONNECT_PASSWORD  "alap_connect_password"
+
+#define ENVIRONMENT_MAX_CONNECT_USER      128 // maximum characters to be supported for the connect name.
+#define ENVIRONMENT_MAX_CONNECT_PASSWORD  512 // maximum characters to be supported for the connect password.
+
+
+// note: these are strings instead of integers so that they act as binary data when passed as a string via the socket.
+#define ERROR_NONE      "\x00"
+#define ERROR_NAME      "\x01" // invalid user name, bad characters, or name too long.
+#define ERROR_LDAP      "\x02" // failed to connect to the ldap server and could not query the ldap name.
+#define ERROR_USER      "\x03" // user name not found in ldap database.
+#define ERROR_DATABASE  "\x04" // failed to connect to the database.
+#define ERROR_SQL       "\x05" // error returned while executing the SQL command.
+#define ERROR_READ      "\x06" // error occured while reading input from the user (such as via recv()).
+#define ERROR_WRITE     "\x07" // error occured while writing input from the user (such as via send()).
+#define ERROR_PACKET    "\x08" // the received packet is invalid, such as wrong length.
+#define ERROR_TIMEOUT   "\x09" // connection timed out when reading or writing.
+#define ERROR_CLOSE     "\x0a" // the connection is being forced closed.
+#define ERROR_QUIT      "\x0b" // the connection is closing because the service is quitting.
+
+#define PROBLEM_COUNT_MAX_SIGNAL_SIZE  10
+
+#ifdef USE_NETWORK
+  #define MACRO_EXIT_STANDARD_1(shared, stack, exit_code) \
+    if (shared.socket_id_client > 0) { \
+      send(shared.socket_id_client, ERROR_QUIT, PACKET_SIZE_OUTPUT, FLAGS_SEND); \
+      shutdown(shared.socket_id_client, SHUT_RDWR); \
+    } \
+    \
+    if (shared.socket_id_target > 0) { \
+      shutdown(shared.socket_id_target, SHUT_RDWR); \
+    } \
+    \
+    if (shared.socket_bound > 0) { \
+      shared.socket_bound = 0; \
+    } \
+    \
+    if (stack != NULL) { \
+      free(stack); \
+      stack = NULL; \
+    } \
+    \
+    if (shared.pid_path != NULL) { \
+      unlink(shared.pid_path); \
+      free(shared.pid_path); \
+      shared.pid_path = NULL; \
+    } \
+    \
+    memset(&shared, 0, sizeof(shared_data)); \
+    \
+    return exit_code;
+
+  typedef struct {
+    char parameter_system[PARAMETER_LENGTH_MAX];
+    char parameter_group[PARAMETER_LENGTH_MAX];
+    char parameter_database[PARAMETER_LENGTH_MAX];
+    char parameter_connect_name[ENVIRONMENT_MAX_CONNECT_USER];
+    char parameter_connect_password[ENVIRONMENT_MAX_CONNECT_PASSWORD];
+
+    int socket_id_target;
+    int socket_id_client;
+    int socket_bound;
+
+    int parameter_port;
+
+    pid_t pid_parent;
+    pid_t pid_child;
+    char *pid_path;
+  } shared_data;
+#elif defined USE_SOCKET
+  #define MACRO_EXIT_STANDARD_1(shared, stack, exit_code) \
+    if (shared.socket_id_client > 0) { \
+      send(shared.socket_id_client, ERROR_QUIT, PACKET_SIZE_OUTPUT, FLAGS_SEND); \
+      shutdown(shared.socket_id_client, SHUT_RDWR); \
+    } \
+    \
+    if (shared.socket_id_target > 0) { \
+      shutdown(shared.socket_id_target, SHUT_RDWR); \
+    } \
+    \
+    if (shared.socket_bound > 0) { \
+      shared.socket_bound = 0; \
+    } \
+    \
+    if (stack != NULL) { \
+      free(stack); \
+      stack = NULL; \
+    } \
+    \
+    if (shared.socket_path != NULL) { \
+      unlink(shared.socket_path); \
+    } \
+    \
+    if (shared.pid_path != NULL) { \
+      unlink(shared.pid_path); \
+      free(shared.pid_path); \
+      shared.pid_path = NULL; \
+    } \
+    \
+    memset(&shared, 0, sizeof(shared_data)); \
+    \
+    return exit_code;
+
+  typedef struct {
+    char parameter_system[PARAMETER_LENGTH_MAX];
+    char parameter_group[PARAMETER_LENGTH_MAX];
+    char parameter_database[PARAMETER_LENGTH_MAX];
+    char parameter_connect_name[ENVIRONMENT_MAX_CONNECT_USER];
+    char parameter_connect_password[ENVIRONMENT_MAX_CONNECT_PASSWORD];
+
+    int socket_id_target;
+    int socket_id_client;
+    int socket_bound;
+
+    char *socket_path;
+
+    pid_t pid_parent;
+    pid_t pid_child;
+    char pid_path[PATH_MAX];
+  } shared_data;
+#endif // USE_SOCKET
+
+#define MACRO_EXIT_STANDARD_2(pid_child, shared, stack, exit_code) \
+  if (pid_child > 0) { \
+    kill(pid_child, SIGQUIT); \
+    pid_child = 0; \
+  } \
+  \
+  MACRO_EXIT_STANDARD_1(shared, stack, exit_code)
+
+
+/**
+ * Immediately writes to system logger.
+ *
+ * @param const unsigned level
+ *   The log messages level.
+ *   LOG_ERR is the most common choice here.
+ *   This is not the 'priority', which is auto-generated.
+ *
+ * @param const char *message
+ *   The complete message string to write to the logger.
+ *
+ * @see vsyslog()
+ * @see printf()
+ */
+void log_write(const int level, const char *message, ...) {
+  va_list arguments;
+
+  #ifdef DEBUG_ENABLED
+    va_start(arguments, message);
+    vprintf(message, arguments);
+    fflush(stdout);
+    va_end(arguments);
+  #endif // DEBUG_ENABLED
+
+  va_start(arguments, message);
+
+  openlog(LOG_ID, LOG_PID | LOG_CONS, LOG_DAEMON);
+  vsyslog(level | LOG_DAEMON, message, arguments);
+  closelog();
+
+  va_end(arguments);
+}
+
+/**
+ * Grants the user access to the specified group in the postgresql database.
+ *
+ * @param char *user_name
+ *   Name of the user/role to grant access to..
+ * @param char *user_password
+ *   Password for the database user.
+ * @param char *group_name
+ *   Name of the group.
+ * @param char *database_name
+ *   Name of the database.
+  * @param char *connect_name
+ *   Name of the role used to connect to the database.
+ * @param char *connect_password
+ *   Password for the role used to connect to the database.
+ *
+ * @return int
+ *   1 on success and -1 on error.
+ */
+int grant_role_in_database(const char *user_name, const char *group_name, const char *database_name, const char *connect_name, const char *connect_password) {
+  PGconn *connection = NULL;
+  char *connection_information = NULL;
+
+  {
+    int connection_information_length = PSQL_CONNECTION_LENGTH + 1;
+
+    connection_information_length += strnlen(database_name, PARAMETER_LENGTH_MAX);
+    connection_information_length += strnlen(connect_name, PARAMETER_LENGTH_MAX);
+    connection_information_length += strnlen(connect_password, PARAMETER_LENGTH_MAX);
+    connection_information = malloc(sizeof(char) * connection_information_length);
+
+    if (connection_information == NULL) {
+      log_write(LOG_ERR, "ERROR: failed to allocate memory when building the postgresql connection information string while processing user '%s', group '%s', and database '%s'.\n", user_name, group_name, database_name);
+      return -1;
+    }
+
+    memset(connection_information, 0, sizeof(char) * connection_information_length);
+    snprintf(connection_information, connection_information_length, PSQL_CONNECTION, database_name, connect_name, connect_password);
+  }
+
+  connection = PQconnectdb(connection_information);
+
+  if (connection == NULL) {
+    log_write(LOG_ERR, "ERROR: failed to establish the postgresql connection while processing user '%s', group '%s', and database '%s', reason: NULL returned.\n", user_name, group_name, database_name);
+    return -1;
+  }
+  else if (PQstatus(connection) != CONNECTION_OK) {
+    log_write(LOG_ERR, "ERROR: failed to establish the postgresql connection while processing user '%s', group '%s', and database '%s', reason (%u): %s.\n", user_name, group_name, database_name, PQstatus(connection), PQerrorMessage(connection));
+    PQfinish(connection);
+    free(connection_information);
+    connection_information = NULL;
+    return -1;
+  }
+
+  // check to see if role exists and has
+  short role_exists = 0;
+  {
+    char *query = NULL;
+    int query_length = PSQL_SELECT_LENGTH + 1;
+
+    query_length += strnlen(user_name, PARAMETER_LENGTH_MAX);
+    query = malloc(sizeof(char) * query_length);
+
+    if (query == NULL) {
+      log_write(LOG_ERR, "ERROR: failed to allocate memory when building the postgresql select query string while processing user '%s', group '%s', and database '%s'.\n", user_name, group_name, database_name);
+
+      PQfinish(connection);
+      free(connection_information);
+      connection_information = NULL;
+      return -1;
+    }
+
+    memset(query, 0, sizeof(char) * query_length);
+    snprintf(query, query_length, PSQL_SELECT, user_name);
+
+    {
+      PGresult *result = NULL;
+
+      result = PQexec(connection, query);
+
+      int status = PQresultStatus(result);
+
+      if (status == PGRES_EMPTY_QUERY) {
+        //role_exists = 0;
+      }
+      else if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
+        if (PQnfields(result) > 0 && PQntuples(result) > 0) {
+          role_exists = 1;
+        }
+      }
+      else {
+        PQclear(result);
+        PQfinish(connection);
+
+        free(query);
+        free(connection_information);
+
+        result = NULL;
+        query = NULL;
+        connection_information = NULL;
+
+        return -1;
+      }
+
+      PQclear(result);
+      result = NULL;
+    }
+
+    free(query);
+    query = NULL;
+  }
+
+  // Create the specified role.
+  if (role_exists == 0) {
+    char *query = NULL;
+    int query_length = PSQL_CREATE_LENGTH + 1;
+
+    query_length += strnlen(user_name, PARAMETER_LENGTH_MAX);
+    query = malloc(sizeof(char) * query_length);
+
+    if (query == NULL) {
+      log_write(LOG_ERR, "ERROR: failed to allocate memory when building the postgresql creates query string while processing user '%s', group '%s', and database '%s'.\n", user_name, group_name, database_name);
+
+      PQfinish(connection);
+      free(connection_information);
+      connection_information = NULL;
+      return -1;
+    }
+
+    memset(query, 0, sizeof(char) * query_length);
+    snprintf(query, query_length, PSQL_CREATE, user_name);
+
+    {
+      PGresult *result = NULL;
+
+      result = PQexec(connection, query);
+
+      int status = PQresultStatus(result);
+
+      if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+        log_write(LOG_ERR, "ERROR: failed to process sql query '%s', reason (%u): %s.\n", query, status, PQerrorMessage(connection));
+
+        PQclear(result);
+        PQfinish(connection);
+
+        free(query);
+        free(connection_information);
+
+        result = NULL;
+        query = NULL;
+        connection_information = NULL;
+
+        return -1;
+      }
+
+      PQclear(result);
+      result = NULL;
+    }
+
+    free(query);
+    query = NULL;
+  }
+
+  // grant the user access to the specified role.
+  {
+    char *query = NULL;
+    int query_length = PSQL_GRANT_LENGTH + 1;
+
+    query_length += strnlen(group_name, PARAMETER_LENGTH_MAX);
+    query_length += strnlen(user_name, PARAMETER_LENGTH_MAX);
+    query = malloc(sizeof(char) * query_length);
+
+    if (query == NULL) {
+      log_write(LOG_ERR, "ERROR: failed to allocate memory when building the postgresql grant query string while processing user '%s', group '%s', and database '%s'.\n", user_name, group_name, database_name);
+
+      PQfinish(connection);
+      free(connection_information);
+      connection_information = NULL;
+      return -1;
+    }
+
+    memset(query, 0, sizeof(char) * query_length);
+    snprintf(query, query_length, PSQL_GRANT, group_name, user_name);
+
+    {
+      PGresult *result = NULL;
+
+      result = PQexec(connection, query);
+
+      int status = PQresultStatus(result);
+
+      if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+        log_write(LOG_ERR, "ERROR: failed to process sql query '%s', reason (%u): %s.\n", query, status, PQerrorMessage(connection));
+
+        PQclear(result);
+        PQfinish(connection);
+
+        free(query);
+        free(connection_information);
+
+        result = NULL;
+        query = NULL;
+        connection_information = NULL;
+
+        return -1;
+      }
+
+      PQclear(result);
+      result = NULL;
+    }
+
+    free(query);
+    query = NULL;
+  }
+
+  if (connection_information != NULL) {
+    PQfinish(connection);
+    free(connection_information);
+  }
+
+  return 1;
+}
+
+/**
+ * Queries the name in the ldap server to see if it exists.
+ *
+ * @param const char *user_name
+ *   The user name to query in the ldap database.
+ *
+ * @return bool
+ *   1 on found, 0 on not found, and -1 on error.
+ */
+int does_name_exist_in_ldap(const char *user_name) {
+  int user_name_length = 0;
+  int ldap_name_length = 0;
+  int ldap_status = 0;
+  LDAP *ldap_settings = NULL;
+  char *ldap_name = NULL;
+
+  user_name_length = strnlen(user_name, PACKET_SIZE_INPUT);
+  ldap_name_length = user_name_length + LDAP_SEARCH_DN_LENGTH;
+
+  ldap_name = malloc(sizeof(char) * (ldap_name_length + 1));
+  if (ldap_name == NULL) {
+    log_write(LOG_ERR, "ERROR: failed to allocate memory when building the ldap user name for the user: '%s'.\n", user_name);
+    return -1;
+  }
+
+  memset(ldap_name, 0, sizeof(char) * (ldap_name_length + 1));
+  sprintf(ldap_name, LDAP_SEARCH_DN, user_name);
+
+
+  ldap_status = ldap_initialize(&ldap_settings, LDAP_SERVER);
+
+  if (ldap_status != LDAP_SUCCESS) {
+    log_write(LOG_ERR, "ERROR: failed to initialize ldap settings for the ldap server '%s' with the ldap name '%s' with the ldap error (%d): %s.\n", LDAP_SERVER, ldap_name, ldap_status, ldap_err2string(ldap_status));
+
+    if (ldap_name != NULL) {
+      free(ldap_name);
+    }
+    return -1;
+  }
+
+
+  // a bind is ldap's way of saying 'login' or 'authenticate', do no use string to search with bind.
+  {
+    int tries = 0;
+    for (; tries < LDAP_RETRY_BIND_RETRY; tries++) {
+      ldap_status = ldap_simple_bind_s(ldap_settings, "", "");
+
+      if (ldap_status == LDAP_SUCCESS) {
+        break;
+      }
+      else if (ldap_status == LDAP_SERVER_DOWN) {
+        if (tries + 1 < LDAP_RETRY_BIND_RETRY) {
+          continue;
+        }
+      }
+      else if (ldap_status == LDAP_TIMEOUT) {
+        if (tries + 1 < LDAP_RETRY_BIND_RETRY) {
+          continue;
+        }
+      }
+
+      log_write(LOG_ERR, "ERROR: failed to connect and bind to the ldap server '%s' with the ldap name '%s' with the ldap error (%d): %s\n", LDAP_SERVER, ldap_name, ldap_status, ldap_err2string(ldap_status));
+
+      if (ldap_name != NULL) {
+        free(ldap_name);
+      }
+      return -1;
+    }
+  }
+
+
+  // once bound, perform the search.
+  {
+    struct timeval ldap_timeout;
+    int ldap_sizelimit = 1;
+    int ldap_message_type = 0;
+    LDAPMessage *ldap_message = NULL;
+    int ldap_matched = 0;
+
+    memset(&ldap_timeout, 0, sizeof(struct timeval));
+    ldap_timeout.tv_sec = 0;
+    ldap_timeout.tv_usec = LDAP_RETRY_SEARCH_TIMEOUT;
+
+    {
+      int tries = 0;
+      for (; tries < LDAP_RETRY_SEARCH_RETRY; tries++) {
+        ldap_status = ldap_search_ext_s(ldap_settings, ldap_name, LDAP_SCOPE_BASE, NULL, NULL, 0, NULL, NULL, &ldap_timeout, ldap_sizelimit, &ldap_message);
+
+        ldap_message_type = ldap_msgtype(ldap_message);
+
+        if (ldap_status == LDAP_SUCCESS) {
+          ldap_matched = ldap_count_entries(ldap_settings, ldap_message);
+
+          // From manpage: "Note that res parameter of ldap_search_ext_s() and ldap_search_s() should be freed with ldap_msgfree() regardless of return value of these functions"
+          ldap_msgfree(ldap_message);
+
+          break;
+        }
+
+        // From manpage: "Note that res parameter of ldap_search_ext_s() and ldap_search_s() should be freed with ldap_msgfree() regardless of return value of these functions"
+        ldap_msgfree(ldap_message);
+
+        if (ldap_status == LDAP_SERVER_DOWN) {
+          if (tries + 1 < LDAP_RETRY_SEARCH_RETRY) {
+            continue;
+          }
+        }
+        else if (ldap_status == LDAP_TIMEOUT) {
+          if (tries + 1 < LDAP_RETRY_SEARCH_RETRY) {
+            continue;
+          }
+        }
+
+        log_write(LOG_ERR, "ERROR: failed to find '%s' on the ldap server '%s' with the ldap name '%s' with the ldap error (%d): %s\n", user_name, LDAP_SERVER, ldap_name, ldap_status, ldap_err2string(ldap_status));
+
+        if (ldap_settings != NULL) {
+          ldap_unbind(ldap_settings);
+        }
+
+        if (ldap_name != NULL) {
+          free(ldap_name);
+        }
+        return -1;
+      }
+    }
+
+    // Ldap is no longer needed.
+    if (ldap_settings != NULL) {
+      ldap_unbind(ldap_settings);
+    }
+
+    if (ldap_name != NULL) {
+      free(ldap_name);
+    }
+
+    if (ldap_matched == 0) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+/**
+ * Handles network connections.
+ *
+ * This is called by clone().
+ *
+ * @param void *argument
+ *   The data shared between the parent and cloned child.
+ *
+ * @see: clone()
+ */
+int handler_child(void *argument) {
+  // do no accept/allow signals in the child handler.
+  sigset_t signal_mask;
+  sigemptyset(&signal_mask);
+  sigprocmask(SIG_BLOCK, &signal_mask, NULL);
+
+  shared_data *shared;
+  shared = (shared_data *) argument;
+
+  //shared->pid_child = syscall(SYS_gettid);
+  shared->pid_child = getpid();
+
+  log_write(LOG_DEBUG, "DEBUG: after clone (child) pid = %u, child pid = %u, target socket id = %u\n", shared->pid_parent, shared->pid_child, shared->socket_id_target);
+
+  const unsigned structure_socket_length = sizeof(struct sockaddr_un);
+
+  #ifdef USE_NETWORK
+    {
+      // bind the socket to port.
+      struct addrinfo *port_information = NULL;
+      struct addrinfo port_setup;
+
+      memset(&port_setup, 0, sizeof(struct addrinfo));
+
+      port_setup.ai_family = INADDR_ANY;
+      port_setup.ai_socktype = SOCKET_TYPE;
+      port_setup.ai_flags = AI_PASSIVE;
+
+      {
+        char string_port[16];
+        memset(&string_port, 0, sizeof(char) * 16);
+
+        sprintf(string_port, "%u", shared->parameter_port);
+
+        int addressed = getaddrinfo(NULL, string_port, &port_setup, &port_information);
+        if (addressed != 0) {
+          log_write(LOG_ERR, "ERROR: failed to process the port '%u' using protocol '%u', 'socket id = '%i': error %i (%u).\n", shared->parameter_port, SOCKET_PROTOCOL, shared->pid_child, addressed, errno);
+
+          freeaddrinfo(port_information);
+          port_information = NULL;
+
+          // send SIGCHLD signal to parent process.
+          if (shared->pid_parent > 0) {
+            kill(shared->pid_parent, SIGCHLD);
+          }
+          return -1;
+        }
+      }
+
+      if (shared->socket_bound == 0) {
+        shared->socket_bound = bind(shared->socket_id_target, port_information->ai_addr, port_information->ai_addrlen);
+        if (shared->socket_bound < 0) {
+          log_write(LOG_ERR, "ERROR: failed to bind the port '%u' using protocol '%u', 'socket id = '%i': error %i (%u).\n", shared->parameter_port, SOCKET_PROTOCOL, shared->pid_child, shared->socket_bound, errno);
+          shared->socket_bound = 0;
+
+          freeaddrinfo(port_information);
+          port_information = NULL;
+
+          // send SIGQUIT signal to parent process.
+          if (shared->pid_parent > 0) {
+            kill(shared->pid_parent, SIGQUIT);
+          }
+          return -1;
+        }
+
+        shared->socket_bound = 1;
+      }
+
+      freeaddrinfo(port_information);
+      port_information = NULL;
+    }
+  #elif defined USE_SOCKET
+    {
+      // bind the socket to the shared.socket_path so that the
+      struct sockaddr_un socket_address;
+
+      memset(&socket_address, 0, structure_socket_length);
+      socket_address.sun_family = SOCKET_FAMILY;
+      strncpy(socket_address.sun_path, shared->socket_path, sizeof(socket_address.sun_path) - 1);
+
+      {
+        int bound = bind(shared->socket_id_target, (struct sockaddr *) &socket_address, structure_socket_length);
+        if (bound < 0) {
+          log_write(LOG_ERR, "ERROR: failed to bind the socket '%s' using protocol '%u', 'socket id = '%i'\n", shared->socket_path, SOCKET_PROTOCOL, shared->socket_id_target);
+
+          // send SIGQUIT signal to parent process.
+          if (shared->pid_parent > 0) {
+            kill(shared->pid_parent, SIGQUIT);
+          }
+          return -1;
+        }
+      }
+    }
+  #endif // USE_SOCKET
+
+  {
+    int listening = listen(shared->socket_id_target, SOCKET_BACKLOG);
+
+    if (listening < 0) {
+      #ifdef USE_NETWORK
+        log_write(LOG_ERR, "ERROR: failed to listen to the port '%u' using protocol '%u', 'socket id = '%i', error %i (%u).\n", shared->parameter_port, SOCKET_PROTOCOL, shared->pid_child, listening, errno);
+      #elif defined USE_SOCKET
+        log_write(LOG_ERR, "ERROR: failed to listen to the socket '%s' using protocol '%u', 'socket id = '%i', error %i (%u).\n", shared->socket_path, SOCKET_PROTOCOL, shared->pid_child, listening, errno);
+      #endif // USE_SOCKET
+
+      // send SIGQUIT signal to parent process.
+      if (shared->pid_parent > 0) {
+        kill(shared->pid_parent, SIGQUIT);
+      }
+      return -1;
+    }
+  }
+
+  int socket_error = 0;
+  socklen_t socket_error_length = 0;
+
+  int message_length = 0;
+  int i = 0;
+  int processed = 0;
+
+  socklen_t length = 0;
+  ssize_t sent = 0;
+
+  char buffer[PACKET_SIZE_INPUT];
+  char user_name[PACKET_SIZE_INPUT];
+  char *error_receive = ERROR_NONE;
+
+  struct sockaddr_un socket_client_address;
+
+  struct timeval timeout;
+  timeout.tv_sec = 0;
+  timeout.tv_usec = SOCKET_TIMEOUT;
+
+  while (1) {
+    length = structure_socket_length;
+    processed = 0;
+    error_receive = ERROR_NONE;
+
+    // make sure that socket_id_client is always closed before continuing.
+    if (shared->socket_id_client != 0) {
+      sent = send(shared->socket_id_client, ERROR_CLOSE, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+      close(shared->socket_id_client);
+      shared->socket_id_client = 0;
+    }
+
+    memset(&socket_client_address, 0, structure_socket_length);
+
+    shared->socket_id_client = accept(shared->socket_id_target, (struct sockaddr *) &socket_client_address, &length);
+
+    if (shared->socket_id_client < 0) {
+      #ifdef USE_NETWORK
+        log_write(LOG_ERR, "ERROR: failed to accept connections on the port '%u' using protocol '%u': error %i (%u).\n", shared->parameter_port, SOCKET_PROTOCOL, shared->socket_id_client, errno);
+      #elif defined USE_SOCKET
+        log_write(LOG_ERR, "ERROR: failed to accept connections on the socket '%s' using protocol '%u': error %i (%u).\n", shared->socket_path, SOCKET_PROTOCOL, shared->socket_id_client, errno);
+      #endif // USE_SOCKET
+
+      shared->socket_id_client = 0;
+
+      // send SIGQUIT signal to parent process.
+      if (shared->pid_parent > 0) {
+        kill(shared->pid_parent, SIGQUIT);
+      }
+
+      return -1;
+    }
+
+    memset(&buffer, 0, sizeof(char) * PACKET_SIZE_INPUT);
+    memset(&user_name, 0, sizeof(char) * PACKET_SIZE_INPUT);
+
+    // define a very short timeout for fast responses (both send and receive).
+    // the socket is expected to be a local connection, so it should be very fast.
+    setsockopt(shared->socket_id_client, PROTOCOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
+    setsockopt(shared->socket_id_client, PROTOCOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
+
+    // linger connection for at most 2 seconds to help properly close connections.
+    {
+      struct linger linger_value = {1, 2};
+      setsockopt(shared->socket_id_target, PROTOCOL_SOCKET, SO_LINGER, &linger_value, sizeof(struct linger));
+    }
+
+    // receive the user name from the client.
+    do {
+      error_receive = ERROR_NONE;
+      message_length = recv(shared->socket_id_client, buffer, PACKET_SIZE_INPUT, FLAGS_RECEIVE);
+
+      if (message_length == 0) {
+        // this happens on proper client connection termination.
+        close(shared->socket_id_client);
+        shared->socket_id_client = 0;
+        break;
+      }
+      else if (message_length < 0) {
+        // this is not working as expected, socket_error is 0 when something like EAGAIN is expected.
+        // the idea here is to detect a timeout and report ERROR_TIMEOUT instead of ERROR_READ.
+        // until this is figured out, assume that ERROR_READ is the same as ERROR_TIMEOUT.
+        // look into recvmsg().
+        //socket_error = 0;
+        //socket_error_length = sizeof(int);
+        //getsockopt(shared->socket_id_client, PROTOCOL_SOCKET, SO_ERROR, (void *) &socket_error, &socket_error_length);
+        send(shared->socket_id_client, ERROR_READ, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+        break;
+      }
+
+      // require a valid packet length.
+      if (message_length > PACKET_SIZE_INPUT) {
+        sent = send(shared->socket_id_client, ERROR_PACKET, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+        break;
+      }
+
+      // only allow the following ASCII characters in the user name (utf8 should be fine for all codes that match the ASCII table).
+      for (i = 0; i < message_length && processed < PACKET_SIZE_INPUT; i++) {
+        // if a NULL char is reached, then the packet is finished.
+        if (buffer[i] == 0) {
+          // force the loop to terminate without processing any more packet data.
+          processed = PACKET_SIZE_INPUT;
+          break;
+        }
+
+        if ((buffer[i] < 'a' || buffer[i] > 'z') && (buffer[i] < 'A' || buffer[i] > 'Z') && (buffer[i] < '0' || buffer[i] > '9')) {
+          if (buffer[i] != '-' && buffer[i] != '_') {
+            error_receive = ERROR_NAME;
+            break;
+          }
+        }
+
+        if (processed + 1 > PACKET_SIZE_INPUT) {
+          sent = send(shared->socket_id_client, ERROR_PACKET, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+          break;
+        }
+
+        user_name[processed] = buffer[i];
+        processed++;
+      } // for
+
+      if (error_receive != ERROR_NONE) {
+        sent = send(shared->socket_id_client, error_receive, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+        break;
+      }
+
+      // require processed to be populated in some manner at this point.
+      if (processed == 0) {
+        error_receive = ERROR_NAME;
+        break;
+      }
+    } while (processed < PACKET_SIZE_INPUT);
+
+    if (error_receive == ERROR_NONE) {
+      int ldap_name_exists = 0;
+      ldap_name_exists = does_name_exist_in_ldap(user_name);
+
+      if (ldap_name_exists < 0) {
+        sent = send(shared->socket_id_client, ERROR_LDAP, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+        shutdown(shared->socket_id_client, SHUT_RDWR);
+        shared->socket_id_client = 0;
+        continue;
+      }
+      else if (ldap_name_exists == 0) {
+        sent = send(shared->socket_id_client, ERROR_NAME, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+        shutdown(shared->socket_id_client, SHUT_RDWR);
+        shared->socket_id_client = 0;
+        continue;
+      }
+
+      {
+        int status = 0;
+        status = grant_role_in_database(user_name, shared->parameter_group, shared->parameter_database, shared->parameter_connect_name, shared->parameter_connect_password);
+
+        if (status < 0) {
+          sent = send(shared->socket_id_client, ERROR_SQL, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+          shutdown(shared->socket_id_client, SHUT_RDWR);
+          shared->socket_id_client = 0;
+          continue;
+        }
+      }
+    }
+
+    if (shared->socket_id_client > 0) {
+      // respond to the client for success or failure and then close the connection
+      if (processed > 0) {
+        sent = send(shared->socket_id_client, error_receive, PACKET_SIZE_OUTPUT, FLAGS_SEND);
+      }
+
+      shutdown(shared->socket_id_client, SHUT_RDWR);
+      shared->socket_id_client = 0;
+    }
+  } // while
+
+  // send SIGQUIT signal to parent process.
+  if (shared->pid_parent > 0) {
+    kill(shared->pid_parent, SIGQUIT);
+  }
+
+  return 0;
+}
+
+/**
+ * Handle command line arguments
+ *
+ * @param int argc
+ *   Total of command line arguments.
+ * @param char *argv[]
+ *   Array of command line argument strings.
+ * @param char *parameter_system
+ *   Name of the system, this value will be updated.
+ * @param char *parameter_group
+ *   Name of the group, this value will be updated.
+ * @param char *parameter_database
+ *   Name of the database, this value will be updated.
+ * @param char *parameter_connect_name
+ *   Name of the user to connect to the database as.
+ * @param char *parameter_connect_password
+ *   Password of the user to connect to the database as.
+ * @param int *parameter_port
+ *   Number of the port, this value will be updated.
+ *   This only appears when USE_NETWORK is defined.
+ *
+ * @return int
+ *   1 is returned on success, 0 on success but exit, and -1 on error.
+ */
+#ifdef USE_NETWORK
+  int populate_parameters(int argc, char *argv[], char *parameter_system, char *parameter_group, char *parameter_database, char *parameter_connect_name, char *parameter_connect_password, int *parameter_port) {
+#elif defined USE_SOCKET
+  int populate_parameters(int argc, char *argv[], char *parameter_system, char *parameter_group, char *parameter_database, char *parameter_connect_name, char *parameter_connect_password) {
+#endif // USE_SOCKET
+  {
+    int do_help = 0;
+    char *program_name = "(program_name)";
+
+    if (argc > 0) {
+      program_name = argv[0];
+    }
+
+    if (argc >= 2) {
+      int i = 0;
+
+      for (; i < argc; i++) {
+        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
+          do_help = 1;
+          break;
+        }
+      }
+    }
+
+    #ifdef USE_NETWORK
+      if (do_help == 0 && !(argc == 4 || argc == 5)) {
+        printf("ERROR: This program requires four or five arguments (with max lengths of %u) 'system name', 'group name', 'database name', 'listen port', example: %s fcs fcs_users fcs_database 125.\n", PARAMETER_LENGTH_MAX, program_name);
+        do_help = 2;
+      }
+    #elif defined USE_SOCKET
+      if (do_help == 0 && !(argc == 3 || argc == 4)) {
+        printf("ERROR: This program requires three or four arguments (with max lengths of %u) 'system name', 'group name', 'database name', example: %s fcs fcs_users fcs_database.\n", PARAMETER_LENGTH_MAX, program_name);
+        do_help = 2;
+      }
+    #endif // USE_SOCKET
+
+    if (do_help > 0) {
+      printf("\n");
+
+      #ifdef USE_NETWORK
+        printf("%s [ system name ] [ group name ] [ database name ] [ listen port ]\n", program_name);
+      #elif defined USE_SOCKET
+        printf("%s [ system name ] [ group name ] [ database name ]\n", program_name);
+      #endif // USE_SOCKET
+
+      printf("  [ system name ]    This argument is used as the name of the socket file, which will end in '.socket'.\n");
+      printf("  [ group name ]     This argument is used as the postgresql role to grant access for in the specified database.\n");
+      printf("  [ database name ]  This argument is used as the postgresql database.\n");
+
+      #ifdef USE_NETWORK
+        printf("  [ listen port ]    This argument is the port to listen on to accept user names.\n");
+      #endif // USE_NETWORK
+
+      printf("\n");
+      printf("Environment Variables:\n");
+      printf("  The following environment variables must be defined:\n");
+      printf("    %s      This parameter is used as the user to connect to the database as to perform operations.\n", ENVIRONMENT_CONNECT_USER);
+      printf("    %s  This parameter is used as the password for the user connecting to the database.\n", ENVIRONMENT_CONNECT_PASSWORD);
+
+      printf("\n");
+      printf("Notes:\n");
+      printf("  All names are limited to a max length of %u.\n", PARAMETER_LENGTH_MAX);
+      printf("  Only Alphanumeric values and '_' or '-' are allowed in the names.\n");
+      printf("  Specifically '_' and '-' are not allowed in the beginning or ending in a name.\n");
+      printf("  The username to connect to as for the current user is used. Make sure the current user has access to the database in question.\n");
+      printf("\n");
+
+      if (do_help == 2) {
+        return -1;
+      }
+
+      return 0;
+    }
+  }
+
+  // make sure the system name and role names are valid names, restricted to alphanumeric and - or _, but does not begin with - or _.
+  {
+    int i = 0;
+    int j = 0;
+
+
+    // process system name.
+    for (; i < PARAMETER_LENGTH_MAX; i++) {
+      if ((argv[1][i] >= 'a' || argv[1][i] <= 'z') || (argv[1][i] >= 'A' || argv[1][i] <= 'Z') || (argv[1][i] >= '0' || argv[1][i] <= '9')) {
+        parameter_system[j] = argv[1][i];
+        j++;
+        i++;
+        break;
+      }
+      else if (argv[1][i] == 0) {
+        break;
+      }
+      else {
+        printf("ERROR: an invalid character '%c' has been specified in the supplied system name '%s'.\n", argv[1][i], argv[1]);
+        return -1;
+      }
+    }
+
+    for (; i < PARAMETER_LENGTH_MAX; i++) {
+      if ((argv[1][i] >= 'a' || argv[1][i] <= 'z') || (argv[1][i] >= 'A' || argv[1][i] <= 'Z') || (argv[1][i] >= '0' || argv[1][i] <= '9')) {
+        parameter_system[j] = argv[1][i];
+        j++;
+      }
+      else if (argv[1][i] == '-' || argv[1][i] == '_') {
+        parameter_system[j] = argv[1][i];
+        j++;
+      }
+      else if (argv[1][i] == 0) {
+        break;
+      }
+      else {
+        printf("ERROR: an invalid character '%c' has been specified in the supplied system name '%s'.\n", argv[1][i], argv[1]);
+        return -1;
+      }
+    }
+
+    if (argv[1][j - 1] == '-' || argv[1][j - 1] == '_') {
+      printf("ERROR: an invalid character '%c' has been specified in the supplied system name '%s'.\n", argv[1][i], argv[1]);
+      return -1;
+    }
+
+    if (j == 0) {
+      printf("ERROR: system name must not be an empty string.\n", argv[3][i], argv[3]);
+      return -1;
+    }
+
+
+    // process group name.
+    for (i = 0, j = 0; i < PARAMETER_LENGTH_MAX; i++) {
+      if ((argv[2][i] >= 'a' || argv[2][i] <= 'z') || (argv[2][i] >= 'A' || argv[2][i] <= 'Z') || (argv[2][i] >= '0' || argv[2][i] <= '9')) {
+        parameter_group[j] = argv[2][i];
+        j++;
+        i++;
+        break;
+      }
+      else if (argv[2][i] == 0) {
+        break;
+      }
+      else {
+        printf("ERROR: an invalid character '%c' has been specified in the supplied group name '%s'.\n", argv[2][i], argv[2]);
+        return -1;
+      }
+    }
+
+    for (; i < PARAMETER_LENGTH_MAX; i++) {
+      if ((argv[2][i] >= 'a' || argv[2][i] <= 'z') || (argv[2][i] >= 'A' || argv[2][i] <= 'Z') || (argv[2][i] >= '0' || argv[2][i] <= '9')) {
+        parameter_group[j] = argv[2][i];
+        j++;
+      }
+      else if (argv[2][i] == '-' || argv[2][i] == '_') {
+        parameter_group[j] = argv[2][i];
+        j++;
+      }
+      else if (argv[2][i] == 0) {
+        break;
+      }
+      else {
+        printf("ERROR: an invalid character '%c' has been specified in the supplied group name '%s'.\n", argv[2][i], argv[2]);
+        return -1;
+      }
+    }
+
+    if (argv[2][j - 1] == '-' || argv[2][j - 1] == '_') {
+      printf("ERROR: an invalid character '%c' has been specified in the supplied group name '%s'.\n", argv[2][i], argv[2]);
+      return -1;
+    }
+
+    if (j == 0) {
+      printf("ERROR: group name must not be an empty string.\n", argv[3][i], argv[3]);
+      return -1;
+    }
+
+
+    // process database name.
+    for (i = 0, j = 0; i < PARAMETER_LENGTH_MAX; i++) {
+      if ((argv[3][i] >= 'a' || argv[3][i] <= 'z') || (argv[3][i] >= 'A' || argv[3][i] <= 'Z') || (argv[3][i] >= '0' || argv[3][i] <= '9')) {
+        parameter_database[j] = argv[3][i];
+        j++;
+        i++;
+        break;
+      }
+      else if (argv[3][i] == 0) {
+        break;
+      }
+      else {
+        printf("ERROR: an invalid character '%c' has been specified in the supplied database name '%s'.\n", argv[3][i], argv[3]);
+        return -1;
+      }
+    }
+
+    for (; i < PARAMETER_LENGTH_MAX; i++) {
+      if ((argv[3][i] >= 'a' || argv[3][i] <= 'z') || (argv[3][i] >= 'A' || argv[3][i] <= 'Z') || (argv[3][i] >= '0' || argv[3][i] <= '9')) {
+        parameter_database[j] = argv[3][i];
+        j++;
+      }
+      else if (argv[3][i] == '-' || argv[3][i] == '_') {
+        parameter_database[j] = argv[3][i];
+        j++;
+      }
+      else if (argv[3][i] == 0) {
+        break;
+      }
+      else {
+        printf("ERROR: an invalid character '%c' has been specified in the supplied database name '%s'.\n", argv[3][i], argv[3]);
+        return -1;
+      }
+    }
+
+    if (argv[3][j - 1] == '-' || argv[3][j - 1] == '_') {
+      printf("ERROR: an invalid character '%c' has been specified in the supplied database name '%s'.\n", argv[3][i], argv[3]);
+      return -1;
+    }
+
+    if (j == 0) {
+      printf("ERROR: database name must not be an empty string.\n", argv[3][i], argv[3]);
+      return -1;
+    }
+
+
+    #ifdef USE_NETWORK
+      // first sanitize the parameter and ensure that only numbers are allowed.
+      for (; i < PARAMETER_LENGTH_MAX; i++) {
+        if (argv[4][i] >= '0' || argv[4][i] <= '9') {
+          continue;
+        }
+        else if (argv[4][i] == 0) {
+          break;
+        }
+
+        printf("ERROR: an invalid character '%c' has been specified in the supplied local port '%s' (only numbers are allowed).\n", argv[4][i], argv[4]);
+        return -1;
+      }
+
+      *parameter_port = atoi(argv[4]);
+    #endif // USE_NETWORK
+
+    // process database connection user name.
+    {
+      char *user_name = getenv(ENVIRONMENT_CONNECT_USER);
+
+      if (user_name) {
+        size_t user_name_length = strnlen(user_name, ENVIRONMENT_MAX_CONNECT_USER);
+
+        for (i = 0, j = 0; i < user_name_length; i++) {
+          if ((user_name[i] >= 'a' || user_name[i] <= 'z') || (user_name[i] >= 'A' || user_name[i] <= 'Z') || (user_name[i] >= '0' || user_name[i] <= '9')) {
+            parameter_connect_name[j] = user_name[i];
+            j++;
+            i++;
+            break;
+          }
+          else if (user_name[i] == 0) {
+            break;
+          }
+          else {
+            printf("ERROR: an invalid character '%c' has been specified in the supplied database name '%s'.\n", user_name[i], user_name);
+            return -1;
+          }
+        }
+
+        for (; i < user_name_length; i++) {
+          if ((user_name[i] >= 'a' || user_name[i] <= 'z') || (user_name[i] >= 'A' || user_name[i] <= 'Z') || (user_name[i] >= '0' || user_name[i] <= '9')) {
+            parameter_connect_name[j] = user_name[i];
+            j++;
+          }
+          else if (user_name[i] == '_') {
+            parameter_connect_name[j] = user_name[i];
+            j++;
+          }
+          else if (user_name[i] == 0) {
+            break;
+          }
+          else {
+            printf("ERROR: an invalid character '%c' has been specified in the supplied database name '%s'.\n", user_name[i], user_name);
+            return -1;
+          }
+        }
+      }
+      else {
+        printf("ERROR: Failed to load required environment variable '%s'.\n", ENVIRONMENT_CONNECT_USER);
+        return -1;
+      }
+    }
+
+
+    // process database connection password.
+    {
+      char *password = getenv(ENVIRONMENT_CONNECT_PASSWORD);
+
+      if (password) {
+        size_t password_length = strnlen(password, ENVIRONMENT_MAX_CONNECT_PASSWORD);
+
+        if (password_length > 0) {
+          strncpy(parameter_connect_password, password, password_length);
+        }
+      }
+      else {
+        printf("ERROR: Failed to load required environment variable '%s'.\n", ENVIRONMENT_CONNECT_PASSWORD);
+        return -1;
+      }
+    }
+  }
+
+  return 1;
+}
+
+/**
+ * Main Function
+ *
+ * @param int argc
+ *   Total of command line arguments.
+ * @param char *argv[]
+ *   Array of command line argument strings.
+ *
+ * @return int
+ *   The return status of the program.
+ */
+int main(int argc, char *argv[]) {
+  shared_data shared;
+  char *stack = NULL;
+
+  memset(&shared, 0, sizeof(shared_data));
+
+  // this pid will change once daemonized, but until then record the current pid.
+  shared.pid_parent = getpid();
+
+  {
+    int populated = 0;
+
+    #ifdef USE_NETWORK
+      populated = populate_parameters(argc, argv, shared.parameter_system, shared.parameter_group, shared.parameter_database, shared.parameter_connect_name, shared.parameter_connect_password, &shared.parameter_port);
+    #elif defined USE_SOCKET
+      populated = populate_parameters(argc, argv, shared.parameter_system, shared.parameter_group, shared.parameter_database, shared.parameter_connect_name, shared.parameter_connect_password);
+    #endif // USE_SOCKET
+
+
+    if (populated == 0) {
+      MACRO_EXIT_STANDARD_1(shared, stack, 0);
+    }
+    else if (populated < 0) {
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+  }
+
+
+  // load the pid_path.
+  shared.pid_path = malloc(sizeof(char) * PATH_MAX);
+  if (shared.pid_path == NULL) {
+    log_write(LOG_ERR, "ERROR: failed to allocate memory for the pid path.\n");
+    MACRO_EXIT_STANDARD_1(shared, stack, -1);
+  }
+
+
+  // check to see if an existing pid file exists before doing anything else.
+  {
+    struct stat pid_stat;
+    int result_stat = 0;
+
+    memset(&pid_stat, 0, sizeof(struct stat));
+    result_stat = stat(shared.pid_path, &pid_stat);
+    if (result_stat > -1 || errno != ENOENT) {
+      if (result_stat > -1) {
+        printf("ERROR: the pid file already exists at '%s', this pid: %u.'\n", shared.pid_path, shared.pid_parent);
+      }
+      else {
+        printf("ERROR: while calling stat() on '%s', this pid: %u.'\n", shared.pid_path, shared.pid_parent);
+      }
+
+      // do not attempt to unlink the path, so manually free and reset before MACRO_EXIT_STANDARD_1() is called.
+      memset(shared.pid_path, 0, sizeof(char) * PATH_MAX);
+      free(shared.pid_path);
+      shared.pid_path = NULL;
+
+      memset(&pid_stat, 0, sizeof(struct stat));
+      result_stat = 0;
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+  }
+
+
+  #ifdef USE_NETWORK
+    shared.socket_id_target = socket(SOCKET_FAMILY, SOCKET_TYPE, SOCKET_PROTOCOL);
+
+    if (shared.socket_id_target < 0) {
+      printf("ERROR: failed to initiailize the port '%u' using protocol '%u': error %i (%u).'\n", shared.parameter_port, SOCKET_PROTOCOL, shared.socket_id_target, errno);
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+  #elif defined USE_SOCKET
+    {
+      int socket_path_length = SOCKET_PATH_LENGTH + 1;
+      socket_path_length += strnlen(shared.parameter_system, PARAMETER_LENGTH_MAX);
+      socket_path_length += strnlen(shared.parameter_group, PARAMETER_LENGTH_MAX);
+      socket_path_length *= sizeof(char);
+
+      shared.socket_path = malloc(socket_path_length);
+      if (shared.socket_path == NULL) {
+        printf("ERROR: failed to allocate enough memory for the socket path string.\n");
+        MACRO_EXIT_STANDARD_1(shared, stack, -1);
+      }
+
+      memset(shared.socket_path, 0, socket_path_length);
+      snprintf(shared.socket_path, socket_path_length, SOCKET_PATH, shared.parameter_system, shared.parameter_group);
+    }
+
+    // make sure that no file exists at shared.socket_path before attempt to create a socket.
+    {
+      struct stat file_stat;
+
+      if (stat(shared.socket_path, &file_stat) >= 0) {
+        printf("ERROR: failed to initiailize the socket '%s' because a file already exists at that path, exiting.\n", shared.socket_path);
+
+        if (shared.socket_path != NULL) {
+          free(shared.socket_path);
+          shared.socket_path = NULL;
+        }
+
+        MACRO_EXIT_STANDARD_1(shared, stack, -1);
+      }
+    }
+
+    shared.socket_id_target = socket(SOCKET_FAMILY, SOCKET_TYPE, SOCKET_PROTOCOL);
+
+    if (shared.socket_id_target < 0) {
+      printf("ERROR: failed to initiailize the socket '%s' using protocol '%u', 'socket id = '%i, exiting.'\n", shared.socket_path, SOCKET_PROTOCOL, shared.socket_id_target);
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+  #endif // USE_SOCKET
+
+
+  // now run the process in the background before cloning and before blocking for signals.
+  {
+    #ifdef DEBUG_ENABLED
+      int daemonized = daemon(0, 1);
+    #else
+      int daemonized = daemon(0, 0);
+    #endif // DEBUG_ENABLED
+
+    if (daemonized < 0) {
+      printf("ERROR: failed to daemonize, error: %i.\n", errno);
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+  }
+
+
+  // The stack must be malloced after the process has daemonized because daemon() calls fork().
+  stack = malloc(STACK_SIZE);
+  if (stack == NULL) {
+    log_write(LOG_ERR, "ERROR: failed to allocate the stack for cloning, error: %i.\n", errno);
+    MACRO_EXIT_STANDARD_1(shared, stack, -1);
+  }
+  memset(stack, 0, sizeof(char) * STACK_SIZE);
+
+
+  // create the pid file or fail if one already exists.
+  shared.pid_parent = getpid();
+  if (snprintf(shared.pid_path, sizeof(char) * PATH_MAX, PATH_PID, shared.parameter_system) < 0) {
+    log_write(LOG_ERR, "ERROR: failed to setup the pid string '%s' using system name '%s', this pid: %u.'\n", PATH_PID, shared.parameter_system, shared.pid_parent);
+    MACRO_EXIT_STANDARD_1(shared, stack, -1);
+  }
+
+  {
+    FILE *pid_file = NULL;
+
+    pid_file = fopen(shared.pid_path, "w");
+    if (pid_file <= 0) {
+      log_write(LOG_ERR, "ERROR: failed to create pid file '%s', this pid: %u.'\n", shared.pid_path, shared.pid_parent);
+      pid_file = NULL;
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+
+    if (fprintf(pid_file, "%u\n", shared.pid_parent) < 0) {
+      log_write(LOG_ERR, "ERROR: failed to create pid file '%s', this pid: %u.'\n", shared.pid_path, shared.pid_parent);
+      fclose(pid_file);
+      pid_file = NULL;
+      MACRO_EXIT_STANDARD_1(shared, stack, -1);
+    }
+
+    fclose(pid_file);
+    pid_file = NULL;
+  }
+
+
+  pid_t pid_child = clone(handler_child, stack + STACK_SIZE, FLAGS_CLONE, &shared);
+
+  if (pid_child < 0) {
+    log_write(LOG_ERR, "ERROR: failed to clone the process, error: %i.\n", errno);
+    MACRO_EXIT_STANDARD_1(shared, stack, -1);
+  }
+
+  // signal blocking is used to help the program safely quit on interrupt.
+  sigset_t signal_mask;
+  siginfo_t signal_information_parent;
+  //siginfo_t signal_information_child;
+  int signal_result = 0;
+  short signal_problem_count = 0;
+  int process_child = 0, process_wait = 0;
+
+  memset(&signal_mask, 0, sizeof(sigset_t));
+  memset(&signal_information_parent, 0, sizeof(siginfo_t));
+  //memset(&signal_information_child, 0, sizeof(siginfo_t));
+
+  // block signals.
+  sigemptyset(&signal_mask);
+  sigaddset(&signal_mask, SIGHUP);
+  sigaddset(&signal_mask, SIGINT);
+  sigaddset(&signal_mask, SIGQUIT);
+  sigaddset(&signal_mask, SIGTERM);
+  sigaddset(&signal_mask, SIGSEGV);
+  sigaddset(&signal_mask, SIGBUS);
+  sigaddset(&signal_mask, SIGILL);
+  sigaddset(&signal_mask, SIGFPE);
+  sigaddset(&signal_mask, SIGABRT);
+  sigaddset(&signal_mask, SIGPWR);
+  sigaddset(&signal_mask, SIGXCPU);
+  sigaddset(&signal_mask, SIGCHLD);
+
+  sigprocmask(SIG_BLOCK, &signal_mask, NULL);
+
+  // sit and wait for signals.
+  while(1) {
+    signal_result = sigwaitinfo(&signal_mask, &signal_information_parent);
+
+    if (signal_result < 0) {
+      if (errno == EAGAIN) {
+        // do nothing.
+        continue;
+      }
+      else if (errno != EINTR) {
+        log_write(LOG_ERR, "ERROR: sigwaitinfo() failed: %i.\n", errno);
+
+        signal_problem_count++;
+        if (signal_problem_count > PROBLEM_COUNT_MAX_SIGNAL_SIZE) {
+          log_write(LOG_ERR, "ERROR: max signal problem count has been reached, exiting.\n");
+          MACRO_EXIT_STANDARD_2(pid_child, shared, stack, -1);
+        }
+
+        continue;
+      }
+    }
+
+    signal_problem_count = 0;
+
+    if (signal_information_parent.si_signo == SIGHUP) {
+      // do nothing.
+    }
+    else if (signal_information_parent.si_signo == SIGINT || signal_information_parent.si_signo == SIGQUIT || signal_information_parent.si_signo == SIGTERM) {
+      MACRO_EXIT_STANDARD_2(pid_child, shared, stack, 0);
+    }
+    else if (signal_information_parent.si_signo == SIGSEGV || signal_information_parent.si_signo == SIGBUS || signal_information_parent.si_signo == SIGILL || signal_information_parent.si_signo == SIGFPE) {
+      MACRO_EXIT_STANDARD_2(pid_child, shared, stack, 0);
+    }
+    else if (signal_information_parent.si_signo == SIGABRT || signal_information_parent.si_signo == SIGIOT || signal_information_parent.si_signo == SIGPWR || signal_information_parent.si_signo == SIGXCPU) {
+      MACRO_EXIT_STANDARD_2(pid_child, shared, stack, 0);
+    }
+    else if (signal_information_parent.si_signo == SIGCHLD) {
+      // do nothing
+    }
+
+    memset(&signal_information_parent, 0, sizeof(siginfo_t));
+    continue;
+  }
+
+  // failsafe, but should not get here.
+  MACRO_EXIT_STANDARD_2(pid_child, shared, stack, 0);
+}
diff --git a/program/autocreate_ldap_accounts_in_postgresql/source/php/autocreate_ldap_accounts_in_postgresql-client.php b/program/autocreate_ldap_accounts_in_postgresql/source/php/autocreate_ldap_accounts_in_postgresql-client.php
new file mode 100644 (file)
index 0000000..fedbe58
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+  // This is an example client script for talking to the service.
+
+  error_reporting(E_ALL);
+
+  // command for executing this via a socket file.
+  #$socket_path = "/var/www/sockets/autocreate_ldap_accounts_in_postgresql/example/example_users.socket";
+  #$socket_family = AF_UNIX;
+  #$socket_port = 0;
+  #$socket_protocol = 0;
+
+  // command for executing via a network socket.
+  $socket_path = "example.com";
+  $socket_family = AF_INET;
+  $socket_port = 1234;
+  $socket_protocol = SOL_TCP;
+
+  $socket_type = SOCK_STREAM;
+
+  $packet_size_target = 63;
+  $packet_size_client = 1;
+
+
+  // open a client socket.
+  $socket = socket_create($socket_family, $socket_type, $socket_protocol);
+
+  if ($socket === FALSE) {
+    print("Something went wrong with socket_create().\n");
+    socket_close($socket);
+    return;
+  }
+
+  // connect to the socket.
+  $connected = socket_connect($socket, $socket_path, $socket_port);
+
+  if ($connected === FALSE) {
+    print("Something went wrong with socket_connect().\n");
+    socket_close($socket);
+    return;
+  }
+
+
+  // build packet for requesting that the user 'example' should be created.
+  $test_name = 'example';
+  $test_name_length = strlen($test_name);
+  $test_name_difference = $packet_size_target - $test_name_length;
+
+  if ($test_name_difference > 0) {
+    // the packet expects a packet to be NULL terminated or at most $packet_size_target.
+    $test_packet = pack('a' . $test_name_length . 'x' . $test_name_difference, $test_name);
+  }
+  else {
+    $test_packet = pack('a' . $test_name_length, $test_name);
+  }
+
+  print("Packet looks like: '$test_packet'\n");
+  $written = socket_write($socket, $test_packet, $packet_size_target);
+
+  if ($written === FALSE) {
+    print("Something went wrong with socket_write().\n");
+    socket_close($socket);
+    return;
+  }
+  elseif ($written == 0) {
+    print("Nothing was written to the socket using socket_write().\n");
+    socket_close($socket);
+    return;
+  }
+
+
+  // read the return result from the target socket.
+  $response = socket_read($socket, $packet_size_client);
+
+  if (!is_string($response) || strlen($response) == 0) {
+    print("Something went wrong with socket_read() and did not get a valid return from the socket.\n");
+    socket_close($socket);
+    return;
+  }
+
+  // an integer is expected to be returned by the socket.
+  $response_packet = unpack('C', $response);
+  $response_value = (int) $response_packet[1];
+
+  print("Target Socket Replied with = " . print_r($response_value, TRUE) . "\n");
+
+  // response codes as defined in the c source file:
+  //    0 = no problems detected.
+  //    1 = invalid user name, bad characters, or name too long.
+  //    2 = failed to connect to the ldap server and could not query the ldap name.
+  //    3 = user name not found in ldap database.
+  //    4 = failed to connect to the database.
+  //    5 = error returned while executing the SQL command.
+  //    6 = error occured while reading input from the user (such as via recv()).
+  //    7 = error occured while writing input from the user (such as via send()).
+  //    8 = the received packet is invalid, such as wrong length.
+  //   10 = connection timed out when reading or writing.
+  //   11 = the connection is being forced closed.
+  //   12 = the connection is closing because the service is quitting.
+
+
+  socket_close($socket);
diff --git a/program/sessionize_accounts/readme.txt b/program/sessionize_accounts/readme.txt
new file mode 100644 (file)
index 0000000..2cb885b
--- /dev/null
@@ -0,0 +1,24 @@
+Installation
+============
+This assumes that the /program/ paths are being used.
+
+Compile the source code:
+  gcc -g source/c/sessionize_ldap_accounts_in_postgresql.c -o /programs/bin/sessionize_ldap_accounts_in_postgresql
+
+Add and enable the init script:
+  cp -v source/bash/sessionize_ldap_accounts_in_postgresql.sh /etc/init.d/sessionize_ldap_accounts_in_postgresql
+  chkconfig --add sessionize_ldap_accounts_in_postgresql
+  chkconfig sessionize_ldap_accounts_in_postgresql on
+
+Configure the settings (assuming system called "example"):
+  mkdir -vp /programs/settings/sessionize_ldap_accounts_in_postgresql/
+  cp -v settings/{example,systems}.settings /programs/settings/sessionize_ldap_accounts_in_postgresql/
+
+Note: rename 'example.settings' to the name of the system as defined in 'systems.settings'.
+
+Start the service
+  service sessionize_ldap_accounts_in_postgresql start
+
+For most users, the /programs/ path needs to be changed to a custom path for your system.
+The source code and bash scripts will need to be updated with these hardcoded paths.
+
diff --git a/program/sessionize_accounts/settings/systems.settings b/program/sessionize_accounts/settings/systems.settings
new file mode 100644 (file)
index 0000000..b97ca19
--- /dev/null
@@ -0,0 +1,3 @@
+# fss-0000
+
+sa_systems example
diff --git a/program/sessionize_accounts/source/bash/sessionize_accounts.sh b/program/sessionize_accounts/source/bash/sessionize_accounts.sh
new file mode 100644 (file)
index 0000000..e230244
--- /dev/null
@@ -0,0 +1,421 @@
+#!/bin/bash
+#
+# sessionize_accounts      Helper service for handling account sessions, namely password storage/retrieval.
+#
+# chkconfig: 345 40 60
+# description: Provide a per-user, per-ip_address, per-session storage of passwords for users.
+
+### BEGIN INIT INFO
+# Provides: sessionize_accounts
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Default-Start: 3 4 5
+# Default-Stop: 0 1 2 6
+# Short-Description: Provides session storage of usernames and passwords on a per ip-address basis.
+# Description: Provides session storage of usernames and passwords on a per ip-address basis.
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+main() {
+  local process_owner=
+  local process_group="apache"
+  local path_programs="/programs/"
+  local path_service="/usr/local/bin/php ${path_programs}bin/sessionize_accounts-server.php"
+  local path_settings="${path_programs}settings/sessionize_accounts/"
+  local path_systems="${path_settings}systems.settings"
+  local path_pids="/var/run/sessionize_accounts/"
+  local path_socket_directory="/var/www/sockets/sessionize_accounts/"
+  local parameter_system=$2
+  local sa_systems=
+  local i=
+  local j=
+
+  if [[ ! -f $path_systems ]] ; then
+    echo "No valid path_systems file defined at: $path_systems"
+    exit -1
+  fi
+
+  if [[ ! -d $path_pids ]] ; then
+    mkdir -p $path_pids
+  fi
+
+  if [[ $process_owner != "" ]] ; then
+    chown $process_owner $path_pids
+  fi
+
+  sa_systems=$(grep -o '^sa_systems[[:space:]][[:space:]]*.*$' $path_systems | sed -e 's|^sa_systems[[:space:]][[:space:]]*||')
+
+  if [[ $sa_systems == "" ]] ; then
+    echo "No valid systems defined by setting 'sa_systems' in file: $path_systems"
+    exit -1
+  fi
+
+  if [[ $parameter_system != "" ]] ; then
+    j=$sa_systems
+    sa_systems=
+
+    for i in $j ; do
+      if [[ $i == $parameter_system ]] ; then
+        sa_systems=$i
+        break;
+      fi
+    done
+
+    i=
+    j=
+
+    if [[ $sa_systems == "" ]] ; then
+      echo "System '$parameter_system' is not a valid system defined by setting 'sa_systems' in file: $path_systems"
+      exit -1
+    fi
+  fi
+
+  # system-specific settings are not needed by this script.
+  #j=$sa_systems
+  #sa_systems=
+  #for i in $j ; do
+  #  if [[ -f $path_settings${i}.settings ]] ; then
+  #    sa_systems="$sa_systems$i "
+  #  else
+  #    echo "Skipping system '$i' because it does not have a settings file defined here: '$path_settings${i}.settings'"
+  #  fi
+  #done
+
+  i=
+  j=
+
+  case "$1" in
+    start)
+      start
+      ;;
+    stop)
+      stop
+      ;;
+    restart)
+      restart
+      ;;
+    status)
+      status
+      ;;
+    *)
+      echo "Usage: sessionize_accounts {start|stop|restart|status}"
+      return 2
+  esac
+
+  return $?
+}
+
+start() {
+  local sa_system=
+  local result=
+  local any_success=0
+  local any_failure=0
+
+  for sa_system in $sa_systems ; do
+    load_system_settings
+    check_pid
+
+    if [[ $result -eq -1 ]] ; then
+      continue
+    elif [[ $result -gt 0 ]] ; then
+      echo "Not starting process for $sa_system, it is already running with pid=$pid."
+      continue
+    fi
+
+    start_command
+
+    if [[ $result -eq 0 ]] ; then
+      wait_pid
+
+      if [[ $result -eq 0 ]] ; then
+        get_pid
+      fi
+
+      if [[ $pid == "" ]] ; then
+        echo "Started process for $sa_system but was unable to determine pid, command: $path_service $sa_system."
+      else
+        echo "Successfully started process for $sa_system, pid=$pid, command: $path_service $sa_system."
+      fi
+    fi
+  done
+
+  if [[ $any_success -ne 0 || $any_failure -eq 1 ]] ; then
+    exit -1
+  fi
+
+  return 0
+}
+
+stop() {
+  local sa_system=
+  local result=
+  local any_success=0
+  local any_failure=0
+  local original_pid=
+
+  for sa_system in $sa_systems ; do
+    load_system_settings
+    get_pid
+
+    if [[ $pid == "" ]] ; then
+      continue
+    fi
+
+    stop_command
+
+    if [[ $result -eq 0 ]] ; then
+      echo "Successfully stopped process for $sa_system, pid=$pid."
+    fi
+  done
+
+  if [[ $any_success -ne 0 || $any_failure -eq 1 ]] ; then
+    exit -1
+  fi
+
+  return 0
+}
+
+restart() {
+  local sa_system=
+  local result=
+  local any_success=0
+  local any_failure=0
+  local original_pid=
+
+  for sa_system in $sa_systems ; do
+    load_system_settings
+    check_pid
+
+    if [[ $result -lt 0 ]] ; then
+      continue
+    elif [[ $result -gt 0 ]] ; then
+      stop_command
+
+      if [[ $result -ne 0 ]] ; then
+        continue
+      fi
+
+      original_pid=$pid
+      check_pid
+
+      if [[ $result -eq -2 ]] ; then
+        echo "Successfully stopped process for $sa_system, pid=$original_pid."
+      else
+        echo "Sent stop command for $sa_system, pid=$original_pid, but pid file ($pid_file) still exists (cannot start process, skipping)."
+        continue
+      fi
+    fi
+
+    start_command
+
+    if [[ $result -eq 0 ]] ; then
+      wait_pid
+
+      if [[ $result -eq 0 ]] ; then
+        get_pid
+      fi
+
+      if [[ $pid == "" ]] ; then
+        echo "Started process for $sa_system but was unable to determine pid, command: $path_service $sa_system."
+      else
+        echo "Successfully started process for $sa_system, pid=$pid, command: $path_service $sa_system."
+      fi
+    fi
+  done
+
+  if [[ $any_success -ne 0 ]] ; then
+    exit -1
+  fi
+
+  return 0
+}
+
+status() {
+  local sa_system=
+  local pid_file=
+  local pid=
+  local result=
+
+  for sa_system in $sa_systems ; do
+    load_system_settings
+    get_pid
+
+    if [[ $pid == "" ]] ; then
+      continue
+    fi
+
+    echo "The system '$sa_system' appears to be running as process $pid."
+  done
+
+  return 0
+}
+
+load_system_settings() {
+  local path_system=$path_settings${sa_system}.settings
+
+  # nothing to load
+}
+
+start_command() {
+  # guarantee that all directories in the socket file's path exist.
+  if [[ ! -d $path_socket_directory/$sa_system/ ]] ; then
+    mkdir -p $path_socket_directory/$sa_system/
+    chown $process_owner $path_socket_directory/$sa_system/
+  fi
+
+  # guarantee that the '$process_group' has read and execute only access to the directory, deny world access.
+  chgrp $process_group $path_socket_directory/$sa_system/
+  chmod u+rwx,g+rx,o-rwx $path_socket_directory/$sa_system/
+
+  # make sure no session socket already exists before starting.
+  # this assumes that the pid file has already been checked and therefore no existing process is using the socket file (aka: assume this is a stale socket file).
+  if [[ -e $path_socket_directory/$sa_system/sessions.socket ]] ; then
+    rm -f $path_socket_directory/$sa_system/sessions.socket
+  fi
+
+  if [[ $process_owner == "" ]] ; then
+    $path_service "$sa_system"
+    result=$?
+  else
+    su $process_owner -l -c "$path_service \"$sa_system\""
+    result=$?
+  fi
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Failed to start process, command: $path_service \"$sa_system\"."
+    any_failure=1
+  else
+    any_success=1
+  fi
+}
+
+stop_command() {
+  # -3 = SIGQUIT, -15 = SIGTERM, -9 = SIGKILL
+  kill -3 $pid
+  result=$?
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Signal to quit failed, command: kill -3 $pid."
+    any_failure=1
+  else
+    any_success=1
+
+    # pause and give the process time to close down.
+    sleep 0.1
+
+    # cleanup the session socket ad pid file.
+    rm -f $path_socket_directory/$sa_system/sessions.socket
+    rm -f $pid_file
+  fi
+}
+
+wait_pid() {
+  local k=
+  local max=32
+
+  pid=
+  pid_file=$path_pids$sa_system.pid
+  result=-1
+
+  # the started process will go into the background, so wait until the pid file is created, but only wait for so long.
+  let k=0
+  while [[ $k -lt $max ]] ; do
+    if [[ -f $pid_file ]] ; then
+      result=0
+      break
+    fi
+
+    sleep 0.05
+
+    let k=$k+1
+  done
+
+  return 0
+}
+
+get_pid() {
+  pid=
+  pid_file=$path_pids$sa_system.pid
+
+  if [[ ! -f $pid_file ]] ; then
+    echo "No pid file ($pid_file) found for system '$sa_system', it must not be running."
+    return 0
+  fi
+
+  pid=$(cat $pid_file)
+  result=$?
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Failed to read the pid file ($pid_file) for system '$sa_system', command: cat $pid_file."
+    pid=
+    return 0
+  fi
+
+  if [[ $pid == "" ]] ; then
+    echo "The pid file ($pid_file) for system '$sa_system' is empty."
+    pid=
+    return 0
+  fi
+
+  result=$(ps --no-headers -o pid -p $pid)
+  if [[ $? -lt 0 ]] ; then
+    echo "An error occured while searching for the process for system '$sa_system', command: ps --no-headers -o pid -p $pid."
+    pid=
+    return 0
+  fi
+
+  if [[ $result == "" ]] ; then
+    echo "No process $pid was found for the system '$sa_system', the pid file might be stale or inaccurate."
+    pid=
+    return 0
+  fi
+}
+
+check_pid() {
+  pid=
+  pid_file=$path_pids$sa_system.pid
+
+  if [[ ! -f $pid_file ]] ; then
+    result=-2
+    return 0
+  fi
+
+  pid=$(cat $pid_file)
+  result=$?
+
+  if [[ $result -ne 0 ]] ; then
+    echo "Failed to read the pid file ($pid_file) for system '$sa_system', command: cat $pid_file."
+    result=-1
+    return 0
+  fi
+
+  if [[ $pid == "" ]] ; then
+    result=0
+    return 0
+  fi
+
+  result=$(ps --no-headers -o pid -p $pid)
+  if [[ $? -lt 0 ]] ; then
+    echo "An error occured while searching for the process for system '$sa_system', command: ps --no-headers -o pid -p $pid."
+    result=-1
+    return 0
+  fi
+
+  if [[ $result == "" ]] ; then
+    result=
+
+    # the pid file is invalid, so remove the pid file.
+    rm -f $pid_file
+
+    # return 0 to allow for starting a new process.
+    result=0
+    return 0
+  fi
+
+  result=1
+  return 0
+}
+
+main "$1" "$2"
diff --git a/program/sessionize_accounts/source/php/sessionize_accounts-client.php b/program/sessionize_accounts/source/php/sessionize_accounts-client.php
new file mode 100644 (file)
index 0000000..0b2a315
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+  // This is an example client script for talking to the service.
+  define('PACKET_MAX_LENGTH', 4096);
+
+  error_reporting(E_ALL);
+
+  // command for executing this via a socket file.
+  $system_name = 'example';
+  $socket_path = '/var/www/sockets/sessionize_accounts/' . $system_name . '/sessions.socket';
+  $socket_family = AF_UNIX;
+  $socket_port = 0;
+  $socket_protocol = 0;
+
+  $socket_type = SOCK_STREAM;
+
+
+  // open a client socket.
+  $socket = socket_create($socket_family, $socket_type, $socket_protocol);
+
+  if ($socket === FALSE) {
+    print("Session Request: Something went wrong with socket_create().\n");
+    socket_close($socket);
+    return;
+  }
+
+  // connect to the socket.
+  $connected = socket_connect($socket, $socket_path, $socket_port);
+
+  if ($connected === FALSE) {
+    print("Session Request: Something went wrong with socket_connect().\n");
+    socket_close($socket);
+    return;
+  }
+
+
+  // build session request packet
+  $request_array = array(
+    'name' => 'some_user',
+    'ip' => '127.0.0.1',
+    'password' => 'This is weak\' password.',
+  );
+
+  $request_json = json_encode($request_array);
+  $written = socket_write($socket, $request_json);
+
+  if ($written === FALSE) {
+    print("Session Request: Something went wrong with socket_write().\n");
+    socket_close($socket);
+    return;
+  }
+  elseif ($written == 0) {
+    print("Session Request: Nothing was written to the socket using socket_write().\n");
+    socket_close($socket);
+    return;
+  }
+
+  // read the return result from the target socket.
+  $response = socket_read($socket, PACKET_MAX_LENGTH);
+
+  if (!is_string($response) || strlen($response) == 0) {
+    print("Session Request: Something went wrong with socket_read() and did not get a valid return from the socket.\n");
+    socket_close($socket);
+    return;
+  }
+
+  // an array is expected to be returned by the socket.
+  $response_array = json_decode($response, TRUE);
+  if (!is_array($response_array['result']) || empty($response_array['result']['session_id']) || !is_string($response_array['result']['session_id'])) {
+    print("Session Request: no valid session id was returned.\n");
+    socket_close($socket);
+    return;
+  }
+
+  socket_close($socket);
+
+  $session_id = $response_array['result']['session_id'];
+  $expire = $response_array['result']['expire'];
+  $max = $response_array['result']['max'];
+
+
+
+  // build password request packet using the returned session id.
+  $socket = socket_create($socket_family, $socket_type, $socket_protocol);
+
+  if ($socket === FALSE) {
+    print("Password Request: Something went wrong with socket_create().\n");
+    socket_close($socket);
+    return;
+  }
+
+  $connected = socket_connect($socket, $socket_path, $socket_port);
+
+  if ($connected === FALSE) {
+    print("Password Request: Something went wrong with socket_connect().\n");
+    socket_close($socket);
+    return;
+  }
+
+  $request_array = array(
+    'ip' => '127.0.0.1',
+    'session_id' => $session_id,
+  );
+
+  $request_json = json_encode($request_array);
+  $written = socket_write($socket, $request_json);
+
+  if ($written === FALSE) {
+    print("Password Request: Something went wrong with socket_write().\n");
+    socket_close($socket);
+    return;
+  }
+  elseif ($written == 0) {
+    print("Password Request: Nothing was written to the socket using socket_write().\n");
+    socket_close($socket);
+    return;
+  }
+
+  // read the return result from the target socket.
+  $response = socket_read($socket, PACKET_MAX_LENGTH);
+
+  if (!is_string($response) || strlen($response) == 0) {
+    print("Password Request: Something went wrong with socket_read() and did not get a valid return from the socket.\n");
+    socket_close($socket);
+    return;
+  }
+
+  // an integer is expected to be returned by the socket.
+  $response_array = json_decode($response, TRUE);
+
+  $password = $response_array['result'];
+  if (is_bool($password)) {
+    print("Password Request: no password was returned.\n");
+    socket_close($socket);
+    return;
+  }
+
+
+  socket_close($socket);
diff --git a/program/sessionize_accounts/source/php/sessionize_accounts-server.php b/program/sessionize_accounts/source/php/sessionize_accounts-server.php
new file mode 100644 (file)
index 0000000..983a237
--- /dev/null
@@ -0,0 +1,804 @@
+<?php
+/**
+ * Helper program for maintaining account sessions between PHP instances.
+ *
+ * This is necessary for accessing postgresql via ldap without requiring the user to enter in a new password every time a page is refreshed.
+ * For security reasons, passwords are only stored in memory and that memory is freed as soon as the session expires.
+ *
+ * The timezone values used and returned will always be in UTC.
+ *
+ * @todo: provide the ability to save the current session information to the disk via a json array and encrypt it.
+ *        On load, this file can be decrypted to load on startup.
+ *        This allows for restarting the service without losing session information.
+ *        This must be manually done by some user so that the password is never written to the disk.
+ *
+ * The program expects the following parameters: [system_name]
+ *
+ * This packet uses json to transmit data to and from the program.
+ *
+ *   A response packet will contain a json string that stores an array with the following keys:
+ *     error:  FALSE on no error, an array containing error details on error.
+ *     result: An array is returned for valid save requests.
+ *             An array is returned for valid load requests.
+ *             TRUE is returned for valid flush requests.
+ *             TRUE is returned for valid close requests.
+ *             FALSE is returned in all other cases.
+ *
+ *     Valid save response array keys:
+ *     - session_id: The id of the session.
+ *     - expire: The session expiration timeout (potentially reduced by hard-limits).
+ *     - max: The max session expiration timeout (potentially reduced by hard-limits).
+ *     - interval: the idle timeout interval, in seconds.
+ *
+ *     Valid load response array keys:
+ *     - name: The user name associated with the session.
+ *     - id_user: The numeric id of the user.
+ *     - password: The password associated with the session.
+ *     - expire: The session expiration timeout.
+ *     - max: The max session expiration timeout.
+ *     - interval: the idle timeout interval, in seconds.
+ *     - settings: an array containing any additional settings associated with the session.
+ *
+ * A save request packet has the following keys:
+ *   - name: The username to associate with the session.
+ *   - id_user: The numeric id of the user associated with this session.
+ *   - ip: The ip address to associate with the session. These sessions are ip-address specific.
+ *   - password: The password to associated with the session (must be defined, but may be NULL).
+ *   - expire: Request idle timeout interval, in seconds (this is a soft limit and cannot exceed the hard limit defined by this server).
+ *   - max: The maximum amount of time a session is allowed to exist, in seconds (this is a soft limit and cannot exceed the hard limit defined by this server).
+ *   - settings: (optional) an array containing additional information that may be pertenent to save with the session information.
+ *
+ * A load request packet has the following keys:
+ *   - ip: The ip address associated with the session.
+ *   - session_id: The session id to load the password from.
+ *
+ * A close request packet has the following keys:
+ *   - ip: The ip address associated with the session.
+ *   - session_id: The session id to load the password from.
+ *   - close: A boolean must be set to TRUE.
+ *
+ * A flush request packet (for manual flushing of expired data) has the following keys:
+ *   flush: Must be set to TRUE.
+ *
+ *   The response will be TRUE on success, FALSE otherwise.
+ *
+ * Copyright Kevin Day, lgpl v2.1 or later.
+ */
+
+// This program needs to run indefinetely.
+ini_set('max_execution_time', 0);
+ini_set('memory_limit', '192M');
+
+error_reporting(E_ALL);
+
+define('SESSION_RANDOM_BYTES', 512);
+//define('SESSION_ID_MAX', 1536);
+define('SOCKET_BACKLOG', 1024);
+define('SOCKET_TIMEOUT_SECONDS', 0);
+define('SOCKET_TIMEOUT_MICROSECONDS', 40000); // 0.04 seconds.
+define('PACKET_MAX_LENGTH', 4096);
+
+define('INTERVAL_TIMEOUT_HARD_EXPIRE', 172800); // 48 hours.
+define('INTERVAL_TIMEOUT_HARD_MAX', 1382400); // 16 days.
+
+/**
+ * Main function is self-contain to prevent leakage.
+ *
+ * @param int $argc
+ *   The number of arguments passed through the command line.
+ * @param array $argv
+ *   An array of command line arguments.
+ *
+ * @return bool
+ *   TRUE on success, FALSE otherwise.
+ */
+function main($argc, $argv) {
+  if (!isset($argv[1]) || !is_string($argv[1]) || empty($argv[1])) {
+    print("ERROR: This program requires exactly 1 argument: 'system_name'.\n");
+    return FALSE;
+  }
+
+  // long running processes should have PHP's garbage collection enabled, otherwise no cleanup ever happens.
+  gc_enable();
+
+  $pid_path_directory = '/var/run/sessionize_accounts/';
+  $pid_path = $pid_path_directory . $argv[1] . '.pid';
+  $socket_path = '/var/www/sockets/sessionize_accounts/' . $argv[1] . '/sessions.socket';
+  $socket_family = AF_UNIX;
+  $socket_port = 0;
+  $socket_protocol = 0;
+  $socket_type = SOCK_STREAM;
+
+  if (file_exists($socket_path)) {
+    print("ERROR: The socket file '$socket_path' already exists.\n");
+    return FALSE;
+  }
+
+  // Used as an unproven attempt to clear passwords from memory before delete in hopes to avoid security issues inherit in a garbage collector.
+  $cleartext = str_repeat(' ', 2048);
+
+  // open a client socket.
+  $socket = socket_create($socket_family, $socket_type, $socket_protocol);
+
+  if ($socket === FALSE) {
+    print("ERROR: Something went wrong with socket_create().\n");
+    return FALSE;
+  }
+
+  $bound = socket_bind($socket, $socket_path);
+
+  if (socket_listen($socket, SOCKET_BACKLOG) === FALSE) {
+    print("ERROR: Something went wrong with socket_listen().\n");
+    socket_close($socket);
+    unlink($socket_path);
+    return FALSE;
+  }
+
+  if (file_exists($pid_path)) {
+    print("ERROR: The pid file '$pid_path' already exists.\n");
+    socket_close($socket);
+    unlink($socket_path);
+    return FALSE;
+  }
+
+  if (!is_dir($pid_path_directory)) {
+    print("ERROR: The pid path directory '$pid_path_directory' does not exist or is not a directory.\n");
+    socket_close($socket);
+    unlink($socket_path);
+    return FALSE;
+  }
+
+  // check to see if the pid file can be written to by writing the current pid.
+  if (file_put_contents($pid_path, getmypid()) === FALSE) {
+    print("ERROR: Cannot write to the pid file '$pid_path'.\n");
+    socket_close($socket);
+    unlink($socket_path);
+    return FALSE;
+  }
+
+  unlink($pid_path);
+
+
+  // background the process by forking and making the parent quit after spawning a child process.
+  $forked = pcntl_fork();
+  if ($forked == -1) {
+    print("ERROR: failed to fork process, could not go into background.\n");
+    return FALSE;
+  }
+
+  if ($forked) {
+    return TRUE;
+  }
+
+  // force the timezone to always be in UTC.
+  date_default_timezone_set('UTC');
+
+
+  // immediately cleanup any unused memory to keep the footprint as small as possible before the program really does anything.
+  gc_collect_cycles();
+
+
+  // save the pid file at the pid path.
+  file_put_contents($pid_path, getmypid());
+
+  $database = array();
+  $timeouts = array();
+
+
+  // listening for connections indefinetely.
+  do {
+    $client_socket = socket_accept($socket);
+    if ($client_socket === FALSE) {
+      print("socket_accept() failed: reason: " . socket_strerror(socket_last_error($socket)) . "\n");
+
+      unset($client_socket);
+      continue;
+    }
+
+    socket_set_option($client_socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => SOCKET_TIMEOUT_SECONDS, 'usec' => SOCKET_TIMEOUT_MICROSECONDS));
+
+    $response = array(
+      'error' => FALSE,
+      'result' => FALSE,
+    );
+
+    $encoded_packet = socket_read($client_socket, PACKET_MAX_LENGTH);
+
+    if ($encoded_packet === FALSE) {
+      print("socket_read() failed: reason: " . socket_strerror(socket_last_error($client_socket)) . "\n");
+
+      socket_close($client_socket);
+      unset($client_socket);
+      unset($encoded_packet);
+      continue;
+    }
+
+    if (!is_string($encoded_packet)) {
+      $response['error'] = array(
+        'target' => 'encoded_packet',
+        'message' => "No valid encoded packet was specified. It must be a valid json string.",
+      );
+
+      socket_write($client_socket, json_encode($response));
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+
+    $decoded_packet = json_decode($encoded_packet, TRUE);
+
+    if (!is_array($decoded_packet) || empty($decoded_packet)) {
+      $response['error'] = array(
+        'target' => 'decoded_packet',
+        'message' => "No valid decoded packet was specified. It must be a valid, non-empty, array.",
+      );
+
+      socket_write($client_socket, json_encode($response));
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+
+    // support manually sending custom packets to periodically flush expired sessions (usefull for cron jobs).
+    if (array_key_exists('flush', $decoded_packet)) {
+      if (isset($decoded_packet['flush']) !== TRUE) {
+        $not_found_error = array(
+          'target' => 'decoded_packet[flush]',
+          'message' => "Invalid flush value provided.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      if (count($decoded_packet) > 1) {
+        $not_found_error = array(
+          'target' => 'decoded_packet[*]',
+          'message' => "Too many values provided, only the flush parameter is allowed.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      $response['result'] = process_expired_sessions($database, $timeouts, $cleartext);
+      if ($response['result'] === FALSE) {
+        $response['error'] = array(
+          'target' => 'failure',
+          'message' => "Failed to flush the expired sessions.",
+        );
+      }
+
+      socket_write($client_socket, json_encode($response));
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+
+    if (!isset($decoded_packet['ip']) || !is_string($decoded_packet['ip']) || strlen($decoded_packet['ip']) == 0 || ip2long($decoded_packet['ip']) === FALSE) {
+      $response['error'] = array(
+        'target' => 'decoded_packet[ip]',
+        'message' => "No valid ip address was specified. A valid, non-empty, ip address string must be provided.",
+      );
+
+      socket_write($client_socket, json_encode($response));
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+
+
+    // support closing sessions before they expire.
+    if (array_key_exists('close', $decoded_packet)) {
+      if (isset($decoded_packet['close']) !== TRUE) {
+        $not_found_error = array(
+          'target' => 'decoded_packet[close]',
+          'message' => "Invalid close value provided.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      if (isset($decoded_packet['session_id']) && strlen($decoded_packet['session_id']) > 0) {
+        $not_found_error = array(
+          'target' => 'decoded_packet[session_id]',
+          'message' => "No valid session was provided.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      if (count($decoded_packet) > 3) {
+        $not_found_error = array(
+          'target' => 'decoded_packet[*]',
+          'message' => "Too many values provided, only the session_id, ip, and close parameters are allowed.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      // provide an error, but specifically do not give details about which field is invalid for security reasons.
+      if (!isset($database['sessions'][$decoded_packet['ip']][$decoded_packet['session_id']])) {
+        $response['error'] = array(
+          'target' => 'not_found',
+          'message' => "No valid session was found associated with the specified user name and ip address.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      $response['result'] = expire_session($database, $timeouts, $decoded_packet['session_id'], $decoded_packet['ip']);
+      if ($response['result'] === FALSE) {
+        $response['error'] = array(
+          'target' => 'failure',
+          'message' => "Failed to close the session by the given session id and ip address.",
+        );
+      }
+
+      socket_write($client_socket, json_encode($response));
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+
+
+    // expire sessions now so that expired session do not get included in the retrieval process.
+    process_expired_sessions($database, $timeouts, $cleartext);
+
+
+    // retrieve password.
+    if (isset($decoded_packet['session_id']) && strlen($decoded_packet['session_id']) > 0) {
+      if (!isset($database['sessions'][$decoded_packet['ip']][$decoded_packet['session_id']])) {
+        $response['error'] = array(
+          'target' => 'not_found',
+          'message' => "No valid session was found associated with the specified user name and ip address.",
+        );
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+
+      // a password request shows that this connection is still active, so extend the timestamp.
+      $db_session = &$database['sessions'][$decoded_packet['ip']][$decoded_packet['session_id']];
+      $unique_id = $db_session['timeouts']['id'];
+
+      if (isset($db_session['timeouts']['expire']) && isset($db_session['timeouts']['max']) && $db_session['timeouts']['expire'] < $db_session['timeouts']['max']) {
+        $stamp_old = $db_session['timeouts']['expire'];
+        $stamp_new = strtotime('+' . $db_session['timeouts']['interval'] . ' seconds');
+        if ($stamp_new > $db_session['timeouts']['max']) {
+          $stamp_new = $db_session['timeouts']['max'];
+        }
+
+        if (isset($timeouts[$stamp_old]) && $stamp_old != $stamp_new) {
+          $db_session['timeouts']['expire'] = $stamp_new;
+          $new_key = !array_key_exists($stamp_new, $timeouts);
+
+          $timeouts[$stamp_new]['expire'][$unique_id] = $timeouts[$stamp_old]['expire'][$unique_id];
+          unset($timeouts[$stamp_old]['expire'][$unique_id]);
+
+          if (empty($timeouts[$stamp_old]['expire'])) {
+            unset($timeouts[$stamp_old]['expire']);
+          }
+
+          if (empty($timeouts[$stamp_old])) {
+            unset($timeouts[$stamp_old]);
+          }
+
+          // only perform expensive sort if its a new key, which might be out of order.
+          if ($new_key) {
+            ksort($timeouts);
+          }
+
+          unset($new_key);
+        }
+
+        unset($stamp_new);
+        unset($stamp_old);
+      }
+
+      $response['result'] = array(
+        'name' => $db_session['name'],
+        'id_user' => (int) $db_session['id_user'],
+        'password' => $db_session['password'],
+        'expire' => $db_session['timeouts']['expire'],
+        'max' => $db_session['timeouts']['max'],
+        'interval' => $db_session['timeouts']['interval'],
+        'settings' => $db_session['settings'],
+      );
+
+      socket_write($client_socket, json_encode($response));
+      unset($db_timeout);
+      unset($unique_id);
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+    // store password.
+    elseif (array_key_exists('password', $decoded_packet) && (is_null($decoded_packet['password']) || is_string($decoded_packet['password']))) {
+      if (!isset($decoded_packet['name']) || strlen($decoded_packet['name']) == 0 || preg_match('/^(\w|-)+$/i', $decoded_packet['name']) != 1) {
+        $response['error'] = array(
+          'target' => 'decoded_packet[name]',
+          'message' => "No valid user name was specified. A valid, non-empty, user name string must be provided.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      if ((is_int($decoded_packet['id_user']) && $decoded_packet['id_user'] < 0) || !is_int($decoded_packet['id_user']) && (!is_string($decoded_packet['id_user']) || !(is_numeric($decoded_packet['id_user']) && (int) $decoded_packet['id_user'] >= 0))) {
+        $response['error'] = array(
+          'target' => 'decoded_packet[id_user]',
+          'message' => "No valid id_user was specified. A valid id_user integer, greater than or equal to 0, must be provided.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      if (isset($decoded_packet['settings']) && !is_array($decoded_packet['settings'])) {
+        $response['error'] = array(
+          'target' => 'decoded_packet[settings]',
+          'message' => "If specified, settings must be a valid array.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      $session_id = build_session_id();
+      if (isset($database['sessions'][$decoded_packet['ip']]) && is_array($database['sessions'][$decoded_packet['ip']]) && array_key_exists($session_id, $database['sessions'][$decoded_packet['ip']])) {
+        $response['error'] = array(
+          'target' => 'conflict',
+          'message' => "Failed to generate a unique session id due to a conflict. Please try again.",
+        );
+
+        socket_write($client_socket, json_encode($response));
+        unset($response);
+        unset($encoded_packet);
+        unset($session_id);
+
+        socket_close($client_socket);
+        unset($client_socket);
+        continue;
+      }
+
+      $unique_id = $decoded_packet['name'] . '-' . uniqid();
+      $database['sessions'][$decoded_packet['ip']][$session_id] = array(
+        'name' => $decoded_packet['name'],
+        'id_user' => (int) $decoded_packet['id_user'],
+        'password' => $decoded_packet['password'],
+        'timeouts' => array(
+          'id' => $unique_id,
+          'expire' => NULL,
+          'max' => NULL,
+        ),
+        'settings' => isset($decoded_packet['settings']) ? $decoded_packet['settings'] : array(),
+      );
+
+
+      // the timeout only needs to contain what is necessary to obtain the session data.
+      $timeout = array(
+        'ip' => $decoded_packet['ip'],
+        'session_id' => $session_id,
+      );
+
+
+      // grab optional soft timeouts and enforce hard timeouts.
+      $timeout_expire = INTERVAL_TIMEOUT_HARD_EXPIRE;
+      $timeout_max = INTERVAL_TIMEOUT_HARD_MAX;
+
+      if (isset($decoded_packet['max']) && is_int($decoded_packet['max']) && $decoded_packet['max'] > 0) {
+        if ($decoded_packet['max'] < INTERVAL_TIMEOUT_HARD_MAX) {
+          $timeout_max = $decoded_packet['max'];
+
+          if ($timeout_expire > $timeout_max) {
+            $timeout_expire = $decoded_packet['max'];
+          }
+        }
+      }
+
+      if (isset($decoded_packet['expire']) && is_int($decoded_packet['expire']) && $decoded_packet['expire'] > 0) {
+        if ($decoded_packet['expire'] < INTERVAL_TIMEOUT_HARD_EXPIRE && $decoded_packet['expire'] < $timeout_max) {
+          $timeout_expire = $decoded_packet['expire'];
+        }
+      }
+
+
+      // save basic timeout.
+      $stamp = strtotime('+' . $timeout_expire . ' seconds');
+      $database['sessions'][$decoded_packet['ip']][$session_id]['timeouts']['expire'] = $stamp;
+      $database['sessions'][$decoded_packet['ip']][$session_id]['timeouts']['interval'] = $timeout_expire;
+      $timeouts[$stamp]['expire'][$unique_id] = $timeout;
+
+
+      // save maxiumum timeout.
+      $stamp = strtotime('+' . $timeout_max . ' seconds');
+      $database['sessions'][$decoded_packet['ip']][$session_id]['timeouts']['max'] = $stamp;
+      $timeouts[$stamp]['max'][$unique_id] = $timeout;
+
+
+      // sort timeouts array for faster cleanups (this action is required).
+      ksort($timeouts);
+
+
+      // return the session key.
+      $response['result'] = array(
+        'session_id' => $session_id,
+        'expire' => $database['sessions'][$decoded_packet['ip']][$session_id]['timeouts']['expire'],
+        'max' => $database['sessions'][$decoded_packet['ip']][$session_id]['timeouts']['max'],
+        'interval' => $timeout_expire,
+      );
+
+      socket_write($client_socket, json_encode($response));
+      unset($session_id);
+      unset($stamp);
+      unset($unique_id);
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+    else {
+      $response['error'] = array(
+        'target' => 'no_session',
+        'message' => "Either a new session_id must be requested by provided the password string or a password must be requested by providing the session id string.",
+      );
+
+      socket_write($client_socket, json_encode($response));
+      unset($response);
+      unset($encoded_packet);
+
+      socket_close($client_socket);
+      unset($client_socket);
+      continue;
+    }
+
+    unset($response);
+    unset($encoded_packet);
+
+    socket_close($client_socket);
+    unset($client_socket);
+  } while (TRUE);
+
+  socket_close($socket);
+  unlink($socket_path);
+  unlink($pid_path);
+  return TRUE;
+}
+
+/**
+ * Builds and returns a session id string.
+ *
+ * Based on drupal's drupal_random_bytes()
+ *
+ * @return string|false
+ *   A base64 string.
+ *   FALSE is returned on error.
+ *
+ * @see: https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_random_bytes/7
+ */
+function build_session_id() {
+  $fh = @fopen('/dev/urandom', 'rb');
+  if (!$fh) {
+    return FALSE;
+  }
+
+  $bytes = fread($fh, SESSION_RANDOM_BYTES);
+  fclose($fh);
+
+  return base64_encode($bytes);
+}
+
+/**
+ * Searches through the database and removes expired keys.
+ *
+ * This uses a simple search design where the keys are expected to be in order.
+ * When the first key that represents a future time is encountered, the function will exit.
+ *
+ * @param array $database
+ *   An array of all usernames, ips, passwords, and sessions.
+ * @param array $timeouts
+ *   An array of all timeouts keyed in numeric order.
+ * @param string $cleartext
+ *  Used as an unproven attempt to clear passwords from memory before delete in hopes to avoid security issues inherit in a garbage collector.
+ *
+ * @param bool
+ *   TRUE on success, FALSE otherwise.
+ */
+function process_expired_sessions(&$database, &$timeouts, $cleartext) {
+  if (!is_array($database)) {
+    return FALSE;
+  }
+
+  if (!is_array($timeouts)) {
+    return FALSE;
+  }
+
+  // this is not a problem, it simply means that there is nothing to do.
+  if (empty($database) || empty($timeouts)) {
+    return TRUE;
+  }
+
+  $now = strtotime('now');
+  $to_delete = array();
+  foreach ($timeouts as $time => &$timeout) {
+    if ($time > $now) {
+      break;
+    }
+
+    // process expire timeouts.
+    if (!empty($timeout['expire'])) {
+      foreach ($timeout['expire'] as $unique_key => $setting) {
+        $to_delete[$unique_key] = array(
+          'ip' => $setting['ip'],
+          'session_id' => $setting['session_id'],
+        );
+      }
+      unset($unique_key);
+      unset($setting);
+    }
+
+    // process max timeouts.
+    if (!empty($timeout['max'])) {
+      foreach ($timeout['max'] as $unique_key => $setting) {
+        $to_delete[$unique_key] = array(
+          'ip' => $setting['ip'],
+          'session_id' => $setting['session_id'],
+        );
+      }
+      unset($unique_key);
+      unset($setting);
+    }
+  }
+  unset($time);
+  unset($timeout);
+
+
+  // remove expired keys.
+  if (!empty($to_delete)) {
+    foreach ($to_delete as $unique_key => $setting) {
+      if (isset($database['session'][$setting['ip']][$setting['session_id']])) {
+        expire_session($database, $timeouts, $setting['session_id'], $setting['ip']);
+      }
+    }
+    unset($unique_key);
+    unset($setting);
+
+
+    // force garbage collection cleanup.
+    gc_collect_cycles();
+  }
+
+  return TRUE;
+}
+
+/**
+ * pre-maturely expire a specific session.
+ *
+ * Use this to 'logout' of a session and remove the username and password information from this process before the session expires.
+ *
+ * @param array $database
+ *   The database array.
+ * @param array $timeouts
+ *   The timeouts array.
+ * @param string $session_id
+ *   The session id string.
+ * @param string $ip
+ *   The ip address string.
+ *
+ * @param bool
+ *   TRUE on success, FALSE otherwise.
+ */
+function expire_session(&$database, &$timeouts, $session_id, $ip) {
+  if (!is_array($database) || !is_array($timeouts)) {
+    return FALSE;
+  }
+
+  if (!is_string($session_id) || !is_string($ip)) {
+    return FALSE;
+  }
+
+  if (!isset($database['sessions'][$ip][$session_id])) {
+    // if it does not exist, then consider the close successful.
+    return TRUE;
+  }
+
+  $session = $database['session'][$ip][$session_id];
+  foreach (array('expire', 'max') as $key) {
+    if (isset($timeouts[$session['timeouts'][$key]][$session['timeouts']['id']])) {
+      unset($timeouts[$session['timeouts'][$key]][$session['timeouts']['id']]);
+
+      if (empty($timeouts[$session['timeouts'][$key]])) {
+        unset($timeouts[$session['timeouts'][$key]]);
+      }
+    }
+  }
+  unset($session);
+  unset($key);
+
+  $database['sessions'][$ip][$session_id]['password'] = $cleartext;
+  unset($database['sessions'][$ip][$session_id]);
+
+  if (empty($database['sessions'][$ip])) {
+    unset($database['sessions'][$ip]);
+  }
+
+  return TRUE;
+}
+
+
+main($argc, $argv);