icwmp/cwmp.c

1097 lines
31 KiB
C

/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2013-2021 iopsys Software Solutions AB
* Author Mohamed Kallel <mohamed.kallel@pivasoftware.com>
* Author Ahmed Zribi <ahmed.zribi@pivasoftware.com>
* Author Omar Kallel <omar.kallel@pivasoftware.com>
*/
#include <math.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <regex.h>
#include "common.h"
#include "ssl_utils.h"
#include "xml.h"
#include "notifications.h"
#include "event.h"
#include "cwmp_uci.h"
#include "log.h"
#include "session.h"
#include "diagnostic.h"
#include "http.h"
#include "rpc_soap.h"
#include "config.h"
#include "backupSession.h"
#include "ubus_utils.h"
#include "digauth.h"
#include "upload.h"
#include "download.h"
#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;
static pthread_t scheduleInform_thread;
static pthread_t change_du_state_thread;
static pthread_t download_thread;
static pthread_t schedule_download_thread;
static pthread_t apply_schedule_download_thread;
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;
struct list_head intf_reset_list;
static void cwmp_invoke_intf_reset(char *path)
{
if (path == NULL) {
CWMP_LOG(ERROR, "iface path is null");
return;
}
CWMP_LOG(INFO, "Reset interface: %s", path);
struct blob_buf b = { 0 };
memset(&b, 0, sizeof(struct blob_buf));
blob_buf_init(&b, 0);
bb_add_string(&b, "path", path);
bb_add_string(&b, "action", "Reset()");
icwmp_ubus_invoke(USP_OBJECT_NAME, "operate", b.head, NULL, NULL);
blob_buf_free(&b);
return;
}
static bool interface_reset_req(char *param_name, char *value)
{
if (param_name == NULL || value == NULL) {
CWMP_LOG(ERROR, "param_name or value is null: %p %p", param_name, value);
return false;
}
char reg_exp[60] = {0};
snprintf(reg_exp, sizeof(reg_exp), "^(%s|%s)[0-9]+.Reset$", DM_IP_INTERFACE_PATH, DM_PPP_INTERFACE_PATH);
regex_t reegex;
int ret = regcomp(&reegex, reg_exp, REG_EXTENDED);
if (ret != 0)
return false;
ret = regexec(&reegex, param_name, 0, NULL, 0);
if (ret != 0)
return false;
if (strcmp(value, "1") != 0 && strcmp(value, "true") != 0)
return false;
return true;
}
void set_interface_reset_request(char *param_name, char *value)
{
if (param_name == NULL || value == NULL){
CWMP_LOG(ERROR, "param_name or value is null: %p %p", param_name, value);
return;
}
if (interface_reset_req(param_name, value) == false)
return;
// Store the interface path to handle after session end
int len = 0;
char *pos = strrchr(param_name, '.');
if (pos == NULL)
return;
len = pos - param_name + 2;
if (len <= 0)
return;
intf_reset_node *node = (intf_reset_node *)malloc(sizeof(intf_reset_node));
if (node == NULL) {
CWMP_LOG(ERROR, "Out of memory");
return;
}
memset(node, 0, sizeof(intf_reset_node));
snprintf(node->path, len, "%s", param_name);
INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, &intf_reset_list);
}
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;
if (heart_beat)
exp = heart_beat_retry_count_session;
else
exp = cwmp->retry_count_session;
if (exp == 0)
return MAX_INT32;
if (exp > 10)
exp = 10;
min = pow(((double)k / 1000), (double)(exp - 1)) * m;
max = pow(((double)k / 1000), (double)exp) * m;
char *rand = generate_random_string(4);
if (rand) {
unsigned int dividend = (unsigned int)strtoul(rand, NULL, 16);
retry_count = dividend % ((unsigned int)max + 1 - (unsigned int)min) + (unsigned int)min;
free(rand);
}
return (retry_count);
}
static int cwmp_rpc_cpe_handle_message(struct session *session, struct rpc *rpc_cpe)
{
if (xml_prepare_msg_out(session))
return -1;
if (rpc_cpe_methods[rpc_cpe->type].handler(session, rpc_cpe))
return -1;
if (xml_set_cwmp_id_rpc_cpe(session))
return -1;
return 0;
}
static void cwmp_prepare_value_change(struct cwmp *cwmp)
{
struct event_container *event_container;
if (list_value_change.next == &(list_value_change))
return;
pthread_mutex_lock(&(cwmp->mutex_session_queue));
event_container = cwmp_add_event_container(cwmp, EVENT_IDX_4VALUE_CHANGE, "");
if (!event_container)
goto end;
pthread_mutex_lock(&(mutex_value_change));
list_splice_init(&(list_value_change), &(event_container->head_dm_parameter));
pthread_mutex_unlock(&(mutex_value_change));
cwmp_save_event_container(event_container);
end:
pthread_mutex_unlock(&(cwmp->mutex_session_queue));
}
int get_firewall_restart_state(char **state)
{
cwmp_uci_reinit();
return uci_get_state_value(UCI_CPE_FIREWALL_RESTART_STATE, state);
}
// wait till firewall restart is not complete or 5 sec, whichever is less
void check_firewall_restart_state()
{
int count = 0;
bool init = false;
do {
char *state = NULL;
if (get_firewall_restart_state(&state) != CWMP_OK)
break;
if (state != NULL && strcmp(state, "init") == 0) {
init = true;
FREE(state);
break;
}
usleep(500 * 1000);
FREE(state);
count++;
} while(count < 10);
// mark the firewall restart as done
g_firewall_restart = false;
if (init == false) { // In case of timeout reset the firewall_restart flag
CWMP_LOG(ERROR, "Firewall restart took longer than usual");
cwmp_uci_set_varstate_value("cwmp", "cpe", "firewall_restart", "init");
cwmp_commit_package("cwmp", UCI_VARSTATE_CONFIG);
}
}
int cwmp_schedule_rpc(struct cwmp *cwmp, struct session *session)
{
struct list_head *ilist;
struct rpc *rpc_acs, *rpc_cpe;
if (http_client_init(cwmp) || thread_end) {
CWMP_LOG(INFO, "Initializing http client failed");
goto retry;
}
while (1) {
list_for_each (ilist, &(session->head_rpc_acs)) {
rpc_acs = list_entry(ilist, struct rpc, list);
if ((rpc_acs->type != RPC_ACS_INFORM) && (rpc_acs_methods[rpc_acs->type].acs_support == RPC_ACS_NOT_SUPPORT)) {
CWMP_LOG(WARNING, "The RPC method %s is not included in the RPCs list supported by the ACS", rpc_acs_methods[rpc_acs->type].name);
continue;
}
if (!rpc_acs->type || thread_end)
goto retry;
CWMP_LOG(INFO, "Preparing the %s RPC message to send to the ACS", rpc_acs_methods[rpc_acs->type].name);
if (rpc_acs_methods[rpc_acs->type].prepare_message(cwmp, session, rpc_acs) || thread_end)
goto retry;
if (xml_set_cwmp_id(session) || thread_end)
goto retry;
CWMP_LOG(INFO, "Send the %s RPC message to the ACS", rpc_acs_methods[rpc_acs->type].name);
if (xml_send_message(cwmp, session, rpc_acs) || thread_end)
goto retry;
CWMP_LOG(INFO, "Get the %sResponse message from the ACS", rpc_acs_methods[rpc_acs->type].name);
if (rpc_acs_methods[rpc_acs->type].parse_response || thread_end)
if (rpc_acs_methods[rpc_acs->type].parse_response(cwmp, session, rpc_acs))
goto retry;
ilist = ilist->prev;
if (rpc_acs_methods[rpc_acs->type].extra_clean != NULL)
rpc_acs_methods[rpc_acs->type].extra_clean(session, rpc_acs);
cwmp_session_rpc_destructor(rpc_acs);
MXML_DELETE(session->tree_in);
MXML_DELETE(session->tree_out);
if (session->hold_request || thread_end)
break;
}
// If restart service caused firewall restart, wait for firewall restart to complete
if (g_firewall_restart == true)
check_firewall_restart_state();
CWMP_LOG(INFO, "Send empty message to the ACS");
if (xml_send_message(cwmp, session, NULL) || thread_end)
goto retry;
if (!session->tree_in || thread_end)
goto next;
CWMP_LOG(INFO, "Receive request from the ACS");
if (xml_handle_message(session) || thread_end)
goto retry;
while (session->head_rpc_cpe.next != &(session->head_rpc_cpe)) {
rpc_cpe = list_entry(session->head_rpc_cpe.next, struct rpc, list);
if (!rpc_cpe->type || thread_end)
goto retry;
CWMP_LOG(INFO, "Preparing the %s%s message", rpc_cpe_methods[rpc_cpe->type].name, (rpc_cpe->type != RPC_CPE_FAULT) ? "Response" : "");
if (cwmp_rpc_cpe_handle_message(session, rpc_cpe) || thread_end)
goto retry;
MXML_DELETE(session->tree_in);
CWMP_LOG(INFO, "Send the %s%s message to the ACS", rpc_cpe_methods[rpc_cpe->type].name, (rpc_cpe->type != RPC_CPE_FAULT) ? "Response" : "");
if (xml_send_message(cwmp, session, rpc_cpe) || thread_end)
goto retry;
MXML_DELETE(session->tree_out);
cwmp_session_rpc_destructor(rpc_cpe);
if (!session->tree_in || thread_end)
break;
CWMP_LOG(INFO, "Receive request from the ACS");
if (xml_handle_message(session) || thread_end)
goto retry;
}
next:
if (session->head_rpc_acs.next == &(session->head_rpc_acs))
break;
MXML_DELETE(session->tree_in);
MXML_DELETE(session->tree_out);
}
session->error = CWMP_OK;
goto end;
retry:
CWMP_LOG(INFO, "Failed");
session->error = CWMP_RETRY_SESSION;
event_remove_noretry_event_container(session, cwmp);
end:
MXML_DELETE(session->tree_in);
MXML_DELETE(session->tree_out);
http_client_exit();
xml_exit();
return session->error;
}
int run_session_end_func(void)
{
if (end_session_flag & END_SESSION_RESTART_SERVICES) {
CWMP_LOG(INFO, "Restart modified services");
icwmp_restart_services();
}
if (end_session_flag & END_SESSION_RELOAD) {
CWMP_LOG(INFO, "Config reload: end session request");
cwmp_uci_reinit();
if (cwmp_apply_acs_changes() != CWMP_OK) {
CWMP_LOG(ERROR, "config reload failed at session end");
}
check_trigger_heartbeat_session();
}
if (end_session_flag & END_SESSION_INIT_NOTIFY) {
CWMP_LOG(INFO, "SetParameterAttributes end session: reinit list notify");
reinit_list_param_notify();
}
if (end_session_flag & END_SESSION_SET_NOTIFICATION_UPDATE) {
CWMP_LOG(INFO, "SetParameterAttributes/Values end session: update enabled notify file");
cwmp_update_enabled_notify_file();
}
if (end_session_flag & END_SESSION_NSLOOKUP_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing nslookupdiagnostic: end session request");
cwmp_nslookup_diagnostics();
}
if (end_session_flag & END_SESSION_TRACEROUTE_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing traceroutediagnostic: end session request");
cwmp_traceroute_diagnostics();
}
if (end_session_flag & END_SESSION_UDPECHO_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing udpechodiagnostic: end session request");
cwmp_udp_echo_diagnostics();
}
if (end_session_flag & END_SESSION_SERVERSELECTION_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing serverselectiondiagnostic: end session request");
cwmp_serverselection_diagnostics();
}
if (end_session_flag & END_SESSION_IPPING_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing ippingdiagnostic: end session request");
cwmp_ip_ping_diagnostics();
}
if (end_session_flag & END_SESSION_DOWNLOAD_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing download diagnostic: end session request");
cwmp_download_diagnostics();
}
if (end_session_flag & END_SESSION_UPLOAD_DIAGNOSTIC) {
CWMP_LOG(INFO, "Executing upload diagnostic: end session request");
cwmp_upload_diagnostics();
}
if (end_session_flag & END_SESSION_REBOOT) {
CWMP_LOG(INFO, "Executing Reboot: end session request");
cwmp_reboot(commandKey);
exit(EXIT_SUCCESS);
}
if (end_session_flag & END_SESSION_FACTORY_RESET) {
CWMP_LOG(INFO, "Executing factory reset: end session request");
cwmp_factory_reset();
exit(EXIT_SUCCESS);
}
if (end_session_flag & END_SESSION_X_FACTORY_RESET_SOFT) {
CWMP_LOG(INFO, "Executing factory reset soft: end session request");
cwmp_factory_reset();
exit(EXIT_SUCCESS);
}
// check if any interface reset request exists then take action
intf_reset_node *iter = NULL, *node = NULL;
list_for_each_entry_safe(iter, node, &intf_reset_list, list) {
CWMP_LOG(INFO, "Executing interface reset: end session request");
cwmp_invoke_intf_reset(iter->path);
list_del(&iter->list);
free(iter);
}
INIT_LIST_HEAD(&intf_reset_list);
cwmp_uci_exit();
icwmp_cleanmem();
end_session_flag = 0;
return CWMP_OK;
}
static void cwmp_schedule_session(struct cwmp *cwmp)
{
int t;
struct timespec time_to_wait = { 0, 0 };
bool retry = false;
char *exec_download = NULL;
int is_notify = 0;
cwmp->cwmp_cr_event = 0;
while (1) {
struct list_head *ilist;
struct session *session;
int error;
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, 0);
time_to_wait.tv_sec = time(NULL) + t;
CWMP_LOG(INFO, "Waiting the next session");
if (thread_end) {
pthread_mutex_unlock(&(cwmp->mutex_session_send));
return;
}
pthread_cond_timedwait(&(cwmp->threshold_session_send), &(cwmp->mutex_session_send), &time_to_wait);
if (thread_end) {
pthread_mutex_unlock(&(cwmp->mutex_session_send));
return;
}
ilist = (&(cwmp->head_session_queue))->next;
retry = false;
}
pthread_mutex_lock(&mutex_heartbeat_session);
if (cwmp->session_status.last_status == SESSION_FAILURE) {
cwmp_config_load(cwmp);
if (thread_end) {
pthread_mutex_unlock(&mutex_heartbeat_session);
pthread_mutex_unlock(&(cwmp->mutex_session_send));
return;
}
}
session = list_entry(ilist, struct session, list);
if (file_exists(DM_ENABLED_NOTIFY)) {
if (!event_exist_in_list(cwmp, EVENT_IDX_4VALUE_CHANGE))
is_notify = check_value_change();
}
if (is_notify > 0 || !file_exists(DM_ENABLED_NOTIFY) || cwmp->custom_notify_active) {
cwmp->custom_notify_active = false;
cwmp_update_enabled_notify_file();
}
cwmp_prepare_value_change(cwmp);
clean_list_value_change();
if ((error = cwmp_move_session_to_session_send(cwmp, session))) {
CWMP_LOG(EMERG, "FATAL error in the mutex process in the session scheduler!");
exit(EXIT_FAILURE);
}
cwmp->session_status.last_end_time = 0;
cwmp->session_status.last_start_time = time(NULL);
cwmp->session_status.last_status = SESSION_RUNNING;
cwmp->session_status.next_retry = 0;
if (file_exists(fc_cookies))
remove(fc_cookies);
cwmp_uci_init();
CWMP_LOG(INFO, "Start session");
uci_get_value(UCI_CPE_EXEC_DOWNLOAD, &exec_download);
if (exec_download && strcmp(exec_download, "1") == 0) {
CWMP_LOG(INFO, "Firmware downloaded and applied successfully");
cwmp_uci_set_value("cwmp", "cpe", "exec_download", "0");
cwmp_commit_package("cwmp", UCI_STANDARD_CONFIG);
}
FREE(exec_download);
error = cwmp_schedule_rpc(cwmp, session);
CWMP_LOG(INFO, "End session");
cwmp_uci_exit();
if (thread_end) {
event_remove_all_event_container(session, RPC_SEND);
run_session_end_func();
cwmp_session_destructor(session);
pthread_mutex_unlock(&mutex_heartbeat_session);
pthread_mutex_unlock(&(cwmp->mutex_session_send));
return;
}
if (session->error == CWMP_RETRY_SESSION && (!list_empty(&(session->head_event_container)) || (list_empty(&(session->head_event_container)) && cwmp->cwmp_cr_event == 0))) {
cwmp_config_load(cwmp);
if (thread_end) {
event_remove_all_event_container(session, RPC_SEND);
run_session_end_func();
cwmp_session_destructor(session);
pthread_mutex_unlock(&mutex_heartbeat_session);
pthread_mutex_unlock(&(cwmp->mutex_session_send));
return;
}
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, 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, 0);
cwmp->session_status.failure_session++;
pthread_mutex_unlock(&mutex_heartbeat_session);
pthread_mutex_unlock(&(cwmp->mutex_session_send));
continue;
}
event_remove_all_event_container(session, RPC_SEND);
run_session_end_func();
cwmp_session_destructor(session);
cwmp->session_send = NULL;
cwmp->retry_count_session = 0;
cwmp->session_status.last_end_time = time(NULL);
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));
}
}
static void check_exit_timer_expiry(struct uloop_timeout *timeout)
{
if (thread_end == true) {
uloop_end();
}
uloop_timeout_set(timeout, 1);
}
static void *thread_uloop_run(void *v __attribute__((unused)))
{
uloop_init();
if (netlink_init()) {
CWMP_LOG(ERROR, "netlink initialization failed");
}
if (cwmp_main.conf.ipv6_enable) {
if (netlink_init_v6()) {
CWMP_LOG(ERROR, "netlink initialization failed");
}
}
ctx = ubus_connect(cwmp_main.conf.ubus_socket);
if (!ctx)
return NULL;
ubus_add_uloop(ctx);
if (icwmp_register_object(ctx))
return NULL;
struct uloop_timeout tm;
memset(&tm, 0, sizeof(tm));
tm.cb = check_exit_timer_expiry;
uloop_timeout_set(&tm, 1);
uloop_run();
uloop_done();
return NULL;
}
static void *thread_http_cr_server_listen(void *v __attribute__((unused)))
{
http_server_listen();
return NULL;
}
void load_forced_inform_json_file(struct cwmp *cwmp)
{
struct blob_buf bbuf;
struct blob_attr *cur;
struct blob_attr *custom_forced_inform_list = NULL;
int rem;
if (cwmp->conf.forced_inform_json_file == NULL || !file_exists(cwmp->conf.forced_inform_json_file))
return;
memset(&bbuf, 0, sizeof(struct blob_buf));
blob_buf_init(&bbuf, 0);
if (blobmsg_add_json_from_file(&bbuf, cwmp->conf.forced_inform_json_file) == false) {
CWMP_LOG(WARNING, "The file %s is not a valid JSON file", cwmp->conf.forced_inform_json_file);
blob_buf_free(&bbuf);
return;
}
const struct blobmsg_policy p[1] = { { "forced_inform", BLOBMSG_TYPE_ARRAY } };
struct blob_attr *tb[1] = { NULL };
blobmsg_parse(p, 1, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head));
if (tb[0] == NULL) {
CWMP_LOG(WARNING, "The JSON file %s doesn't contain a forced inform parameters list", cwmp->conf.custom_notify_json);
blob_buf_free(&bbuf);
return;
}
custom_forced_inform_list = tb[0];
blobmsg_for_each_attr(cur, custom_forced_inform_list, rem)
{
char parameter_path[128];
char *val = NULL;
snprintf(parameter_path, sizeof(parameter_path), "%s", blobmsg_get_string(cur));
if (parameter_path[strlen(parameter_path)-1] == '.') {
CWMP_LOG(WARNING, "%s is rejected as inform parameter. Only leaf parameters are allowed.", parameter_path);
continue;
}
int fault = cwmp_get_leaf_value(parameter_path, &val);
if (fault != 0) {
CWMP_LOG(WARNING, "%s is rejected as inform parameter. Wrong parameter path.", parameter_path);
continue;
}
custom_forced_inform_parameters[nbre_custom_inform++] = strdup(parameter_path);
FREE(val);
}
blob_buf_free(&bbuf);
}
void load_boot_inform_json_file(struct cwmp *cwmp)
{
struct blob_buf bbuf;
struct blob_attr *cur;
struct blob_attr *custom_boot_inform_list = NULL;
int rem;
if (cwmp->conf.boot_inform_json_file == NULL || !file_exists(cwmp->conf.boot_inform_json_file))
return;
memset(&bbuf, 0, sizeof(struct blob_buf));
blob_buf_init(&bbuf, 0);
if (blobmsg_add_json_from_file(&bbuf, cwmp->conf.boot_inform_json_file) == false) {
CWMP_LOG(WARNING, "The file %s is not a valid JSON file", cwmp->conf.boot_inform_json_file);
blob_buf_free(&bbuf);
return;
}
const struct blobmsg_policy p[1] = { { "boot_inform", BLOBMSG_TYPE_ARRAY } };
struct blob_attr *tb[1] = { NULL };
blobmsg_parse(p, 1, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head));
if (tb[0] == NULL) {
CWMP_LOG(WARNING, "The JSON file %s doesn't contain a boot inform parameters list", cwmp->conf.custom_notify_json);
blob_buf_free(&bbuf);
return;
}
custom_boot_inform_list = tb[0];
blobmsg_for_each_attr(cur, custom_boot_inform_list, rem)
{
char parameter_path[128];
char *val = NULL;
snprintf(parameter_path, sizeof(parameter_path), "%s", blobmsg_get_string(cur));
if (parameter_path[strlen(parameter_path)-1] == '.') {
CWMP_LOG(WARNING, "%s is rejected as inform parameter. Only leaf parameters are allowed.", parameter_path);
continue;
}
int fault = cwmp_get_leaf_value(parameter_path, &val);
if (fault != 0) {
CWMP_LOG(WARNING, "%s is rejected as inform parameter. Wrong parameter path.", parameter_path);
continue;
}
boot_inform_parameters[nbre_boot_inform++] = strdup(parameter_path);
FREE(val);
}
blob_buf_free(&bbuf);
}
void clean_custom_inform_parameters()
{
int i;
for (i=0; i < nbre_custom_inform; i++) {
free(custom_forced_inform_parameters[i]);
custom_forced_inform_parameters[i] = NULL;
}
nbre_custom_inform = 0;
for (i=0; i < nbre_boot_inform; i++) {
free(boot_inform_parameters[i]);
boot_inform_parameters[i] = NULL;
}
nbre_boot_inform = 0;
}
int create_cwmp_var_state_files()
{
/*
* Create Notifications empty uci package
*/
if (!file_exists(CWMP_VARSTATE_UCI_PACKAGE)) {
FILE *fptr = fopen(CWMP_VARSTATE_UCI_PACKAGE, "w+");
if (fptr)
fclose(fptr);
else
return CWMP_GEN_ERR;
}
if (!folder_exists("/var/run/icwmpd")) {
if (mkdir("/var/run/icwmpd", S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
CWMP_LOG(INFO, "Not able to create the folder /var/run/icwmpd");
return CWMP_GEN_ERR;
}
}
return CWMP_OK;
}
static bool g_usp_object_available = false;
static void lookup_event_cb(struct ubus_context *ctx __attribute__((unused)),
struct ubus_event_handler *ev __attribute__((unused)),
const char *type, struct blob_attr *msg)
{
const struct blobmsg_policy policy = {
"path", BLOBMSG_TYPE_STRING
};
struct blob_attr *attr;
const char *path;
if (type && strcmp(type, "ubus.object.add") != 0)
return;
blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg));
if (!attr)
return;
path = blobmsg_data(attr);
if (path && strcmp(path, USP_OBJECT_NAME) == 0) {
g_usp_object_available = true;
uloop_end();
}
}
static void lookup_timeout_cb(struct uloop_timeout *timeout __attribute__((unused)))
{
uloop_end();
}
static int wait_for_usp_raw_object()
{
#define USP_RAW_WAIT_TIMEOUT 60
struct ubus_context *uctx;
int ret;
uint32_t ubus_id;
struct ubus_event_handler add_event;
struct uloop_timeout u_timeout;
g_usp_object_available = false;
uctx = ubus_connect(NULL);
if (uctx == NULL) {
CWMP_LOG(ERROR, "Can't create ubus context");
return FAULT_CPE_INTERNAL_ERROR;
}
uloop_init();
ubus_add_uloop(uctx);
// register for add event
memset(&add_event, 0, sizeof(struct ubus_event_handler));
add_event.cb = lookup_event_cb;
ubus_register_event_handler(uctx, &add_event, "ubus.object.add");
// check if object already present
ret = ubus_lookup_id(uctx, USP_OBJECT_NAME, &ubus_id);
if (ret == 0) {
g_usp_object_available = true;
goto end;
}
// Set timeout to expire lookup
memset(&u_timeout, 0, sizeof(struct uloop_timeout));
u_timeout.cb = lookup_timeout_cb;
uloop_timeout_set(&u_timeout, USP_RAW_WAIT_TIMEOUT * 1000);
uloop_run();
uloop_done();
end:
ubus_free(uctx);
if (g_usp_object_available == false) {
CWMP_LOG(ERROR, "%s object not found", USP_OBJECT_NAME);
return FAULT_CPE_INTERNAL_ERROR;
}
return 0;
}
static int cwmp_init(struct cwmp *cwmp)
{
int error;
icwmp_init_list_services();
cwmp->event_id = 0;
cwmp->cwmp_period = 0;
cwmp->cwmp_periodic_time = 0;
cwmp->cwmp_periodic_enable = false;
/* Only One instance should run*/
cwmp->pid_file = fopen("/var/run/icwmpd.pid", "w+");
fcntl(fileno(cwmp->pid_file), F_SETFD, fcntl(fileno(cwmp->pid_file), F_GETFD) | FD_CLOEXEC);
int rc = flock(fileno(cwmp->pid_file), LOCK_EX | LOCK_NB);
if (rc) {
if (EWOULDBLOCK != errno) {
char *piderr = "PID file creation failed: Quit the daemon!";
fprintf(stderr, "%s\n", piderr);
CWMP_LOG(ERROR, "%s", piderr);
exit(EXIT_FAILURE);
} else
exit(EXIT_SUCCESS);
}
if (cwmp->pid_file)
fclose(cwmp->pid_file);
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);
INIT_LIST_HEAD(&(cwmp->head_session_queue));
if ((error = create_cwmp_var_state_files()))
return error;
CWMP_LOG(DEBUG, "Loading icwmpd configuration");
cwmp_config_load(cwmp);
if (thread_end == true)
return CWMP_GEN_ERR;
CWMP_LOG(DEBUG, "Successfully load icwmpd configuration");
cwmp_get_deviceid(cwmp);
load_forced_inform_json_file(cwmp);
load_boot_inform_json_file(cwmp);
load_custom_notify_json(cwmp);
init_list_param_notify();
get_nonce_key();
memset(&intf_reset_list, 0, sizeof(struct list_head));
INIT_LIST_HEAD(&intf_reset_list);
return CWMP_OK;
}
static void cwmp_free(struct cwmp *cwmp)
{
FREE(cwmp->deviceid.manufacturer);
FREE(cwmp->deviceid.serialnumber);
FREE(cwmp->deviceid.productclass);
FREE(cwmp->deviceid.oui);
FREE(cwmp->deviceid.softwareversion);
FREE(cwmp->conf.lw_notification_hostname);
FREE(cwmp->conf.ip);
FREE(cwmp->conf.ipv6);
FREE(cwmp->conf.acsurl);
FREE(cwmp->conf.acs_userid);
FREE(cwmp->conf.acs_passwd);
FREE(cwmp->conf.interface);
FREE(cwmp->conf.cpe_userid);
FREE(cwmp->conf.cpe_passwd);
FREE(cwmp->conf.ubus_socket);
FREE(cwmp->conf.connection_request_path);
FREE(cwmp->conf.default_wan_iface);
FREE(cwmp->conf.forced_inform_json_file);
FREE(cwmp->conf.custom_notify_json);
FREE(cwmp->conf.boot_inform_json_file);
FREE(nonce_key);
clean_list_param_notify();
bkp_tree_clean();
if (ctx) {
icwmp_delete_object(ctx);
ubus_free(ctx);
}
clean_custom_inform_parameters();
icwmp_cleanmem();
cwmp_uci_exit();
}
static void icwmp_signal_handler(int signal_num)
{
if (signal_num == SIGINT || signal_num == SIGTERM) {
thread_end = true;
CWMP_LOG(INFO, "Received signal %d", signal_num);
if (cwmp_main.session_status.last_status == SESSION_RUNNING)
http_set_timeout();
pthread_cond_signal(&(cwmp_main.threshold_session_send));
pthread_cond_signal(&(cwmp_main.threshold_periodic));
pthread_cond_signal(&(cwmp_main.threshold_notify_periodic));
pthread_cond_signal(&threshold_schedule_inform);
pthread_cond_signal(&threshold_download);
pthread_cond_signal(&threshold_change_du_state);
pthread_cond_signal(&threshold_schedule_download);
pthread_cond_signal(&threshold_apply_schedule_download);
pthread_cond_signal(&threshold_upload);
pthread_cond_signal(&threshold_heartbeat_session);
pthread_cond_signal(&threasheld_retry_session);
shutdown(cwmp_main.cr_socket_desc, SHUT_RDWR);
}
}
static void configure_var_state(struct cwmp *cwmp)
{
char *zone_name = NULL;
if (!file_exists(VARSTATE_CONFIG"/cwmp"))
creat(VARSTATE_CONFIG"/cwmp", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
cwmp_uci_reinit();
cwmp_uci_add_section_with_specific_name("cwmp", "acs", "acs", UCI_VARSTATE_CONFIG);
cwmp_uci_add_section_with_specific_name("cwmp", "cpe", "cpe", UCI_VARSTATE_CONFIG);
get_firewall_zone_name_by_wan_iface(cwmp->conf.default_wan_iface, &zone_name);
cwmp_uci_set_varstate_value("cwmp", "acs", "zonename", zone_name ? zone_name : "wan");
cwmp_commit_package("cwmp", UCI_VARSTATE_CONFIG);
}
int main(int argc, char **argv)
{
struct sigaction act;
int error;
struct env env;
memset(&cwmp_main, 0, sizeof(struct cwmp));
openlog("cwmp", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
/* This is to initialize the global context in mxml,
* with out init mxml sometimes segfaults, when calling the destructor.
*/
mxml_error(NULL);
cwmp_main.init_complete = false;
error = wait_for_usp_raw_object();
if (error)
return error;
memset(&env, 0, sizeof(struct env));
if ((error = global_env_init(argc, argv, &env)))
return error;
memcpy(&(cwmp_main.env), &env, sizeof(struct env));
error = get_preinit_config(&(cwmp_main.conf));
if (error) {
return error;
}
error = pthread_create(&ubus_thread, NULL, &thread_uloop_run, NULL);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the ubus thread!");
}
if ((error = cwmp_init(&cwmp_main))) {
thread_end = true;
pthread_join(ubus_thread, NULL);
cwmp_free(&cwmp_main);
return error;
}
CWMP_LOG(INFO, "STARTING ICWMP with PID :%d", getpid());
cwmp_main.start_time = time(NULL);
if ((error = cwmp_init_backup_session(&cwmp_main, NULL, ALL)))
return error;
if ((error = cwmp_root_cause_events(&cwmp_main)))
return error;
configure_var_state(&cwmp_main);
http_server_init();
memset(&act, 0, sizeof(act));
act.sa_handler = icwmp_signal_handler;
sigaction(SIGINT, &act, 0);
sigaction(SIGTERM, &act, 0);
error = pthread_create(&http_cr_server_thread, NULL, &thread_http_cr_server_listen, NULL);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the http connection request server thread!");
}
error = pthread_create(&periodic_event_thread, NULL, &thread_event_periodic, (void *)&cwmp_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the periodic event thread!");
}
error = pthread_create(&periodic_check_notify, NULL, &thread_periodic_check_notify, (void *)&cwmp_main);
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_main);
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_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the scheduled inform thread!");
}
error = pthread_create(&download_thread, NULL, &thread_cwmp_rpc_cpe_download, (void *)&cwmp_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the download thread!");
}
error = pthread_create(&change_du_state_thread, NULL, &thread_cwmp_rpc_cpe_change_du_state, (void *)&cwmp_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the state change thread!");
}
error = pthread_create(&schedule_download_thread, NULL, &thread_cwmp_rpc_cpe_schedule_download, (void *)&cwmp_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the schedule download thread!");
}
error = pthread_create(&apply_schedule_download_thread, NULL, &thread_cwmp_rpc_cpe_apply_schedule_download, (void *)&cwmp_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the schedule download thread!");
}
error = pthread_create(&upload_thread, NULL, &thread_cwmp_rpc_cpe_upload, (void *)&cwmp_main);
if (error < 0) {
CWMP_LOG(ERROR, "Error when creating the download thread!");
}
cwmp_schedule_session(&cwmp_main);
/* Join all threads */
pthread_join(periodic_event_thread, NULL);
pthread_join(periodic_check_notify, NULL);
pthread_join(scheduleInform_thread, NULL);
pthread_join(download_thread, NULL);
pthread_join(upload_thread, NULL);
pthread_join(schedule_download_thread, NULL);
pthread_join(apply_schedule_download_thread, NULL);
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_main);
CWMP_LOG(INFO, "EXIT ICWMP");
closelog();
return CWMP_OK;
}