diff options
Diffstat (limited to 'src/openvpn/sig.c')
-rw-r--r-- | src/openvpn/sig.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/src/openvpn/sig.c b/src/openvpn/sig.c new file mode 100644 index 0000000..0ebde24 --- /dev/null +++ b/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; +} |