diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f7b18f7..66770a2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -69,4 +69,4 @@ run_functional_test: - funl-test-coverage.xml - funl-test-memory-report.xml - funl-test-result.log - - funl-test-debug.log + - funl-test-debug.log \ No newline at end of file diff --git a/backupSession.c b/backupSession.c index 12ab001..01cadc5 100644 --- a/backupSession.c +++ b/backupSession.c @@ -862,6 +862,7 @@ void load_download(mxml_node_t *tree) .file_size = &download_request->file_size, .time = &download_request->scheduled_time }; load_specific_backup_attributes(tree, &bkp_attrs); + download_request->handler_timer.cb = cwmp_start_download; list_for_each (ilist, &(list_download)) { idownload_request = list_entry(ilist, struct download, list); @@ -872,6 +873,7 @@ void load_download(mxml_node_t *tree) list_add(&(download_request->list), ilist->prev); if (download_request->scheduled_time != 0) count_download_queue++; + cwmp_set_end_session(END_SESSION_DOWNLOAD); } void load_schedule_download(mxml_node_t *tree) diff --git a/bin/Makefile.am b/bin/Makefile.am index 0415e7a..cb35095 100755 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -21,6 +21,7 @@ icwmpd_SOURCES = \ ../notifications.c \ ../cwmp_zlib.c \ ../cwmp_du_state.c \ + ../subprocess.c \ ../download.c \ ../upload.c \ ../sched_inform.c \ diff --git a/config.c b/config.c index 2d6b6a6..8cb000e 100755 --- a/config.c +++ b/config.c @@ -628,11 +628,11 @@ end: int cwmp_get_deviceid() { - cwmp_get_leaf_value("Device.DeviceInfo.Manufacturer", &cwmp->deviceid.manufacturer); - cwmp_get_leaf_value("Device.DeviceInfo.SerialNumber", &cwmp->deviceid.serialnumber); - cwmp_get_leaf_value("Device.DeviceInfo.ProductClass", &cwmp->deviceid.productclass); - cwmp_get_leaf_value("Device.DeviceInfo.ManufacturerOUI", &cwmp->deviceid.oui); - cwmp_get_leaf_value("Device.DeviceInfo.SoftwareVersion", &cwmp->deviceid.softwareversion); + cwmp_get_leaf_value("Device.DeviceInfo.Manufacturer", &cwmp_main->deviceid.manufacturer); + cwmp_get_leaf_value("Device.DeviceInfo.SerialNumber", &cwmp_main->deviceid.serialnumber); + cwmp_get_leaf_value("Device.DeviceInfo.ProductClass", &cwmp_main->deviceid.productclass); + cwmp_get_leaf_value("Device.DeviceInfo.ManufacturerOUI", &cwmp_main->deviceid.oui); + cwmp_get_leaf_value("Device.DeviceInfo.SoftwareVersion", &cwmp_main->deviceid.softwareversion); return CWMP_OK; } diff --git a/cwmp.c b/cwmp.c index 8d9f63f..1d6b942 100644 --- a/cwmp.c +++ b/cwmp.c @@ -387,16 +387,7 @@ static void cwmp_exit() void cwmp_end_handler(int signal_num __attribute__((unused))) { - cwmp_stop = true; - - if (cwmp_main->session->session_status.last_status == SESSION_RUNNING) - http_set_timeout(); - - uloop_timeout_cancel(&retry_session_timer); - uloop_timeout_cancel(&priodic_session_timer); - uloop_timeout_cancel(&session_timer); - uloop_end(); - shutdown(cwmp_main->cr_socket_desc, SHUT_RDWR); + cwmp_ubus_call("tr069", "exit", CWMP_UBUS_ARGS{ {} }, 0, NULL, NULL); } int main(int argc, char **argv) diff --git a/download.c b/download.c index 7d95d41..271e502 100644 --- a/download.c +++ b/download.c @@ -9,6 +9,7 @@ */ #include +#include #include "common.h" #include "download.h" @@ -19,13 +20,14 @@ #include "cwmp_time.h" #include "event.h" #include "cwmp_uci.h" +#include "session.h" +#include "subprocess.h" LIST_HEAD(list_download); LIST_HEAD(list_schedule_download); LIST_HEAD(list_apply_schedule_download); pthread_mutex_t mutex_download = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t threshold_download; pthread_mutex_t mutex_schedule_download = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t threshold_schedule_download; pthread_mutex_t mutex_apply_schedule_download = PTHREAD_MUTEX_INITIALIZER; @@ -70,6 +72,57 @@ int download_file(const char *file_path, const char *url, const char *username, return res_code; } +char *download_file_task_function(char *task) +{ + + struct blob_buf bbuf; + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + + if (blobmsg_add_json_from_string(&bbuf, task) == false) { + blob_buf_free(&bbuf); + return NULL; + } + const struct blobmsg_policy p[5] = { { "task", BLOBMSG_TYPE_STRING }, { "file_path", BLOBMSG_TYPE_STRING }, { "url", BLOBMSG_TYPE_STRING }, { "username", BLOBMSG_TYPE_STRING }, { "password", BLOBMSG_TYPE_STRING } }; + + struct blob_attr *tb[5] = { NULL, NULL, NULL, NULL, NULL}; + blobmsg_parse(p, 5, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); + char *task_name = blobmsg_get_string(tb[0]); + if (!task_name || strcmp(task_name, "download") != 0) + return NULL; + char *file_path = blobmsg_get_string(tb[1]); + char *url = blobmsg_get_string(tb[2]); + char *username = blobmsg_get_string(tb[3]); + char *password = blobmsg_get_string(tb[4]); + + int http_code = download_file(file_path, url, username, password); + char *http_ret = (char *)malloc(4 * sizeof(char)); + snprintf(http_ret, 4, "%d", http_code); + http_ret[3] = 0; + return http_ret; +} + +int download_file_in_subprocess(const char *file_path, const char *url, const char *username, const char *password) +{ + subprocess_start(download_file_task_function); + + struct blob_buf bbuf; + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + blobmsg_add_string(&bbuf, "task", "download"); + blobmsg_add_string(&bbuf, "file_path", file_path ? file_path : ""); + blobmsg_add_string(&bbuf, "url", url ? url : ""); + blobmsg_add_string(&bbuf, "username", username ? username : ""); + blobmsg_add_string(&bbuf, "password", password ? password : ""); + char *download_task = blobmsg_format_json(bbuf.head, true); + blob_buf_free(&bbuf); + + if (download_task != NULL) { + char *ret = execute_task_in_subprocess(download_task); + return atoi(ret); + } + return 500; +} /* * Check if the downloaded image can be applied */ @@ -140,6 +193,58 @@ int get_available_bank_id() return bank_id; } +/* + * Get Bank Status + */ +void ubus_get_bank_status_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg) +{ + int *bank_id = (int *)req->priv; + int *status = bank_id; + bool bank_found = false; + struct blob_attr *banks = NULL; + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, msg, rem) + { + if (blobmsg_type(cur) == BLOBMSG_TYPE_ARRAY) { + banks = cur; + break; + } + } + + const struct blobmsg_policy p[8] = { { "name", BLOBMSG_TYPE_STRING }, { "id", BLOBMSG_TYPE_INT32 }, { "active", BLOBMSG_TYPE_BOOL }, { "upgrade", BLOBMSG_TYPE_BOOL }, + { "fwver", BLOBMSG_TYPE_STRING }, { "swver", BLOBMSG_TYPE_STRING }, { "fwver", BLOBMSG_TYPE_STRING }, { "status", BLOBMSG_TYPE_STRING } }; + + blobmsg_for_each_attr(cur, banks, rem) + { + struct blob_attr *tb[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + blobmsg_parse(p, 8, tb, blobmsg_data(cur), blobmsg_len(cur)); + if (!tb[0]) + continue; + + if (blobmsg_get_u32(tb[1]) == (uint32_t)*bank_id) { + bank_found = true; + if (strcmp(blobmsg_get_string(tb[7]), "Available") == 0 || strcmp(blobmsg_get_string(tb[7]), "Active")) + *status = 1; + else + *status = 0; + } + } + if (bank_found == false) + *status = 0; +} + +int get_applied_firmware_status(int *bank_id_status) +{ + int e; + e = cwmp_ubus_call("fwbank", "dump", CWMP_UBUS_ARGS{ {} }, 0, ubus_get_bank_status_callback, &bank_id_status); + if (e != 0) { + CWMP_LOG(INFO, "fwbank dump ubus method failed: Ubus err code: %d", e); + } + return e; +} + /* * Apply the new firmware */ @@ -154,6 +259,24 @@ int cwmp_apply_firmware() return e; } +void wait_firmware_to_be_applied(int bank_id) +{ + int count = 0; + + do { + int bank_id_status = bank_id; + + if (get_applied_firmware_status(&bank_id_status) != CWMP_OK) + break; + + if (bank_id_status == 1) + break; + + usleep(1000 * 1000); + count++; + } while(count < 15); +} + int cwmp_apply_multiple_firmware() { int e; @@ -166,9 +289,28 @@ int cwmp_apply_multiple_firmware() CWMP_LOG(INFO, "fwbank upgrade ubus method failed: Ubus err code: %d", e); return -1; } + //wait until the apply completes + wait_firmware_to_be_applied(bank_id); return CWMP_OK; } +char *apply_multiple_firmware_task_function(char *task __attribute__((unused))) +{ + int ret = cwmp_apply_multiple_firmware(); + + char *ret_str = (char *)malloc(2 * sizeof(char)); + snprintf(ret_str, 2, "%d", ret); + ret_str[1] = 0; + return ret_str; +} + +int cwmp_apply_multiple_firmware_in_subprocess() +{ + subprocess_start(apply_multiple_firmware_task_function); + char *ret = execute_task_in_subprocess("{}"); //empty json object + return atoi(ret); +} + int cwmp_launch_download(struct download *pdownload, char *download_file_name, enum load_type ltype, struct transfer_complete **ptransfer_complete) { int error = FAULT_CPE_NO_FAULT; @@ -185,7 +327,7 @@ int cwmp_launch_download(struct download *pdownload, char *download_file_name, e goto end_download; } - int http_code = download_file(ICWMP_DOWNLOAD_FILE, pdownload->url, pdownload->username, pdownload->password); + int http_code = download_file_in_subprocess(ICWMP_DOWNLOAD_FILE, pdownload->url, pdownload->username, pdownload->password); if (http_code == 404) error = FAULT_CPE_DOWNLOAD_FAIL_CONTACT_SERVER; else if (http_code == 401) @@ -314,7 +456,7 @@ int apply_downloaded_file(struct download *pdownload, char *download_file_name, error = FAULT_CPE_NO_FAULT; } else if (strcmp(pdownload->file_type, STORED_FIRMWARE_IMAGE_FILE_TYPE) == 0) { - int err = cwmp_apply_multiple_firmware(); + int err = cwmp_apply_multiple_firmware_in_subprocess(); if (err == CWMP_OK) error = FAULT_CPE_NO_FAULT; else @@ -330,10 +472,12 @@ int apply_downloaded_file(struct download *pdownload, char *download_file_name, } return FAULT_CPE_NO_FAULT; } + if (error != FAULT_CPE_NO_FAULT) { bkp_session_delete_transfer_complete(ptransfer_complete); ptransfer_complete->fault_code = error; } + bkp_session_insert_transfer_complete(ptransfer_complete); bkp_session_save(); cwmp_root_cause_transfer_complete(ptransfer_complete); @@ -356,78 +500,6 @@ struct transfer_complete *set_download_error_transfer_complete(struct download * return ptransfer_complete; } -void *thread_cwmp_rpc_cpe_download(void *v __attribute__((unused))) -{ - struct download *pdownload; - struct timespec download_timeout = { 0, 0 }; - time_t current_time, stime; - int error = FAULT_CPE_NO_FAULT; - struct transfer_complete *ptransfer_complete; - long int time_of_grace = 3600, timeout; - - for (;;) { - - if (cwmp_stop) - break; - - if (list_download.next != &(list_download)) { - pdownload = list_entry(list_download.next, struct download, list); - stime = pdownload->scheduled_time; - current_time = time(NULL); - if (pdownload->scheduled_time != 0) - timeout = current_time - pdownload->scheduled_time; - else - timeout = 0; - if ((timeout >= 0) && (timeout > time_of_grace)) { - pthread_mutex_lock(&mutex_download); - bkp_session_delete_download(pdownload); - error = FAULT_CPE_DOWNLOAD_FAILURE; - ptransfer_complete = set_download_error_transfer_complete(pdownload, TYPE_DOWNLOAD); - list_del(&(pdownload->list)); - if (pdownload->scheduled_time != 0) - count_download_queue--; - cwmp_free_download_request(pdownload); - pthread_mutex_unlock(&mutex_download); - continue; - } - if ((timeout >= 0) && (timeout <= time_of_grace)) { - char *download_file_name = get_file_name_by_download_url(pdownload->url); - CWMP_LOG(INFO, "Launch download file %s", pdownload->url); - error = cwmp_launch_download(pdownload, download_file_name, TYPE_DOWNLOAD, &ptransfer_complete); - sleep(3); - if (error != FAULT_CPE_NO_FAULT) { - bkp_session_insert_transfer_complete(ptransfer_complete); - bkp_session_save(); - cwmp_root_cause_transfer_complete(ptransfer_complete); - bkp_session_delete_transfer_complete(ptransfer_complete); - } else { - error = apply_downloaded_file(pdownload, download_file_name, ptransfer_complete); - if (error || pdownload->file_type[0] == '6') - bkp_session_delete_transfer_complete(ptransfer_complete); - } - if (pdownload->file_type[0] == '6') - sleep(30); - pthread_mutex_lock(&mutex_download); - list_del(&(pdownload->list)); - if (pdownload->scheduled_time != 0) - count_download_queue--; - cwmp_free_download_request(pdownload); - pthread_mutex_unlock(&mutex_download); - continue; - } - pthread_mutex_lock(&mutex_download); - download_timeout.tv_sec = stime; - pthread_cond_timedwait(&threshold_download, &mutex_download, &download_timeout); - pthread_mutex_unlock(&mutex_download); - } else { - pthread_mutex_lock(&mutex_download); - pthread_cond_wait(&threshold_download, &mutex_download); - pthread_mutex_unlock(&mutex_download); - } - } - return NULL; -} - int cwmp_add_apply_schedule_download(struct download *schedule_download, char *start_time) { int i = 0; @@ -889,3 +961,45 @@ int cwmp_rpc_acs_destroy_data_transfer_complete(struct rpc *rpc) FREE(rpc->extra_data); return 0; } + +void cwmp_start_download(struct uloop_timeout *timeout) +{ + struct download *pdownload; + int error = FAULT_CPE_NO_FAULT; + struct transfer_complete *ptransfer_complete; + pdownload = container_of(timeout, struct download, handler_timer); + + char *download_file_name = get_file_name_by_download_url(pdownload->url); + CWMP_LOG(INFO, "Launch download file %s", pdownload->url); + error = cwmp_launch_download(pdownload, download_file_name, TYPE_DOWNLOAD, &ptransfer_complete); + sleep(3); + if (error != FAULT_CPE_NO_FAULT) { + CWMP_LOG(ERROR, "Error while downloading the file: %s", pdownload->url); + bkp_session_insert_transfer_complete(ptransfer_complete); + bkp_session_save(); + cwmp_root_cause_transfer_complete(ptransfer_complete); + bkp_session_delete_transfer_complete(ptransfer_complete); + } else { + error = apply_downloaded_file(pdownload, download_file_name, ptransfer_complete); + if (error != FAULT_CPE_NO_FAULT) { + CWMP_LOG(ERROR, "Error while applying the downloaded file: %s", download_file_name); + bkp_session_insert_transfer_complete(ptransfer_complete); + bkp_session_save(); + cwmp_root_cause_transfer_complete(ptransfer_complete); + bkp_session_delete_transfer_complete(ptransfer_complete); + } + } + if (error == FAULT_CPE_NO_FAULT && pdownload->file_type[0] == '3') { + cwmp_root_cause_transfer_complete(ptransfer_complete); + bkp_session_delete_download(pdownload); + bkp_session_delete_transfer_complete(ptransfer_complete); + bkp_session_save(); + } + pthread_mutex_lock(&mutex_download); + list_del(&(pdownload->list)); + if (pdownload->scheduled_time != 0) + count_download_queue--; + cwmp_free_download_request(pdownload); + pthread_mutex_unlock(&mutex_download); + trigger_cwmp_session_timer(); +} diff --git a/event.c b/event.c index 3d4a251..cb688ad 100644 --- a/event.c +++ b/event.c @@ -218,6 +218,9 @@ int cwmp_root_cause_transfer_complete(struct transfer_complete *p) if (event_container == NULL) { return CWMP_MEM_ERR; } + if ((rpc_acs = cwmp_add_session_rpc_acs(RPC_ACS_TRANSFER_COMPLETE)) == NULL) { + return CWMP_MEM_ERR; + } switch (p->type) { case TYPE_DOWNLOAD: event_container = cwmp_add_event_container(EVENT_IDX_M_Download, p->command_key ? p->command_key : ""); @@ -238,9 +241,7 @@ int cwmp_root_cause_transfer_complete(struct transfer_complete *p) } break; } - if ((rpc_acs = cwmp_add_session_rpc_acs(RPC_ACS_TRANSFER_COMPLETE)) == NULL) { - return CWMP_MEM_ERR; - } + rpc_acs->extra_data = (void *)p; return CWMP_OK; } diff --git a/gitlab-ci/functional-test.sh b/gitlab-ci/functional-test.sh index 5cfeb63..58dbd5c 100755 --- a/gitlab-ci/functional-test.sh +++ b/gitlab-ci/functional-test.sh @@ -24,6 +24,8 @@ configure_genieacs echo "Checking cwmp status" check_cwmp_status +mkdir -p /var/run/icwmpd + [ -f funl-test-result.log ] && rm -f funl-test-result.log echo "## Running script verification of functionalities ##" diff --git a/gitlab-ci/iopsys-supervisord.conf b/gitlab-ci/iopsys-supervisord.conf index e107550..45c0fd5 100644 --- a/gitlab-ci/iopsys-supervisord.conf +++ b/gitlab-ci/iopsys-supervisord.conf @@ -46,6 +46,12 @@ startretries=0 priority=8 command=/bin/bash -c "/usr/sbin/genieacs-ui --ui-jwt-secret secret" +[program:lighttpd] +autorestart=false +startretries=0 +priority=9 +command=service lighttpd restart + [program:icwmpd] autorestart=false startretries=0 diff --git a/gitlab-ci/unit-test.sh b/gitlab-ci/unit-test.sh index 2f0fe0c..4196170 100755 --- a/gitlab-ci/unit-test.sh +++ b/gitlab-ci/unit-test.sh @@ -7,6 +7,16 @@ source ./gitlab-ci/shared.sh trap cleanup EXIT trap cleanup SIGINT +echo "Install lighttpd" +apt-get update +apt-get install -y lighttpd +exec_cmd dd if=/dev/zero of=/builds/iopsys/icwmp/firmware_v1.0.bin bs=25MB count=1 +echo "Valid" > /builds/iopsys/icwmp/firmware_v1.0.bin +exec_cmd cp /builds/iopsys/icwmp/firmware_v1.0.bin /var/www/html +exec_cmd dd if=/dev/zero of=/builds/iopsys/icwmp/invalid_firmware_v1.0.bin bs=25MB count=1 +echo "Invalid" > /builds/iopsys/icwmp/invalid_firmware_v1.0.bin +exec_cmd cp /builds/iopsys/icwmp/invalid_firmware_v1.0.bin /var/www/html + echo "Install Inform json files" exec_cmd mkdir -p /etc/icwmpd exec_cmd cp /builds/iopsys/icwmp/test/files/etc/icwmpd/* /etc/icwmpd diff --git a/inc/common.h b/inc/common.h index 8d4b933..e87ed85 100644 --- a/inc/common.h +++ b/inc/common.h @@ -344,6 +344,7 @@ typedef struct timewindow { typedef struct download { struct list_head list; + struct uloop_timeout handler_timer ; time_t scheduled_time; int file_size; char *command_key; @@ -388,6 +389,7 @@ typedef struct operations { typedef struct upload { struct list_head list; + struct uloop_timeout handler_timer ; time_t scheduled_time; char *command_key; char *file_type; diff --git a/inc/download.h b/inc/download.h index b590aa0..4094da1 100644 --- a/inc/download.h +++ b/inc/download.h @@ -23,7 +23,6 @@ extern struct list_head list_schedule_download; extern struct list_head list_apply_schedule_download; extern pthread_mutex_t mutex_download; -extern pthread_cond_t threshold_download; extern pthread_mutex_t mutex_schedule_download; extern pthread_cond_t threshold_schedule_download; extern pthread_mutex_t mutex_apply_schedule_download; @@ -38,8 +37,8 @@ int cwmp_scheduledDownload_remove_all(); int cwmp_scheduled_Download_remove_all(); int cwmp_apply_scheduled_Download_remove_all(); int cwmp_rpc_acs_destroy_data_transfer_complete(struct rpc *rpc); -void *thread_cwmp_rpc_cpe_download(void *v); void *thread_cwmp_rpc_cpe_schedule_download(void *v); void *thread_cwmp_rpc_cpe_apply_schedule_download(void *v); int cwmp_launch_download(struct download *pdownload, char *download_file_name, enum load_type ltype, struct transfer_complete **ptransfer_complete); +void cwmp_start_download(struct uloop_timeout *timeout); #endif diff --git a/inc/session.h b/inc/session.h index a6c8903..26ea229 100644 --- a/inc/session.h +++ b/inc/session.h @@ -58,7 +58,9 @@ enum end_session_enum END_SESSION_SERVERSELECTION_DIAGNOSTIC = 1 << 11, END_SESSION_SET_NOTIFICATION_UPDATE = 1 << 12, END_SESSION_RESTART_SERVICES = 1 << 13, - END_SESSION_INIT_NOTIFY = 1 << 14 + END_SESSION_INIT_NOTIFY = 1 << 14, + END_SESSION_DOWNLOAD = 1 << 15, + END_SESSION_UPLOAD = 1 << 16 }; enum enum_session_status diff --git a/inc/soap.h b/inc/soap.h index 8946754..78f213d 100644 --- a/inc/soap.h +++ b/inc/soap.h @@ -19,6 +19,10 @@ #include "session.h" #include "xml.h" + +#define PROCESSING_DELAY (1) // In download/upload the message enqueued before sending the response, which cause the download/upload + // to start just before the time. This delay is to compensate the time lapsed during the message enqueue and response + #define MAX_NBRE_CUSTOM_INFORM 256 extern char *custom_forced_inform_parameters[MAX_NBRE_CUSTOM_INFORM]; extern char *boot_inform_parameters[MAX_NBRE_CUSTOM_INFORM]; diff --git a/inc/subprocess.h b/inc/subprocess.h new file mode 100644 index 0000000..4df3ced --- /dev/null +++ b/inc/subprocess.h @@ -0,0 +1,7 @@ +#ifndef SUB_PROC_H +#define SUB_PROC_H + +typedef char* (*task_function)(char *task_arg); +int subprocess_start(task_function task_fun); +char *execute_task_in_subprocess(char *task); +#endif diff --git a/inc/upload.h b/inc/upload.h index c643af5..c56aab1 100644 --- a/inc/upload.h +++ b/inc/upload.h @@ -13,10 +13,10 @@ extern struct list_head list_upload; extern pthread_mutex_t mutex_upload; -extern pthread_cond_t threshold_upload; int cwmp_launch_upload(struct upload *pupload, struct transfer_complete **ptransfer_complete); void *thread_cwmp_rpc_cpe_upload(void *v); int cwmp_scheduledUpload_remove_all(); int cwmp_free_upload_request(struct upload *upload); +void cwmp_start_upload(struct uloop_timeout *timeout); #endif diff --git a/notifications.c b/notifications.c index ef8e795..7a74775 100644 --- a/notifications.c +++ b/notifications.c @@ -570,7 +570,6 @@ void sotfware_version_value_change(struct transfer_complete *p) void periodic_check_notifiy(struct uloop_timeout *timeout __attribute__((unused))) { - int periodic_interval = cwmp_main->conf.periodic_notify_interval; int is_notify = 0; if (cwmp_stop) return; diff --git a/session.c b/session.c index 74299e3..6fef1d8 100644 --- a/session.c +++ b/session.c @@ -20,6 +20,8 @@ #include "diagnostic.h" #include "soap.h" #include "ubus.h" +#include "download.h" +#include "upload.h" pthread_mutex_t start_session_mutext = PTHREAD_MUTEX_INITIALIZER; static void cwmp_priodic_session_timer(struct uloop_timeout *timeout); @@ -71,23 +73,6 @@ int cwmp_session_rpc_destructor(struct rpc *rpc) int cwmp_session_exit() { - struct rpc *rpc; - while (cwmp_main->session->head_rpc_acs.next != &(cwmp_main->session->head_rpc_acs)) { - rpc = list_entry(cwmp_main->session->head_rpc_acs.next, struct rpc, list); - if (!rpc) - break; - if (rpc_acs_methods[rpc->type].extra_clean != NULL) - rpc_acs_methods[rpc->type].extra_clean(rpc); - cwmp_session_rpc_destructor(rpc); - } - - while (cwmp_main->session->head_rpc_cpe.next != &(cwmp_main->session->head_rpc_cpe)) { - rpc = list_entry(cwmp_main->session->head_rpc_cpe.next, struct rpc, list); - if (!rpc) - break; - cwmp_session_rpc_destructor(rpc); - } - cwmp_uci_exit(); icwmp_cleanmem(); return CWMP_OK; @@ -97,17 +82,14 @@ static int cwmp_rpc_cpe_handle_message(struct rpc *rpc_cpe) { if (xml_prepare_msg_out()) return -1; - if (rpc_cpe_methods[rpc_cpe->type].handler(rpc_cpe)) return -1; - if (xml_set_cwmp_id_rpc_cpe()) return -1; return 0; } - static int cwmp_schedule_rpc() { struct list_head *ilist; @@ -231,6 +213,26 @@ void set_cwmp_session_status(int status, int retry_time) } } +void rpc_exit() +{ + struct rpc *rpc; + while (cwmp_main->session->head_rpc_acs.next != &(cwmp_main->session->head_rpc_acs)) { + rpc = list_entry(cwmp_main->session->head_rpc_acs.next, struct rpc, list); + if (!rpc) + break; + if (rpc_acs_methods[rpc->type].extra_clean != NULL) + rpc_acs_methods[rpc->type].extra_clean(rpc); + cwmp_session_rpc_destructor(rpc); + } + + while (cwmp_main->session->head_rpc_cpe.next != &(cwmp_main->session->head_rpc_cpe)) { + rpc = list_entry(cwmp_main->session->head_rpc_cpe.next, struct rpc, list); + if (!rpc) + break; + cwmp_session_rpc_destructor(rpc); + } +} + void start_cwmp_session() { int t, error; @@ -286,6 +288,8 @@ void start_cwmp_session() event_remove_all_event_container(RPC_SEND); event_remove_all_event_container(RPC_QUEUE); run_session_end_func(); + cwmp_session_exit(); + rpc_exit(); return; } @@ -300,6 +304,7 @@ void start_cwmp_session() //event_remove_all_event_container(RPC_QUEUE); cwmp_main->retry_count_session = 0; set_cwmp_session_status(SESSION_SUCCESS, 0); + rpc_exit(); } run_session_end_func(); cwmp_session_exit(); @@ -506,6 +511,34 @@ int run_session_end_func(void) cwmp_factory_reset(); exit(EXIT_SUCCESS); } + + if (end_session_flag & END_SESSION_DOWNLOAD) { + CWMP_LOG(INFO, "Trigger Uloop Downaload Calls"); + struct list_head *ilist; + list_for_each (ilist, &(list_download)) { + struct download *download = list_entry(ilist, struct download, list); + int download_delay = 0; + if (download->scheduled_time > time(NULL)) { + download_delay = download->scheduled_time - time(NULL); + } + uloop_timeout_set(&download->handler_timer, 1000 * download_delay); + } + } + + + if (end_session_flag & END_SESSION_UPLOAD) { + CWMP_LOG(INFO, "Trigger Uloop Upload Calls"); + struct list_head *ilist; + list_for_each (ilist, &(list_upload)) { + struct download *upload = list_entry(ilist, struct download, list); + int upload_delay = 0; + if (upload->scheduled_time > time(NULL)) { + upload_delay = upload->scheduled_time - time(NULL); + } + uloop_timeout_set(&upload->handler_timer, 1000 * upload_delay); + } + } + end_session_flag = 0; return CWMP_OK; } diff --git a/soap.c b/soap.c index b72edb4..a43fcd4 100755 --- a/soap.c +++ b/soap.c @@ -32,8 +32,6 @@ #include "upload.h" #include "sched_inform.h" -#define PROCESSING_DELAY (1) // In download/upload the message enqueued before sending the response, which cause the download/upload - // to start just before the time. This delay is to compensate the time lapsed during the message enqueue and response #define DM_CONN_REQ_URL "Device.ManagementServer.ConnectionRequestURL" struct cwmp_namespaces ns; @@ -533,7 +531,6 @@ int cwmp_rpc_acs_prepare_transfer_complete(struct rpc *rpc) { mxml_node_t *tree, *n; struct transfer_complete *p; - p = (struct transfer_complete *)rpc->extra_data; tree = mxmlLoadString(NULL, CWMP_RESPONSE_MESSAGE, MXML_OPAQUE_CALLBACK); n = mxmlFindElement(tree, tree, "soap_env:Envelope", NULL, NULL, MXML_DESCEND); @@ -2064,6 +2061,7 @@ int cwmp_handle_rpc_cpe_download(struct rpc *rpc) count_download_queue++; download->scheduled_time = scheduled_time; } + download->handler_timer.cb = cwmp_start_download; bkp_session_insert_download(download); bkp_session_save(); if (download_delay != 0) { @@ -2071,9 +2069,9 @@ int cwmp_handle_rpc_cpe_download(struct rpc *rpc) } else { CWMP_LOG(INFO, "Download will start at the end of session"); } - + cwmp_set_end_session(END_SESSION_DOWNLOAD); pthread_mutex_unlock(&mutex_download); - pthread_cond_signal(&threshold_download); + } return 0; @@ -2409,15 +2407,17 @@ int cwmp_handle_rpc_cpe_upload(struct rpc *rpc) count_download_queue++; upload->scheduled_time = scheduled_time; } - bkp_session_insert_upload(upload); + upload->handler_timer.cb = cwmp_start_upload; + bkp_session_save(upload); bkp_session_save(); if (upload_delay != 0) { - CWMP_LOG(INFO, "Upload will start in %us", upload_delay); + CWMP_LOG(INFO, "Download will start in %us", upload_delay); } else { - CWMP_LOG(INFO, "Upload will start at the end of session"); + CWMP_LOG(INFO, "Download will start at the end of session"); } + cwmp_set_end_session(END_SESSION_UPLOAD); pthread_mutex_unlock(&mutex_upload); - pthread_cond_signal(&threshold_upload); + } return 0; diff --git a/subprocess.c b/subprocess.c new file mode 100644 index 0000000..263b90e --- /dev/null +++ b/subprocess.c @@ -0,0 +1,136 @@ +#include +#include + +#include "common.h" +#include "subprocess.h" +#include "log.h" + +#define END_TASK "{\"task\":\"end\"}" +#define EXIT_TASK "{\"task\":\"exit\"}" + +static int pipefd1[2], pipefd2[2]; + +bool check_task_name(char *task, char *name) +{ + struct blob_buf bbuf; + + if (strcmp(task, "{}") == 0) + return false; + + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + + if (blobmsg_add_json_from_string(&bbuf, task) == false) { + blob_buf_free(&bbuf); + return false; + } + + const struct blobmsg_policy p[1] = { { "task", BLOBMSG_TYPE_STRING } }; + + struct blob_attr *tb[1] = { NULL }; + blobmsg_parse(p, 1, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); + if (tb[0] == NULL) + return false; + + char *task_name = blobmsg_get_string(tb[0]); + + if (strcmp(task_name, name) == 0) { + blob_buf_free(&bbuf); + return true; + } + blob_buf_free(&bbuf); + return false; +} + +bool check_task_is_end(char *task) +{ + return check_task_name(task, "end"); +} + +bool check_task_is_exit(char *task) +{ + return check_task_name(task, "exit"); +} + +int subprocess_start(task_function task_fun) +{ + if(task_fun == NULL) + return CWMP_GEN_ERR; + + pid_t p; + if (pipe(pipefd1) == -1) { + CWMP_LOG(ERROR, "pipefd1 failed\n"); + return CWMP_GEN_ERR; + } + if (pipe(pipefd2) == -1) { + CWMP_LOG(ERROR, "pipefd2 failed\n"); + return CWMP_GEN_ERR; + } + p = fork(); + + if (p == 0) { + while(1) { + char from_parent[512]; + read(pipefd1[0], from_parent, 512); //The received string should has the form {"task":"TaskName", "arg1_name":"xxx", "arg2_name":"xxxx"} + if (strlen(from_parent) == 0) + continue; + //get the task name + //if the task name is end + if (check_task_is_end(from_parent)){ + write(pipefd2[1], EXIT_TASK, strlen(EXIT_TASK)+1); + exit(EXIT_SUCCESS); + } + char *to_child = task_fun(from_parent); + + struct blob_buf bbuf; + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + blobmsg_add_string(&bbuf, "ret", to_child); + char *to_child_json = blobmsg_format_json(bbuf.head, true); + write(pipefd2[1], to_child_json, strlen(to_child_json)+1); + blob_buf_free(&bbuf); + } + } + return CWMP_OK; +} + +char *execute_task_in_subprocess(char *task) +{ + char *ret = NULL; + + write(pipefd1[1], task, strlen(task) + 1); + + while(1) { + char from_child[512]; + read(pipefd2[0], from_child, 512); + if(strlen(from_child) == 0) + continue; + //The received string from the child should has the format {"task":"exit"} or {"ret":"exit"} + if (check_task_is_exit(from_child)){ + close(pipefd2[1]); + close(pipefd2[0]); + break; + } + + struct blob_buf bbuf; + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + if (blobmsg_add_json_from_string(&bbuf, from_child) == false) { + blob_buf_free(&bbuf); + continue; + } + const struct blobmsg_policy p[1] = { { "ret", BLOBMSG_TYPE_STRING } }; + struct blob_attr *tb[1] = { NULL }; + blobmsg_parse(p, 1, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); + if (tb[0] == NULL) { + blob_buf_free(&bbuf); + continue; + } + ret = blobmsg_get_string(tb[0]); + write(pipefd1[1], END_TASK, strlen(END_TASK) +1); + } + close(pipefd1[0]); + close(pipefd1[1]); + return ret; +} + diff --git a/test/cmocka/Makefile b/test/cmocka/Makefile index e0e919c..655aa4f 100644 --- a/test/cmocka/Makefile +++ b/test/cmocka/Makefile @@ -10,7 +10,7 @@ UNIT_TESTS = icwmp_datamodel_interface_unit_testd icwmp_soap_msg_unit_testd icwm VALGRIND = valgrind --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all libobj: - $(CC) $(LIBCFLAGS) ../../download.c ../../upload.c ../../log.c ../../md5.c ../../digestauth.c ../../netlink.c ../../cwmp_cli.c ../../cwmp_du_state.c ../../sched_inform.c \ + $(CC) $(LIBCFLAGS) ../../subprocess.c ../../download.c ../../upload.c ../../log.c ../../md5.c ../../digestauth.c ../../netlink.c ../../cwmp_cli.c ../../cwmp_du_state.c ../../sched_inform.c \ ../../diagnostic.c ../../reboot.c ../../notifications.c ../../cwmp_zlib.c ../../datamodel_interface.c ../../http.c ../../backupSession.c \ ../../cwmp_time.c ../../config.c ../../event.c ../../session.c ../../ubus.c ../../common.c ../../xml.c ../../soap.c ../../cwmp_uci.c ../../cwmp.c $(LDFLAGS) diff --git a/test/script/test_seq.txt b/test/script/test_seq.txt index 35a2530..d690461 100644 --- a/test/script/test_seq.txt +++ b/test/script/test_seq.txt @@ -2,4 +2,5 @@ verify_add_method.sh verify_delete_method.sh verify_get_method.sh verify_set_method.sh +verify_download_method.sh verify_cmd_line.sh diff --git a/test/script/verify_download_method.sh b/test/script/verify_download_method.sh index 8583e71..ea650a5 100755 --- a/test/script/verify_download_method.sh +++ b/test/script/verify_download_method.sh @@ -19,11 +19,15 @@ if [ "$status" != "1" ]; then exit 1 fi -rm /etc/icwmpd/dm_enabled_notify +rm /var/run/icwmpd/dm_enabled_notify remove_icwmp_log -echo "Restarting icwmpd in order to apply the new firmware" >> ./funl-test-debug.log -supervisorctl restart icwmpd >> ./funl-test-debug.log sleep 5 +echo "Restarting icwmpd in order to apply the new firmware" >> ./funl-test-debug.log +#/builds/iopsys/icwmp/bin/icwmpd -b & +supervisorctl stop icwmpd +sleep 3 +supervisorctl start icwmpd >> ./funl-test-debug.log +sleep 10 check_session "TransferComplete" received_command_key=$(print_tag_value "cwmp:TransferComplete" "CommandKey") if [ "$sent_command_key" != "$received_command_key" ]; then diff --git a/ubus.c b/ubus.c index 078f64d..87ad30b 100755 --- a/ubus.c +++ b/ubus.c @@ -97,7 +97,16 @@ static int cwmp_handle_command(struct ubus_context *ctx, struct ubus_object *obj if (snprintf(info, sizeof(info), "icwmpd daemon stopped") == -1) return -1; - cwmp_end_handler(SIGTERM); + cwmp_stop = true; + + if (cwmp_main->session->session_status.last_status == SESSION_RUNNING) + http_set_timeout(); + + uloop_timeout_cancel(&retry_session_timer); + uloop_timeout_cancel(&priodic_session_timer); + uloop_timeout_cancel(&session_timer); + uloop_end(); + shutdown(cwmp_main->cr_socket_desc, SHUT_RDWR); } else { blobmsg_add_u32(&b, "status", -1); diff --git a/upload.c b/upload.c index 0ef35c4..6ccaac9 100644 --- a/upload.c +++ b/upload.c @@ -10,6 +10,7 @@ #include #include +#include #include "common.h" #include "upload.h" @@ -20,12 +21,13 @@ #include "backupSession.h" #include "cwmp_uci.h" #include "event.h" +#include "subprocess.h" +#include "session.h" #define CURL_TIMEOUT 20 LIST_HEAD(list_upload); -pthread_cond_t threshold_upload; pthread_mutex_t mutex_upload = PTHREAD_MUTEX_INITIALIZER; int lookup_vcf_name(char *instance, char **value) @@ -62,6 +64,9 @@ int lookup_vlf_name(char *instance, char **value) return 0; } +/* + * Upload file + */ int upload_file(const char *file_path, const char *url, const char *username, const char *password) { int res_code = 0; @@ -92,6 +97,58 @@ int upload_file(const char *file_path, const char *url, const char *username, co return res_code; } +char *upload_file_task_function(char *task) +{ + + struct blob_buf bbuf; + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + + if (blobmsg_add_json_from_string(&bbuf, task) == false) { + blob_buf_free(&bbuf); + return NULL; + } + const struct blobmsg_policy p[5] = { { "task", BLOBMSG_TYPE_STRING }, { "file_path", BLOBMSG_TYPE_STRING }, { "url", BLOBMSG_TYPE_STRING }, { "username", BLOBMSG_TYPE_STRING }, { "password", BLOBMSG_TYPE_STRING } }; + + struct blob_attr *tb[5] = { NULL, NULL, NULL, NULL, NULL}; + blobmsg_parse(p, 5, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); + char *task_name = blobmsg_get_string(tb[0]); + if (!task_name || strcmp(task_name, "upload") != 0) + return NULL; + char *file_path = blobmsg_get_string(tb[1]); + char *url = blobmsg_get_string(tb[2]); + char *username = blobmsg_get_string(tb[3]); + char *password = blobmsg_get_string(tb[4]); + + int http_code = upload_file(file_path, url, username, password); + char *http_ret = (char *)malloc(4 * sizeof(char)); + snprintf(http_ret, 4, "%d", http_code); + http_ret[3] = 0; + return http_ret; +} + +int upload_file_in_subprocess(const char *file_path, const char *url, const char *username, const char *password) +{ + subprocess_start(upload_file_task_function); + + struct blob_buf bbuf; + memset(&bbuf, 0, sizeof(struct blob_buf)); + blob_buf_init(&bbuf, 0); + blobmsg_add_string(&bbuf, "task", "upload"); + blobmsg_add_string(&bbuf, "file_path", file_path); + blobmsg_add_string(&bbuf, "url", url); + blobmsg_add_string(&bbuf, "username", username); + blobmsg_add_string(&bbuf, "password", password); + char *upload_task = blobmsg_format_json(bbuf.head, true); + blob_buf_free(&bbuf); + + if (upload_task != NULL) { + char *ret = execute_task_in_subprocess(upload_task); + return atoi(ret); + } + return 500; +} + int cwmp_launch_upload(struct upload *pupload, struct transfer_complete **ptransfer_complete) { int error = FAULT_CPE_NO_FAULT; @@ -160,86 +217,6 @@ end_upload: return error; } -void *thread_cwmp_rpc_cpe_upload(void *v __attribute__((unused))) -{ - struct upload *pupload; - struct timespec upload_timeout = { 0, 0 }; - time_t current_time, stime; - int error = FAULT_CPE_NO_FAULT; - struct transfer_complete *ptransfer_complete; - long int time_of_grace = 3600, timeout; - - for (;;) { - - if (cwmp_stop) - break; - - if (list_upload.next != &(list_upload)) { - pupload = list_entry(list_upload.next, struct upload, list); - stime = pupload->scheduled_time; - current_time = time(NULL); - if (pupload->scheduled_time != 0) - timeout = current_time - pupload->scheduled_time; - else - timeout = 0; - if ((timeout >= 0) && (timeout > time_of_grace)) { - pthread_mutex_lock(&mutex_upload); - bkp_session_delete_upload(pupload); - ptransfer_complete = calloc(1, sizeof(struct transfer_complete)); - if (ptransfer_complete != NULL) { - error = FAULT_CPE_DOWNLOAD_FAILURE; - - ptransfer_complete->command_key = strdup(pupload->command_key); - ptransfer_complete->start_time = strdup(mix_get_time()); - ptransfer_complete->complete_time = strdup(ptransfer_complete->start_time); - ptransfer_complete->fault_code = error; - ptransfer_complete->type = TYPE_UPLOAD; - bkp_session_insert_transfer_complete(ptransfer_complete); - cwmp_root_cause_transfer_complete(ptransfer_complete); - } - list_del(&(pupload->list)); - if (pupload->scheduled_time != 0) - count_download_queue--; - cwmp_free_upload_request(pupload); - pthread_mutex_unlock(&mutex_download); - continue; - } - if ((timeout >= 0) && (timeout <= time_of_grace)) { - CWMP_LOG(INFO, "Launch upload file %s", pupload->url); - error = cwmp_launch_upload(pupload, &ptransfer_complete); - if (error != FAULT_CPE_NO_FAULT) { - bkp_session_insert_transfer_complete(ptransfer_complete); - bkp_session_save(); - cwmp_root_cause_transfer_complete(ptransfer_complete); - bkp_session_delete_transfer_complete(ptransfer_complete); - } else { - bkp_session_delete_transfer_complete(ptransfer_complete); - ptransfer_complete->fault_code = error; - bkp_session_insert_transfer_complete(ptransfer_complete); - bkp_session_save(); - cwmp_root_cause_transfer_complete(ptransfer_complete); - } - pthread_mutex_lock(&mutex_upload); - list_del(&(pupload->list)); - if (pupload->scheduled_time != 0) - count_download_queue--; - cwmp_free_upload_request(pupload); - pthread_mutex_unlock(&mutex_upload); - continue; - } - pthread_mutex_lock(&mutex_upload); - upload_timeout.tv_sec = stime; - pthread_cond_timedwait(&threshold_upload, &mutex_upload, &upload_timeout); - pthread_mutex_unlock(&mutex_upload); - } else { - pthread_mutex_lock(&mutex_upload); - pthread_cond_wait(&threshold_upload, &mutex_upload); - pthread_mutex_unlock(&mutex_upload); - } - } - return NULL; -} - int cwmp_free_upload_request(struct upload *upload) { if (upload != NULL) { @@ -282,3 +259,30 @@ int cwmp_scheduledUpload_remove_all() return CWMP_OK; } + +void cwmp_start_upload(struct uloop_timeout *timeout) +{ + struct upload *pupload; + int error = FAULT_CPE_NO_FAULT; + struct transfer_complete *ptransfer_complete; + + pupload = container_of(timeout, struct upload, handler_timer); + + CWMP_LOG(INFO, "Launch download file %s", pupload->url); + error = cwmp_launch_upload(pupload, &ptransfer_complete); + sleep(3); + if (error != FAULT_CPE_NO_FAULT) { + CWMP_LOG(ERROR, "Error while uploading the file: %s", pupload->url); + } + + bkp_session_insert_transfer_complete(ptransfer_complete); + bkp_session_save(); + cwmp_root_cause_transfer_complete(ptransfer_complete); + pthread_mutex_lock(&mutex_upload); + list_del(&(pupload->list)); + if (pupload->scheduled_time != 0) + count_download_queue--; + cwmp_free_upload_request(pupload); + pthread_mutex_unlock(&mutex_upload); + trigger_cwmp_session_timer(); +} diff --git a/xml.c b/xml.c index 06464a0..108c460 100644 --- a/xml.c +++ b/xml.c @@ -182,7 +182,7 @@ int xml_send_message(struct rpc *rpc) if (b) { b = mxmlWalkNext(b, cwmp_main->session->tree_in, MXML_DESCEND_FIRST); if (b && b->type == MXML_OPAQUE && b->value.opaque) - cwmp_main->session->hold_request = atoi(b->value.opaque); + cwmp_main->session->hold_request = (atoi(b->value.opaque)) ? true : false; } else { if (snprintf(c, sizeof(c), "%s:%s", ns.cwmp, "HoldRequests") == -1) goto error; @@ -191,7 +191,7 @@ int xml_send_message(struct rpc *rpc) if (b) { b = mxmlWalkNext(b, cwmp_main->session->tree_in, MXML_DESCEND_FIRST); if (b && b->type == MXML_OPAQUE && b->value.opaque) - cwmp_main->session->hold_request = atoi(b->value.opaque); + cwmp_main->session->hold_request = (atoi(b->value.opaque)) ? true : false; } }