mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-28 04:03:51 +08:00
225d5fe945
exit from initial management hold. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@1427 e7ae566f-a301-0410-adde-c780ea21d3b5
384 lines
9.1 KiB
C
384 lines
9.1 KiB
C
/*
|
|
* 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"
|
|
|
|
#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);
|
|
}
|
|
|
|
/* temporary signal handler, before we are fully initialized */
|
|
static void
|
|
signal_handler_exit (const int signum)
|
|
{
|
|
msg (M_FATAL,
|
|
"Signal %d (%s) received during initialization, exiting",
|
|
signum, signal_description (signum, NULL));
|
|
}
|
|
|
|
#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)
|
|
{
|
|
#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 */
|
|
}
|
|
|
|
void
|
|
post_init_signal_catch (void)
|
|
{
|
|
#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 */
|
|
}
|
|
|
|
/* 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, PACKAGE_NAME " 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 USE_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_win32_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.explicit_exit_notification);
|
|
if (now >= c->c2.explicit_exit_notification_time_wait + c->options.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.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;
|
|
}
|