/* * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SIZE_MAX #define SIZE_MAX ((size_t) - 1) #endif static const char *dp_db_column[] = { [DP_PROP_NONE] = NULL, [DP_PROP_URL] = DP_DB_COL_URL, [DP_PROP_PROXY] = DP_DB_COL_PROXY, [DP_PROP_DESTINATION] = DP_DB_COL_DESTINATION, [DP_PROP_FILENAME] = DP_DB_COL_FILENAME, [DP_PROP_STATE_CALLBACK] = DP_DB_COL_STATE_EVENT, [DP_PROP_PROGRESS_CALLBACK] = DP_DB_COL_PROGRESS_EVENT, [DP_PROP_AUTO_DOWNLOAD] = DP_DB_COL_AUTO_DOWNLOAD, [DP_PROP_NETWORK_TYPE] = DP_DB_COL_NETWORK_TYPE, [DP_PROP_NETWORK_BONDING] = DP_DB_COL_NETWORK_BONDING, [DP_PROP_SAVED_PATH] = DP_DB_COL_SAVED_PATH, [DP_PROP_TEMP_SAVED_PATH] = DP_DB_COL_TMP_SAVED_PATH, [DP_PROP_MIME_TYPE] = DP_DB_COL_MIMETYPE, [DP_PROP_RECEIVED_SIZE] = NULL, [DP_PROP_TOTAL_FILE_SIZE] = DP_DB_COL_CONTENT_SIZE, [DP_PROP_CONTENT_NAME] = DP_DB_COL_CONTENT_NAME, [DP_PROP_HTTP_STATUS] = DP_DB_COL_HTTP_STATUS, [DP_PROP_ETAG] = DP_DB_COL_ETAG, [DP_PROP_STATE] = DP_DB_COL_STATE, [DP_PROP_ERROR] = DP_DB_COL_ERRORCODE, [DP_PROP_HTTP_HEADERS] = NULL, [DP_PROP_HTTP_HEADER] = NULL, [DP_PROP_NOTIFICATION_RAW] = NULL, [DP_PROP_NOTIFICATION_SUBJECT] = DP_DB_COL_NOTI_SUBJECT, [DP_PROP_NOTIFICATION_DESCRIPTION] = DP_DB_COL_NOTI_DESCRIPTION, [DP_PROP_NOTIFICATION_TYPE] = DP_DB_COL_NOTI_TYPE, [DP_PROP_CACHE] = DP_DB_COL_CACHE, [DP_PROP_CREATE] = NULL, [DP_PROP_START] = NULL, [DP_PROP_PAUSE] = NULL, [DP_PROP_CANCEL] = NULL, [DP_PROP_DESTROY] = NULL, }; char *dp_print_state(int state) { switch (state) { case DP_STATE_NONE: return "NONE"; case DP_STATE_READY: return "READY"; case DP_STATE_QUEUED: return "QUEUED"; case DP_STATE_CONNECTING: return "CONNECTING"; case DP_STATE_DOWNLOADING: return "DOWNLOADING"; case DP_STATE_PAUSED: return "PAUSED"; case DP_STATE_COMPLETED: return "COMPLETED"; case DP_STATE_CANCELED: return "CANCELED"; case DP_STATE_FAILED: return "FAILED"; default: break; } return "UNKNOWN"; } char *dp_print_errorcode(int errorcode) { switch (errorcode) { case DP_ERROR_NONE: return "NONE"; case DP_ERROR_INVALID_PARAMETER: return "INVALID_PARAMETER"; case DP_ERROR_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; case DP_ERROR_IO_ERROR: return "IO_ERROR"; case DP_ERROR_NETWORK_UNREACHABLE: return "NETWORK_UNREACHABLE"; case DP_ERROR_CONNECTION_TIMED_OUT: return "CONNECTION_TIMED_OUT"; case DP_ERROR_NO_SPACE: return "NO_SPACE"; case DP_ERROR_FIELD_NOT_FOUND: return "FIELD_NOT_FOUND"; case DP_ERROR_INVALID_STATE: return "INVALID_STATE"; case DP_ERROR_CONNECTION_FAILED: return "CONNECTION_FAILED"; case DP_ERROR_INVALID_URL: return "INVALID_URL"; case DP_ERROR_INVALID_DESTINATION: return "INVALID_DESTINATION"; case DP_ERROR_QUEUE_FULL: return "QUEUE_FULL"; case DP_ERROR_ALREADY_COMPLETED: return "ALREADY_COMPLETED"; case DP_ERROR_FILE_ALREADY_EXISTS: return "FILE_ALREADY_EXISTS"; case DP_ERROR_TOO_MANY_DOWNLOADS: return "TOO_MANY_DOWNLOADS"; case DP_ERROR_NO_DATA: return "NO_DATA"; case DP_ERROR_UNHANDLED_HTTP_CODE: return "UNHANDLED_HTTP_CODE"; case DP_ERROR_CANNOT_RESUME: return "CANNOT_RESUME"; case DP_ERROR_PERMISSION_DENIED: return "PERMISSION_DENIED"; case DP_ERROR_RESPONSE_TIMEOUT: return "RESPONSE_TIMEOUT"; case DP_ERROR_REQUEST_TIMEOUT: return "REQUEST_TIMEOUT"; case DP_ERROR_SYSTEM_DOWN: return "SYSTEM_DOWN"; case DP_ERROR_CLIENT_DOWN: return "CLIENT_DOWN"; case DP_ERROR_DISK_BUSY: return "DISK_BUSY"; case DP_ERROR_ID_NOT_FOUND: return "ID_NOT_FOUND"; default: break; } return "UNKNOWN"; } char *dp_print_section(short section) { switch (section) { case DP_SEC_NONE: return "NONE"; case DP_SEC_INIT: return "INIT"; case DP_SEC_DEINIT: return "DEINIT"; case DP_SEC_CONTROL: return "CONTROL"; case DP_SEC_GET: return "GET"; case DP_SEC_SET: return "SET"; case DP_SEC_UNSET: return "UNSET"; default: break; } return "UNKNOWN"; } char *dp_print_property(unsigned property) { switch (property) { case DP_PROP_NONE: return "NONE"; case DP_PROP_CREATE: return "CREATE"; case DP_PROP_START: return "START"; case DP_PROP_PAUSE: return "PAUSE"; case DP_PROP_CANCEL: return "CANCEL"; case DP_PROP_DESTROY: return "DESTROY"; case DP_PROP_URL: return "URL"; case DP_PROP_PROXY: return "PROXY"; case DP_PROP_DESTINATION: return "DESTINATION"; case DP_PROP_FILENAME: return "FILENAME"; case DP_PROP_STATE_CALLBACK: return "STATE_CB"; case DP_PROP_PROGRESS_CALLBACK: return "PROGRESS_CB"; case DP_PROP_AUTO_DOWNLOAD: return "AUTO_DOWNLOAD"; case DP_PROP_NETWORK_TYPE: return "NETWORK_TYPE"; case DP_PROP_NETWORK_BONDING: return "NETWORK_BONDING"; case DP_PROP_SAVED_PATH: return "SAVED_PATH"; case DP_PROP_TEMP_SAVED_PATH: return "TEMP_SAVED_PATH"; case DP_PROP_MIME_TYPE: return "MIME_TYPE"; case DP_PROP_RECEIVED_SIZE: return "RECEIVED_SIZE"; case DP_PROP_TOTAL_FILE_SIZE: return "TOTAL_FILE_SIZE"; case DP_PROP_CONTENT_NAME: return "CONTENT_NAME"; case DP_PROP_HTTP_STATUS: return "HTTP_STATUS"; case DP_PROP_ETAG: return "ETAG"; case DP_PROP_STATE: return "STATE"; case DP_PROP_ERROR: return "ERROR"; case DP_PROP_NOTIFICATION_RAW: return "NOTIFICATION_RAW"; case DP_PROP_NOTIFICATION_SUBJECT: return "NOTIFICATION_SUBJECT"; case DP_PROP_NOTIFICATION_DESCRIPTION: return "NOTIFICATION_DESCRIPTION"; case DP_PROP_NOTIFICATION_TYPE: return "NOTIFICATION_TYPE"; case DP_PROP_HTTP_HEADERS: return "HTTP_HEADERS"; case DP_PROP_HTTP_HEADER: return "HTTP_HEADER"; case DP_PROP_VERIFY_HOST: return "VERIFY_HOST"; case DP_PROP_CACHE: return "CACHE"; case DP_PROP_RESET_CACHE: return "RESET_CACHE"; case DP_PROP_SET_CACHE_MAX_SIZE: return "SET_CACHE_MAX_SIZE"; case DP_PROP_GET_CACHE_MAX_SIZE: return "GET_CACHE_MAX_SIZE"; case DP_PROP_SET_CACHE_PATH: return "SET_CACHE_PATH"; case DP_PROP_GET_CACHE_PATH: return "GET_CACHE_PATH"; case DP_PROP_SET_CACHE_LIFECYCLE: return "SET_CACHE_LIFECYCLE"; case DP_PROP_GET_CACHE_LIFECYCLE: return "GET_CACHE_LIFECYCLE"; case DP_PROP_RESET_ALL_CACHE: return "RESET_ALL_CACHE"; default: break; } return "UNKNOWN"; } static const char *__dp_get_db_table_name(unsigned property) { switch (property) { case DP_PROP_URL: case DP_PROP_PROXY: case DP_PROP_DESTINATION: case DP_PROP_FILENAME: case DP_PROP_STATE_CALLBACK: case DP_PROP_PROGRESS_CALLBACK: case DP_PROP_NETWORK_TYPE: case DP_PROP_NETWORK_BONDING: case DP_PROP_CACHE: return DP_TABLE_REQUEST; case DP_PROP_AUTO_DOWNLOAD: case DP_PROP_STATE: case DP_PROP_ERROR: return DP_TABLE_LOGGING; case DP_PROP_SAVED_PATH: case DP_PROP_TEMP_SAVED_PATH: case DP_PROP_MIME_TYPE: case DP_PROP_CONTENT_NAME: case DP_PROP_ETAG: case DP_PROP_TOTAL_FILE_SIZE: case DP_PROP_HTTP_STATUS: return DP_TABLE_DOWNLOAD; case DP_PROP_NOTIFICATION_RAW: case DP_PROP_NOTIFICATION_SUBJECT: case DP_PROP_NOTIFICATION_DESCRIPTION: case DP_PROP_NOTIFICATION_TYPE: return DP_TABLE_NOTIFICATION; case DP_PROP_HTTP_HEADERS: case DP_PROP_HTTP_HEADER: return DP_TABLE_HEADERS; case DP_PROP_RECEIVED_SIZE: case DP_PROP_NONE: case DP_PROP_CREATE: case DP_PROP_START: case DP_PROP_PAUSE: case DP_PROP_CANCEL: case DP_PROP_DESTROY: default: break; } return NULL; } static const int __dp_get_cache_req_type(unsigned property) { switch (property) { case DP_PROP_RESET_CACHE: return DP_CACHE_REQUEST_RESET_CACHE; case DP_PROP_SET_CACHE_MAX_SIZE: return DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE; case DP_PROP_GET_CACHE_MAX_SIZE: return DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE; case DP_PROP_RESET_ALL_CACHE: return DP_CACHE_REQUEST_RESET_ALL_CACHE; case DP_PROP_SET_CACHE_PATH: return DP_CACHE_REQUEST_SET_CACHE_PATH; case DP_PROP_GET_CACHE_PATH: return DP_CACHE_REQUEST_GET_CACHE_PATH; case DP_PROP_SET_CACHE_LIFECYCLE: return DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE; case DP_PROP_GET_CACHE_LIFECYCLE: return DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE; default: break; } return -1; } static int __dp_get_download_id(dp_client_fmt *client) { int download_id = -1; int check_duplicate = 1; int retry_count = 0; int errorcode = DP_ERROR_NONE; struct timeval tval; if (dp_db_get_max_download_id(client->dbhandle, DP_TABLE_LOGGING, &download_id, &errorcode) < 0) { TRACE_ERROR("ERROR [%d]", errorcode); // database is empty start with id : 1 download_id = DP_FIRST_DOWNLOAD_ID; } else { if (download_id < INT_MAX) download_id++; } retry_count = 0; long long temp_id = download_id; do { retry_count++; if (temp_id >= 0 && temp_id < INT_MAX) { TRACE_INFO("download_id [%lld]", temp_id); check_duplicate = dp_db_check_duplicated_int(client->dbhandle, DP_TABLE_LOGGING, DP_DB_COL_ID, (int)temp_id, &errorcode); if (errorcode == DP_ERROR_NONE) { if (check_duplicate == 0) break; } else { TRACE_ERROR("ERROR [%d]", errorcode); } if (retry_count < 3) { temp_id++; } else if (retry_count < 10) { gettimeofday(&tval, NULL); tval.tv_usec = (tval.tv_usec & 0x0fff); temp_id += 1171 * retry_count + tval.tv_usec; } else if (retry_count < 20) { gettimeofday(&tval, NULL); tval.tv_usec = (tval.tv_usec & 0xff33ff) + retry_count; temp_id += tval.tv_usec; } else { TRACE_ERROR("failed to generate unique download_id [%lld]", temp_id); return -1; } } else { TRACE_ERROR("reached INT_MAX limit or overflow"); temp_id = DP_FIRST_DOWNLOAD_ID; } } while (check_duplicate != 0); download_id = (int)temp_id; return download_id; } void dp_request_create(dp_client_fmt *client, dp_request_fmt *request) { // new request // find the tail of linked list & check limitation int i = 0; dp_request_fmt *tailp = client->requests; dp_request_fmt *prevp = NULL; for (; i < MAX_DOWNLOAD_HANDLE; i++) { if (tailp == NULL) break; TRACE_DEBUG("link %d info: id:%d access-time:%d", i, tailp->id, tailp->access_time); prevp = tailp; tailp = tailp->next; } request->next = NULL; if (prevp == NULL) // it's first link client->requests = request; else prevp->next = request; // attach at the tail TRACE_INFO("sock:%d download-id:%d", client->channel, request->id); } static int __dp_request_create(dp_client_fmt *client, dp_ipc_fmt *ipc_info) { int errorcode = DP_ERROR_NONE; int download_id = __dp_get_download_id(client); // allocation new request. dp_request_fmt *request = (dp_request_fmt *) calloc(1, sizeof(dp_request_fmt)); if (request == NULL) { TRACE_ERROR("check memory sock:%d download-id:%d", client->channel, download_id); return DP_ERROR_OUT_OF_MEMORY; } if (dp_db_new_logging(client->dbhandle, download_id, DP_STATE_READY, DP_ERROR_NONE, &errorcode) < 0) { TRACE_ERROR("new log sock:%d download-id:%d errorcode:%s", client->channel, download_id, dp_print_errorcode(errorcode)); free(request); return errorcode; } request->id = download_id; request->agent_id = -1; request->cache_agent_id = -1; request->state = DP_STATE_READY; request->error = DP_ERROR_NONE; request->network_type = DP_NETWORK_ALL; request->access_time = (int)time(NULL); request->state_cb = 0; request->progress_cb = 0; request->startcount = 0; request->noti_type = DP_NOTIFICATION_TYPE_NONE; request->progress_lasttime = 0; request->received_size = 0; request->content_type = DP_CONTENT_UNKNOWN; request->file_size = 0; request->noti_priv_id = -1; request->file_name = NULL; dp_request_create(client, request); ipc_info->id = download_id; return errorcode; } void dp_request_free(dp_request_fmt *request) { // free notification handle here TRACE_DEBUG("destory id:%d", request->id); if (request->file_name) { free(request->file_name); request->file_name = NULL; } free(request); } static int _dp_client_can_remove_request(dp_request_fmt *request) { if (request->id <= 0 || request->state == DP_STATE_NONE) { TRACE_ERROR("id:%d unexpected request.", request->id); return 1; } int now_time = (int)time(NULL); if (request->access_time > 0 && (now_time - request->access_time) > DP_CARE_CLIENT_CLEAR_INTERVAL) { // check accesstime. if difference is bigger than DP_CARE_CLIENT_CLEAR_INTERVAL, clear. if (request->state == DP_STATE_READY || request->state == DP_STATE_COMPLETED || request->state == DP_STATE_CANCELED || request->state == DP_STATE_FAILED) return 1; } if (request->state == DP_STATE_PAUSED) { int ret; if (request->cache_agent_id >= 0) ret = dp_is_alive_cache_download(request->cache_agent_id); else ret = dp_is_alive_download(request->agent_id); if (ret == 0) { // paused & agent_id not exist.... unload from memory. TRACE_ERROR("id:%d hanged as paused (%d/%d)",request->id, request->access_time, now_time); return 1; } } return 0; } static int _dp_client_check_zombie_request(dp_request_fmt *request) { int now_time = (int)time(NULL); int time_diff = 0; if (request->access_time > 0) time_diff = now_time - request->access_time; if (time_diff > DP_CARE_CLIENT_CLEAR_INTERVAL && request->state == DP_STATE_CONNECTING) { TRACE_ERROR("id:%d connection timeout (%d/%d)", request->id, request->access_time, now_time); return 1; } return 0; } void dp_client_clear_requests(void *slotp) { dp_client_slots_fmt *slot = (dp_client_slots_fmt *)slotp; if (slot == NULL) { TRACE_ERROR("check slot memory"); return ; } dp_client_fmt *client = &slot->client; dp_request_fmt *tailp = client->requests; dp_request_fmt *prevp = NULL; unsigned queued_count = 0; unsigned ongoing_count = 0; for (int i = 0; tailp != NULL; i++) { int can_remove = _dp_client_can_remove_request(tailp); int is_zombie = _dp_client_check_zombie_request(tailp); if (is_zombie) { TRACE_ERROR("id:%d is zombie request.", tailp->id); int ret; if (tailp->cache_agent_id >= 0) { ret = dp_cancel_cache_download_without_update(tailp->cache_agent_id); } else { ret = dp_cancel_agent_download_without_update(tailp->agent_id); } if (ret < 0) { TRACE_ERROR("failed to cancel download for id(%d). agent-id:%d cache-id:%d", tailp->id, tailp->agent_id, tailp->cache_agent_id); } tailp->state = DP_STATE_FAILED; tailp->error = DP_ERROR_CONNECTION_TIMED_OUT; if (tailp->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || tailp->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, tailp, DP_NOTIFICATION) < 0) TRACE_ERROR("failed to register notification for id:%d", tailp->id); } } if (can_remove) { dp_request_fmt *removep = tailp; if (prevp == NULL) // first request. client->requests = tailp->next; else prevp->next = tailp->next; tailp = tailp->next; TRACE_DEBUG("request %d clear: id:%d state:%s", i, removep->id, dp_print_state(removep->state)); dp_request_free(removep); continue; } else { ongoing_count++; } if (tailp->state == DP_STATE_QUEUED) queued_count++; prevp = tailp; tailp = tailp->next; } TRACE_DEBUG("info requests:%d queued:%d", ongoing_count, queued_count); if (queued_count > 0) dp_queue_manager_wake_up(); } int dp_request_destroy(dp_client_fmt *client, dp_ipc_fmt *ipc_info, dp_request_fmt *requestp) { int errorcode = DP_ERROR_NONE; if (requestp != NULL && client->requests != NULL) { if (requestp == client->requests) { // cancel downloading ... after checking status client->requests = requestp->next; dp_request_free(requestp); } else { int i = 1; dp_request_fmt *prevp = client->requests; dp_request_fmt *removep = client->requests->next; for (; i < MAX_DOWNLOAD_HANDLE; i++) { if (removep == NULL) { errorcode = DP_ERROR_ID_NOT_FOUND; break; } if (removep == requestp) { // cancel downloading ... after checking status prevp->next = removep->next; dp_request_free(removep); break; } prevp = removep; removep = removep->next; } } } TRACE_DEBUG("sock:%d id:%d errorcode:%s", client->channel, (ipc_info) ? ipc_info->id : -1, dp_print_errorcode(errorcode)); return errorcode; } static void _dp_client_clear_stale_db(dp_client_fmt *client) { TRACE_DEBUG("Clear the stale DB data."); int errorcode = DP_ERROR_NONE; if (client && client->dbhandle != 0) { // maintain only 1000 rows for each client if (dp_db_limit_rows(client->dbhandle, DP_TABLE_LOGGING, DP_LOG_DB_LIMIT_ROWS, &errorcode) < 0) TRACE_INFO("limit rows error:%s", dp_print_errorcode(errorcode)); // maintain for 48 hours if (dp_db_limit_time(client->dbhandle, DP_TABLE_LOGGING, DP_CARE_CLIENT_INFO_PERIOD, &errorcode) < 0) TRACE_INFO("limit rows error:%s", dp_print_errorcode(errorcode)); } } static int __dp_request_read_int(int sock, dp_ipc_fmt *ipc_info, int *value) { int errorcode = DP_ERROR_NONE; if (ipc_info->size == sizeof(int)) { if (dp_ipc_read(sock, value, ipc_info->size, __FUNCTION__) < 0) { TRACE_ERROR("sock:%d check ipc length:%zd", sock, ipc_info->size); errorcode = DP_ERROR_IO_ERROR; } } else { errorcode = DP_ERROR_IO_ERROR; } return errorcode; } static int __dp_request_feedback_string(int sock, dp_ipc_fmt *ipc_info, void *string, size_t length, int errorvalue) { int errorcode = DP_ERROR_NONE; if (length == 0 && errorvalue == DP_ERROR_NONE) errorvalue = DP_ERROR_NO_DATA; if (dp_ipc_query(sock, ipc_info->id, ipc_info->section, ipc_info->property, errorvalue, length * sizeof(char)) < 0) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("sock:%d check ipc length:%zd", sock, length); } if (errorvalue == DP_ERROR_NONE && errorcode == DP_ERROR_NONE) { if (dp_ipc_write(sock, string, sizeof(char) * length) < 0) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("sock:%d check ipc length:%zd", sock, length); } } return errorcode; } static int __dp_request_read_string(int sock, dp_ipc_fmt *ipc_info, char **string) { int errorcode = DP_ERROR_NONE; if (ipc_info->size > 0) { char *recv_str = (char *)calloc((ipc_info->size + (size_t)1), sizeof(char)); if (recv_str == NULL) { TRACE_ERROR("sock:%d check memory length:%zd", sock, ipc_info->size); errorcode = DP_ERROR_OUT_OF_MEMORY; } else { if (dp_ipc_read(sock, recv_str, ipc_info->size, __FUNCTION__) <= 0) { TRACE_ERROR("sock:%d check ipc length:%zd", sock, ipc_info->size); errorcode = DP_ERROR_IO_ERROR; free(recv_str); } else { recv_str[ipc_info->size] = '\0'; TRACE_DEBUG("sock:%d length:%zd string:%s", sock, ipc_info->size, recv_str); *string = recv_str; } } } else { errorcode = DP_ERROR_IO_ERROR; } return errorcode; } static int __dp_request_feedback_int(int sock, dp_ipc_fmt *ipc_info, void *value, int errorvalue, size_t extra_size) { int errorcode = DP_ERROR_NONE; if (errorvalue != DP_ERROR_NONE) extra_size = 0; if (dp_ipc_query(sock, ipc_info->id, ipc_info->section, ipc_info->property, errorvalue, extra_size) < 0) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("sock:%d check ipc length:%zd", sock, extra_size); } if (errorvalue == DP_ERROR_NONE && errorcode == DP_ERROR_NONE) { if (dp_ipc_write(sock, value, extra_size) < 0) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("sock:%d check ipc length:%zd", sock, extra_size); } } return errorcode; } static int __dp_request_get_info_string(dp_client_fmt *client, dp_ipc_fmt *ipc_info) { int errorcode = DP_ERROR_NONE; int bundle_type = -1; // DP_PROP_NOTIFICATION_RAW unsigned length = 0; char *string = NULL; const char *table = __dp_get_db_table_name(ipc_info->property); const char *column = dp_db_column[ipc_info->property]; if (ipc_info->property == DP_PROP_NOTIFICATION_RAW) { errorcode = __dp_request_read_int(client->channel, ipc_info, &bundle_type); TRACE_DEBUG("read %s type:%d", dp_print_property(ipc_info->property), bundle_type); if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_ONGOING) column = DP_DB_COL_NOTI_RAW_ONGOING; else if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE) column = DP_DB_COL_NOTI_RAW_COMPLETE; else if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_FAILED) column = DP_DB_COL_NOTI_RAW_FAIL; if (column == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; TRACE_ERROR("invalid type %s type:%d", dp_print_property(ipc_info->property), bundle_type); if (dp_ipc_query(client->channel, ipc_info->id, ipc_info->section, ipc_info->property, errorcode, 0) < 0) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } return errorcode; } } if (dp_db_get_property_string(client->dbhandle, ipc_info->id, table, column, (unsigned char **)&string, &length, &errorcode) < 0) errorcode = DP_ERROR_NO_DATA; if (__dp_request_feedback_string(client->channel, ipc_info, string, length, errorcode) == DP_ERROR_IO_ERROR) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } free(string); return errorcode; } static int __dp_request_get_info_int_from_db(dp_client_fmt *client, dp_ipc_fmt *ipc_info) { int errorcode = DP_ERROR_NONE; int is_file_size = 0; int ival = 0; unsigned long long lval = 0; const char *table = __dp_get_db_table_name(ipc_info->property); size_t size = sizeof(int); if (ipc_info->property == DP_PROP_RECEIVED_SIZE || ipc_info->property == DP_PROP_TOTAL_FILE_SIZE) { is_file_size = 1; size = sizeof(unsigned long long); } if (dp_db_get_property_int(client->dbhandle, ipc_info->id, table, dp_db_column[ipc_info->property], (is_file_size ? (void *)&lval : (void *)&ival), &errorcode) < 0) { TRACE_ERROR("failed to get %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_NO_DATA; } if (__dp_request_feedback_int(client->channel, ipc_info, (is_file_size ? (void *)&lval : (void *)&ival), errorcode, size) == DP_ERROR_IO_ERROR) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } return errorcode; } static int __dp_request_get_info_int_from_request(dp_client_fmt *client, dp_ipc_fmt *ipc_info, dp_request_fmt *requestp) { void *val = NULL; int errorcode = DP_ERROR_NONE; size_t size = sizeof(int); if (requestp) { switch (ipc_info->property) { case DP_PROP_RECEIVED_SIZE: val = &requestp->received_size; size = sizeof(unsigned long long); break; case DP_PROP_STATE_CALLBACK: val = &requestp->state_cb; break; case DP_PROP_PROGRESS_CALLBACK: val = &requestp->progress_cb; break; case DP_PROP_NETWORK_TYPE: val = &requestp->network_type; break; case DP_PROP_STATE: val = &requestp->state; break; case DP_PROP_ERROR: val = &requestp->error; break; case DP_PROP_TOTAL_FILE_SIZE: val = &requestp->file_size; size = sizeof(unsigned long long); break; case DP_PROP_NOTIFICATION_TYPE: val = &requestp->noti_type; break; default: break; } } else { if (ipc_info->property == DP_PROP_RECEIVED_SIZE) { errorcode = DP_ERROR_INVALID_STATE; goto DONE; } return __dp_request_get_info_int_from_db(client, ipc_info); } DONE: if (__dp_request_feedback_int(client->channel, ipc_info, val, errorcode, size) == DP_ERROR_IO_ERROR) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } return errorcode; } static int __dp_request_get_http_headers_info(dp_client_fmt *client, dp_ipc_fmt *ipc_info) { int errorcode = DP_ERROR_NONE; int field_count = dp_db_check_duplicated_int(client->dbhandle, DP_TABLE_HEADERS, DP_DB_COL_ID, ipc_info->id, &errorcode); if (field_count < 0) { TRACE_ERROR("failed to get %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; field_count = 0; } int result = __dp_request_feedback_int(client->channel, ipc_info, (void *)&field_count, errorcode, sizeof(int)); if (result == DP_ERROR_IO_ERROR) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } else if (field_count > 0) { // get fields from database. int *ids = (int *)calloc(field_count, sizeof(int)); if (ids == NULL) { TRACE_ERROR("failed to allocate the clients"); errorcode = DP_ERROR_OUT_OF_MEMORY; } else { // getting ids of clients int i = 0; int rows_count = dp_db_get_cond_ids(client->dbhandle, DP_TABLE_HEADERS, DP_DB_COL_ROW_ID, DP_DB_COL_ID, ipc_info->id, ids, field_count, &errorcode); for (; i < rows_count; i++) { char *string = NULL; unsigned length = 0; if (dp_db_get_cond_string(client->dbhandle, DP_TABLE_HEADERS, DP_DB_COL_ROW_ID, ids[i], DP_DB_COL_HEADER_FIELD, (unsigned char **)&string, &length, &errorcode) < 0) { TRACE_ERROR("failed to get %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_NO_DATA; } result = __dp_request_feedback_string(client->channel, ipc_info, string, length, errorcode); free(string); if (result == DP_ERROR_IO_ERROR) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } } } if (ids) free(ids); } return errorcode; } /* 1. read field string * 2. response with extra size * 3. send string. */ static int __dp_request_get_http_header_info(dp_client_fmt *client, dp_ipc_fmt *ipc_info) { char *header_field = NULL; char *string = NULL; unsigned length = 0; int errorcode = __dp_request_read_string(client->channel, ipc_info, &header_field); if (errorcode == DP_ERROR_NONE && header_field != NULL) { if (dp_db_get_header_value(client->dbhandle, ipc_info->id, header_field, (unsigned char **)&string, &length, &errorcode) < 0) errorcode = DP_ERROR_NO_DATA; } else if (header_field == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } if (__dp_request_feedback_string(client->channel, ipc_info, string, length, errorcode) == DP_ERROR_IO_ERROR) { errorcode = DP_ERROR_IO_ERROR; TRACE_ERROR("check ipc sock:%d", client->channel); } free(header_field); free(string); return errorcode; } static int __dp_request_get_info(dp_client_fmt *client, dp_ipc_fmt *ipc_info, dp_request_fmt *requestp) { int errorcode = DP_ERROR_NONE; switch (ipc_info->property) { case DP_PROP_URL: case DP_PROP_PROXY: case DP_PROP_DESTINATION: case DP_PROP_FILENAME: case DP_PROP_SAVED_PATH: case DP_PROP_TEMP_SAVED_PATH: case DP_PROP_MIME_TYPE: case DP_PROP_CONTENT_NAME: case DP_PROP_ETAG: case DP_PROP_NOTIFICATION_SUBJECT: case DP_PROP_NOTIFICATION_DESCRIPTION: case DP_PROP_NOTIFICATION_RAW: errorcode = __dp_request_get_info_string(client, ipc_info); break; case DP_PROP_RECEIVED_SIZE: case DP_PROP_STATE_CALLBACK: case DP_PROP_PROGRESS_CALLBACK: case DP_PROP_NETWORK_TYPE: case DP_PROP_STATE: case DP_PROP_ERROR: case DP_PROP_TOTAL_FILE_SIZE: case DP_PROP_NOTIFICATION_TYPE: errorcode = __dp_request_get_info_int_from_request(client, ipc_info, requestp); break; case DP_PROP_NETWORK_BONDING: case DP_PROP_AUTO_DOWNLOAD: case DP_PROP_HTTP_STATUS: case DP_PROP_CACHE: errorcode = __dp_request_get_info_int_from_db(client, ipc_info); break; case DP_PROP_HTTP_HEADERS: errorcode = __dp_request_get_http_headers_info(client, ipc_info); break; case DP_PROP_HTTP_HEADER: errorcode = __dp_request_get_http_header_info(client, ipc_info); break; default: errorcode = DP_ERROR_INVALID_PARAMETER; break; } if (errorcode != DP_ERROR_NONE) TRACE_ERROR("failed to get %s, error:%s", dp_print_property(ipc_info->property), dp_print_errorcode(errorcode)); return errorcode; } static int __dp_request_set_info(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info, dp_request_fmt *requestp) { if (slot == NULL) { TRACE_ERROR("check slot memory"); return DP_ERROR_INVALID_PARAMETER; } dp_client_fmt *client = &slot->client; if (client == NULL || ipc_info == NULL) { TRACE_ERROR("check client or ipc info."); return DP_ERROR_INVALID_PARAMETER; } int errorcode = DP_ERROR_NONE; // if completed or downloading, invalid state. int download_state = DP_STATE_NONE; if (requestp != NULL) { download_state = requestp->state; } else { if (dp_db_get_property_int(client->dbhandle, ipc_info->id, DP_TABLE_LOGGING, DP_DB_COL_STATE, &download_state, &errorcode) < 0) { TRACE_ERROR("failed to get %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_ID_NOT_FOUND; // feedback if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); } return errorcode; } } // should the state be checked ? TRACE_DEBUG("state:%s set property:%s", dp_print_state(download_state), dp_print_property(ipc_info->property)); switch (ipc_info->property) { case DP_PROP_URL: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // write to database here if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_URL, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } free(recv_str); } } break; } case DP_PROP_PROXY: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // write to database here if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_PROXY, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) TRACE_ERROR("failed to set %s errorcode:%s", dp_print_property(ipc_info->property), dp_print_errorcode(errorcode)); free(recv_str); } } break; } case DP_PROP_DESTINATION: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // check here destination is available errorcode = dp_is_valid_dir(slot->credential, recv_str); if (errorcode == DP_ERROR_NONE && dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_DESTINATION, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } free(recv_str); } } break; } case DP_PROP_TEMP_SAVED_PATH: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { errorcode = dp_is_valid_dir(slot->credential, recv_str); if (errorcode == DP_ERROR_NONE && dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_TEMP_FILE_PATH, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } free(recv_str); } } break; } case DP_PROP_FILENAME: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // write to database here if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_FILENAME, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } free(recv_str); } } break; } case DP_PROP_STATE_CALLBACK: { // check state here // DP_ERROR_INVALID_STATE if downloading or finished // update database here if (requestp != NULL) requestp->state_cb = 1; int enable_cb = 1; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_STATE_EVENT, (void *)&enable_cb, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_PROGRESS_CALLBACK: { // check state here // DP_ERROR_INVALID_STATE if downloading or finished // update database here if (requestp != NULL) requestp->progress_cb = 1; int enable_cb = 1; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_PROGRESS_EVENT, (void *)&enable_cb, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_AUTO_DOWNLOAD: { // update autodownload property as 1 in database int enable_cb = 1; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_LOGGING, DP_DB_COL_AUTO_DOWNLOAD, (void *)&enable_cb, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_NETWORK_TYPE: { int recv_int = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int); if (recv_int <= DP_NETWORK_OFF || recv_int > DP_NETWORK_ALL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // update in database if (requestp != NULL) { if (requestp->state == DP_STATE_QUEUED) { dp_queue_manager_clear_queue(requestp); } else { requestp->network_type = recv_int; if (requestp->state == DP_STATE_CONNECTING || requestp->state == DP_STATE_DOWNLOADING) { // pause & push queue int ret; if (requestp->cache_agent_id >= 0) { ret = dp_pause_cache_download_without_update(requestp->cache_agent_id); } else { ret = dp_pause_agent_download_without_update(requestp->agent_id); } if (ret < 0) { TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", ipc_info->id, requestp->agent_id, requestp->cache_agent_id); } else { requestp->state = DP_STATE_PAUSED; requestp->error = DP_ERROR_NONE; if (dp_queue_manager_push_queue(slot, requestp) < 0) { if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0) TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id); requestp->state = DP_STATE_FAILED; requestp->error = DP_ERROR_QUEUE_FULL; errorcode = DP_ERROR_QUEUE_FULL; } } } } } int enable_cb = recv_int; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_NETWORK_TYPE, (void *)&enable_cb, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } break; } case DP_PROP_NETWORK_BONDING: { int recv_int = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int); if (errorcode == DP_ERROR_NONE) { if (requestp != NULL && requestp->network_type != DP_NETWORK_ALL) { errorcode = DP_ERROR_INVALID_NETWORK_TYPE; TRACE_ERROR("[ERROR] wrong network type"); } else if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_NETWORK_BONDING, (void *)&recv_int, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } break; } case DP_PROP_NOTIFICATION_TYPE: { int recv_int = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int); if (recv_int == DP_NOTIFICATION_TYPE_NONE || recv_int == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || recv_int == DP_NOTIFICATION_TYPE_ALL) { // check state request->state == DP_STATE_COMPLETED // DP_ERROR_INVALID_STATE // update notificatio type in database int noti_type = recv_int; if (requestp != NULL) { if (recv_int < requestp->noti_type) { // if already notification, unregister from notification bar. if (recv_int == DP_NOTIFICATION_TYPE_NONE) { if (dp_notification_manager_clear_notification(slot, requestp, DP_NOTIFICATION) < 0) TRACE_ERROR("failed to clear notification %s", dp_print_property(ipc_info->property)); } if (dp_notification_manager_clear_notification(slot, requestp, DP_NOTIFICATION_ONGOING) < 0) TRACE_ERROR("failed to clear ongoing %s", dp_print_property(ipc_info->property)); } requestp->noti_type = recv_int; } if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE, (void *)¬i_type, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } else { errorcode = DP_ERROR_INVALID_PARAMETER; } break; } case DP_PROP_NOTIFICATION_SUBJECT: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // write to database here if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_SUBJECT, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } free(recv_str); } } break; } case DP_PROP_NOTIFICATION_DESCRIPTION: { char *recv_str = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &recv_str); if (errorcode == DP_ERROR_NONE) { if (recv_str == NULL) { errorcode = DP_ERROR_INVALID_PARAMETER; } else { // write to database here if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_DESCRIPTION, (void *)recv_str, ipc_info->size, 2, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } free(recv_str); } } break; } case DP_PROP_NOTIFICATION_RAW: // bundle_type(db column) + bundle_binary { int bundle_type = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &bundle_type); TRACE_DEBUG("read %s type:%d", dp_print_property(ipc_info->property), bundle_type); char *raw_column = NULL; if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_ONGOING) raw_column = DP_DB_COL_NOTI_RAW_ONGOING; else if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE) raw_column = DP_DB_COL_NOTI_RAW_COMPLETE; else if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_FAILED) raw_column = DP_DB_COL_NOTI_RAW_FAIL; else errorcode = DP_ERROR_INVALID_PARAMETER; // feedback if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); errorcode = DP_ERROR_IO_ERROR; } if (errorcode == DP_ERROR_NONE) { dp_ipc_fmt *raw_info = dp_ipc_get_fmt(client->channel); if (raw_info == NULL || raw_info->section != ipc_info->section || raw_info->property != ipc_info->property || (raw_info->id != ipc_info->id)) { TRACE_ERROR("there is a confusion waiting raw binary in %s section", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_IO_ERROR; } if (raw_info != NULL && raw_info->size > 0 && raw_info->size < SIZE_MAX) { unsigned char *recv_raws = (unsigned char *)calloc(raw_info->size, sizeof(unsigned char)); if (recv_raws == NULL) { TRACE_ERROR("sock:%d check memory length:%zd", client->channel, raw_info->size); errorcode = DP_ERROR_OUT_OF_MEMORY; } else { if (dp_ipc_read(client->channel, recv_raws, raw_info->size, __FUNCTION__) <= 0) { TRACE_ERROR("sock:%d check ipc length:%zd", client->channel, raw_info->size); errorcode = DP_ERROR_IO_ERROR; } else { TRACE_DEBUG("sock:%d length:%zd raws", client->channel, raw_info->size); // save to database if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, raw_column, (void *)recv_raws, raw_info->size, 3, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } free(recv_raws); } } else { errorcode = DP_ERROR_IO_ERROR; } free(raw_info); } break; } case DP_PROP_HTTP_HEADER: // a request can have one or more fields, a fields can have only one value. { char *header_field = NULL; char *header_value = NULL; // 1. read field string // 2. response after checking sql status // 3. read query IPC for checking IPC // 4. read value string // 5. response errorcode = __dp_request_read_string(client->channel, ipc_info, &header_field); if (errorcode == DP_ERROR_NONE && header_field != NULL) { // check sql int check_field = dp_db_check_duplicated_string(client->dbhandle, ipc_info->id, DP_TABLE_HEADERS, DP_DB_COL_HEADER_FIELD, 0, header_field, &errorcode); if (check_field < 0) { errorcode = DP_ERROR_DISK_BUSY; } else { errorcode = DP_ERROR_NONE; // feedback if (dp_ipc_query(client->channel, ipc_info->id, ipc_info->section, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); errorcode = DP_ERROR_IO_ERROR; } else { dp_ipc_fmt *header_ipc = dp_ipc_get_fmt(client->channel); if (header_ipc == NULL || header_ipc->section != ipc_info->section || header_ipc->property != ipc_info->property || (header_ipc->id != ipc_info->id)) { TRACE_ERROR("there is a confusion during waiting http string in %s section", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_IO_ERROR; } else { errorcode = __dp_request_read_string(client->channel, header_ipc, &header_value); if (errorcode == DP_ERROR_NONE && header_value != NULL) { if (check_field == 0) { // insert if (dp_db_new_header(client->dbhandle, ipc_info->id, header_field, header_value, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } else { // update if (dp_db_update_header(client->dbhandle, ipc_info->id, header_field, header_value, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } } else { if (errorcode != DP_ERROR_NONE) TRACE_ERROR("failed to set %s, error:%s", dp_print_property(ipc_info->property), dp_print_errorcode(errorcode)); if (header_value == NULL) { TRACE_ERROR("failed to set %s, do you want to run as unset?", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_INVALID_PARAMETER; } } } free(header_ipc); } } } else { if (errorcode != DP_ERROR_NONE) TRACE_ERROR("failed to set %s, error:%s", dp_print_property(ipc_info->property), dp_print_errorcode(errorcode)); if (header_field == NULL) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_INVALID_PARAMETER; } } free(header_field); free(header_value); break; } case DP_PROP_VERIFY_HOST: { int recv_int = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int); if (errorcode == DP_ERROR_NONE) { if (requestp != NULL) requestp->disable_verify_host = recv_int ? 0 : 1; } break; } case DP_PROP_CACHE: { int recv_int = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int); if (errorcode == DP_ERROR_NONE) { if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (void *)&recv_int, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } } break; } default: errorcode = DP_ERROR_INVALID_PARAMETER; break; } // feedback if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET, ipc_info->property, errorcode, 0) < 0) TRACE_ERROR("check ipc sock:%d", client->channel); return errorcode; } static int __dp_request_unset_info(dp_client_fmt *client, dp_ipc_fmt *ipc_info, dp_request_fmt *requestp) { if (client == NULL || ipc_info == NULL) { TRACE_ERROR("check client or ipc info."); return DP_ERROR_INVALID_PARAMETER; } int errorcode = DP_ERROR_NONE; switch (ipc_info->property) { case DP_PROP_URL: // it would be run like cancel operation if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_URL, &errorcode) < 0) TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); break; case DP_PROP_PROXY: if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_PROXY, &errorcode) < 0) TRACE_ERROR("failed to unset %s errorcode:%s", dp_print_property(ipc_info->property), dp_print_errorcode(errorcode)); break; case DP_PROP_DESTINATION: // if downloading, change destination to da_agent if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_DESTINATION, &errorcode) < 0) TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); break; case DP_PROP_FILENAME: if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_FILENAME, &errorcode) < 0) TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); break; case DP_PROP_STATE_CALLBACK: { if (requestp != NULL) requestp->state_cb = 0; int enable_cb = 0; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_STATE_EVENT, (void *)&enable_cb, 0, 0, &errorcode) < 0) { TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_PROGRESS_CALLBACK: { if (requestp != NULL) requestp->progress_cb = 0; int enable_cb = 0; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_PROGRESS_EVENT, (void *)&enable_cb, 0, 0, &errorcode) < 0) { TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_AUTO_DOWNLOAD: { // update autodownload property as 0 in database int enable_cb = 0; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_LOGGING, DP_DB_COL_AUTO_DOWNLOAD, (void *)&enable_cb, 0, 0, &errorcode) < 0) { TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_NETWORK_TYPE: { // check state here // update database here if (requestp != NULL) requestp->network_type = DP_NETWORK_OFF; int enable_cb = DP_NETWORK_OFF; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_NETWORK_TYPE, (void *)&enable_cb, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_NOTIFICATION_TYPE: { int noti_type = DP_NOTIFICATION_TYPE_NONE; if (requestp != NULL) requestp->noti_type = noti_type; if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE, (void *)¬i_type, ipc_info->size, 0, &errorcode) < 0) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_DISK_BUSY; } break; } case DP_PROP_NOTIFICATION_SUBJECT: { if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_SUBJECT, &errorcode) < 0) TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); break; } case DP_PROP_NOTIFICATION_DESCRIPTION: { if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_DESCRIPTION, &errorcode) < 0) TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); break; } case DP_PROP_NOTIFICATION_RAW: { int bundle_type = -1; errorcode = __dp_request_read_int(client->channel, ipc_info, &bundle_type); TRACE_DEBUG("read %s type:%d", dp_print_property(ipc_info->property), bundle_type); char *raw_column = NULL; if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_ONGOING) raw_column = DP_DB_COL_NOTI_RAW_ONGOING; else if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE) raw_column = DP_DB_COL_NOTI_RAW_COMPLETE; else if (bundle_type == DP_NOTIFICATION_BUNDLE_TYPE_FAILED) raw_column = DP_DB_COL_NOTI_RAW_FAIL; if (raw_column != NULL) { if (dp_db_unset_property_string(client->dbhandle, ipc_info->id, DP_TABLE_NOTIFICATION, raw_column, &errorcode) < 0) TRACE_ERROR("failed to unset %s", dp_print_property(ipc_info->property)); } else { TRACE_ERROR("invalid param set: %s type:%d", dp_print_property(ipc_info->property), bundle_type); errorcode = DP_ERROR_INVALID_PARAMETER; } break; } case DP_PROP_HTTP_HEADER: // unset value by field { char *header_field = NULL; errorcode = __dp_request_read_string(client->channel, ipc_info, &header_field); if (errorcode == DP_ERROR_NONE && header_field != NULL) { int is_present = dp_db_check_duplicated_string(client->dbhandle, ipc_info->id, DP_TABLE_HEADERS, DP_DB_COL_HEADER_FIELD, 0, header_field, &errorcode); if (is_present < 0) errorcode = DP_ERROR_DISK_BUSY; else if (is_present == 0) errorcode = DP_ERROR_FIELD_NOT_FOUND; else if (dp_db_cond_delete(client->dbhandle, ipc_info->id, DP_TABLE_HEADERS, DP_DB_COL_HEADER_FIELD, header_field, 2, &errorcode) < 0) { TRACE_ERROR("failed to unset %s for %s", dp_print_property(ipc_info->property), header_field); errorcode = DP_ERROR_DISK_BUSY; } } else { if (errorcode != DP_ERROR_NONE) TRACE_ERROR("failed to set %s, error:%s", dp_print_property(ipc_info->property), dp_print_errorcode(errorcode)); if (header_field == NULL) { TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_INVALID_PARAMETER; } } free(header_field); break; } default: errorcode = DP_ERROR_INVALID_PARAMETER; break; } // feedback if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_UNSET, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); } return errorcode; } static int __dp_call_cancel_agent(dp_request_fmt *request) { int ret = -1; if (request != NULL) { if (request->cache_agent_id >= 0) { TRACE_INFO("cancel download for id(%d). cache-id:%d state:%s", request->id, request->cache_agent_id, dp_print_state(request->state)); if (dp_cancel_cache_download_without_update(request->cache_agent_id) == 0) ret = 0; } else if (request->agent_id >= 0) { TRACE_INFO("cancel download for id(%d). agent-id:%d state:%s", request->id, request->agent_id, dp_print_state(request->state)); if (dp_cancel_agent_download_without_update(request->agent_id) == 0) ret = 0; } else { TRACE_ERROR("invalid agent-id:%d cache-id:%d id:%d", request->agent_id, request->cache_agent_id, request->id); } } return ret; } static int __dp_request_controls(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info, dp_request_fmt *requestp) { if (slot == NULL) { TRACE_ERROR("check slot memory"); return DP_ERROR_INVALID_PARAMETER; } dp_client_fmt *client = &slot->client; if (client == NULL || ipc_info == NULL) { TRACE_ERROR("check client or ipc info."); return DP_ERROR_INVALID_PARAMETER; } int errorcode = DP_ERROR_NONE; if (ipc_info->property == DP_PROP_CREATE) { // check packets again if (ipc_info->size != 0 || ipc_info->id != -1) errorcode = DP_ERROR_IO_ERROR; else errorcode = __dp_request_create(client, ipc_info); } else { // get now state. int download_state = DP_STATE_NONE; if (requestp != NULL) { download_state = requestp->state; } else { if (dp_db_get_property_int(client->dbhandle, ipc_info->id, DP_TABLE_LOGGING, DP_DB_COL_STATE, &download_state, &errorcode) < 0) { TRACE_ERROR("failed to get %s", dp_print_property(ipc_info->property)); errorcode = DP_ERROR_ID_NOT_FOUND; // feedback if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); } return errorcode; } } TRACE_DEBUG("id:%d state:%s set property:%s", ipc_info->id, dp_print_state(download_state), dp_print_property(ipc_info->property)); if (ipc_info->property == DP_PROP_START) { if (download_state == DP_STATE_COMPLETED || download_state == DP_STATE_DOWNLOADING) { errorcode = DP_ERROR_INVALID_STATE; } else { if (requestp == NULL) { // load from databse // check state // load and add new request to client->requests. } if (requestp == NULL) { TRACE_ERROR("failed to load id:%d from database sock:%d", ipc_info->id, client->channel); errorcode = DP_ERROR_DISK_BUSY; } if (errorcode == DP_ERROR_NONE) { // update database if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_QUEUED, DP_ERROR_NONE, &errorcode) < 0) { TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id); errorcode = DP_ERROR_DISK_BUSY; } else { requestp->state = DP_STATE_QUEUED; requestp->error = DP_ERROR_NONE; // if it's the first request for this client-> push a request at the head of queue. // check count queued, connecting.downloading in requets of client // else push at the tail of queue. // push to queue if (dp_queue_manager_push_queue(slot, requestp) < 0) { if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0) TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id); requestp->state = DP_STATE_FAILED; requestp->error = DP_ERROR_QUEUE_FULL; errorcode = DP_ERROR_QUEUE_FULL; } else { // push ok dp_queue_manager_wake_up(); // notification if (requestp->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, requestp, DP_NOTIFICATION_ONGOING) < 0) TRACE_ERROR("failed to register notification for id:%d", ipc_info->id); } } } } TRACE_DEBUG("id:%d check start error:%s", ipc_info->id, dp_print_errorcode(errorcode)); } } else if (ipc_info->property == DP_PROP_PAUSE) { if (download_state > DP_STATE_DOWNLOADING) { errorcode = DP_ERROR_INVALID_STATE; } else { // change state regardless it's on memory or not. if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_PAUSED, DP_ERROR_NONE, &errorcode) < 0) { TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id); errorcode = DP_ERROR_DISK_BUSY; } else { // call da_pause API if (requestp != NULL) { // pop from queue. if state is queued. if (requestp->state == DP_STATE_QUEUED) { dp_queue_manager_clear_queue(requestp); } else if (requestp->state == DP_STATE_CONNECTING || requestp->state == DP_STATE_DOWNLOADING) { int ret; if (requestp->cache_agent_id >= 0) { ret = dp_pause_cache_download_without_update(requestp->cache_agent_id); } else { ret = dp_pause_agent_download_without_update(requestp->agent_id); } if (ret < 0) { TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", ipc_info->id, requestp->agent_id, requestp->cache_agent_id); } } requestp->state = DP_STATE_PAUSED; requestp->error = DP_ERROR_NONE; } } } } else if (ipc_info->property == DP_PROP_CANCEL) { if (download_state > DP_STATE_COMPLETED) { errorcode = DP_ERROR_INVALID_STATE; } else { // change state regardless it's on memory or not. if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_CANCELED, DP_ERROR_NONE, &errorcode) < 0) { TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id); errorcode = DP_ERROR_DISK_BUSY; } else { // call da_cancel API if (requestp != NULL) { // pop from queue. if state is queued. if (requestp->state == DP_STATE_QUEUED) { dp_queue_manager_clear_queue(requestp); } else if (requestp->state == DP_STATE_CONNECTING || requestp->state == DP_STATE_DOWNLOADING || requestp->state == DP_STATE_PAUSED) { if (__dp_call_cancel_agent(requestp) < 0) TRACE_ERROR("failed to cancel download(%d) id:%d", requestp->agent_id, ipc_info->id); } requestp->agent_id = -1; requestp->state = DP_STATE_CANCELED; requestp->error = DP_ERROR_NONE; if (requestp->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || requestp->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, requestp, DP_NOTIFICATION) < 0) TRACE_ERROR("failed to register notification for id:%d", ipc_info->id); } } } } } else if (ipc_info->property == DP_PROP_DESTROY) { // check state // pop from queue. if state is queued. if (requestp != NULL) { if (requestp->state == DP_STATE_QUEUED) dp_queue_manager_clear_queue(requestp); if (requestp->state == DP_STATE_CONNECTING || requestp->state == DP_STATE_DOWNLOADING) { // update state property database; if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_CANCELED, DP_ERROR_NONE, &errorcode) < 0) { TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id); } else { // call da_cancel API if (__dp_call_cancel_agent(requestp) < 0) TRACE_ERROR("failed to cancel download(%d) id:%d", requestp->agent_id, ipc_info->id); } requestp->state = DP_STATE_CANCELED; } if (requestp->state == DP_STATE_QUEUED || requestp->state == DP_STATE_CANCELED) { if (requestp->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || requestp->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, requestp, DP_NOTIFICATION) < 0) TRACE_ERROR("failed to register notification for id:%d", ipc_info->id); } } requestp->agent_id = -1; } errorcode = dp_request_destroy(client, ipc_info, requestp); } else { errorcode = DP_ERROR_INVALID_PARAMETER; TRACE_ERROR("invalid param - id:%d set property:%s", ipc_info->id, dp_print_property(ipc_info->property)); } } // feedback if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_CONTROL, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); } // workaround. client still request the feedback by cancelation if (ipc_info->property == DP_PROP_CANCEL || ipc_info->property == DP_PROP_PAUSE) { if (requestp != NULL && requestp->state_cb == 1) { if (slot->client.notify < 0 || dp_notify_feedback(slot->client.notify, slot, ipc_info->id, requestp->state, errorcode, 0) < 0) { TRACE_ERROR("id:%d disable state callback by IO_ERROR", ipc_info->id); requestp->state_cb = 0; } } } return errorcode; } static int __dp_client_requests(dp_client_slots_fmt *slot, dp_ipc_fmt *ipc_info) { if (slot == NULL) { TRACE_ERROR("check slot memory"); return DP_ERROR_INVALID_PARAMETER; } dp_client_fmt *client = &slot->client; if (client == NULL || ipc_info == NULL) { TRACE_ERROR("check client or ipc info."); return DP_ERROR_INVALID_PARAMETER; } int errorcode = DP_ERROR_NONE; // check id except create command /////////// DP_ERROR_ID_NOT_FOUND dp_request_fmt *requestp = NULL; if (ipc_info->section != DP_SEC_CONTROL || ipc_info->property != DP_PROP_CREATE) { // check on requests int i = 0; requestp = client->requests; errorcode = DP_ERROR_ID_NOT_FOUND; for (; i < MAX_DOWNLOAD_HANDLE; i++) { if (requestp == NULL) break; //TRACE_DEBUG("link %d info: id:%d access-time:%d", i, requestp->id, requestp->access_time); if (requestp->id == ipc_info->id) { errorcode = DP_ERROR_NONE; break; } requestp = requestp->next; } if (errorcode == DP_ERROR_ID_NOT_FOUND) { // check in database if (dp_db_check_duplicated_int(client->dbhandle, DP_TABLE_LOGGING, DP_DB_COL_ID, ipc_info->id, &errorcode) > 0) { //TRACE_DEBUG("found %d from database", ipc_info->id); errorcode = DP_ERROR_NONE; } } } if (errorcode != DP_ERROR_NONE) { // prechecking TRACE_ERROR("precheck errorcode:%s sock:%d id:%d section:%s property:%s", dp_print_errorcode(errorcode), client->channel, ipc_info->id, dp_print_section(ipc_info->section), dp_print_property(ipc_info->property)); // clear followed packets. if (ipc_info->size > 0) { char garbage[ipc_info->size]; if (read(client->channel, &garbage, ipc_info->size) == 0) { TRACE_ERROR("sock:%d closed peer", client->channel); errorcode = DP_ERROR_IO_ERROR; } } if (dp_ipc_query(client->channel, ipc_info->id, ipc_info->section, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); errorcode = DP_ERROR_IO_ERROR; } return errorcode; } if (ipc_info->property >= DP_PROP_RESET_CACHE && ipc_info->property <= DP_PROP_RESET_ALL_CACHE) return dp_cache_manager_handle_request(slot, ipc_info, __dp_get_cache_req_type(ipc_info->property)); switch (ipc_info->section) { case DP_SEC_CONTROL: errorcode = __dp_request_controls(slot, ipc_info, requestp); break; case DP_SEC_GET: errorcode = __dp_request_get_info(client, ipc_info, requestp); break; case DP_SEC_SET: errorcode = __dp_request_set_info(slot, ipc_info, requestp); break; case DP_SEC_UNSET: errorcode = __dp_request_unset_info(client, ipc_info, requestp); break; default: errorcode = DP_ERROR_INVALID_PARAMETER; break; } return errorcode; } static void __dp_client_stop_all_requests(dp_client_slots_fmt *slot) { unsigned push_count = 0; int errorcode = DP_ERROR_NONE; int i = 0; dp_request_fmt *tailp = slot->client.requests; for (; tailp != NULL; i++) { TRACE_DEBUG("request %d stop id:%d state:%s", i, tailp->id, dp_print_state(tailp->state)); int state = tailp->state; if (state == DP_STATE_CONNECTING) { int ret; if (tailp->cache_agent_id >= 0) { ret = dp_cancel_cache_download_without_update(tailp->cache_agent_id); } else { ret = dp_cancel_agent_download_without_update(tailp->agent_id); } if (ret < 0) { TRACE_ERROR("failed to cancel download for id(%d). agent-id:%d cache-id:%d", tailp->id, tailp->agent_id, tailp->cache_agent_id); } } else if (state == DP_STATE_DOWNLOADING) { int ret; if (tailp->cache_agent_id >= 0) { ret = dp_pause_cache_download(tailp->cache_agent_id); } else { ret = dp_pause_agent_download(tailp->agent_id); } if (ret < 0) { TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", tailp->id, tailp->agent_id, tailp->cache_agent_id); } } if (state == DP_STATE_DOWNLOADING || state == DP_STATE_CONNECTING) { tailp->state = DP_STATE_QUEUED; // This is error code for checking the reason when changing ip configuration process tailp->error = DP_ERROR_IO_EAGAIN; // push to queue now // in da callback, check DP_ERROR_IO_EAGAIN, then ignore. if (dp_db_update_logging(slot->client.dbhandle, tailp->id, tailp->state, DP_ERROR_NONE, &errorcode) < 0) TRACE_ERROR("update log sock:%d download-id:%d", slot->client.channel, tailp->id); if (dp_queue_manager_push_queue(slot, tailp) < 0) { TRACE_ERROR("Fail to push queueg sock:%d download-id:%d", slot->client.channel, tailp->id); // FIXME later : error case. How can handle this item? } else { push_count++; } } tailp = tailp->next; } if (push_count > 0) dp_queue_manager_wake_up(); } void dp_client_sig_handler(int signo) { TRACE_INFO("thread:%lu signal:%d", pthread_self(), signo); } void *dp_client_request_thread(void *arg) { dp_client_slots_fmt *slot = (dp_client_slots_fmt *)arg; if (slot == NULL) { TRACE_ERROR("slot null, can not launch the thread for client"); return 0; } // wait detaching thread CLIENT_MUTEX_LOCK(&slot->mutex); TRACE_INFO("slot %p thread:%lu", slot, slot->thread); struct sigaction act = {{0},}; sigset_t newmask; sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); act.sa_handler = dp_client_sig_handler; sigaction(SIGUSR1, &act, NULL); fd_set imask, emask; int errorcode = DP_ERROR_NONE; dp_client_fmt *client = &slot->client; int client_sock = client->channel; struct timeval timeout; // for timeout of select CLIENT_MUTEX_UNLOCK(&slot->mutex); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); while (slot != NULL && client_sock >= 0 && client_sock == slot->client.channel) { memset(&timeout, 0x00, sizeof(struct timeval)); timeout.tv_sec = DP_CARE_CLIENT_REQUEST_INTERVAL; FD_ZERO(&imask); FD_ZERO(&emask); FD_SET(client_sock, &imask); FD_SET(client_sock, &emask); if (select(client_sock + 1, &imask, 0, &emask, &timeout) < 0) { if (slot != NULL && slot->client.channel >= 0) { TRACE_INFO("broadcast by client-manager"); CLIENT_MUTEX_LOCK(&slot->mutex); // check all requests __dp_client_stop_all_requests(slot); CLIENT_MUTEX_UNLOCK(&slot->mutex); continue; } else { TRACE_ERROR("interrupted by client-manager sock:%d", client_sock); break; } } if (FD_ISSET(client_sock, &imask) > 0) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); CLIENT_MUTEX_LOCK(&slot->mutex); client->access_time = (int)time(NULL); errorcode = DP_ERROR_NONE; // read ipc_fmt first. below func will deal followed packets dp_ipc_fmt *ipc_info = dp_ipc_get_fmt(client_sock); if (ipc_info == NULL) { TRACE_ERROR("sock:%d maybe closed", client_sock); errorcode = DP_ERROR_IO_ERROR; } else { TRACE_INFO("sock:%d id:%d section:%s property:%s errorcode:%s size:%zd", client_sock, ipc_info->id, dp_print_section(ipc_info->section), dp_print_property(ipc_info->property), dp_print_errorcode(ipc_info->errorcode), ipc_info->size); if (client->dbhandle == 0 || dp_db_check_connection(client->dbhandle) < 0) { if (dp_db_open_client(&client->dbhandle, slot->pkgname, &errorcode) < 0) { TRACE_ERROR("failed to open database for sock:%d errorcode:%s", client_sock, dp_print_errorcode(errorcode)); if (dp_ipc_query(client->channel, ipc_info->id, ipc_info->section, ipc_info->property, errorcode, 0) < 0) { TRACE_ERROR("check ipc sock:%d", client->channel); } } } if (errorcode == DP_ERROR_NONE) { // JOB errorcode = __dp_client_requests(slot, ipc_info); } } free(ipc_info); CLIENT_MUTEX_UNLOCK(&slot->mutex); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (errorcode == DP_ERROR_IO_ERROR || errorcode == DP_ERROR_OUT_OF_MEMORY || errorcode == DP_ERROR_INVALID_PARAMETER) { TRACE_ERROR("disconnect client errorcode:%s sock:%d", dp_print_errorcode(errorcode), client_sock); break; } } else if (FD_ISSET(client_sock, &emask) > 0) { TRACE_ERROR("[EXCEPTION]"); break; } else { TRACE_ERROR("[TIMEOUT]"); if (CLIENT_MUTEX_TRYLOCK(&slot->mutex) == 0) { // 1. clear zombie requests. clean requests finished. paused or ready for long time dp_client_clear_requests(slot); // 2. if no requests, exit by itself. if (slot->client.requests == NULL) { TRACE_DEBUG("no requests"); CLIENT_MUTEX_UNLOCK(&slot->mutex); break; } CLIENT_MUTEX_UNLOCK(&slot->mutex); } } } // while (slot != NULL && client_sock >= 0 && ....) _dp_client_clear_stale_db(client); if (client_sock >= 0 && client_sock < FD_SETSIZE) { FD_CLR(client_sock, &imask); FD_CLR(client_sock, &emask); } // if no requests, clear slot after disconnect with client. CLIENT_MUTEX_LOCK(&slot->mutex); TRACE_INFO("thread done slot %p thread:%lu", slot, slot->thread); slot->thread = 0;// to prevent kill thread twice int i = 0; dp_request_fmt *tailp = slot->client.requests; dp_request_fmt *prevp = NULL; for (; tailp != NULL; i++) { if (tailp->state != DP_STATE_QUEUED && tailp->state != DP_STATE_CONNECTING && tailp->state != DP_STATE_DOWNLOADING) { dp_request_fmt *removep = tailp; if (prevp == NULL) // first request. client->requests = tailp->next; else prevp->next = tailp->next; tailp = tailp->next; TRACE_DEBUG("request %d remove: id:%d state:%s", i, removep->id, dp_print_state(removep->state)); dp_request_free(removep); continue; } TRACE_DEBUG("request %d remain: id:%d state:%s", i, tailp->id, dp_print_state(tailp->state)); prevp = tailp; tailp = tailp->next; } // if no requests after clear finished requests. if (slot->client.requests == NULL) { dp_client_slot_free(slot); } else { if (slot->client.notify >= 0) close(slot->client.notify); dp_notify_deinit(slot->credential.pid); slot->client.notify = -1; if (slot->client.channel > 0) close(slot->client.channel); slot->client.channel = -1; } TRACE_INFO("thread done slot %p thread:%lu sock:%d", slot, slot->thread, client_sock); CLIENT_MUTEX_UNLOCK(&slot->mutex); return 0; }