From 491957b998107a45177b67c44fbb23459e7ddc5d Mon Sep 17 00:00:00 2001 From: Omar Kallel Date: Wed, 27 Apr 2022 14:35:09 +0100 Subject: [PATCH] Ticket refs #7896: icwmp: Implement Heartbeat event --- Makefile.am | 1 + config.c | 45 ++++++++++++++ cwmp.c | 31 ++++++++-- event.c | 3 +- heartbeat.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ inc/common.h | 6 ++ inc/cwmp_uci.h | 3 + inc/event.h | 1 + inc/heartbeat.h | 14 +++++ inc/http.h | 2 +- session.c | 3 + ubus_utils.c | 16 ++--- 12 files changed, 264 insertions(+), 15 deletions(-) create mode 100644 heartbeat.c create mode 100644 inc/heartbeat.h diff --git a/Makefile.am b/Makefile.am index 2edd492..a2e3451 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,6 +28,7 @@ icwmpd_SOURCES = \ ./rpc_soap.c \ ./diagnostic.c \ ./reboot.c \ + ./heartbeat.c \ ./cwmp.c \ ./ssl_utils.c diff --git a/config.c b/config.c index a84611b..9722344 100755 --- a/config.c +++ b/config.c @@ -17,6 +17,7 @@ #include "reboot.h" #include "ssl_utils.h" #include "datamodel_interface.h" +#include "heartbeat.h" pthread_mutex_t mutex_config_load = PTHREAD_MUTEX_INITIALIZER; @@ -571,6 +572,50 @@ int get_global_config(struct config *conf) CWMP_LOG(DEBUG, "CWMP CONFIG - cpe json custom notify file: %s", conf->custom_notify_json); } } + + if ((error = uci_get_value(UCI_ACS_HEARTBEAT_ENABLE, &value)) == CWMP_OK) { + if (value != NULL) { + if ((strcasecmp(value, "true") == 0) || (strcmp(value, "1") == 0)) { + conf->heart_beat_enable = true; + } else { + conf->heart_beat_enable = false; + } + FREE(value); + } else { + conf->heart_beat_enable = false; + } + + CWMP_LOG(DEBUG, "CWMP CONFIG - heart beat enable: %d ", conf->heart_beat_enable); + } else { + return error; + } + + if ((error = uci_get_value(UCI_ACS_HEARTBEAT_INTERVAL, &value)) == CWMP_OK) { + int a = 30; + + if (value != NULL) { + a = atoi(value); + FREE(value); + } + conf->heartbeat_interval = a; + + CWMP_LOG(DEBUG, "CWMP CONFIG - heart beat interval: %d ", conf->heartbeat_interval); + } else { + return error; + } + + if ((error = uci_get_value(UCI_ACS_HEARTBEAT_TIME, &value)) == CWMP_OK) { + if (value != NULL) { + conf->heart_time = convert_datetime_to_timestamp(value); + FREE(value); + } else { + conf->heart_time = 0; + } + + CWMP_LOG(DEBUG, "CWMP CONFIG - heart beat time: %d ", conf->heart_time); + } else { + return error; + } return CWMP_OK; } diff --git a/cwmp.c b/cwmp.c index d4e4a77..5f5776e 100644 --- a/cwmp.c +++ b/cwmp.c @@ -37,6 +37,7 @@ #include "sched_inform.h" #include "datamodel_interface.h" #include "cwmp_du_state.h" +#include "heartbeat.h" #include "netlink.h" static pthread_t periodic_event_thread; @@ -49,17 +50,22 @@ static pthread_t upload_thread; static pthread_t ubus_thread; static pthread_t http_cr_server_thread; static pthread_t periodic_check_notify; +static pthread_t heart_beat_session_thread; bool g_firewall_restart = false; static struct ubus_context *ctx = NULL; -static int cwmp_get_retry_interval(struct cwmp *cwmp) +int cwmp_get_retry_interval(struct cwmp *cwmp, bool heart_beat) { unsigned int retry_count = 0; double min = 0; double max = 0; int m = cwmp->conf.retry_min_wait_interval; int k = cwmp->conf.retry_interval_multiplier; - int exp = cwmp->retry_count_session; + int exp; + if (heart_beat) + exp = heart_beat_retry_count_session; + else + exp = cwmp->retry_count_session; if (exp == 0) return MAX_INT32; if (exp > 10) @@ -145,7 +151,7 @@ void check_firewall_restart_state() } } -static int cwmp_schedule_rpc(struct cwmp *cwmp, struct session *session) +int cwmp_schedule_rpc(struct cwmp *cwmp, struct session *session) { struct list_head *ilist; struct rpc *rpc_acs, *rpc_cpe; @@ -263,6 +269,7 @@ int run_session_end_func(void) CWMP_LOG(INFO, "Config reload: end session request"); cwmp_uci_reinit(); cwmp_apply_acs_changes(); + check_trigger_heartbeat_session(); } if (end_session_flag & END_SESSION_INIT_NOTIFY) { @@ -350,7 +357,7 @@ static void cwmp_schedule_session(struct cwmp *cwmp) pthread_mutex_lock(&(cwmp->mutex_session_send)); ilist = (&(cwmp->head_session_queue))->next; while ((ilist == &(cwmp->head_session_queue)) || retry) { - t = cwmp_get_retry_interval(cwmp); + t = cwmp_get_retry_interval(cwmp, 0); time_to_wait.tv_sec = time(NULL) + t; CWMP_LOG(INFO, "Waiting the next session"); @@ -369,6 +376,7 @@ static void cwmp_schedule_session(struct cwmp *cwmp) ilist = (&(cwmp->head_session_queue))->next; retry = false; } + pthread_mutex_lock(&mutex_heartbeat_session); cwmp_uci_init(); if (cwmp->session_status.last_status == SESSION_FAILURE) reload_networking_config(); @@ -419,12 +427,13 @@ static void cwmp_schedule_session(struct cwmp *cwmp) reload_networking_config(); run_session_end_func(); error = cwmp_move_session_to_session_queue(cwmp, session); - CWMP_LOG(INFO, "Retry session, retry count = %d, retry in %ds", cwmp->retry_count_session, cwmp_get_retry_interval(cwmp)); + CWMP_LOG(INFO, "Retry session, retry count = %d, retry in %ds", cwmp->retry_count_session, cwmp_get_retry_interval(cwmp, 0)); retry = true; cwmp->session_status.last_end_time = time(NULL); cwmp->session_status.last_status = SESSION_FAILURE; - cwmp->session_status.next_retry = time(NULL) + cwmp_get_retry_interval(cwmp); + cwmp->session_status.next_retry = time(NULL) + cwmp_get_retry_interval(cwmp, 0); cwmp->session_status.failure_session++; + pthread_mutex_unlock(&mutex_heartbeat_session); pthread_mutex_unlock(&(cwmp->mutex_session_send)); continue; } @@ -437,6 +446,8 @@ static void cwmp_schedule_session(struct cwmp *cwmp) cwmp->session_status.last_status = SESSION_SUCCESS; cwmp->session_status.next_retry = 0; cwmp->session_status.success_session++; + pthread_cond_signal(&threasheld_retry_session); + pthread_mutex_unlock(&mutex_heartbeat_session); pthread_mutex_unlock(&(cwmp->mutex_session_send)); } } @@ -747,6 +758,8 @@ static int cwmp_init(int argc, char **argv, struct cwmp *cwmp) pthread_mutex_init(&cwmp->mutex_periodic, NULL); pthread_mutex_init(&cwmp->mutex_session_queue, NULL); pthread_mutex_init(&cwmp->mutex_session_send, NULL); + pthread_mutex_init(&mutex_heartbeat_session, NULL); + pthread_mutex_init(&mutex_heartbeat, NULL); memcpy(&(cwmp->env), &env, sizeof(struct env)); INIT_LIST_HEAD(&(cwmp->head_session_queue)); @@ -889,6 +902,11 @@ int main(int argc, char **argv) if (error < 0) { CWMP_LOG(ERROR, "Error when creating the periodic check notify thread!"); } + error = pthread_create(&heart_beat_session_thread, NULL, &thread_heartbeat_session, (void *)cwmp); + if (error < 0) { + CWMP_LOG(ERROR, "Error when creating heartbeat session thread!"); + } + error = pthread_create(&scheduleInform_thread, NULL, &thread_cwmp_rpc_cpe_scheduleInform, (void *)cwmp); if (error < 0) { CWMP_LOG(ERROR, "Error when creating the scheduled inform thread!"); @@ -932,6 +950,7 @@ int main(int argc, char **argv) pthread_join(change_du_state_thread, NULL); pthread_join(http_cr_server_thread, NULL); pthread_join(ubus_thread, NULL); + pthread_join(heart_beat_session_thread, NULL); /* Free all memory allocation */ cwmp_free(cwmp); diff --git a/event.c b/event.c index 4a50659..7e1e433 100644 --- a/event.c +++ b/event.c @@ -35,7 +35,8 @@ const struct EVENT_CONST_STRUCT EVENT_CONST[] = {[EVENT_IDX_0BOOTSTRAP] = { "0 B [EVENT_IDX_M_Download] = { "M Download", EVENT_TYPE_MULTIPLE, EVENT_RETRY_AFTER_TRANSMIT_FAIL | EVENT_RETRY_AFTER_REBOOT }, [EVENT_IDX_M_Schedule_Download] = { "M ScheduleDownload", EVENT_TYPE_MULTIPLE, EVENT_RETRY_AFTER_TRANSMIT_FAIL | EVENT_RETRY_AFTER_REBOOT }, [EVENT_IDX_M_Upload] = { "M Upload", EVENT_TYPE_MULTIPLE, EVENT_RETRY_AFTER_TRANSMIT_FAIL | EVENT_RETRY_AFTER_REBOOT }, - [EVENT_IDX_M_ChangeDUState] = { "M ChangeDUState", EVENT_TYPE_MULTIPLE, EVENT_RETRY_AFTER_TRANSMIT_FAIL | EVENT_RETRY_AFTER_REBOOT } }; + [EVENT_IDX_M_ChangeDUState] = { "M ChangeDUState", EVENT_TYPE_MULTIPLE, EVENT_RETRY_AFTER_TRANSMIT_FAIL | EVENT_RETRY_AFTER_REBOOT }, + [EVENT_IDX_14HEARTBEAT] = { "14 HEARTBEAT", EVENT_TYPE_SINGLE, EVENT_RETRY_AFTER_TRANSMIT_FAIL | EVENT_RETRY_AFTER_REBOOT } }; void cwmp_save_event_container(struct event_container *event_container) //to be moved to backupsession { diff --git a/heartbeat.c b/heartbeat.c new file mode 100644 index 0000000..3b5acf6 --- /dev/null +++ b/heartbeat.c @@ -0,0 +1,154 @@ +#include +#include + +#include "heartbeat.h" +#include "common.h" +#include "config.h" +#include "session.h" +#include "cwmp_uci.h" +#include "backupSession.h" +#include "log.h" +#include "event.h" +#include "http.h" + +pthread_cond_t threshold_heartbeat_session; +pthread_cond_t threasheld_retry_session; +pthread_mutex_t mutex_heartbeat; +pthread_mutex_t mutex_heartbeat_session; +bool old_heartbeat_enable = false; +int heart_beat_retry_count_session = 0; + +static struct session_status heart_beat_session_status = {0}; + +void check_trigger_heartbeat_session() +{ + if (cwmp_main.conf.heart_beat_enable && !old_heartbeat_enable) + pthread_cond_signal(&threshold_heartbeat_session); +} + +int add_heart_beat_event(struct session *heartbeat_session) +{ + struct event_container *event_container; + event_container = calloc(1, sizeof(struct event_container)); + if (event_container == NULL) { + return -1; + } + INIT_LIST_HEAD(&(event_container->head_dm_parameter)); + list_add(&(event_container->list), heartbeat_session->head_event_container.prev); + event_container->code = EVENT_IDX_14HEARTBEAT; + event_container->command_key = strdup(""); + event_container->id = 1; + /* + * event_container will be freed in the destruction of the session heartbeat_session + */ + // cppcheck-suppress memleak + return 0; +} + +void *thread_heartbeat_session(void *v __attribute__((unused))) +{ + static struct timespec heartbeat_interval = { 0, 0 }; + + sleep(2); + for (;;) { + if (cwmp_main.conf.heart_beat_enable) { + heartbeat_interval.tv_sec = time(NULL) + cwmp_main.conf.heartbeat_interval; + pthread_mutex_lock(&mutex_heartbeat); + pthread_cond_timedwait(&threshold_heartbeat_session, &mutex_heartbeat, &heartbeat_interval); + if (thread_end) + break; + + if (cwmp_main.session_status.last_status == SESSION_FAILURE) { + CWMP_LOG(WARNING, "Not able to start HEARTBEAT Session for this period: CWMP Session is retrying"); + pthread_cond_wait(&threasheld_retry_session, &mutex_heartbeat); + //continue; + } + + pthread_mutex_lock(&mutex_heartbeat_session); + struct session *heartbeat_session = NULL; + heartbeat_session = calloc(1, sizeof(struct session)); + if (heartbeat_session == NULL) { + pthread_mutex_unlock(&mutex_heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat); + continue; + } + INIT_LIST_HEAD(&(heartbeat_session->head_event_container)); + INIT_LIST_HEAD(&(heartbeat_session->head_rpc_acs)); + INIT_LIST_HEAD(&(heartbeat_session->head_rpc_cpe)); + struct rpc *rpc_acs; + rpc_acs = cwmp_add_session_rpc_acs_head(heartbeat_session, RPC_ACS_INFORM); + if (rpc_acs == NULL) { + cwmp_session_destructor(heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat); + continue; + } + if (add_heart_beat_event(heartbeat_session) != 0) { + cwmp_session_destructor(heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat); + continue; + } + cwmp_uci_init(); + if (heart_beat_session_status.last_status == SESSION_FAILURE) + reload_networking_config(); + heart_beat_session_status.last_end_time = 0; + heart_beat_session_status.last_start_time = time(NULL); + heart_beat_session_status.last_status = SESSION_RUNNING; + heart_beat_session_status.next_retry = 0; + + if (file_exists(fc_cookies)) + remove(fc_cookies); + + CWMP_LOG(INFO, "Start HEARTBEAT session"); + int error = cwmp_schedule_rpc(&cwmp_main, heartbeat_session); + CWMP_LOG(INFO, "End HEARTBEAT session"); + + if (thread_end) { + event_remove_all_event_container(heartbeat_session, RPC_SEND); + run_session_end_func(); + cwmp_session_destructor(heartbeat_session); + cwmp_uci_exit(); + pthread_mutex_unlock(&(cwmp_main.mutex_session_send)); + pthread_mutex_unlock(&mutex_heartbeat); + break; + } + + if (error || heartbeat_session->error == CWMP_RETRY_SESSION) { + heart_beat_retry_count_session++; + reload_networking_config(); + run_session_end_func(); + CWMP_LOG(INFO, "Retry HEARTBEAT session, retry count = %d, retry in %ds", cwmp_main.retry_count_session, cwmp_get_retry_interval(&cwmp_main, 1)); + heart_beat_session_status.last_end_time = time(NULL); + heart_beat_session_status.last_status = SESSION_FAILURE; + heart_beat_session_status.next_retry = time(NULL) + cwmp_get_retry_interval(&cwmp_main, 1); + heartbeat_interval.tv_sec = time(NULL) + cwmp_get_retry_interval(&cwmp_main, 1); + heart_beat_session_status.failure_session++; + cwmp_uci_exit(); + pthread_mutex_unlock(&mutex_heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat); + continue; + } + event_remove_all_event_container(heartbeat_session, RPC_SEND); + run_session_end_func(); + cwmp_session_destructor(heartbeat_session); + heart_beat_retry_count_session = 0; + heart_beat_session_status.last_end_time = time(NULL); + heart_beat_session_status.last_status = SESSION_SUCCESS; + heart_beat_session_status.next_retry = 0; + heart_beat_session_status.success_session++; + heartbeat_interval.tv_sec = time(NULL) + cwmp_main.conf.heartbeat_interval; + cwmp_uci_exit(); + pthread_mutex_unlock(&mutex_heartbeat_session); + pthread_mutex_unlock(&mutex_heartbeat); + + if (thread_end) + break; + } else { + pthread_mutex_lock(&mutex_heartbeat); + pthread_cond_wait(&threshold_heartbeat_session, &mutex_heartbeat); + pthread_mutex_unlock(&mutex_heartbeat); + } + } + return NULL; +} diff --git a/inc/common.h b/inc/common.h index d82309b..be975bf 100644 --- a/inc/common.h +++ b/inc/common.h @@ -91,13 +91,16 @@ typedef struct config { int periodic_notify_interval; int compression; int delay_reboot; + int heartbeat_interval; time_t schedule_reboot; time_t time; + time_t heart_time; unsigned int periodic_entropy; bool periodic_enable; bool periodic_notify_enable; bool insecure_enable; bool ipv6_enable; + bool heart_beat_enable; int retry_min_wait_interval; int retry_interval_multiplier; bool lw_notification_enable; @@ -529,5 +532,8 @@ int get_connection_interface(); char *get_time(time_t t_time); bool is_obj_excluded(const char *object_name); time_t convert_datetime_to_timestamp(char *value); +int cwmp_get_retry_interval(struct cwmp *cwmp, bool heart_beat); +int cwmp_schedule_rpc(struct cwmp *cwmp, struct session *session); +int run_session_end_func(void); #endif diff --git a/inc/cwmp_uci.h b/inc/cwmp_uci.h index c3bc0a3..1d59f54 100644 --- a/inc/cwmp_uci.h +++ b/inc/cwmp_uci.h @@ -58,6 +58,9 @@ #define LW_NOTIFICATION_HOSTNAME "cwmp.lwn.hostname" #define LW_NOTIFICATION_PORT "cwmp.lwn.port" #define UCI_DHCP_ACS_URL "cwmp.acs.dhcp_url" +#define UCI_ACS_HEARTBEAT_ENABLE "cwmp.acs.heartbeat_enable" +#define UCI_ACS_HEARTBEAT_INTERVAL "cwmp.acs.heartbeat_interval" +#define UCI_ACS_HEARTBEAT_TIME "cwmp.acs.heartbeat_time" #define UCI_CPE_FIREWALL_RESTART_STATE "cwmp.cpe.firewall_restart" diff --git a/inc/event.h b/inc/event.h index 3e90f36..8803d6e 100644 --- a/inc/event.h +++ b/inc/event.h @@ -63,6 +63,7 @@ enum event_idx_enum EVENT_IDX_M_Schedule_Download, EVENT_IDX_M_Upload, EVENT_IDX_M_ChangeDUState, + EVENT_IDX_14HEARTBEAT, __EVENT_IDX_MAX }; diff --git a/inc/heartbeat.h b/inc/heartbeat.h new file mode 100644 index 0000000..cde6978 --- /dev/null +++ b/inc/heartbeat.h @@ -0,0 +1,14 @@ +#ifndef HEARTBEAT_H +#define HEARTBEAT_H + +#include +extern pthread_mutex_t mutex_heartbeat; +extern pthread_mutex_t mutex_heartbeat_session; +extern pthread_cond_t threshold_heartbeat_session; +extern pthread_cond_t threasheld_retry_session; +extern int heart_beat_retry_count_session; +extern bool old_heartbeat_enable; + +void *thread_heartbeat_session(void *v); +void check_trigger_heartbeat_session(); +#endif diff --git a/inc/http.h b/inc/http.h index 3b79a31..049367f 100644 --- a/inc/http.h +++ b/inc/http.h @@ -14,7 +14,7 @@ extern char *fc_cookies; -#define HTTP_TIMEOUT 30 +#define HTTP_TIMEOUT 60 struct http_client { struct curl_slist *header_list; diff --git a/session.c b/session.c index 223a1df..6725a42 100644 --- a/session.c +++ b/session.c @@ -16,6 +16,7 @@ #include "event.h" #include "rpc_soap.h" #include "backupSession.h" +#include "heartbeat.h" unsigned int end_session_flag = 0; @@ -54,6 +55,8 @@ int cwmp_apply_acs_changes(void) { int error; + old_heartbeat_enable = cwmp_main.conf.heart_beat_enable; + if ((error = cwmp_config_reload(&cwmp_main))) return error; diff --git a/ubus_utils.c b/ubus_utils.c index 93c9e07..b22069e 100644 --- a/ubus_utils.c +++ b/ubus_utils.c @@ -281,13 +281,15 @@ static void icwmp_inform_event(struct ubus_context *ctx, struct ubus_request_dat pthread_mutex_lock(&(cwmp_main.mutex_session_queue)); cwmp_add_event_container(&cwmp_main, event_code, ""); pthread_mutex_unlock(&(cwmp_main.mutex_session_queue)); - pthread_cond_signal(&(cwmp_main.threshold_session_send)); - if (cwmp_main.session_status.last_status == SESSION_RUNNING) { - blobmsg_add_u32(&bb, "status", -1); - blobmsg_add_string(&bb, "info", "Session already running, event will be sent at the end of the session"); - } else { - blobmsg_add_u32(&bb, "status", 1); - blobmsg_add_string(&bb, "info", "Session started"); + if (event_code != EVENT_IDX_14HEARTBEAT) { + pthread_cond_signal(&(cwmp_main.threshold_session_send)); + if (cwmp_main.session_status.last_status == SESSION_RUNNING) { + blobmsg_add_u32(&bb, "status", -1); + blobmsg_add_string(&bb, "info", "Session already running, event will be sent at the end of the session"); + } else { + blobmsg_add_u32(&bb, "status", 1); + blobmsg_add_string(&bb, "info", "Session started"); + } } ubus_send_reply(ctx, req, bb.head);