summaryrefslogtreecommitdiff
path: root/openvpn/src/openvpn/sig.c
diff options
context:
space:
mode:
Diffstat (limited to 'openvpn/src/openvpn/sig.c')
-rw-r--r--openvpn/src/openvpn/sig.c386
1 files changed, 386 insertions, 0 deletions
diff --git a/openvpn/src/openvpn/sig.c b/openvpn/src/openvpn/sig.c
new file mode 100644
index 0000000..0ebde24
--- /dev/null
+++ b/openvpn/src/openvpn/sig.c
@@ -0,0 +1,386 @@
+/*
+ * 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-2010 OpenVPN Technologies, Inc. <sales@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 HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include "buffer.h"
+#include "error.h"
+#include "win32.h"
+#include "init.h"
+#include "status.h"
+#include "sig.h"
+#include "occ.h"
+#include "manage.h"
+#include "openvpn.h"
+
+#include "memdbg.h"
+
+/* Handle signals */
+
+struct signal_info siginfo_static; /* GLOBAL */
+
+struct signame {
+ int value;
+ const char *upper;
+ const char *lower;
+};
+
+static const struct signame signames[] = {
+ { SIGINT, "SIGINT", "sigint"},
+ { SIGTERM, "SIGTERM", "sigterm" },
+ { SIGHUP, "SIGHUP", "sighup" },
+ { SIGUSR1, "SIGUSR1", "sigusr1" },
+ { SIGUSR2, "SIGUSR2", "sigusr2" }
+};
+
+int
+parse_signal (const char *signame)
+{
+ int i;
+ for (i = 0; i < (int)SIZE (signames); ++i)
+ {
+ if (!strcmp (signame, signames[i].upper))
+ return signames[i].value;
+ }
+ return -1;
+}
+
+const char *
+signal_name (const int sig, const bool upper)
+{
+ int i;
+ for (i = 0; i < (int)SIZE (signames); ++i)
+ {
+ if (sig == signames[i].value)
+ return upper ? signames[i].upper : signames[i].lower;
+ }
+ return "UNKNOWN";
+}
+
+const char *
+signal_description (const int signum, const char *sigtext)
+{
+ if (sigtext)
+ return sigtext;
+ else
+ return signal_name (signum, false);
+}
+
+void
+throw_signal (const int signum)
+{
+ siginfo_static.signal_received = signum;
+ siginfo_static.hard = true;
+}
+
+void
+throw_signal_soft (const int signum, const char *signal_text)
+{
+ siginfo_static.signal_received = signum;
+ siginfo_static.hard = false;
+ siginfo_static.signal_text = signal_text;
+}
+
+static void
+signal_reset (struct signal_info *si)
+{
+ if (si)
+ {
+ si->signal_received = 0;
+ si->signal_text = NULL;
+ si->hard = false;
+ }
+}
+
+void
+print_signal (const struct signal_info *si, const char *title, int msglevel)
+{
+ if (si)
+ {
+ const char *hs = (si->hard ? "hard" : "soft");
+ const char *type = (si->signal_text ? si->signal_text : "");
+ const char *t = (title ? title : "process");
+
+ switch (si->signal_received)
+ {
+ case SIGINT:
+ case SIGTERM:
+ msg (msglevel, "%s[%s,%s] received, %s exiting",
+ signal_name (si->signal_received, true), hs, type, t);
+ break;
+ case SIGHUP:
+ case SIGUSR1:
+ msg (msglevel, "%s[%s,%s] received, %s restarting",
+ signal_name (si->signal_received, true), hs, type, t);
+ break;
+ default:
+ msg (msglevel, "Unknown signal %d [%s,%s] received by %s", si->signal_received, hs, type, t);
+ break;
+ }
+ }
+ else
+ msg (msglevel, "Unknown signal received");
+}
+
+/*
+ * Call management interface with restart info
+ */
+void
+signal_restart_status (const struct signal_info *si)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ int state = -1;
+ switch (si->signal_received)
+ {
+ case SIGINT:
+ case SIGTERM:
+ state = OPENVPN_STATE_EXITING;
+ break;
+ case SIGHUP:
+ case SIGUSR1:
+ state = OPENVPN_STATE_RECONNECTING;
+ break;
+ }
+
+ if (state >= 0)
+ management_set_state (management,
+ state,
+ si->signal_text ? si->signal_text : signal_name (si->signal_received, true),
+ (in_addr_t)0,
+ (in_addr_t)0);
+ }
+#endif
+}
+
+#ifdef HAVE_SIGNAL_H
+
+/* normal signal handler, when we are in event loop */
+static void
+signal_handler (const int signum)
+{
+ throw_signal (signum);
+ signal (signum, signal_handler);
+}
+
+#endif
+
+/* set handlers for unix signals */
+
+#ifdef HAVE_SIGNAL_H
+#define SM_UNDEF 0
+#define SM_PRE_INIT 1
+#define SM_POST_INIT 2
+static int signal_mode; /* GLOBAL */
+#endif
+
+void
+pre_init_signal_catch (void)
+{
+#ifndef WIN32
+#ifdef HAVE_SIGNAL_H
+ signal_mode = SM_PRE_INIT;
+ signal (SIGINT, signal_handler);
+ signal (SIGTERM, signal_handler);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+ signal (SIGUSR2, SIG_IGN);
+ signal (SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGNAL_H */
+#endif /* WIN32 */
+}
+
+void
+post_init_signal_catch (void)
+{
+#ifndef WIN32
+#ifdef HAVE_SIGNAL_H
+ signal_mode = SM_POST_INIT;
+ signal (SIGINT, signal_handler);
+ signal (SIGTERM, signal_handler);
+ signal (SIGHUP, signal_handler);
+ signal (SIGUSR1, signal_handler);
+ signal (SIGUSR2, signal_handler);
+ signal (SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGNAL_H */
+#endif
+}
+
+/* called after daemonization to retain signal settings */
+void
+restore_signal_state (void)
+{
+#ifdef HAVE_SIGNAL_H
+ if (signal_mode == SM_PRE_INIT)
+ pre_init_signal_catch ();
+ else if (signal_mode == SM_POST_INIT)
+ post_init_signal_catch ();
+#endif
+}
+
+/*
+ * Print statistics.
+ *
+ * Triggered by SIGUSR2 or F2 on Windows.
+ */
+void
+print_status (const struct context *c, struct status_output *so)
+{
+ struct gc_arena gc = gc_new ();
+
+ status_reset (so);
+
+ status_printf (so, "OpenVPN STATISTICS");
+ status_printf (so, "Updated,%s", time_string (0, 0, false, &gc));
+ status_printf (so, "TUN/TAP read bytes," counter_format, c->c2.tun_read_bytes);
+ status_printf (so, "TUN/TAP write bytes," counter_format, c->c2.tun_write_bytes);
+ status_printf (so, "TCP/UDP read bytes," counter_format, c->c2.link_read_bytes);
+ status_printf (so, "TCP/UDP write bytes," counter_format, c->c2.link_write_bytes);
+ status_printf (so, "Auth read bytes," counter_format, c->c2.link_read_bytes_auth);
+#ifdef ENABLE_LZO
+ if (lzo_defined (&c->c2.lzo_compwork))
+ lzo_print_stats (&c->c2.lzo_compwork, so);
+#endif
+#ifdef PACKET_TRUNCATION_CHECK
+ status_printf (so, "TUN read truncations," counter_format, c->c2.n_trunc_tun_read);
+ status_printf (so, "TUN write truncations," counter_format, c->c2.n_trunc_tun_write);
+ status_printf (so, "Pre-encrypt truncations," counter_format, c->c2.n_trunc_pre_encrypt);
+ status_printf (so, "Post-decrypt truncations," counter_format, c->c2.n_trunc_post_decrypt);
+#endif
+#ifdef WIN32
+ if (tuntap_defined (c->c1.tuntap))
+ status_printf (so, "TAP-WIN32 driver status,\"%s\"",
+ tap_win_getinfo (c->c1.tuntap, &gc));
+#endif
+
+ status_printf (so, "END");
+ status_flush (so);
+ gc_free (&gc);
+}
+
+#ifdef ENABLE_OCC
+/*
+ * Handle the triggering and time-wait of explicit
+ * exit notification.
+ */
+
+static void
+process_explicit_exit_notification_init (struct context *c)
+{
+ msg (M_INFO, "SIGTERM received, sending exit notification to peer");
+ event_timeout_init (&c->c2.explicit_exit_notification_interval, 1, 0);
+ reset_coarse_timers (c);
+ signal_reset (c->sig);
+ halt_non_edge_triggered_signals ();
+ c->c2.explicit_exit_notification_time_wait = now;
+}
+
+void
+process_explicit_exit_notification_timer_wakeup (struct context *c)
+{
+ if (event_timeout_trigger (&c->c2.explicit_exit_notification_interval,
+ &c->c2.timeval,
+ ETT_DEFAULT))
+ {
+ ASSERT (c->c2.explicit_exit_notification_time_wait && c->options.ce.explicit_exit_notification);
+ if (now >= c->c2.explicit_exit_notification_time_wait + c->options.ce.explicit_exit_notification)
+ {
+ event_timeout_clear (&c->c2.explicit_exit_notification_interval);
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "exit-with-notification";
+ }
+ else
+ {
+ c->c2.occ_op = OCC_EXIT;
+ }
+ }
+}
+#endif
+
+/*
+ * Process signals
+ */
+
+void
+remap_signal (struct context *c)
+{
+ if (c->sig->signal_received == SIGUSR1 && c->options.remap_sigusr1)
+ c->sig->signal_received = c->options.remap_sigusr1;
+}
+
+static void
+process_sigusr2 (const struct context *c)
+{
+ struct status_output *so = status_open (NULL, 0, M_INFO, NULL, 0);
+ print_status (c, so);
+ status_close (so);
+ signal_reset (c->sig);
+}
+
+static bool
+process_sigterm (struct context *c)
+{
+ bool ret = true;
+#ifdef ENABLE_OCC
+ if (c->options.ce.explicit_exit_notification
+ && !c->c2.explicit_exit_notification_time_wait)
+ {
+ process_explicit_exit_notification_init (c);
+ ret = false;
+ }
+#endif
+ return ret;
+}
+
+bool
+process_signal (struct context *c)
+{
+ bool ret = true;
+
+ if (c->sig->signal_received == SIGTERM || c->sig->signal_received == SIGINT)
+ {
+ ret = process_sigterm (c);
+ }
+ else if (c->sig->signal_received == SIGUSR2)
+ {
+ process_sigusr2 (c);
+ ret = false;
+ }
+ return ret;
+}
+
+void
+register_signal (struct context *c, int sig, const char *text)
+{
+ if (c->sig->signal_received != SIGTERM)
+ c->sig->signal_received = sig;
+ c->sig->signal_text = text;
+}