summaryrefslogtreecommitdiff
path: root/openvpn/manage.c
diff options
context:
space:
mode:
Diffstat (limited to 'openvpn/manage.c')
-rw-r--r--openvpn/manage.c2153
1 files changed, 0 insertions, 2153 deletions
diff --git a/openvpn/manage.c b/openvpn/manage.c
deleted file mode 100644
index 587a971..0000000
--- a/openvpn/manage.c
+++ /dev/null
@@ -1,2153 +0,0 @@
-/*
- * OpenVPN -- An application to securely tunnel IP networks
- * over a single TCP/UDP port, with support for SSL/TLS-based
- * session authentication and key exchange,
- * packet encryption, packet authentication, and
- * packet compression.
- *
- * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program (see the file COPYING included with this
- * distribution); if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef WIN32
-#include "config-win32.h"
-#else
-#include "config.h"
-#endif
-
-#include "syshead.h"
-
-#ifdef ENABLE_MANAGEMENT
-
-#include "error.h"
-#include "fdmisc.h"
-#include "options.h"
-#include "sig.h"
-#include "event.h"
-#include "otime.h"
-#include "integer.h"
-#include "manage.h"
-
-#include "memdbg.h"
-
-struct management *management; /* GLOBAL */
-
-/* static forward declarations */
-static void man_output_standalone (struct management *man, volatile int *signal_received);
-static void man_reset_client_socket (struct management *man, const bool listen);
-
-static void
-man_help ()
-{
- msg (M_CLIENT, "Management Interface for %s", title_string);
- msg (M_CLIENT, "Commands:");
- msg (M_CLIENT, "auth-retry t : Auth failure retry mode (none,interact,nointeract).");
- msg (M_CLIENT, "echo [on|off] [N|all] : Like log, but only show messages in echo buffer.");
- msg (M_CLIENT, "exit|quit : Close management session.");
- msg (M_CLIENT, "help : Print this message.");
- msg (M_CLIENT, "hold [on|off|release] : Set/show hold flag to on/off state, or");
- msg (M_CLIENT, " release current hold and start tunnel.");
- msg (M_CLIENT, "kill cn : Kill the client instance(s) having common name cn.");
- msg (M_CLIENT, "kill IP:port : Kill the client instance connecting from IP:port.");
- msg (M_CLIENT, "log [on|off] [N|all] : Turn on/off realtime log display");
- msg (M_CLIENT, " + show last N lines or 'all' for entire history.");
- msg (M_CLIENT, "mute [n] : Set log mute level to n, or show level if n is absent.");
- msg (M_CLIENT, "net : (Windows only) Show network info and routing table.");
- msg (M_CLIENT, "password type p : Enter password p for a queried OpenVPN password.");
- msg (M_CLIENT, "signal s : Send signal s to daemon,");
- msg (M_CLIENT, " s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
- msg (M_CLIENT, "state [on|off] [N|all] : Like log, but show state history.");
- msg (M_CLIENT, "status [n] : Show current daemon status info using format #n.");
- msg (M_CLIENT, "test n : Produce n lines of output for testing/debugging.");
- msg (M_CLIENT, "username type u : Enter username u for a queried OpenVPN username.");
- msg (M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent.");
- msg (M_CLIENT, "version : Show current version number.");
- msg (M_CLIENT, "END");
-}
-
-static const char *
-man_state_name (const int state)
-{
- switch (state)
- {
- case OPENVPN_STATE_INITIAL:
- return "INITIAL";
- case OPENVPN_STATE_CONNECTING:
- return "CONNECTING";
- case OPENVPN_STATE_WAIT:
- return "WAIT";
- case OPENVPN_STATE_AUTH:
- return "AUTH";
- case OPENVPN_STATE_GET_CONFIG:
- return "GET_CONFIG";
- case OPENVPN_STATE_ASSIGN_IP:
- return "ASSIGN_IP";
- case OPENVPN_STATE_ADD_ROUTES:
- return "ADD_ROUTES";
- case OPENVPN_STATE_CONNECTED:
- return "CONNECTED";
- case OPENVPN_STATE_RECONNECTING:
- return "RECONNECTING";
- case OPENVPN_STATE_EXITING:
- return "EXITING";
- default:
- return "?";
- }
-}
-
-static void
-man_welcome (struct management *man)
-{
- msg (M_CLIENT, ">INFO:OpenVPN Management Interface Version %d -- type 'help' for more info",
- MANAGEMENT_VERSION);
- if (man->persist.special_state_msg)
- msg (M_CLIENT, "%s", man->persist.special_state_msg);
-}
-
-static inline bool
-man_password_needed (struct management *man)
-{
- return man->settings.up.defined && !man->connection.password_verified;
-}
-
-static void
-man_check_password (struct management *man, const char *line)
-{
- if (man_password_needed (man))
- {
- if (streq (line, man->settings.up.password))
- {
- man->connection.password_verified = true;
- msg (M_CLIENT, "SUCCESS: password is correct");
- man_welcome (man);
- }
- else
- {
- man->connection.password_verified = false;
- msg (M_CLIENT, "ERROR: bad password");
- if (++man->connection.password_tries >= MANAGEMENT_N_PASSWORD_RETRIES)
- {
- msg (M_WARN, "MAN: client connection rejected after %d failed password attempts",
- MANAGEMENT_N_PASSWORD_RETRIES);
- man->connection.halt = true;
- }
- }
- }
-}
-
-static void
-man_update_io_state (struct management *man)
-{
- if (socket_defined (man->connection.sd_cli))
- {
- if (output_list_defined (man->connection.out))
- {
- man->connection.state = MS_CC_WAIT_WRITE;
- }
- else
- {
- man->connection.state = MS_CC_WAIT_READ;
- }
- }
-}
-
-static void
-man_output_list_push (struct management *man, const char *str)
-{
- if (management_connected (man))
- {
- if (str)
- output_list_push (man->connection.out, (const unsigned char *) str);
- man_update_io_state (man);
- if (!man->persist.standalone_disabled)
- man_output_standalone (man, NULL);
- }
-}
-
-static void
-man_prompt (struct management *man)
-{
- if (man_password_needed (man))
- man_output_list_push (man, "ENTER PASSWORD:");
-#if 0 /* should we use prompt? */
- else
- man_output_list_push (man, PACKAGE_NAME ">");
-#endif
-}
-
-static void
-man_close_socket (struct management *man, const socket_descriptor_t sd)
-{
-#ifndef WIN32
- /*
- * Windows doesn't need this because the ne32 event is permanently
- * enabled at struct management scope.
- */
- if (man->persist.callback.delete_event)
- (*man->persist.callback.delete_event) (man->persist.callback.arg, sd);
-#endif
- openvpn_close_socket (sd);
-}
-
-static void
-virtual_output_callback_func (void *arg, const unsigned int flags, const char *str)
-{
- static int recursive_level = 0; /* GLOBAL */
-
- if (!recursive_level) /* don't allow recursion */
- {
- struct gc_arena gc = gc_new ();
- struct management *man = (struct management *) arg;
- struct log_entry e;
- const char *out = NULL;
-
- ++recursive_level;
-
- CLEAR (e);
- update_time ();
- e.timestamp = now;
- e.u.msg_flags = flags;
- e.string = str;
-
- if (flags & M_FATAL)
- man->persist.standalone_disabled = false;
-
- if (flags != M_CLIENT)
- log_history_add (man->persist.log, &e);
-
- if (!man_password_needed (man))
- {
- if (flags == M_CLIENT)
- out = log_entry_print (&e, LOG_PRINT_CRLF, &gc);
- else if (man->connection.log_realtime)
- out = log_entry_print (&e, LOG_PRINT_INT_DATE
- | LOG_PRINT_MSG_FLAGS
- | LOG_PRINT_LOG_PREFIX
- | LOG_PRINT_CRLF, &gc);
- if (out)
- man_output_list_push (man, out);
- if (flags & M_FATAL)
- {
- out = log_entry_print (&e, LOG_FATAL_NOTIFY|LOG_PRINT_CRLF, &gc);
- if (out)
- {
- man_output_list_push (man, out);
- man_reset_client_socket (man, false);
- }
- }
- }
-
- --recursive_level;
- gc_free (&gc);
- }
-}
-
-static void
-man_signal (struct management *man, const char *name)
-{
- const int sig = parse_signal (name);
- if (sig >= 0)
- {
- throw_signal (sig);
- msg (M_CLIENT, "SUCCESS: signal %s thrown", signal_name (sig, true));
- }
- else
- {
- msg (M_CLIENT, "ERROR: signal '%s' is not a known signal type", name);
- }
-}
-
-static void
-man_status (struct management *man, const int version, struct status_output *so)
-{
- if (man->persist.callback.status)
- {
- (*man->persist.callback.status) (man->persist.callback.arg, version, so);
- }
- else
- {
- msg (M_CLIENT, "ERROR: The 'status' command is not supported by the current daemon mode");
- }
-}
-
-static void
-man_kill (struct management *man, const char *victim)
-{
- struct gc_arena gc = gc_new ();
-
- if (man->persist.callback.kill_by_cn && man->persist.callback.kill_by_addr)
- {
- struct buffer buf;
- char p1[128];
- char p2[128];
- int n_killed;
-
- buf_set_read (&buf, (uint8_t*) victim, strlen (victim) + 1);
- buf_parse (&buf, ':', p1, sizeof (p1));
- buf_parse (&buf, ':', p2, sizeof (p2));
-
- if (strlen (p1) && strlen (p2))
- {
- /* IP:port specified */
- bool status;
- const in_addr_t addr = getaddr (GETADDR_HOST_ORDER|GETADDR_MSG_VIRT_OUT, p1, 0, &status, NULL);
- if (status)
- {
- const int port = atoi (p2);
- if (port > 0 && port < 65536)
- {
- n_killed = (*man->persist.callback.kill_by_addr) (man->persist.callback.arg, addr, port);
- if (n_killed > 0)
- {
- msg (M_CLIENT, "SUCCESS: %d client(s) at address %s:%d killed",
- n_killed,
- print_in_addr_t (addr, 0, &gc),
- port);
- }
- else
- {
- msg (M_CLIENT, "ERROR: client at address %s:%d not found",
- print_in_addr_t (addr, 0, &gc),
- port);
- }
- }
- else
- {
- msg (M_CLIENT, "ERROR: port number is out of range: %s", p2);
- }
- }
- else
- {
- msg (M_CLIENT, "ERROR: error parsing IP address: %s", p1);
- }
- }
- else if (strlen (p1))
- {
- /* common name specified */
- n_killed = (*man->persist.callback.kill_by_cn) (man->persist.callback.arg, p1);
- if (n_killed > 0)
- {
- msg (M_CLIENT, "SUCCESS: common name '%s' found, %d client(s) killed", p1, n_killed);
- }
- else
- {
- msg (M_CLIENT, "ERROR: common name '%s' not found", p1);
- }
- }
- else
- {
- msg (M_CLIENT, "ERROR: kill parse");
- }
- }
- else
- {
- msg (M_CLIENT, "ERROR: The 'kill' command is not supported by the current daemon mode");
- }
-
- gc_free (&gc);
-}
-
-/*
- * General-purpose history command handler
- * for the log and echo commands.
- */
-static void
-man_history (struct management *man,
- const char *parm,
- const char *type,
- struct log_history *log,
- bool *realtime,
- const unsigned int lep_flags)
-{
- struct gc_arena gc = gc_new ();
- int n = 0;
-
- if (streq (parm, "on"))
- {
- *realtime = true;
- msg (M_CLIENT, "SUCCESS: real-time %s notification set to ON", type);
- }
- else if (streq (parm, "off"))
- {
- *realtime = false;
- msg (M_CLIENT, "SUCCESS: real-time %s notification set to OFF", type);
- }
- else if (streq (parm, "all") || (n = atoi (parm)) > 0)
- {
- const int size = log_history_size (log);
- const int start = (n ? n : size) - 1;
- int i;
-
- for (i = start; i >= 0; --i)
- {
- const struct log_entry *e = log_history_ref (log, i);
- if (e)
- {
- const char *out = log_entry_print (e, lep_flags, &gc);
- virtual_output_callback_func (man, M_CLIENT, out);
- }
- }
- msg (M_CLIENT, "END");
- }
- else
- {
- msg (M_CLIENT, "ERROR: %s parameter must be 'on' or 'off' or some number n or 'all'", type);
- }
-
- gc_free (&gc);
-}
-
-static void
-man_log (struct management *man, const char *parm)
-{
- man_history (man,
- parm,
- "log",
- man->persist.log,
- &man->connection.log_realtime,
- LOG_PRINT_INT_DATE|LOG_PRINT_MSG_FLAGS);
-}
-
-static void
-man_echo (struct management *man, const char *parm)
-{
- man_history (man,
- parm,
- "echo",
- man->persist.echo,
- &man->connection.echo_realtime,
- LOG_PRINT_INT_DATE);
-}
-
-static void
-man_state (struct management *man, const char *parm)
-{
- man_history (man,
- parm,
- "state",
- man->persist.state,
- &man->connection.state_realtime,
- LOG_PRINT_INT_DATE|LOG_PRINT_STATE|LOG_PRINT_LOCAL_IP);
-}
-
-static void
-man_up_finalize (struct management *man)
-{
- switch (man->connection.up_query_mode)
- {
- case UP_QUERY_DISABLED:
- man->connection.up_query.defined = false;
- break;
- case UP_QUERY_USER_PASS:
- if (strlen (man->connection.up_query.username) && strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
- case UP_QUERY_PASS:
- if (strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
- default:
- ASSERT (0);
- }
-}
-
-static void
-man_query_user_pass (struct management *man,
- const char *type,
- const char *string,
- const bool needed,
- const char *prompt,
- char *dest,
- int len)
-{
- if (needed)
- {
- ASSERT (man->connection.up_query_type);
- if (streq (man->connection.up_query_type, type))
- {
- strncpynt (dest, string, len);
- man_up_finalize (man);
- msg (M_CLIENT, "SUCCESS: '%s' %s entered, but not yet verified",
- type,
- prompt);
- }
- else
- msg (M_CLIENT, "ERROR: %s of type '%s' entered, but we need one of type '%s'",
- prompt,
- type,
- man->connection.up_query_type);
- }
- else
- {
- msg (M_CLIENT, "ERROR: no %s is currently needed at this time", prompt);
- }
-}
-
-static void
-man_query_username (struct management *man, const char *type, const char *string)
-{
- const bool needed = (man->connection.up_query_mode == UP_QUERY_USER_PASS && man->connection.up_query_type);
- man_query_user_pass (man, type, string, needed, "username", man->connection.up_query.username, USER_PASS_LEN);
-}
-
-static void
-man_query_password (struct management *man, const char *type, const char *string)
-{
- const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
- || man->connection.up_query_mode == UP_QUERY_PASS)
- && man->connection.up_query_type);
- man_query_user_pass (man, type, string, needed, "password", man->connection.up_query.password, USER_PASS_LEN);
-}
-
-static void
-man_net (struct management *man)
-{
- if (man->persist.callback.show_net)
- {
- (*man->persist.callback.show_net) (man->persist.callback.arg, M_CLIENT);
- }
- else
- {
- msg (M_CLIENT, "ERROR: The 'net' command is not supported by the current daemon mode");
- }
-}
-
-static void
-man_hold (struct management *man, const char *cmd)
-{
- if (cmd)
- {
- if (streq (cmd, "on"))
- {
- man->settings.hold = true;
- msg (M_CLIENT, "SUCCESS: hold flag set to ON");
- }
- else if (streq (cmd, "off"))
- {
- man->settings.hold = false;
- msg (M_CLIENT, "SUCCESS: hold flag set to OFF");
- }
- else if (streq (cmd, "release"))
- {
- man->persist.hold_release = true;
- msg (M_CLIENT, "SUCCESS: hold release succeeded");
- }
- else
- {
- msg (M_CLIENT, "ERROR: bad hold command parameter");
- }
- }
- else
- msg (M_CLIENT, "SUCCESS: hold=%d", (int) man->settings.hold);
-}
-
-#define MN_AT_LEAST (1<<0)
-
-static bool
-man_need (struct management *man, const char **p, const int n, unsigned int flags)
-{
- int i;
- ASSERT (p[0]);
- for (i = 1; i <= n; ++i)
- {
- if (!p[i])
- {
- msg (M_CLIENT, "ERROR: the '%s' command requires %s%d parameter%s",
- p[0],
- (flags & MN_AT_LEAST) ? "at least " : "",
- n,
- n > 1 ? "s" : "");
- return false;
- }
- }
- return true;
-}
-
-static void
-man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms)
-{
- struct gc_arena gc = gc_new ();
-
- ASSERT (p[0]);
- if (streq (p[0], "exit") || streq (p[0], "quit"))
- {
- man->connection.halt = true;
- goto done;
- }
- else if (streq (p[0], "help"))
- {
- man_help ();
- }
- else if (streq (p[0], "version"))
- {
- msg (M_CLIENT, "OpenVPN Version: %s", title_string);
- msg (M_CLIENT, "Management Version: %d", MANAGEMENT_VERSION);
- msg (M_CLIENT, "END");
- }
- else if (streq (p[0], "signal"))
- {
- if (man_need (man, p, 1, 0))
- man_signal (man, p[1]);
- }
- else if (streq (p[0], "status"))
- {
- int version = 0;
- if (p[1])
- version = atoi (p[1]);
- man_status (man, version, so);
- }
- else if (streq (p[0], "kill"))
- {
- if (man_need (man, p, 1, 0))
- man_kill (man, p[1]);
- }
- else if (streq (p[0], "verb"))
- {
- if (p[1])
- {
- const int level = atoi(p[1]);
- if (set_debug_level (level, 0))
- msg (M_CLIENT, "SUCCESS: verb level changed");
- else
- msg (M_CLIENT, "ERROR: verb level is out of range");
- }
- else
- msg (M_CLIENT, "SUCCESS: verb=%d", get_debug_level ());
- }
- else if (streq (p[0], "mute"))
- {
- if (p[1])
- {
- const int level = atoi(p[1]);
- if (set_mute_cutoff (level))
- msg (M_CLIENT, "SUCCESS: mute level changed");
- else
- msg (M_CLIENT, "ERROR: mute level is out of range");
- }
- else
- msg (M_CLIENT, "SUCCESS: mute=%d", get_mute_cutoff ());
- }
- else if (streq (p[0], "auth-retry"))
- {
-#if P2MP
- if (p[1])
- {
- if (auth_retry_set (M_CLIENT, p[1]))
- msg (M_CLIENT, "SUCCESS: auth-retry parameter changed");
- else
- msg (M_CLIENT, "ERROR: bad auth-retry parameter");
- }
- else
- msg (M_CLIENT, "SUCCESS: auth-retry=%s", auth_retry_print ());
-#else
- msg (M_CLIENT, "ERROR: auth-retry feature is unavailable");
-#endif
- }
- else if (streq (p[0], "state"))
- {
- if (!p[1])
- {
- man_state (man, "1");
- }
- else
- {
- if (p[1])
- man_state (man, p[1]);
- if (p[2])
- man_state (man, p[2]);
- }
- }
- else if (streq (p[0], "log"))
- {
- if (man_need (man, p, 1, MN_AT_LEAST))
- {
- if (p[1])
- man_log (man, p[1]);
- if (p[2])
- man_log (man, p[2]);
- }
- }
- else if (streq (p[0], "echo"))
- {
- if (man_need (man, p, 1, MN_AT_LEAST))
- {
- if (p[1])
- man_echo (man, p[1]);
- if (p[2])
- man_echo (man, p[2]);
- }
- }
- else if (streq (p[0], "username"))
- {
- if (man_need (man, p, 2, 0))
- man_query_username (man, p[1], p[2]);
- }
- else if (streq (p[0], "password"))
- {
- if (man_need (man, p, 2, 0))
- man_query_password (man, p[1], p[2]);
- }
- else if (streq (p[0], "net"))
- {
- man_net (man);
- }
- else if (streq (p[0], "hold"))
- {
- man_hold (man, p[1]);
- }
-#if 1
- else if (streq (p[0], "test"))
- {
- if (man_need (man, p, 1, 0))
- {
- int i;
- const int n = atoi (p[1]);
- for (i = 0; i < n; ++i)
- {
- msg (M_CLIENT, "[%d] The purpose of this command is to generate large amounts of output.", i);
- }
- }
- }
-#endif
- else
- {
- msg (M_CLIENT, "ERROR: unknown command, enter 'help' for more options");
- }
-
- done:
- gc_free (&gc);
-}
-
-#ifdef WIN32
-
-static void
-man_start_ne32 (struct management *man)
-{
- switch (man->connection.state)
- {
- case MS_LISTEN:
- net_event_win32_start (&man->connection.ne32, FD_ACCEPT, man->connection.sd_top);
- break;
- case MS_CC_WAIT_READ:
- case MS_CC_WAIT_WRITE:
- net_event_win32_start (&man->connection.ne32, FD_READ|FD_WRITE|FD_CLOSE, man->connection.sd_cli);
- break;
- default:
- ASSERT (0);
- }
-}
-
-static void
-man_stop_ne32 (struct management *man)
-{
- net_event_win32_stop (&man->connection.ne32);
-}
-
-#endif
-
-static void
-man_accept (struct management *man)
-{
- struct gc_arena gc = gc_new ();
-
- /*
- * Accept the TCP client.
- */
- man->connection.sd_cli = socket_do_accept (man->connection.sd_top, &man->connection.remote, false);
- if (socket_defined (man->connection.sd_cli))
- {
- if (socket_defined (man->connection.sd_top))
- {
-#ifdef WIN32
- man_stop_ne32 (man);
-#endif
- }
-
- /*
- * Set misc socket properties
- */
- set_nonblock (man->connection.sd_cli);
- set_cloexec (man->connection.sd_cli);
-
- man->connection.state_realtime = false;
- man->connection.log_realtime = false;
- man->connection.echo_realtime = false;
- man->connection.password_verified = false;
- man->connection.password_tries = 0;
- man->connection.halt = false;
- man->connection.state = MS_CC_WAIT_WRITE;
-
-#ifdef WIN32
- man_start_ne32 (man);
-#endif
-
- msg (D_MANAGEMENT, "MANAGEMENT: Client connected from %s",
- print_sockaddr (&man->settings.local, &gc));
-
- output_list_reset (man->connection.out);
-
- if (!man_password_needed (man))
- man_welcome (man);
- man_prompt (man);
- man_update_io_state (man);
- }
-
- gc_free (&gc);
-}
-
-static void
-man_listen (struct management *man)
-{
- struct gc_arena gc = gc_new ();
-
- /*
- * Initialize state
- */
- man->connection.state = MS_LISTEN;
- man->connection.sd_cli = SOCKET_UNDEFINED;
-
- /*
- * Initialize listening socket
- */
- if (man->connection.sd_top == SOCKET_UNDEFINED)
- {
- man->connection.sd_top = create_socket_tcp ();
-
- /*
- * Bind socket
- */
- if (bind (man->connection.sd_top, (struct sockaddr *) &man->settings.local, sizeof (man->settings.local)))
- msg (M_SOCKERR, "MANAGEMENT: Cannot bind TCP socket on %s",
- print_sockaddr (&man->settings.local, &gc));
-
- /*
- * Listen for connection
- */
- if (listen (man->connection.sd_top, 1))
- msg (M_SOCKERR, "MANAGEMENT: listen() failed");
-
- /*
- * Set misc socket properties
- */
- set_nonblock (man->connection.sd_top);
- set_cloexec (man->connection.sd_top);
-
- msg (D_MANAGEMENT, "MANAGEMENT: TCP Socket listening on %s",
- print_sockaddr (&man->settings.local, &gc));
- }
-
-#ifdef WIN32
- man_start_ne32 (man);
-#endif
-
- gc_free (&gc);
-}
-
-static void
-man_reset_client_socket (struct management *man, const bool listen)
-{
- if (socket_defined (man->connection.sd_cli))
- {
- msg (D_MANAGEMENT, "MANAGEMENT: Client disconnected");
-#ifdef WIN32
- man_stop_ne32 (man);
-#endif
- man_close_socket (man, man->connection.sd_cli);
- command_line_reset (man->connection.in);
- output_list_reset (man->connection.out);
- }
- if (listen)
- man_listen (man);
-}
-
-static void
-man_process_command (struct management *man, const char *line)
-{
- struct gc_arena gc = gc_new ();
- struct status_output *so;
- int nparms;
- char *parms[MAX_PARMS+1];
-
- CLEAR (parms);
- so = status_open (NULL, 0, -1, &man->persist.vout, 0);
-
- if (man_password_needed (man))
- {
- man_check_password (man, line);
- }
- else
- {
- nparms = parse_line (line, parms, MAX_PARMS, "TCP", 0, M_CLIENT, &gc);
- if (parms[0] && streq (parms[0], "password"))
- msg (D_MANAGEMENT_DEBUG, "MANAGEMENT: CMD 'password [...]'");
- else
- msg (D_MANAGEMENT_DEBUG, "MANAGEMENT: CMD '%s'", line);
-
-#if 0
- /* DEBUGGING -- print args */
- {
- int i;
- for (i = 0; i < nparms; ++i)
- msg (M_INFO, "[%d] '%s'", i, parms[i]);
- }
-#endif
-
- if (nparms > 0)
- man_dispatch_command (man, so, (const char **)parms, nparms);
- }
-
- CLEAR (parms);
- status_close (so);
- gc_free (&gc);
-}
-
-static bool
-man_io_error (struct management *man, const char *prefix)
-{
- const int err = openvpn_errno_socket ();
-
- if (!ignore_sys_error (err))
- {
- struct gc_arena gc = gc_new ();
- msg (D_MANAGEMENT, "MANAGEMENT: TCP %s error: %s",
- prefix,
- strerror_ts (err, &gc));
- gc_free (&gc);
- return true;
- }
- else
- return false;
-}
-
-static int
-man_read (struct management *man)
-{
- /*
- * read command line from socket
- */
- unsigned char buf[256];
- int len = 0;
-
- len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL);
- if (len == 0)
- {
- man_reset_client_socket (man, true);
- }
- else if (len > 0)
- {
- bool processed_command = false;
-
- ASSERT (len <= (int) sizeof (buf));
- command_line_add (man->connection.in, buf, len);
-
- /*
- * Reset output object
- */
- output_list_reset (man->connection.out);
-
- /*
- * process command line if complete
- */
- {
- const unsigned char *line;
- while ((line = command_line_get (man->connection.in)))
- {
- man_process_command (man, (char *) line);
- if (man->connection.halt)
- break;
- command_line_next (man->connection.in);
- processed_command = true;
- }
- }
-
- /*
- * Reset output state to MS_CC_WAIT_(READ|WRITE)
- */
- if (man->connection.halt)
- {
- man_reset_client_socket (man, true);
- len = 0;
- }
- else
- {
- if (processed_command)
- man_prompt (man);
- man_update_io_state (man);
- }
- }
- else /* len < 0 */
- {
- if (man_io_error (man, "recv"))
- man_reset_client_socket (man, true);
- }
- return len;
-}
-
-static int
-man_write (struct management *man)
-{
- const int max_send = 256;
- int sent = 0;
-
- const struct buffer *buf = output_list_peek (man->connection.out);
- if (buf && BLEN (buf))
- {
- const int len = min_int (max_send, BLEN (buf));
- sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL);
- if (sent >= 0)
- {
- output_list_advance (man->connection.out, sent);
- }
- else if (sent < 0)
- {
- if (man_io_error (man, "send"))
- man_reset_client_socket (man, true);
- }
- }
-
- /*
- * Reset output state to MS_CC_WAIT_(READ|WRITE)
- */
- man_update_io_state (man);
-
- return sent;
-}
-
-static void
-man_connection_clear (struct man_connection *mc)
-{
- CLEAR (*mc);
-
- /* set initial state */
- mc->state = MS_INITIAL;
-
- /* clear socket descriptors */
- mc->sd_top = SOCKET_UNDEFINED;
- mc->sd_cli = SOCKET_UNDEFINED;
-}
-
-static void
-man_persist_init (struct management *man,
- const int log_history_cache,
- const int echo_buffer_size,
- const int state_buffer_size)
-{
- struct man_persist *mp = &man->persist;
- if (!mp->defined)
- {
- CLEAR (*mp);
-
- /* initialize log history store */
- mp->log = log_history_init (log_history_cache);
-
- /*
- * Initialize virtual output object, so that functions
- * which write to a virtual_output object can be redirected
- * here to the management object.
- */
- mp->vout.func = virtual_output_callback_func;
- mp->vout.arg = man;
- mp->vout.flags_default = M_CLIENT;
- msg_set_virtual_output (&mp->vout);
-
- /*
- * Initialize --echo list
- */
- man->persist.echo = log_history_init (echo_buffer_size);
-
- /*
- * Initialize --state list
- */
- man->persist.state = log_history_init (state_buffer_size);
-
- mp->defined = true;
- }
-}
-
-static void
-man_persist_close (struct man_persist *mp)
-{
- if (mp->log)
- {
- msg_set_virtual_output (NULL);
- log_history_close (mp->log);
- }
-
- if (mp->echo)
- log_history_close (mp->echo);
-
- if (mp->state)
- log_history_close (mp->state);
-
- CLEAR (*mp);
-}
-
-static void
-man_settings_init (struct man_settings *ms,
- const char *addr,
- const int port,
- const char *pass_file,
- const bool server,
- const bool query_passwords,
- const int log_history_cache,
- const int echo_buffer_size,
- const int state_buffer_size,
- const bool hold)
-{
- if (!ms->defined)
- {
- CLEAR (*ms);
-
- /*
- * Are we a server? If so, it will influence
- * the way we handle state transitions.
- */
- ms->server = server;
-
- /*
- * Get username/password
- */
- if (pass_file)
- get_user_pass (&ms->up, pass_file, true, "Management", 0);
-
- /*
- * Should OpenVPN query the management layer for
- * passwords?
- */
- ms->up_query_passwords = query_passwords;
-
- /*
- * Should OpenVPN hibernate on startup?
- */
- ms->hold = hold;
-
- /*
- * Initialize socket address
- */
- ms->local.sin_family = AF_INET;
- ms->local.sin_addr.s_addr = 0;
- ms->local.sin_port = htons (port);
-
- /*
- * Run management over tunnel, or
- * separate channel?
- */
- if (streq (addr, "tunnel"))
- {
- ms->management_over_tunnel = true;
- }
- else
- {
- ms->local.sin_addr.s_addr = getaddr
- (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
- }
-
- /*
- * Log history and echo buffer may need to be resized
- */
- ms->log_history_cache = log_history_cache;
- ms->echo_buffer_size = echo_buffer_size;
- ms->state_buffer_size = state_buffer_size;
-
- ms->defined = true;
- }
-}
-
-static void
-man_settings_close (struct man_settings *ms)
-{
- CLEAR (*ms);
-}
-
-
-static void
-man_connection_init (struct management *man)
-{
- if (man->connection.state == MS_INITIAL)
- {
-#ifdef WIN32
- /*
- * This object is a sort of TCP/IP helper
- * for Windows.
- */
- net_event_win32_init (&man->connection.ne32);
-#endif
-
- /*
- * Allocate helper objects for command line input and
- * command output from/to the socket.
- */
- man->connection.in = command_line_new (256);
- man->connection.out = output_list_new (0);
-
- /*
- * Initialize event set for standalone usage, when we are
- * running outside of the primary event loop.
- */
- {
- int maxevents = 1;
- man->connection.es = event_set_init (&maxevents, EVENT_METHOD_FAST);
- }
-
- /*
- * Listen on socket
- */
- man_listen (man);
- }
-}
-
-static void
-man_connection_close (struct management *man)
-{
- struct man_connection *mc = &man->connection;
-
- if (mc->es)
- event_free (mc->es);
-#ifdef WIN32
- net_event_win32_close (&mc->ne32);
-#endif
- if (socket_defined (mc->sd_top))
- man_close_socket (man, mc->sd_top);
- if (socket_defined (mc->sd_cli))
- man_close_socket (man, mc->sd_cli);
- if (mc->in)
- command_line_free (mc->in);
- if (mc->out)
- output_list_free (mc->out);
- man_connection_clear (mc);
-}
-
-struct management *
-management_init (void)
-{
- struct management *man;
- ALLOC_OBJ_CLEAR (man, struct management);
-
- man_persist_init (man,
- MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
- MANAGEMENT_ECHO_BUFFER_SIZE,
- MANAGEMENT_STATE_BUFFER_SIZE);
-
- man_connection_clear (&man->connection);
-
- return man;
-}
-
-bool
-management_open (struct management *man,
- const char *addr,
- const int port,
- const char *pass_file,
- const bool server,
- const bool query_passwords,
- const int log_history_cache,
- const int echo_buffer_size,
- const int state_buffer_size,
- const bool hold)
-{
- bool ret = false;
-
- /*
- * Save the settings only if they have not
- * been saved before.
- */
- man_settings_init (&man->settings,
- addr,
- port,
- pass_file,
- server,
- query_passwords,
- log_history_cache,
- echo_buffer_size,
- state_buffer_size,
- hold);
-
- /*
- * The log is initially sized to MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
- * but may be changed here. Ditto for echo and state buffers.
- */
- log_history_resize (man->persist.log, man->settings.log_history_cache);
- log_history_resize (man->persist.echo, man->settings.echo_buffer_size);
- log_history_resize (man->persist.state, man->settings.state_buffer_size);
-
- /*
- * If connection object is uninitialized and we are not doing
- * over-the-tunnel management, then open (listening) connection.
- */
- if (man->connection.state == MS_INITIAL)
- {
- if (!man->settings.management_over_tunnel)
- {
- man_connection_init (man);
- ret = true;
- }
- }
-
- return ret;
-}
-
-void
-management_close (struct management *man)
-{
- man_connection_close (man);
- man_settings_close (&man->settings);
- man_persist_close (&man->persist);
- free (man);
-}
-
-void
-management_set_callback (struct management *man,
- const struct management_callback *cb)
-{
- man->persist.standalone_disabled = true;
- man->persist.callback = *cb;
-}
-
-void
-management_clear_callback (struct management *man)
-{
- man->persist.standalone_disabled = false;
- man->persist.hold_release = false;
- CLEAR (man->persist.callback);
- man_output_list_push (man, NULL); /* flush output queue */
-}
-
-void
-management_set_state (struct management *man,
- const int state,
- const char *detail,
- const in_addr_t tun_local_ip)
-{
- if (man->persist.state && (!man->settings.server || state < OPENVPN_STATE_CLIENT_BASE))
- {
- struct gc_arena gc = gc_new ();
- struct log_entry e;
- const char *out = NULL;
-
- update_time ();
- CLEAR (e);
- e.timestamp = now;
- e.u.state = state;
- e.string = detail;
- e.local_ip = tun_local_ip;
-
- log_history_add (man->persist.state, &e);
-
- if (man->connection.state_realtime)
- out = log_entry_print (&e, LOG_PRINT_STATE_PREFIX
- | LOG_PRINT_INT_DATE
- | LOG_PRINT_STATE
- | LOG_PRINT_LOCAL_IP
- | LOG_PRINT_CRLF, &gc);
-
- if (out)
- man_output_list_push (man, out);
-
- gc_free (&gc);
- }
-}
-
-void
-management_echo (struct management *man, const char *string)
-{
- if (man->persist.echo)
- {
- struct gc_arena gc = gc_new ();
- struct log_entry e;
- const char *out = NULL;
-
- update_time ();
- CLEAR (e);
- e.timestamp = now;
- e.u.msg_flags = 0;
- e.string = string;
-
- log_history_add (man->persist.echo, &e);
-
- if (man->connection.echo_realtime)
- out = log_entry_print (&e, LOG_PRINT_INT_DATE|LOG_PRINT_ECHO_PREFIX|LOG_PRINT_CRLF, &gc);
-
- if (out)
- man_output_list_push (man, out);
-
- gc_free (&gc);
- }
-}
-
-void
-management_post_tunnel_open (struct management *man, const in_addr_t tun_local_ip)
-{
- /*
- * If we are running management over the tunnel,
- * this is the place to initialize the connection.
- */
- if (man->settings.management_over_tunnel
- && man->connection.state == MS_INITIAL)
- {
- /* listen on our local TUN/TAP IP address */
- man->settings.local.sin_addr.s_addr = htonl (tun_local_ip);
- man_connection_init (man);
- }
-
-}
-
-void
-management_pre_tunnel_close (struct management *man)
-{
- if (man->settings.management_over_tunnel)
- man_connection_close (man);
-}
-
-void
-management_auth_failure (struct management *man, const char *type)
-{
- msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s'", type);
-}
-
-static inline bool
-man_persist_state (unsigned int *persistent, const int n)
-{
- if (persistent)
- {
- if (*persistent == (unsigned int)n)
- return false;
- *persistent = n;
- }
- return true;
-}
-
-#ifdef WIN32
-
-void
-management_socket_set (struct management *man,
- struct event_set *es,
- void *arg,
- unsigned int *persistent)
-{
- if (man->connection.state != MS_INITIAL)
- {
- event_t ev = net_event_win32_get_event (&man->connection.ne32);
- net_event_win32_reset_write (&man->connection.ne32);
-
- switch (man->connection.state)
- {
- case MS_LISTEN:
- if (man_persist_state (persistent, 1))
- event_ctl (es, ev, EVENT_READ, arg);
- break;
- case MS_CC_WAIT_READ:
- if (man_persist_state (persistent, 2))
- event_ctl (es, ev, EVENT_READ, arg);
- break;
- case MS_CC_WAIT_WRITE:
- if (man_persist_state (persistent, 3))
- event_ctl (es, ev, EVENT_READ|EVENT_WRITE, arg);
- break;
- default:
- ASSERT (0);
- }
- }
-}
-
-void
-management_io (struct management *man)
-{
- if (man->connection.state != MS_INITIAL)
- {
- long net_events;
- net_event_win32_reset (&man->connection.ne32);
- net_events = net_event_win32_get_event_mask (&man->connection.ne32);
-
- if (net_events & FD_CLOSE)
- {
- man_reset_client_socket (man, true);
- }
- else
- {
- if (man->connection.state == MS_LISTEN)
- {
- if (net_events & FD_ACCEPT)
- {
- man_accept (man);
- net_event_win32_clear_selected_events (&man->connection.ne32, FD_ACCEPT);
- }
- }
- else if (man->connection.state == MS_CC_WAIT_READ)
- {
- if (net_events & FD_READ)
- {
- man_read (man);
- net_event_win32_clear_selected_events (&man->connection.ne32, FD_READ);
- }
- }
-
- if (man->connection.state == MS_CC_WAIT_WRITE)
- {
- if (net_events & FD_WRITE)
- {
- int status;
- /* dmsg (M_INFO, "FD_WRITE set"); */
- status = man_write (man);
- if (status < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
- {
- /* dmsg (M_INFO, "FD_WRITE cleared"); */
- net_event_win32_clear_selected_events (&man->connection.ne32, FD_WRITE);
- }
- }
- }
- }
- }
-}
-
-#else
-
-void
-management_socket_set (struct management *man,
- struct event_set *es,
- void *arg,
- unsigned int *persistent)
-{
- switch (man->connection.state)
- {
- case MS_LISTEN:
- if (man_persist_state (persistent, 1))
- event_ctl (es, man->connection.sd_top, EVENT_READ, arg);
- break;
- case MS_CC_WAIT_READ:
- if (man_persist_state (persistent, 2))
- event_ctl (es, man->connection.sd_cli, EVENT_READ, arg);
- break;
- case MS_CC_WAIT_WRITE:
- if (man_persist_state (persistent, 3))
- event_ctl (es, man->connection.sd_cli, EVENT_WRITE, arg);
- break;
- case MS_INITIAL:
- break;
- default:
- ASSERT (0);
- }
-}
-
-void
-management_io (struct management *man)
-{
- switch (man->connection.state)
- {
- case MS_LISTEN:
- man_accept (man);
- break;
- case MS_CC_WAIT_READ:
- man_read (man);
- break;
- case MS_CC_WAIT_WRITE:
- man_write (man);
- break;
- case MS_INITIAL:
- break;
- default:
- ASSERT (0);
- }
-}
-
-#endif
-
-static inline bool
-man_standalone_ok (const struct management *man)
-{
- return !man->settings.management_over_tunnel && man->connection.state != MS_INITIAL;
-}
-
-/*
- * Wait for socket I/O when outside primary event loop
- */
-static int
-man_block (struct management *man, volatile int *signal_received, const time_t expire)
-{
- struct timeval tv;
- struct event_set_return esr;
- int status = -1;
-
- if (man_standalone_ok (man))
- {
- do
- {
- event_reset (man->connection.es);
- management_socket_set (man, man->connection.es, NULL, NULL);
- tv.tv_usec = 0;
- tv.tv_sec = 1;
- status = event_wait (man->connection.es, &tv, &esr, 1);
- update_time ();
- if (signal_received)
- {
- get_signal (signal_received);
- if (*signal_received)
- {
- status = -1;
- break;
- }
- }
- /* set SIGINT signal if expiration time exceeded */
- if (expire && now >= expire)
- {
- status = 0;
- if (signal_received)
- *signal_received = SIGINT;
- break;
- }
- } while (status != 1);
- }
- return status;
-}
-
-/*
- * Perform management socket output outside primary event loop
- */
-static void
-man_output_standalone (struct management *man, volatile int *signal_received)
-{
- if (man_standalone_ok (man))
- {
- while (man->connection.state == MS_CC_WAIT_WRITE)
- {
- management_io (man);
- if (man->connection.state == MS_CC_WAIT_WRITE)
- man_block (man, signal_received, 0);
- if (signal_received && *signal_received)
- break;
- }
- }
-}
-
-/*
- * Process management event loop outside primary event loop
- */
-static int
-man_standalone_event_loop (struct management *man, volatile int *signal_received, const time_t expire)
-{
- int status;
- ASSERT (man_standalone_ok (man));
- status = man_block (man, signal_received, expire);
- if (status > 0)
- management_io (man);
- return status;
-}
-
-#define MWCC_PASSWORD_WAIT (1<<0)
-#define MWCC_HOLD_WAIT (1<<1)
-
-/*
- * Block until client connects
- */
-static void
-man_wait_for_client_connection (struct management *man,
- volatile int *signal_received,
- const time_t expire,
- unsigned int flags)
-{
- ASSERT (man_standalone_ok (man));
- if (man->connection.state == MS_LISTEN)
- {
- if (flags & MWCC_PASSWORD_WAIT)
- msg (D_MANAGEMENT, "Need password(s) from management interface, waiting...");
- if (flags & MWCC_HOLD_WAIT)
- msg (D_MANAGEMENT, "Need hold release from management interface, waiting...");
- do {
- man_standalone_event_loop (man, signal_received, expire);
- if (signal_received && *signal_received)
- break;
- } while (man->connection.state == MS_LISTEN || man_password_needed (man));
- }
-}
-
-/*
- * Process the management event loop for sec seconds
- */
-void
-management_event_loop_n_seconds (struct management *man, int sec)
-{
- if (man_standalone_ok (man))
- {
- volatile int signal_received = 0;
- const bool standalone_disabled_save = man->persist.standalone_disabled;
- time_t expire;
-
- man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
-
- /* set expire time */
- update_time ();
- expire = now + sec;
-
- /* if no client connection, wait for one */
- man_wait_for_client_connection (man, &signal_received, expire, 0);
- if (signal_received)
- return;
-
- /* run command processing event loop until we get our username/password */
- while (true)
- {
- man_standalone_event_loop (man, &signal_received, expire);
- if (signal_received)
- return;
- }
-
- /* revert state */
- man->persist.standalone_disabled = standalone_disabled_save;
- }
- else
- {
- sleep (sec);
- }
-}
-
-/*
- * Get a username/password from management channel in standalone mode.
- */
-bool
-management_query_user_pass (struct management *man,
- struct user_pass *up,
- const char *type,
- const bool password_only)
-{
- struct gc_arena gc = gc_new ();
- bool ret = false;
-
- if (man_standalone_ok (man))
- {
- volatile int signal_received = 0;
- const bool standalone_disabled_save = man->persist.standalone_disabled;
- struct buffer alert_msg = alloc_buf_gc (128, &gc);
-
- ret = true;
- man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
- man->persist.special_state_msg = NULL;
-
- CLEAR (man->connection.up_query);
-
- buf_printf (&alert_msg, ">PASSWORD:Need '%s' %s",
- type,
- password_only ? "password" : "username/password");
-
- man_wait_for_client_connection (man, &signal_received, 0, MWCC_PASSWORD_WAIT);
- if (signal_received)
- ret = false;
-
- if (ret)
- {
- man->persist.special_state_msg = BSTR (&alert_msg);
- msg (M_CLIENT, "%s", man->persist.special_state_msg);
-
- /* tell command line parser which info we need */
- man->connection.up_query_mode = password_only ? UP_QUERY_PASS : UP_QUERY_USER_PASS;
- man->connection.up_query_type = type;
-
- /* run command processing event loop until we get our username/password */
- do
- {
- man_standalone_event_loop (man, &signal_received, 0);
- if (signal_received)
- {
- ret = false;
- break;
- }
- } while (!man->connection.up_query.defined);
- }
-
- /* revert state */
- man->connection.up_query_mode = UP_QUERY_DISABLED;
- man->connection.up_query_type = NULL;
- man->persist.standalone_disabled = standalone_disabled_save;
- man->persist.special_state_msg = NULL;
-
- /*
- * Transfer u/p to return object, zero any record
- * we hold in the management object.
- */
- if (ret)
- {
- man->connection.up_query.nocache = up->nocache; /* preserve caller's nocache setting */
- *up = man->connection.up_query;
- }
- CLEAR (man->connection.up_query);
- }
-
- gc_free (&gc);
- return ret;
-}
-
-/*
- * Return true if management_hold() would block
- */
-bool
-management_would_hold (struct management *man)
-{
- return man->settings.hold && !man->persist.hold_release && man_standalone_ok (man);
-}
-
-/*
- * Return true if (from the management interface's perspective) OpenVPN should
- * daemonize.
- */
-bool
-management_should_daemonize (struct management *man)
-{
- return management_would_hold (man) || man->settings.up_query_passwords;
-}
-
-/*
- * If the hold flag is enabled, hibernate until a management client releases the hold.
- * Return true if the caller should not sleep for an additional time interval.
- */
-bool
-management_hold (struct management *man)
-{
- if (management_would_hold (man))
- {
- volatile int signal_received = 0;
- const bool standalone_disabled_save = man->persist.standalone_disabled;
-
- man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
- man->persist.special_state_msg = NULL;
-
- man_wait_for_client_connection (man, &signal_received, 0, MWCC_HOLD_WAIT);
-
- if (!signal_received)
- {
- man->persist.special_state_msg = ">HOLD:Waiting for hold release";
- msg (M_CLIENT, "%s", man->persist.special_state_msg);
-
- /* run command processing event loop until we get our username/password */
- do
- {
- man_standalone_event_loop (man, &signal_received, 0);
- if (signal_received)
- break;
- } while (!man->persist.hold_release);
- }
-
- /* revert state */
- man->persist.standalone_disabled = standalone_disabled_save;
- man->persist.special_state_msg = NULL;
-
- return true;
- }
- return false;
-}
-
-/*
- * struct command_line
- */
-
-struct command_line *
-command_line_new (const int buf_len)
-{
- struct command_line *cl;
- ALLOC_OBJ_CLEAR (cl, struct command_line);
- cl->buf = alloc_buf (buf_len);
- cl->residual = alloc_buf (buf_len);
- return cl;
-}
-
-void
-command_line_reset (struct command_line *cl)
-{
- buf_clear (&cl->buf);
- buf_clear (&cl->residual);
-}
-
-void
-command_line_free (struct command_line *cl)
-{
- command_line_reset (cl);
- free_buf (&cl->buf);
- free_buf (&cl->residual);
- free (cl);
-}
-
-void
-command_line_add (struct command_line *cl, const unsigned char *buf, const int len)
-{
- int i;
- for (i = 0; i < len; ++i)
- {
- if (buf[i] && (isprint(buf[i]) || buf[i] == '\n'))
- {
- if (!buf_write_u8 (&cl->buf, buf[i]))
- buf_clear (&cl->buf);
- }
- }
-}
-
-const unsigned char *
-command_line_get (struct command_line *cl)
-{
- int i;
- const unsigned char *ret = NULL;
-
- i = buf_substring_len (&cl->buf, '\n');
- if (i >= 0)
- {
- buf_copy_excess (&cl->residual, &cl->buf, i);
- buf_chomp (&cl->buf);
- ret = (const unsigned char *) BSTR (&cl->buf);
- }
- return ret;
-}
-
-void
-command_line_next (struct command_line *cl)
-{
- buf_clear (&cl->buf);
- buf_copy (&cl->buf, &cl->residual);
- buf_clear (&cl->residual);
-}
-
-/*
- * struct output_list
- */
-
-struct output_list *
-output_list_new (const int max_size)
-{
- struct output_list *ret;
- ALLOC_OBJ_CLEAR (ret, struct output_list);
- ret->max_size = max_size;
- ret->size = 0;
- return ret;
-}
-
-void
-output_list_free (struct output_list *ol)
-{
- output_list_reset (ol);
- free (ol);
-}
-
-bool
-output_list_defined (const struct output_list *ol)
-{
- return ol->head != NULL;
-}
-
-void
-output_list_reset (struct output_list *ol)
-{
- struct output_entry *e = ol->head;
- while (e)
- {
- struct output_entry *next = e->next;
- free_buf (&e->buf);
- free (e);
- e = next;
- }
- ol->head = ol->tail = NULL;
- ol->size = 0;
-}
-
-void
-output_list_push (struct output_list *ol, const unsigned char *str)
-{
- if (!ol->max_size || ol->size < ol->max_size)
- {
- struct output_entry *e;
- ALLOC_OBJ_CLEAR (e, struct output_entry);
-
- ++ol->size;
- if (ol->tail)
- {
- ASSERT (ol->head);
- ol->tail->next = e;
- }
- else
- {
- ASSERT (!ol->head);
- ol->head = e;
- }
- e->buf = string_alloc_buf ((const char *) str, NULL);
- ol->tail = e;
- }
-}
-
-const struct buffer *
-output_list_peek (struct output_list *ol)
-{
- if (ol->head)
- return &ol->head->buf;
- else
- return NULL;
-}
-
-static void
-output_list_pop (struct output_list *ol)
-{
- if (ol->head)
- {
- struct output_entry *e = ol->head->next;
- free_buf (&ol->head->buf);
- free (ol->head);
- ol->head = e;
- --ol->size;
- if (!e)
- ol->tail = NULL;
- }
-}
-
-void
-output_list_advance (struct output_list *ol, int n)
-{
- if (ol->head)
- {
- struct buffer *buf = &ol->head->buf;
- ASSERT (buf_advance (buf, n));
- if (!BLEN (buf))
- output_list_pop (ol);
- }
-}
-
-/*
- * struct log_entry
- */
-
-const char *
-log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena *gc)
-{
- struct buffer out = alloc_buf_gc (ERR_BUF_SIZE, gc);
- if (flags & LOG_FATAL_NOTIFY)
- buf_printf (&out, ">FATAL:");
- if (flags & LOG_PRINT_LOG_PREFIX)
- buf_printf (&out, ">LOG:");
- if (flags & LOG_PRINT_ECHO_PREFIX)
- buf_printf (&out, ">ECHO:");
- if (flags & LOG_PRINT_STATE_PREFIX)
- buf_printf (&out, ">STATE:");
- if (flags & LOG_PRINT_INT_DATE)
- buf_printf (&out, "%u,", (unsigned int)e->timestamp);
- if (flags & LOG_PRINT_MSG_FLAGS)
- buf_printf (&out, "%s,", msg_flags_string (e->u.msg_flags, gc));
- if (flags & LOG_PRINT_STATE)
- buf_printf (&out, "%s,", man_state_name (e->u.state));
- if (e->string)
- buf_printf (&out, "%s", e->string);
- if (flags & LOG_PRINT_LOCAL_IP)
- buf_printf (&out, ",%s", print_in_addr_t (e->local_ip, IA_EMPTY_IF_UNDEF, gc));
- if (flags & LOG_PRINT_CRLF)
- buf_printf (&out, "\r\n");
- return BSTR (&out);
-}
-
-static void
-log_entry_free_contents (struct log_entry *e)
-{
- if (e->string)
- free ((char *)e->string);
- CLEAR (*e);
-}
-
-/*
- * struct log_history
- */
-
-static inline int
-log_index (const struct log_history *h, int i)
-{
- return modulo_add (h->base, i, h->capacity);
-}
-
-static void
-log_history_obj_init (struct log_history *h, int capacity)
-{
- CLEAR (*h);
- h->capacity = capacity;
- ALLOC_ARRAY_CLEAR (h->array, struct log_entry, capacity);
-}
-
-struct log_history *
-log_history_init (const int capacity)
-{
- struct log_history *h;
- ASSERT (capacity > 0);
- ALLOC_OBJ (h, struct log_history);
- log_history_obj_init (h, capacity);
- return h;
-}
-
-static void
-log_history_free_contents (struct log_history *h)
-{
- int i;
- for (i = 0; i < h->size; ++i)
- log_entry_free_contents (&h->array[log_index(h, i)]);
- free (h->array);
-}
-
-void
-log_history_close (struct log_history *h)
-{
- log_history_free_contents (h);
- free (h);
-}
-
-void
-log_history_add (struct log_history *h, const struct log_entry *le)
-{
- struct log_entry *e;
- ASSERT (h->size >= 0 && h->size <= h->capacity);
- if (h->size == h->capacity)
- {
- e = &h->array[h->base];
- log_entry_free_contents (e);
- h->base = log_index (h, 1);
- }
- else
- {
- e = &h->array[log_index(h, h->size)];
- ++h->size;
- }
-
- *e = *le;
- e->string = string_alloc (le->string, NULL);
-}
-
-void
-log_history_resize (struct log_history *h, const int capacity)
-{
- if (capacity != h->capacity)
- {
- struct log_history newlog;
- int i;
-
- ASSERT (capacity > 0);
- log_history_obj_init (&newlog, capacity);
-
- for (i = 0; i < h->size; ++i)
- log_history_add (&newlog, &h->array[log_index(h, i)]);
-
- log_history_free_contents (h);
- *h = newlog;
- }
-}
-
-const struct log_entry *
-log_history_ref (const struct log_history *h, const int index)
-{
- if (index >= 0 && index < h->size)
- return &h->array[log_index(h, (h->size - 1) - index)];
- else
- return NULL;
-}
-
-#else
-static void dummy(void) {}
-#endif /* ENABLE_MANAGEMENT */