src/openvpn/sig.c
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
 }