6fbf66fa |
/*
* 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.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* 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.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#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 { |
81d882d5 |
int value;
const char *upper;
const char *lower; |
6fbf66fa |
};
static const struct signame signames[] = { |
81d882d5 |
{ SIGINT, "SIGINT", "sigint"},
{ SIGTERM, "SIGTERM", "sigterm" },
{ SIGHUP, "SIGHUP", "sighup" },
{ SIGUSR1, "SIGUSR1", "sigusr1" },
{ SIGUSR2, "SIGUSR2", "sigusr2" } |
6fbf66fa |
};
int |
81d882d5 |
parse_signal(const char *signame) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < (int)SIZE(signames); ++i) |
6fbf66fa |
{ |
81d882d5 |
if (!strcmp(signame, signames[i].upper))
{
return signames[i].value;
} |
6fbf66fa |
} |
81d882d5 |
return -1; |
6fbf66fa |
}
const char * |
81d882d5 |
signal_name(const int sig, const bool upper) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < (int)SIZE(signames); ++i) |
6fbf66fa |
{ |
81d882d5 |
if (sig == signames[i].value)
{
return upper ? signames[i].upper : signames[i].lower;
} |
6fbf66fa |
} |
81d882d5 |
return "UNKNOWN"; |
6fbf66fa |
}
const char * |
81d882d5 |
signal_description(const int signum, const char *sigtext) |
6fbf66fa |
{ |
81d882d5 |
if (sigtext)
{
return sigtext;
}
else
{
return signal_name(signum, false);
} |
6fbf66fa |
}
void |
81d882d5 |
throw_signal(const int signum) |
6fbf66fa |
{ |
81d882d5 |
siginfo_static.signal_received = signum;
siginfo_static.source = SIG_SOURCE_HARD; |
6fbf66fa |
}
|
4f404ad3 |
void |
81d882d5 |
throw_signal_soft(const int signum, const char *signal_text) |
4f404ad3 |
{ |
81d882d5 |
siginfo_static.signal_received = signum;
siginfo_static.source = SIG_SOURCE_SOFT;
siginfo_static.signal_text = signal_text; |
4f404ad3 |
}
|
6fbf66fa |
static void |
81d882d5 |
signal_reset(struct signal_info *si) |
6fbf66fa |
{ |
81d882d5 |
if (si) |
6fbf66fa |
{ |
81d882d5 |
si->signal_received = 0;
si->signal_text = NULL;
si->source = SIG_SOURCE_SOFT; |
6fbf66fa |
}
}
void |
81d882d5 |
print_signal(const struct signal_info *si, const char *title, int msglevel) |
6fbf66fa |
{ |
81d882d5 |
if (si) |
6fbf66fa |
{ |
81d882d5 |
const char *type = (si->signal_text ? si->signal_text : "");
const char *t = (title ? title : "process");
const char *hs = NULL;
switch (si->source) |
23d61c56 |
{ |
81d882d5 |
case SIG_SOURCE_SOFT:
hs = "soft";
break;
case SIG_SOURCE_HARD:
hs = "hard";
break;
case SIG_SOURCE_CONNECTION_FAILED:
hs = "connection failed(soft)";
break;
default:
ASSERT(0); |
23d61c56 |
} |
6fbf66fa |
|
81d882d5 |
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"); |
6fbf66fa |
}
}
/*
* Call management interface with restart info
*/
void |
81d882d5 |
signal_restart_status(const struct signal_info *si) |
6fbf66fa |
{
#ifdef ENABLE_MANAGEMENT |
81d882d5 |
if (management) |
6fbf66fa |
{ |
81d882d5 |
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),
NULL,
NULL,
NULL,
NULL);
} |
6fbf66fa |
} |
81d882d5 |
#endif /* ifdef ENABLE_MANAGEMENT */ |
6fbf66fa |
}
#ifdef HAVE_SIGNAL_H
/* normal signal handler, when we are in event loop */
static void |
81d882d5 |
signal_handler(const int signum) |
6fbf66fa |
{ |
81d882d5 |
throw_signal(signum);
signal(signum, signal_handler); |
6fbf66fa |
}
#endif
|
225d5fe9 |
/* 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
|
6fbf66fa |
void |
81d882d5 |
pre_init_signal_catch(void) |
6fbf66fa |
{ |
445b192a |
#ifndef _WIN32 |
6fbf66fa |
#ifdef HAVE_SIGNAL_H |
81d882d5 |
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); |
6fbf66fa |
#endif /* HAVE_SIGNAL_H */ |
445b192a |
#endif /* _WIN32 */ |
6fbf66fa |
}
void |
81d882d5 |
post_init_signal_catch(void) |
6fbf66fa |
{ |
445b192a |
#ifndef _WIN32 |
6fbf66fa |
#ifdef HAVE_SIGNAL_H |
81d882d5 |
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); |
6fbf66fa |
#endif /* HAVE_SIGNAL_H */ |
51bd56f4 |
#endif |
6fbf66fa |
}
|
225d5fe9 |
/* called after daemonization to retain signal settings */
void |
81d882d5 |
restore_signal_state(void) |
225d5fe9 |
{
#ifdef HAVE_SIGNAL_H |
81d882d5 |
if (signal_mode == SM_PRE_INIT)
{
pre_init_signal_catch();
}
else if (signal_mode == SM_POST_INIT)
{
post_init_signal_catch();
} |
225d5fe9 |
#endif
}
|
6fbf66fa |
/*
* Print statistics.
*
* Triggered by SIGUSR2 or F2 on Windows.
*/
void |
81d882d5 |
print_status(const struct context *c, struct status_output *so) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
6fbf66fa |
|
81d882d5 |
status_reset(so); |
6fbf66fa |
|
81d882d5 |
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); |
38d96bd7 |
#ifdef USE_COMP |
81d882d5 |
if (c->c2.comp_context)
{
comp_print_stats(c->c2.comp_context, so);
} |
6fbf66fa |
#endif |
3c7f2f55 |
#ifdef PACKET_TRUNCATION_CHECK |
81d882d5 |
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); |
3c7f2f55 |
#endif |
445b192a |
#ifdef _WIN32 |
81d882d5 |
if (tuntap_defined(c->c1.tuntap))
{
status_printf(so, "TAP-WIN32 driver status,\"%s\"",
tap_win_getinfo(c->c1.tuntap, &gc));
} |
6fbf66fa |
#endif
|
81d882d5 |
status_printf(so, "END");
status_flush(so);
gc_free(&gc); |
6fbf66fa |
}
#ifdef ENABLE_OCC
/*
* Handle the triggering and time-wait of explicit
* exit notification.
*/
static void |
81d882d5 |
process_explicit_exit_notification_init(struct context *c) |
6fbf66fa |
{ |
81d882d5 |
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; |
6fbf66fa |
}
void |
81d882d5 |
process_explicit_exit_notification_timer_wakeup(struct context *c) |
6fbf66fa |
{ |
81d882d5 |
if (event_timeout_trigger(&c->c2.explicit_exit_notification_interval,
&c->c2.timeval,
ETT_DEFAULT)) |
6fbf66fa |
{ |
81d882d5 |
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;
} |
6fbf66fa |
}
} |
81d882d5 |
#endif /* ifdef ENABLE_OCC */ |
6fbf66fa |
/*
* Process signals
*/
void |
81d882d5 |
remap_signal(struct context *c) |
6fbf66fa |
{ |
81d882d5 |
if (c->sig->signal_received == SIGUSR1 && c->options.remap_sigusr1)
{
c->sig->signal_received = c->options.remap_sigusr1;
} |
6fbf66fa |
}
static void |
81d882d5 |
process_sigusr2(const struct context *c) |
6fbf66fa |
{ |
81d882d5 |
struct status_output *so = status_open(NULL, 0, M_INFO, NULL, 0);
print_status(c, so);
status_close(so);
signal_reset(c->sig); |
6fbf66fa |
}
static bool |
81d882d5 |
process_sigterm(struct context *c) |
6fbf66fa |
{ |
81d882d5 |
bool ret = true; |
6fbf66fa |
#ifdef ENABLE_OCC |
81d882d5 |
if (c->options.ce.explicit_exit_notification
&& !c->c2.explicit_exit_notification_time_wait) |
6fbf66fa |
{ |
81d882d5 |
process_explicit_exit_notification_init(c);
ret = false; |
6fbf66fa |
}
#endif |
81d882d5 |
return ret; |
6fbf66fa |
}
|
63b3e000 |
/**
* If a restart signal is received during exit-notification, reset the |
f25a0217 |
* signal and return true. If its a soft restart signal from the event loop
* which implies the loop cannot continue, remap to SIGTERM to exit promptly. |
63b3e000 |
*/
static bool |
81d882d5 |
ignore_restart_signals(struct context *c) |
63b3e000 |
{ |
81d882d5 |
bool ret = false; |
63b3e000 |
#ifdef ENABLE_OCC |
81d882d5 |
if ( (c->sig->signal_received == SIGUSR1 || c->sig->signal_received == SIGHUP)
&& event_timeout_defined(&c->c2.explicit_exit_notification_interval) ) |
63b3e000 |
{ |
81d882d5 |
if (c->sig->source == SIG_SOURCE_HARD)
{
msg(M_INFO, "Ignoring %s received during exit notification",
signal_name(c->sig->signal_received, true));
signal_reset(c->sig); |
f25a0217 |
ret = true; |
81d882d5 |
}
else
{
msg(M_INFO, "Converting soft %s received during exit notification to SIGTERM",
signal_name(c->sig->signal_received, true)); |
f25a0217 |
register_signal(c, SIGTERM, "exit-with-notification");
ret = false; |
81d882d5 |
} |
63b3e000 |
}
#endif |
81d882d5 |
return ret; |
63b3e000 |
}
|
6fbf66fa |
bool |
81d882d5 |
process_signal(struct context *c) |
6fbf66fa |
{ |
81d882d5 |
bool ret = true; |
6fbf66fa |
|
81d882d5 |
if (ignore_restart_signals(c))
{
ret = false;
}
else if (c->sig->signal_received == SIGTERM || c->sig->signal_received == SIGINT) |
6fbf66fa |
{ |
81d882d5 |
ret = process_sigterm(c); |
6fbf66fa |
} |
81d882d5 |
else if (c->sig->signal_received == SIGUSR2) |
6fbf66fa |
{ |
81d882d5 |
process_sigusr2(c);
ret = false; |
6fbf66fa |
} |
81d882d5 |
return ret; |
6fbf66fa |
} |
7fc00d4c |
void |
81d882d5 |
register_signal(struct context *c, int sig, const char *text) |
7fc00d4c |
{ |
81d882d5 |
if (c->sig->signal_received != SIGTERM)
{
c->sig->signal_received = sig;
}
c->sig->signal_text = text; |
7fc00d4c |
} |