From 580f923cfc89aa9f151096d8606dde71e4604d08 Mon Sep 17 00:00:00 2001 From: Suvendhu Hansa Date: Thu, 8 Aug 2024 14:32:01 +0000 Subject: [PATCH] Support to apply non-critical services immediately From icwmp point of view, services which can cause upstream/wan disruption are critical services and these are applied at the end of the sessions. These services are defined in '/etc/icwmpd/critical_services.json'. --- src/common.c | 149 +++++++++++++++++++++---- src/common.h | 16 ++- src/cwmp.c | 4 +- src/datamodel_interface.c | 1 - src/rpc.c | 23 ++-- src/session.c | 2 +- test/cmocka/icwmp_soap_msg_unit_test.c | 4 +- test/script/03_verify_set_method.sh | 2 +- 8 files changed, 165 insertions(+), 36 deletions(-) diff --git a/src/common.c b/src/common.c index 5adaa4b..bb8da7d 100755 --- a/src/common.c +++ b/src/common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "common.h" #include "cwmp_cli.h" @@ -31,15 +32,20 @@ bool cwmp_stop = false; unsigned int flashsize = 256000000; struct cwmp *cwmp_main = NULL; struct session_timer_event *global_session_event = NULL; -static int nbre_services = 0; -static char *list_services[MAX_NBRE_SERVICES] = { 0 }; +LIST_HEAD(critical_service_list); LIST_HEAD(cwmp_memory_list); +LIST_HEAD(services_reload); struct cwmp_mem { struct list_head list; char mem[0]; }; +struct cwmp_services { + struct list_head list; + char *service; +}; + struct option cwmp_long_options[] = { { "boot-event", no_argument, NULL, 'b' }, { "get-rpc-methods", no_argument, NULL, 'g' }, @@ -645,35 +651,121 @@ void icwmp_cleanmem() /* * Services Management */ -void icwmp_init_list_services() +void icwmp_init_critical_services() { - int i; + struct blob_buf bbuf = {0}; + struct blob_attr *cur = NULL; + struct blob_attr *service_list = NULL; + int rem = 0; - nbre_services = 0; - for (i = 0; i < MAX_NBRE_SERVICES; i++) - list_services[i] = NULL; + if (!file_exists(CWMP_CRITICAL_SERVICES)) + return; + + CWMP_MEMSET(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + + if (blobmsg_add_json_from_file(&bbuf, CWMP_CRITICAL_SERVICES) == false) { + CWMP_LOG(WARNING, "The file %s is not a valid JSON file", CWMP_CRITICAL_SERVICES); + blob_buf_free(&bbuf); + return; + } + + struct blob_attr *tb_service[1] = {0}; + const struct blobmsg_policy p_service[1] = { + { "services_list", BLOBMSG_TYPE_ARRAY } + }; + + blobmsg_parse(p_service, 1, tb_service, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); + if (tb_service[0] == NULL) { + CWMP_LOG(WARNING, "The JSON file %s doesn't contain any service", CWMP_CRITICAL_SERVICES); + blob_buf_free(&bbuf); + return; + } + + service_list = tb_service[0]; + + blobmsg_for_each_attr(cur, service_list, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + char *serv_name = blobmsg_get_string(cur); + if (CWMP_STRLEN(serv_name) == 0) + continue; + + struct cwmp_services *serv = malloc(sizeof(struct cwmp_services)); + if (serv == NULL) + break; + + serv->service = CWMP_STRDUP(serv_name); + list_add(&serv->list, &critical_service_list); + } + blob_buf_free(&bbuf); +} + +void icwmp_free_critical_services() +{ + struct cwmp_services *serv = NULL, *node = NULL; + + list_for_each_entry_safe(serv, node, &critical_service_list, list) { + list_del(&serv->list); + FREE(serv->service); + free(serv); + } +} + +bool icwmp_critical_service(const char *service) +{ + bool ret = false; + + if (CWMP_STRLEN(service) == 0 || list_empty(&critical_service_list)) + return ret; + + struct cwmp_services *serv = NULL; + list_for_each_entry(serv, &critical_service_list, list) { + if (CWMP_STRCMP(service, serv->service) == 0) { + ret = true; + break; + } + } + + return ret; } int icwmp_add_service(char *service) { - if (nbre_services >= MAX_NBRE_SERVICES) + if (CWMP_STRLEN(service) == 0) return -1; - list_services[nbre_services++] = strdup(service); + + struct cwmp_services *serv = malloc(sizeof(struct cwmp_services)); + if (serv == NULL) + return -1; + + serv->service = CWMP_STRDUP(service); + list_add(&serv->list, &services_reload); + return 0; } void icwmp_free_list_services() { - int i = 0; - for (i = 0; i < nbre_services; i++) { - FREE(list_services[i]); + struct cwmp_services *serv = NULL, *node = NULL; + + list_for_each_entry_safe(serv, node, &services_reload, list) { + list_del(&serv->list); + FREE(serv->service); + free(serv); } - nbre_services = 0; } -void icwmp_restart_services() +bool end_session_reload_pending() +{ + return !list_empty(&services_reload); +} + +void icwmp_restart_services(int type) { struct blob_buf bb = {0}; + struct cwmp_services *serv = NULL, *node = NULL; memset(&bb, 0, sizeof(struct blob_buf)); @@ -681,16 +773,35 @@ void icwmp_restart_services() void *array = blobmsg_open_array(&bb, "services"); - for (int i = 0; i < nbre_services; i++) { - if (list_services[i] == NULL) + list_for_each_entry_safe(serv, node, &services_reload, list) { + if (CWMP_STRLEN(serv->service) == 0) { + list_del(&serv->list); + FREE(serv->service); + free(serv); continue; + } + + // If RELOAD_IMMIDIATE then only reload the non-critical services and delete from the list + // otherwise reload all services in the list and clear the list + if ((type == RELOAD_IMMIDIATE) && (icwmp_critical_service(serv->service) == true)) { + continue; + } + + CWMP_LOG(DEBUG, "Detected service: %s will be restarted", serv->service); + + if (CWMP_STRCMP(serv->service, "cwmp") == 0) { + list_del(&serv->list); + FREE(serv->service); + free(serv); - if (CWMP_STRCMP(list_services[i], "cwmp") == 0) { commit_uci_package("cwmp"); continue; } - blobmsg_add_string(&bb, NULL, list_services[i]); + blobmsg_add_string(&bb, NULL, serv->service); + list_del(&serv->list); + FREE(serv->service); + free(serv); } blobmsg_close_array(&bb, array); @@ -698,8 +809,6 @@ void icwmp_restart_services() icwmp_ubus_invoke("bbf.config", "commit", bb.head, NULL, NULL); blob_buf_free(&bb); - - icwmp_free_list_services(); } /* diff --git a/src/common.h b/src/common.h index 6ae9eee..0e57efb 100644 --- a/src/common.h +++ b/src/common.h @@ -75,6 +75,7 @@ #define ICWMP_TMP_PATH "/tmp/icwmp" #define FIREWALL_CWMP "/etc/firewall.cwmp" +#define CWMP_CRITICAL_SERVICES "/etc/icwmpd/critical_services.json" #define DM_PPP_INTERFACE_PATH "Device\\.PPP\\.Interface\\." #define DM_IP_INTERFACE_PATH "Device\\.IP\\.Interface\\." #define DEFAULT_CR_TIMEOUT 5 /* In Seconds */ @@ -93,6 +94,12 @@ extern struct uloop_timeout retry_session_timer; extern struct list_head intf_reset_list; extern struct list_head force_inform_list; +enum service_apply_type { + RELOAD_END_SESSION, + RELOAD_IMMIDIATE, + __INVALID_TYPE +}; + typedef struct env { unsigned short boot; unsigned short periodic; @@ -647,10 +654,9 @@ char *icwmp_strdup(const char *s); int icwmp_asprintf(char **s, const char *format, ...); void icwmp_free(void *m); void icwmp_cleanmem(); -void icwmp_init_list_services(); int icwmp_add_service(char *service); -void icwmp_free_list_services(); -void icwmp_restart_services(); +void icwmp_free_list_services(void); +void icwmp_restart_services(int type); bool icwmp_validate_string_length(char *arg, int max_length); bool icwmp_validate_boolean_value(char *arg); bool icwmp_validate_unsignedint(char *arg); @@ -687,4 +693,8 @@ void *cwmp_memcpy(void *dst, const void *src, size_t size, const char *origin, i void cwmp_restart_service(struct uloop_timeout *timeout __attribute__((unused))); int regex_replace(char **str, const char *pattern, const char *replace, int *match_count); void stop_service(void); +void icwmp_init_critical_services(void); +void icwmp_free_critical_services(void); +bool end_session_reload_service(const char *service); +bool end_session_reload_pending(void); #endif diff --git a/src/cwmp.c b/src/cwmp.c index bc87a8e..a5c15dc 100644 --- a/src/cwmp.c +++ b/src/cwmp.c @@ -263,7 +263,7 @@ static int cwmp_init(void) CWMP_LOG(INFO, "STARTING ICWMP with PID :%d", getpid()); - icwmp_init_list_services(); + icwmp_init_critical_services(); /* Only One instance should run*/ cwmp_main->pid_file = fopen("/var/run/icwmpd.pid", "w+"); @@ -335,6 +335,8 @@ static void cwmp_free() icwmp_cleanmem(); rpc_exit(); clean_cwmp_session_structure(); + icwmp_free_critical_services(); + icwmp_free_list_services(); FREE(cwmp_main); CWMP_LOG(INFO, "EXIT ICWMP"); closelog(); diff --git a/src/datamodel_interface.c b/src/datamodel_interface.c index 36ec20a..31fe1d7 100755 --- a/src/datamodel_interface.c +++ b/src/datamodel_interface.c @@ -101,7 +101,6 @@ static void ubus_transaction_callback(struct ubus_request *req, int type __attri if (CWMP_STRLEN(service_name) == 0) continue; - CWMP_LOG(DEBUG, "Detected service: %s will be restarted in the end session", service_name); icwmp_add_service(service_name); } } diff --git a/src/rpc.c b/src/rpc.c index 4d0efd0..783cfa5 100755 --- a/src/rpc.c +++ b/src/rpc.c @@ -1130,6 +1130,19 @@ int cwmp_handle_rpc_cpe_set_parameter_values(struct rpc *rpc) cwmp_free_all_xml_data_list(&xml_list_set_param_value); cwmp_free_all_dm_parameter_list(&list_set_param_value); + if (!cwmp_transaction("commit")) { + fault_code = FAULT_CPE_INTERNAL_ERROR; + err_msg = "Failed to commit the transaction"; + goto fault; + } + + icwmp_restart_services(RELOAD_IMMIDIATE); + + int status = 0; + if (end_session_reload_pending() == true) { + status = 1; + } + b = build_top_body_soap_response(cwmp_main->session->tree_out, "SetParameterValues"); if (!b) { @@ -1138,8 +1151,6 @@ int cwmp_handle_rpc_cpe_set_parameter_values(struct rpc *rpc) goto fault; } - int status = 1; - struct xml_data_struct spv_resp_xml_attrs = {.status = &status}; fault_code = build_xml_node_data(SOAP_RESP_SPV, b, &spv_resp_xml_attrs); if (fault_code) { @@ -1147,13 +1158,11 @@ int cwmp_handle_rpc_cpe_set_parameter_values(struct rpc *rpc) goto fault; } - if (!cwmp_transaction("commit")) { - fault_code = FAULT_CPE_INTERNAL_ERROR; - err_msg = "Failed to commit the transaction"; - goto fault; + cwmp_set_end_session(END_SESSION_SET_NOTIFICATION_UPDATE | END_SESSION_RELOAD); + if (status == 1) { + cwmp_set_end_session(END_SESSION_RESTART_SERVICES); } - cwmp_set_end_session(END_SESSION_RESTART_SERVICES | END_SESSION_SET_NOTIFICATION_UPDATE | END_SESSION_RELOAD); return 0; fault: diff --git a/src/session.c b/src/session.c index 1f2863a..553916b 100644 --- a/src/session.c +++ b/src/session.c @@ -645,7 +645,7 @@ int run_session_end_func(void) if (end_session_flag & END_SESSION_RESTART_SERVICES) { CWMP_LOG(INFO, "Restart modified services"); - icwmp_restart_services(); + icwmp_restart_services(RELOAD_END_SESSION); } if (cwmp_apply_acs_changes() != CWMP_OK) { diff --git a/test/cmocka/icwmp_soap_msg_unit_test.c b/test/cmocka/icwmp_soap_msg_unit_test.c index dd018cb..fc37760 100644 --- a/test/cmocka/icwmp_soap_msg_unit_test.c +++ b/test/cmocka/icwmp_soap_msg_unit_test.c @@ -299,7 +299,7 @@ static void soap_add_object_message_test(void **state) int ret = cwmp_handle_rpc_cpe_add_object(rpc_cpe); assert_int_equal(ret, 0); - icwmp_restart_services(); + icwmp_restart_services(RELOAD_IMMIDIATE); env = mxmlFindElement(cwmp_main->session->tree_out, cwmp_main->session->tree_out, "soap_env:Envelope", NULL, NULL, MXML_DESCEND); assert_non_null(env); @@ -451,7 +451,7 @@ static void soap_delete_object_message_test(void **state) int ret = cwmp_handle_rpc_cpe_delete_object(rpc_cpe); assert_int_equal(ret, 0); - icwmp_restart_services(); + icwmp_restart_services(RELOAD_IMMIDIATE); env = mxmlFindElement(cwmp_main->session->tree_out, cwmp_main->session->tree_out, "soap_env:Envelope", NULL, NULL, MXML_DESCEND); assert_non_null(env); diff --git a/test/script/03_verify_set_method.sh b/test/script/03_verify_set_method.sh index d2e6b15..644ff8f 100755 --- a/test/script/03_verify_set_method.sh +++ b/test/script/03_verify_set_method.sh @@ -24,7 +24,7 @@ check_ret $? wait_for_session_end check_session "SetParameterValues" get_status=$(print_tag_value "cwmp:SetParameterValuesResponse" "Status") -if [ "$get_status" != "1" ]; then +if [ "$get_status" != "0" ]; then echo "Error: Set Value doesn't work correctly" >> ./funl-test-debug.log exit 1 fi