From 2ec3443b1d6570e042bd7705b6d811e51f52fe67 Mon Sep 17 00:00:00 2001 From: Bill Williams Date: Tue, 29 Oct 2019 11:34:29 -0700 Subject: [PATCH] update parodus shutdown --- CHANGELOG.md | 2 ++ src/conn_interface.c | 5 +++- src/conn_interface.h | 2 +- src/connection.c | 46 +++++++++++++++++++++++++++---- src/connection.h | 5 ++++ src/main.c | 54 ++++++++++++++++++++++++++----------- tests/test_conn_interface.c | 16 +++++++++++ tests/test_connection.c | 54 ++++++++++++++++++++++++++++++++++++- 8 files changed, 160 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb8921..28926d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - requestNewAuthToken will clear the token if it fails. - request auth token on every retry, not just after 403 - update to use nopoll v 1.0.2 +- provide signal handlers so we shut down properly when INCLUDE_BREAKPAD active +- send status code and reason in close message - Add pause/resume heartBeatTimer - parodus event handler to listen to interface_down and interface_up event - Pause connection retry during interface_down event diff --git a/src/conn_interface.c b/src/conn_interface.c index b89e366..41d6514 100644 --- a/src/conn_interface.c +++ b/src/conn_interface.c @@ -214,13 +214,16 @@ void createSocketConnection(void (* initKeypress)()) deleteAllClients (); + ParodusInfo ("reconnect reason at close %s\n", get_global_reconnect_reason()); + ParodusInfo ("shutdown reason at close %s\n", get_global_shutdown_reason()); close_and_unref_connection(get_global_conn()); nopoll_ctx_unref(ctx); nopoll_cleanup_library(); curl_global_cleanup(); } -void shutdownSocketConnection(void) { +void shutdownSocketConnection(char *reason) { + set_global_shutdown_reason (reason); g_shutdown = true; } diff --git a/src/conn_interface.h b/src/conn_interface.h index bc39cd5..eb6afec 100644 --- a/src/conn_interface.h +++ b/src/conn_interface.h @@ -45,7 +45,7 @@ extern UpStreamMsg *UpStreamMsgQ; * and creates the intial connection and manages the connection wait, close mechanisms. */ void createSocketConnection(void (* initKeypress)()); -void shutdownSocketConnection(void); +void shutdownSocketConnection(char *reason); #ifdef __cplusplus } diff --git a/src/connection.c b/src/connection.c index 8bb786b..15efece 100644 --- a/src/connection.c +++ b/src/connection.c @@ -39,10 +39,29 @@ #define HTTP_CUSTOM_HEADER_COUNT 5 #define INITIAL_CJWT_RETRY -2 +/* Close codes defined in RFC 6455, section 11.7. */ +enum { + CloseNormalClosure = 1000, + CloseGoingAway = 1001, + CloseProtocolError = 1002, + CloseUnsupportedData = 1003, + CloseNoStatus = 1005, + CloseAbnormalClosure = 1006, + CloseInvalidFramePayloadData = 1007, + ClosePolicyViolation = 1008, + CloseMessageTooBig = 1009, + CloseMandatoryExtension = 1010, + CloseInternalServerErr = 1011, + CloseServiceRestart = 1012, + CloseTryAgainLater = 1013, + CloseTLSHandshake = 1015 +}; + /*----------------------------------------------------------------------------*/ /* File Scoped Variables */ /*----------------------------------------------------------------------------*/ +static char *shutdown_reason = SHUTDOWN_REASON_PARODUS_STOP; /* goes in the close message */ static char *reconnect_reason = "webpa_process_starts"; static int cloud_disconnect_max_time = 5; static noPollConn *g_conn = NULL; @@ -65,6 +84,16 @@ void set_global_conn(noPollConn *conn) g_conn = conn; } +char *get_global_shutdown_reason() +{ + return shutdown_reason; +} + +void set_global_shutdown_reason(char *reason) +{ + shutdown_reason = reason; +} + char *get_global_reconnect_reason() { return reconnect_reason; @@ -458,7 +487,7 @@ int wait_connection_ready (create_connection_ctx_t *ctx) memset (cfg->webpa_auth_token, 0, sizeof(cfg->webpa_auth_token)); ParodusError("Received Unauthorized response with status: %d\n", wait_status); OnboardLog("Received Unauthorized response with status: %d\n", wait_status); - return WAIT_ACTION_RETRY; + return WAIT_FAIL; } ParodusError("Client connection timeout\n"); ParodusError("RDK-10037 - WebPA Connection Lost\n"); @@ -694,10 +723,17 @@ static noPollConnOpts * createConnOpts (char * extra_headers, bool secure) void close_and_unref_connection(noPollConn *conn) { if (conn) { - nopoll_conn_close(conn); - - get_parodus_cfg()->cloud_status = CLOUD_STATUS_OFFLINE; - ParodusInfo("cloud_status set as %s after connection close\n", get_parodus_cfg()->cloud_status); + const char *reason = get_global_shutdown_reason(); + int reason_len = 0; + int status = CloseNoStatus; + if (NULL != reason) { + reason_len = (int) strlen (reason); + status = CloseNormalClosure; + } + nopoll_conn_close_ext(conn, status, reason, reason_len); + if (0 < nopoll_conn_ref_count (conn)) { + nopoll_conn_unref(conn); + } } } diff --git a/src/connection.h b/src/connection.h index ecb4394..0ddcaad 100644 --- a/src/connection.h +++ b/src/connection.h @@ -33,6 +33,8 @@ extern "C" { /*----------------------------------------------------------------------------*/ /* File Scoped Variables */ /*----------------------------------------------------------------------------*/ +#define SHUTDOWN_REASON_PARODUS_STOP "parodus_stopping" +#define SHUTDOWN_REASON_SYSTEM_RESTART "system_restarting" /** * parodusOnPingStatusChangeHandler - Function pointer @@ -57,6 +59,9 @@ void close_and_unref_connection(noPollConn *); noPollConn *get_global_conn(void); void set_global_conn(noPollConn *); +char *get_global_shutdown_reason(); +void set_global_shutdown_reason(char *reason); + char *get_global_reconnect_reason(); void set_global_reconnect_reason(char *reason); diff --git a/src/main.c b/src/main.c index b03870c..9a5d3f0 100644 --- a/src/main.c +++ b/src/main.c @@ -19,14 +19,14 @@ #include "stdlib.h" #include "config.h" #include "auth_token.h" +#include "connection.h" #include "conn_interface.h" #include "parodus_log.h" #include #ifdef INCLUDE_BREAKPAD #include "breakpad_wrapper.h" -#else -#include "signal.h" #endif +#include "signal.h" /*----------------------------------------------------------------------------*/ /* Macros */ @@ -36,7 +36,7 @@ /*----------------------------------------------------------------------------*/ /* Data Structures */ /*----------------------------------------------------------------------------*/ -/* none */ +typedef void Sigfunc(int); /*----------------------------------------------------------------------------*/ /* File Scoped Variables */ @@ -46,30 +46,53 @@ /*----------------------------------------------------------------------------*/ /* Function Prototypes */ /*----------------------------------------------------------------------------*/ -#ifndef INCLUDE_BREAKPAD static void sig_handler(int sig); + +Sigfunc * +signal (int signo, Sigfunc *func) +{ + struct sigaction act, oact; + + act.sa_handler = func; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + if (signo == SIGALRM) { +#ifdef SA_INTERRUPT + act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ #endif + } else { +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD */ +#endif + } + if (sigaction (signo, &act, &oact) < 0) { + ParodusError ("Signal Handler for signal %d not installed!\n", signo); + return (SIG_ERR); + } + return (oact.sa_handler); +} /*----------------------------------------------------------------------------*/ /* External Functions */ /*----------------------------------------------------------------------------*/ int main( int argc, char **argv) { -#ifdef INCLUDE_BREAKPAD - breakpad_ExceptionHandler(); -#else + set_global_shutdown_reason (SHUTDOWN_REASON_PARODUS_STOP); signal(SIGTERM, sig_handler); - signal(SIGINT, sig_handler); + signal(SIGINT, sig_handler); signal(SIGUSR1, sig_handler); signal(SIGUSR2, sig_handler); - signal(SIGSEGV, sig_handler); - signal(SIGBUS, sig_handler); signal(SIGKILL, sig_handler); - signal(SIGFPE, sig_handler); - signal(SIGILL, sig_handler); signal(SIGQUIT, sig_handler); signal(SIGHUP, sig_handler); signal(SIGALRM, sig_handler); +#ifdef INCLUDE_BREAKPAD + breakpad_ExceptionHandler(); +#else + signal(SIGSEGV, sig_handler); + signal(SIGBUS, sig_handler); + signal(SIGFPE, sig_handler); + signal(SIGILL, sig_handler); #endif ParodusCfg *cfg; @@ -97,7 +120,6 @@ const char *rdk_logger_module_fetch(void) /*----------------------------------------------------------------------------*/ /* Internal functions */ /*----------------------------------------------------------------------------*/ -#ifndef INCLUDE_BREAKPAD static void sig_handler(int sig) { @@ -105,12 +127,13 @@ static void sig_handler(int sig) { signal(SIGINT, sig_handler); /* reset it to this function */ ParodusInfo("SIGINT received!\n"); - shutdownSocketConnection(); + shutdownSocketConnection(SHUTDOWN_REASON_PARODUS_STOP); } else if ( sig == SIGUSR1 ) { signal(SIGUSR1, sig_handler); /* reset it to this function */ ParodusInfo("SIGUSR1 received!\n"); + shutdownSocketConnection(SHUTDOWN_REASON_SYSTEM_RESTART); } else if ( sig == SIGUSR2 ) { @@ -134,8 +157,7 @@ static void sig_handler(int sig) else { ParodusInfo("Signal %d received!\n", sig); - shutdownSocketConnection(); + shutdownSocketConnection(SHUTDOWN_REASON_PARODUS_STOP); } } -#endif diff --git a/tests/test_conn_interface.c b/tests/test_conn_interface.c index 624f8e2..5c4c2ba 100644 --- a/tests/test_conn_interface.c +++ b/tests/test_conn_interface.c @@ -33,6 +33,7 @@ /*----------------------------------------------------------------------------*/ /* File Scoped Variables */ /*----------------------------------------------------------------------------*/ +static char *reconnect_reason = "webpa_process_starts"; UpStreamMsg *UpStreamMsgQ; ParodusMsg *ParodusMsgQ; pthread_mutex_t g_mutex=PTHREAD_MUTEX_INITIALIZER; @@ -75,6 +76,21 @@ noPollMutexUnlock mutex_unlock function_called(); } +char *get_global_reconnect_reason() +{ + return reconnect_reason; +} + +char *get_global_shutdown_reason() +{ + return SHUTDOWN_REASON_PARODUS_STOP; +} + +void set_global_shutdown_reason(char *reason) +{ + UNUSED(reason); +} + void start_conn_in_progress (void) { } diff --git a/tests/test_connection.c b/tests/test_connection.c index 1441637..4cebfc3 100644 --- a/tests/test_connection.c +++ b/tests/test_connection.c @@ -181,6 +181,18 @@ void nopoll_conn_close (noPollConn *conn) UNUSED(conn); } +void nopoll_conn_close_ext (noPollConn *conn, int status, const char *reason, int reason_size) +{ + UNUSED(conn); UNUSED(status); UNUSED(reason); UNUSED(reason_size); +} + +int nopoll_conn_ref_count (noPollConn *conn) +{ + UNUSED(conn); + function_called (); + return (nopoll_bool) mock(); +} + int checkHostIp(char * serverIP) { UNUSED(serverIP); @@ -669,7 +681,7 @@ void test_wait_connection_ready () mock_wait_status = 403; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); - assert_int_equal (wait_connection_ready (&ctx), WAIT_ACTION_RETRY); + assert_int_equal (wait_connection_ready (&ctx), WAIT_FAIL); free_extra_headers (&ctx); free_header_info (&ctx.header_info); @@ -722,6 +734,8 @@ void test_connect_and_wait () expect_function_call (nopoll_conn_is_ok); will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); assert_int_equal (connect_and_wait (&ctx), CONN_WAIT_RETRY_DNS); Cfg.flags = 0; @@ -731,6 +745,8 @@ void test_connect_and_wait () expect_function_call (nopoll_conn_new_opts); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); assert_int_equal (connect_and_wait (&ctx), CONN_WAIT_RETRY_DNS); will_return (nopoll_conn_new_opts, &connection1); @@ -768,6 +784,8 @@ void test_connect_and_wait () expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, NULL); expect_function_call (nopoll_conn_tls_new); will_return (checkHostIp, 0); @@ -778,6 +796,8 @@ void test_connect_and_wait () expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -797,6 +817,8 @@ void test_connect_and_wait () mock_redirect = "mydns.mycom.net"; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); assert_int_equal (connect_and_wait (&ctx), CONN_WAIT_RETRY_DNS); will_return (nopoll_conn_tls_new, &connection1); @@ -807,6 +829,8 @@ void test_connect_and_wait () mock_redirect = "https://mydns.mycom.net"; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); assert_int_equal (connect_and_wait (&ctx), CONN_WAIT_ACTION_RETRY); } @@ -858,6 +882,8 @@ void test_keep_trying () mock_redirect = "https://mydns.mycom.net"; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -876,12 +902,16 @@ void test_keep_trying () mock_redirect = "https://mydns.mycom.net"; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); expect_function_call (nopoll_conn_is_ok); will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); init_backoff_timer (&backoff_timer, 5); rtn = keep_trying_to_connect (&ctx, &backoff_timer); assert_int_equal (rtn, false); @@ -904,6 +934,8 @@ void test_keep_trying () expect_function_call (nopoll_conn_is_ok); will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); init_backoff_timer (&backoff_timer, 5); rtn = keep_trying_to_connect (&ctx, &backoff_timer); assert_int_equal (rtn, false); @@ -953,6 +985,8 @@ void test_create_nopoll_connection() expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -966,6 +1000,8 @@ void test_create_nopoll_connection() expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -974,11 +1010,15 @@ void test_create_nopoll_connection() mock_redirect = "https://mydns.mycom.net"; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new6, &connection1); expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -1022,6 +1062,8 @@ void test_create_nopoll_connection() expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); mock_wait_status = 0; will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); @@ -1098,6 +1140,8 @@ void test_interface_down_retry() expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -1111,6 +1155,8 @@ void test_interface_down_retry() expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -1119,11 +1165,15 @@ void test_interface_down_retry() mock_redirect = "https://mydns.mycom.net"; will_return (nopoll_conn_wait_for_status_until_connection_ready, nopoll_false); expect_function_call (nopoll_conn_wait_for_status_until_connection_ready); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new6, &connection1); expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new); will_return (nopoll_conn_is_ok, nopoll_true); @@ -1167,6 +1217,8 @@ void test_interface_down_retry() expect_function_call (nopoll_conn_tls_new6); will_return (nopoll_conn_is_ok, nopoll_false); expect_function_call (nopoll_conn_is_ok); + will_return (nopoll_conn_ref_count, 0); + expect_function_call (nopoll_conn_ref_count); mock_wait_status = 0; will_return (nopoll_conn_tls_new, &connection1); expect_function_call (nopoll_conn_tls_new);