diff options
author | Jiwoong Im <jiwoong.im@samsung.com> | 2015-05-29 16:54:32 +0900 |
---|---|---|
committer | Jiwoong Im <jiwoong.im@samsung.com> | 2015-06-03 14:08:32 +0900 |
commit | ba3841dce24c90e1795436997fa408696ed21311 (patch) | |
tree | 7389037a1716a7d30de213ce2c545ad87a35664b | |
parent | 864cfb623d9467091502d16b006daa307869ed83 (diff) | |
download | application-tizen_3.0.2015.q2_common.tar.gz application-tizen_3.0.2015.q2_common.tar.bz2 application-tizen_3.0.2015.q2_common.zip |
update preference source from tizen 2.4submit/tizen_tv/20150611.000000submit/tizen_3.0.2015.q2_common/20150615.075539submit/tizen_3.0.2015.q2_common/20150609.170642submit/tizen/20150605.114228submit/tizen/20150604.025832accepted/tizen/wearable/20150609.085858accepted/tizen/tv/20150612.003517accepted/tizen/mobile/20150609.085836accepted/tizen/common/20150609.084317accepted/tizen/3.0.2015.q2/common/20150615.091844accepted/tizen/3.0.2015.q2/common/20150609.181955tizen_3.0.2015.q2_commonaccepted/tizen_3.0.2015.q2_common
change logic and apply inotify.
Change-Id: If361b058c81cf1cb235c661e967417fc9cca9e19
Signed-off-by: Jiwoong Im <jiwoong.im@samsung.com>
-rw-r--r-- | include/app_preference.h | 176 | ||||
-rwxr-xr-x | include/app_preference_internal.h | 142 | ||||
-rwxr-xr-x | include/app_preference_log.h | 92 | ||||
-rw-r--r-- | preference/CMakeLists.txt | 1 | ||||
-rw-r--r-- | preference/preference.c | 1688 | ||||
-rwxr-xr-x | preference/preference_db.c | 915 | ||||
-rwxr-xr-x | preference/preference_inoti.c | 416 |
7 files changed, 2816 insertions, 614 deletions
diff --git a/include/app_preference.h b/include/app_preference.h index 9aeb3af..8b2ff86 100644 --- a/include/app_preference.h +++ b/include/app_preference.h @@ -11,7 +11,7 @@ * 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. + * limitations under the License. */ @@ -25,32 +25,38 @@ extern "C" { #endif /** - * @addtogroup CAPI_PREFERENCE_MODULE + * @file app_preference.h + */ + +/** + * @addtogroup CAPI_PREFERENCE_MODULE * @{ */ /** - * @brief Enumerations of error code for Preference. + * @brief Enumeration for Preference Error. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif */ typedef enum { PREFERENCE_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ PREFERENCE_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ PREFERENCE_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */ - PREFERENCE_ERROR_NO_KEY = TIZEN_ERROR_KEY_NOT_AVAILABLE, /**< Required key not available */ + PREFERENCE_ERROR_NO_KEY = TIZEN_ERROR_APPLICATION | 0x30, /**< Required key not available */ PREFERENCE_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR , /**< Internal I/O Error */ } preference_error_e; /** - * @brief Called when the given key's value in the preference changes. + * @brief Called when the given key's value in the preference changes. * - * @details When the @a key is added or removed, this callback function is skipped. (only update can be handled) + * @details When the @a key is added or removed, this callback function is skipped(only update can be handled). * - * @param [in] key The name of the key in the preference - * @param [in] user_data The user data passed from the callback registration function - * @pre This function is invoked when the value of the key is overwritten after you register this callback using preference_set_changed_cb() + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key in the preference + * @param[in] user_data The user data passed from the callback registration function + * @pre This function is invoked when the value of the key is overwritten after you register this callback using preference_set_changed_cb(). * @see preference_set_changed_cb() * @see preference_unset_changed_cb() * @see preference_set_boolean() @@ -62,42 +68,47 @@ typedef void (*preference_changed_cb) (const char *key, void *user_data); /** -* @brief Called to get key string once for each key-value pair in the preference. -* -* @remarks You should not free @a key returned by this function. -* -* @param [in] key The key of the value added to the preference -* @param [in] value The value associated with the key -* @param [in] user_data The user data passed from the foreach function -* @return @c true to continue with the next iteration of the loop, \n @c false to break out of the loop. -* @pre preference_foreach_item() will invoke this callback function. -* @see preference_foreach_item() -*/ + * @brief Called to get key string, once for each key-value pair in the preference. + * + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @remarks You should not free the @a key returned by this function. + * + * @param[in] key The key of the value added to the preference + * @param[in] value The value associated with the key + * @param[in] user_data The user data passed from the foreach function + * @return @c true to continue with the next iteration of the loop, + * otherwise @c false to break out of the loop + * @pre preference_foreach_item() will invoke this callback function. + * @see preference_foreach_item() + */ typedef bool (*preference_item_cb)(const char *key, void *user_data); /** * @brief Sets an integer value in the preference. * - * @param [in] key The name of the key to modify - * @param [in] value The new @c int value for the given key - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to modify + * @param[in] value The new @c int value for the given key + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error * @see preference_get_int() - * */ int preference_set_int(const char *key, int value); /** - * @brief Gets a integer value from the preference. + * @brief Gets an integer value from the preference. * - * @param [in] key The name of the key to retrieve - * @param [out] value The @c int value for the given key - * @return 0 on success, otherwise a negative error value. - * @retval #PREFERENCE_ERROR_NONE Successful + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to retrieve + * @param[out] value The @c int value for the given key + * @return @c 0 on success, + * otherwise a negative error value + * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_NO_KEY Required key not available * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error @@ -109,14 +120,15 @@ int preference_get_int(const char *key, int *value); /** * @brief Sets a double value in the preference. * - * @param [in] key The name of the key to modify - * @param [in] value The new @c double value associated with given key - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to modify + * @param[in] value The new @c double value associated with the given key + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error * @see preference_get_double() - * */ int preference_set_double(const char *key, double value); @@ -124,15 +136,16 @@ int preference_set_double(const char *key, double value); /** * @brief Gets a double value from the preference. * - * @param [in] key The name of the key to retrieve - * @param [out] value The @c double value associated with given key - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to retrieve + * @param[out] value The @c double value associated with the given key + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_NO_KEY Required key not available * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error * @see preference_set_double() - * */ int preference_get_double(const char *key, double *value); @@ -141,15 +154,16 @@ int preference_get_double(const char *key, double *value); * @brief Sets a string value in the preference. * * @details It makes a deep copy of the added string value. - * - * @param [in] key The name of the key to modify - * @param [in] value The new @c string value associated with given key - * @return 0 on success, otherwise a negative error value. + * + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to modify + * @param[in] value The new @c string value associated with the given key + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error * @see preference_get_string() - * */ int preference_set_string(const char *key, const char *value); @@ -157,11 +171,13 @@ int preference_set_string(const char *key, const char *value); /** * @brief Gets a string value from the preference. * - * @remarks @a value must be released with free() by you. - * @param [in] key The name of the key to retrieve - * @param [out] value The @c string value associated with given key - * @return 0 on success, otherwise a negative error value. - * @retval #PREFERENCE_ERROR_NONE Successful + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @remarks @a value must be released using free(). + * @param[in] key The name of the key to retrieve + * @param[out] value The @c string value associated with the given key + * @return @c 0 on success, + * otherwise a negative error value + * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_OUT_OF_MEMORY Out of memory * @retval #PREFERENCE_ERROR_NO_KEY Required key not available @@ -174,12 +190,14 @@ int preference_get_string(const char *key, char **value); /** * @brief Sets a boolean value in the preference. * - * @param [in] key The name of the key to modify - * @param [in] value The new boolean @c value associated with given key - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to modify + * @param[in] value The new @c boolean value associated with the given key + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error + * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error * @see preference_get_boolean() */ int preference_set_boolean(const char *key, bool value); @@ -188,9 +206,11 @@ int preference_set_boolean(const char *key, bool value); /** * @brief Gets a boolean value from the preference. * - * @param [in] key The name of the key to retrieve - * @param [out] value The boolean @c value associated with given key - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to retrieve + * @param[out] value The @c boolean value associated with the given key + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_NO_KEY Required key not available @@ -203,22 +223,26 @@ int preference_get_boolean(const char *key, bool *value); /** * @brief Removes any value with the given @a key from the preference. * - * @param [in] key The name of the key to remove - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to remove + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful - * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error - * */ int preference_remove(const char *key); /** - * @brief Checks whether if the given @a key exists in the preference. + * @brief Checks whether the given @a key exists in the preference. * - * @param [in] key The name of the key to check - * @param [out] existing @c true if the @a key exists in the preference, otherwise @c false - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to check + * @param[out] existing If @c true the @a key exists in the preference, + * otherwise @c false + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error @@ -229,7 +253,9 @@ int preference_is_existing(const char *key, bool *existing); /** * @brief Removes all key-value pairs from the preference. * - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error * @see preference_remove() @@ -240,10 +266,12 @@ int preference_remove_all(void); /** * @brief Registers a callback function to be invoked when value of the given key in the preference changes. * - * @param [in] key The name of the key to monitor - * @param [in] callback The callback function to register - * @param [in] user_data The user data to be passed to the callback function - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to monitor + * @param[in] callback The callback function to register + * @param[in] user_data The user data to be passed to the callback function + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_OUT_OF_MEMORY Out of memory @@ -259,8 +287,10 @@ int preference_set_changed_cb(const char *key, preference_changed_cb callback, v /** * @brief Unregisters the callback function. * - * @param [in] key The name of the key to monitor - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] key The name of the key to monitor + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error @@ -272,9 +302,11 @@ int preference_unset_changed_cb(const char *key); /** * @brief Retrieves all key-value pairs in the preference by invoking the callback function. * - * @param [in] callback The callback function to get key value once for each key-value pair in the preference - * @param [in] user_data The user data to be passed to the callback function - * @return 0 on success, otherwise a negative error value. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + * @param[in] callback The callback function to get key value once for each key-value pair in the preference + * @param[in] user_data The user data to be passed to the callback function + * @return @c 0 on success, + * otherwise a negative error value * @retval #PREFERENCE_ERROR_NONE Successful * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error diff --git a/include/app_preference_internal.h b/include/app_preference_internal.h new file mode 100755 index 0000000..8108903 --- /dev/null +++ b/include/app_preference_internal.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2011 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. + */ + + +#ifndef __TIZEN_APPFW_PREFERENCE_INTERNAL_H__ +#define __TIZEN_APPFW_PREFERENCE_INTERNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "app_preference_log.h" + +#define BUF_LEN (4096) +#define PREF_DIR ".pref/" + +#define PREFERENCE_KEY_PATH_LEN 1024 +#define ERR_LEN 128 + +#define PREF_DB_NAME ".pref.db" +#define PREF_TBL_NAME "pref" +#define PREF_F_KEY_NAME "pref_key" +#define PREF_F_TYPE_NAME "pref_type" +#define PREF_F_DATA_NAME "pref_data" + +/** + * @brief Definition for PREFERENCE_ERROR_WRONG_PREFIX. + */ +#define PREFERENCE_ERROR_WRONG_PREFIX -2 + +/** + * @brief Definition for PREFERENCE_ERROR_WRONG_TYPE. + */ +#define PREFERENCE_ERROR_WRONG_TYPE -3 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_OPEN. + */ +#define PREFERENCE_ERROR_FILE_OPEN -21 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_FREAD. + */ +#define PREFERENCE_ERROR_FILE_FREAD -22 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_FGETS. + */ +#define PREFERENCE_ERROR_FILE_FGETS -23 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_WRITE. + */ +#define PREFERENCE_ERROR_FILE_WRITE -24 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_SYNC. + */ +#define PREFERENCE_ERROR_FILE_SYNC -25 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_CHMOD. + */ +#define PREFERENCE_ERROR_FILE_CHMOD -28 + +/** + * @brief Definition for PREFERENCE_ERROR_FILE_LOCK. + */ +#define PREFERENCE_ERROR_FILE_LOCK -29 + +typedef enum +{ + PREFERENCE_TYPE_NONE = 0, + PREFERENCE_TYPE_STRING, + PREFERENCE_TYPE_INT, + PREFERENCE_TYPE_DOUBLE, + PREFERENCE_TYPE_BOOLEAN, +} preference_type_e; + +typedef struct _pref_changed_cb_node_t{ + char *key; + preference_changed_cb cb; + void *user_data; + struct _pref_changed_cb_node_t *prev; + struct _pref_changed_cb_node_t *next; +} pref_changed_cb_node_t; + +typedef struct _keynode_t { + char *keyname; /**< Keyname for keynode */ + int type; /**< Keynode type */ + union { + int i; /**< Integer type */ + int b; /**< Bool type */ + double d; /**< Double type */ + char *s; /**< String type */ + } value; /**< Value for keynode */ + struct _keynode_t *next; /**< Next keynode */ +} keynode_t; + +/** + * @brief The structure type for opaque type. It must be used via accessor functions. + * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif + */ +typedef struct _keylist_t { + int num; /**< Number of list */ + keynode_t *head; /**< Head node */ + keynode_t *cursor; /**< Cursor node */ +} keylist_t; + + +int _preference_kdb_add_notify + (const char *keyname, preference_changed_cb cb, void *data); +int _preference_kdb_del_notify + (const char *keyname); + +int _preference_get_key_path(const char *keyname, char *path); +int _preference_get_key(keynode_t *keynode); + +int _preference_keynode_set_keyname(keynode_t *keynode, const char *keyname); +inline void _preference_keynode_set_null(keynode_t *keynode); +inline keynode_t *_preference_keynode_new(void); +inline void _preference_keynode_free(keynode_t *keynode); + + +#ifdef __cplusplus +} +#endif + +#endif /* __TIZEN_APPFW_PREFERENCE_INTERNAL_H__ */ diff --git a/include/app_preference_log.h b/include/app_preference_log.h new file mode 100755 index 0000000..8bd87a7 --- /dev/null +++ b/include/app_preference_log.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "CAPI_APPFW_APPLICATION_PREFERENCE" +#define DBG_MODE (1) + +#ifndef __PREFERENCE_LOG_H__ +#define __PREFERENCE_LOG_H__ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <dlog.h> + + +#define INFO(fmt, arg...) +#define DBG(fmt, arg...) SECURE_SLOGI(fmt, ##arg) +#define ERR(fmt, arg...) LOGE(fmt, ##arg) +#define SECURE_ERR(fmt, arg...) SECURE_SLOGE(fmt, ##arg) +#define FATAL(fmt, arg...) SECURE_SLOGF(fmt, ##arg) +#define WARN(fmt, arg...) SECURE_SLOGW(fmt, ##arg) + + +/************** Return ***************/ +#define ret_if(expr) \ + do { \ + if (expr) { \ + ERR("(%s) -> %s() return", #expr, __FUNCTION__); \ + return; \ + } \ + } while (0) +#define retv_if(expr, val) \ + do { \ + if (expr) { \ + ERR("(%s) -> %s() return", #expr, __FUNCTION__); \ + return (val); \ + } \ + } while (0) +#define retm_if(expr, fmt, arg...) \ + do { \ + if (expr) { \ + ERR(fmt, ##arg); \ + return; \ + } \ + } while (0) +#define retvm_if(expr, val, fmt, arg...) \ + do { \ + if (expr) { \ + ERR(fmt, ##arg); \ + return (val); \ + } \ + } while (0) +#define retex_if(expr, fmt, arg...) \ + do { \ + if (expr) { \ + ERR(fmt, ##arg); \ + goto CATCH; \ + } \ + } while (0) + + +/************** TimeCheck ***************/ +#ifdef PREFERENCE_TIMECHECK +#define START_TIME_CHECK \ + init_time();\ + startT = set_start_time(); +#define END_TIME_CHECK \ + PREFERENCE_DEBUG("time = %f ms\n", exec_time(startT)); +#else +#define START_TIME_CHECK +#define END_TIME_CHECK +#endif + + +#endif /* __PREFERENCE_LOG_H__ */ diff --git a/preference/CMakeLists.txt b/preference/CMakeLists.txt index 73b2d4e..21da809 100644 --- a/preference/CMakeLists.txt +++ b/preference/CMakeLists.txt @@ -32,6 +32,7 @@ SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}") add_library(${fw_name} SHARED preference.c + preference_inoti.c ) TARGET_LINK_LIBRARIES(${fw_name} capi-appfw-app-common ${${fw_name}_LDFLAGS}) diff --git a/preference/preference.c b/preference/preference.c index 98faf5d..c19b641 100644 --- a/preference/preference.c +++ b/preference/preference.c @@ -8,796 +8,1400 @@ * 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, + * 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 <stdio.h> #include <stdlib.h> -#include <unistd.h> +#include <limits.h> #include <string.h> -#include <sqlite3.h> - -#include <app_private.h> +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/xattr.h> +#include <ctype.h> +#include <string.h> +#include <execinfo.h> #include <app_preference.h> -#include <app_preference_private.h> +#include <app_preference_internal.h> +#include <app_common.h> -#include <dlog.h> - -#ifdef LOG_TAG -#undef LOG_TAG +#ifndef API +#define API __attribute__ ((visibility("default"))) #endif -#define LOG_TAG "CAPI_APPFW_APPLICATION_PREFERENCE" -#define DBG_MODE (1) +#define PREFERENCE_ERROR_RETRY_CNT 7 +#define PREFERENCE_ERROR_RETRY_SLEEP_UTIME 10000 + +#define DELIMITER 29 + +static int g_posix_errno; +static int g_preference_errno; +static char *g_pref_dir_path = NULL; -static sqlite3 *pref_db; -static bool is_update_hook_registered; -static pref_changed_cb_node_t *head; -static void _finish(void *data) +enum preference_op_t { + PREFERENCE_OP_GET = 0, + PREFERENCE_OP_SET = 1 +}; + +#ifdef PREFERENCE_TIMECHECK +double correction, startT; + +double set_start_time(void) { - if (pref_db != NULL) { - sqlite3_close(pref_db); - pref_db = NULL; - } + struct timeval tv; + double curtime; + + gettimeofday(&tv, NULL); + curtime = tv.tv_sec * 1000 + (double)tv.tv_usec / 1000; + return curtime; } -static int _busy_handler(void *pData, int count) +double exec_time(double start) { - if (count < 5) { - LOGD("Busy Handler Called! : PID(%d) / CNT(%d)\n", - getpid(), count+1); - usleep((count+1)*100000); - return 1; - } else { - LOGD("Busy Handler will be returned SQLITE_BUSY error : PID(%d)\n", - getpid()); - return 0; - } + double end = set_start_time(); + return (end - start - correction); } -static int _initialize(void) +int init_time(void) { - char *data_path; - char db_path[TIZEN_PATH_MAX]; - int ret; - char *errmsg; + double temp_t; + temp_t = set_start_time(); + correction = exec_time(temp_t); - data_path = app_get_data_path(); - if (data_path == NULL) { - LOGE("IO_ERROR(0x%08x) : fail to get data directory", - PREFERENCE_ERROR_IO_ERROR); - return PREFERENCE_ERROR_IO_ERROR; - } - snprintf(db_path, sizeof(db_path), "%s/%s", data_path, PREF_DB_NAME); - free(data_path); + return 0; +} +#endif - ret = sqlite3_open_v2(db_path, &pref_db, SQLITE_OPEN_CREATE, NULL); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to open db(%s)", - PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db)); - _finish(NULL); - return PREFERENCE_ERROR_IO_ERROR; +char* _preference_get_pref_dir_path() +{ + char *app_data_path = NULL; + + if (!g_pref_dir_path) + { + g_pref_dir_path = (char *)malloc(PREFERENCE_KEY_PATH_LEN + 1); + if ((app_data_path = app_get_data_path()) == NULL) + { + ERR("IO_ERROR(0x%08x) : fail to get data directory", PREFERENCE_ERROR_IO_ERROR); + free(g_pref_dir_path); + g_pref_dir_path = NULL; + return NULL; + } + snprintf(g_pref_dir_path, PREFERENCE_KEY_PATH_LEN, "%s%s", app_data_path, PREF_DIR); + INFO("pref_dir_path: %s", g_pref_dir_path); + free(app_data_path); } + return g_pref_dir_path; +} - ret = sqlite3_busy_handler(pref_db, _busy_handler, NULL); - if (ret != SQLITE_OK) { - LOGW("IO_ERROR(0x%08x) : fail to register busy handler(%s)\n", - PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db)); - } +int _preference_keynode_set_keyname(keynode_t *keynode, const char *keyname) +{ + if (keynode->keyname) free(keynode->keyname); + keynode->keyname = strndup(keyname, PREFERENCE_KEY_PATH_LEN); + retvm_if(keynode->keyname == NULL, PREFERENCE_ERROR_IO_ERROR, "strndup Fails"); + return PREFERENCE_ERROR_NONE; +} - ret = sqlite3_exec(pref_db, - "CREATE TABLE IF NOT EXISTS pref ( pref_key TEXT PRIMARY KEY, pref_type TEXT, pref_data TEXT)", - NULL, NULL, &errmsg); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to create db table(%s)", - PREFERENCE_ERROR_IO_ERROR, errmsg); - sqlite3_free(errmsg); - _finish(NULL); - return PREFERENCE_ERROR_IO_ERROR; - } +static inline void _preference_keynode_set_value_int(keynode_t *keynode, const int value) +{ + keynode->type = PREFERENCE_TYPE_INT; + keynode->value.i = value; +} - app_finalizer_add(_finish, NULL); +static inline void _preference_keynode_set_value_boolean(keynode_t *keynode, const int value) +{ + keynode->type = PREFERENCE_TYPE_BOOLEAN; + keynode->value.b = !!value; +} - return PREFERENCE_ERROR_NONE; +static inline void _preference_keynode_set_value_double(keynode_t *keynode, const double value) +{ + keynode->type = PREFERENCE_TYPE_DOUBLE; + keynode->value.d = value; } -static int _prepare_and_bind_stmt(char *buf, const char *type, - const char *data, const char *key, sqlite3_stmt **stmt) +static inline void _preference_keynode_set_value_string(keynode_t *keynode, const char *value) { - int ret; + keynode->type = PREFERENCE_TYPE_STRING; + keynode->value.s = strdup(value); +} - ret = sqlite3_prepare(pref_db, buf, -1, stmt, NULL); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - return PREFERENCE_ERROR_IO_ERROR; - } +inline keynode_t *_preference_keynode_new(void) +{ + keynode_t *keynode; + keynode = calloc(1, sizeof(keynode_t)); - ret = sqlite3_bind_text(*stmt, 1, type, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - sqlite3_finalize(*stmt); - return PREFERENCE_ERROR_IO_ERROR; - } - ret = sqlite3_bind_text(*stmt, 2, data, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to bind(2) query (%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - sqlite3_finalize(*stmt); - return PREFERENCE_ERROR_IO_ERROR; + return keynode; +} + +inline void _preference_keynode_free(keynode_t *keynode) +{ + if(keynode) { + if (keynode->keyname) + free(keynode->keyname); + if (keynode->type == PREFERENCE_TYPE_STRING && keynode->value.s) + free(keynode->value.s); + free(keynode); } - ret = sqlite3_bind_text(*stmt, 3, key, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to bind(3) query (%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - sqlite3_finalize(*stmt); - return PREFERENCE_ERROR_IO_ERROR; +} + +int _preference_get_key_name(const char *keyfile, char *keyname) +{ + char convert_key[PREFERENCE_KEY_PATH_LEN] = {0,}; + char *chrptr = NULL; + + strncpy(convert_key, keyfile, strlen(keyfile)); + + chrptr = strchr((const char*)convert_key, DELIMITER); + if(chrptr) { + chrptr = strchr((const char*)convert_key, DELIMITER); + while(chrptr) { + convert_key[chrptr-convert_key] = '/'; + chrptr = strchr((const char*)chrptr+1, DELIMITER); + } } + snprintf(keyname, PREFERENCE_KEY_PATH_LEN, "%s", (const char*)convert_key); return PREFERENCE_ERROR_NONE; } -static int _write_data(const char *key, const char *type, const char *data) + +int _preference_get_key_path(const char *keyname, char *path) { - int ret; - bool exist = false; - sqlite3_stmt *stmt; - char buf[BUF_LEN]; + const char *key = NULL; - if (key == NULL || key[0] == '\0' || data == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; + if(!keyname) { + ERR("keyname is null"); + return PREFERENCE_ERROR_WRONG_PREFIX; } - /* insert data or update data if data already exist */ - ret = preference_is_existing(key, &exist); - if (ret != PREFERENCE_ERROR_NONE) - return ret; - - if (exist) - snprintf(buf, sizeof(buf), "UPDATE %s SET %s=?1, %s=?2 WHERE %s=?3;", - PREF_TBL_NAME, PREF_F_TYPE_NAME, - PREF_F_DATA_NAME, PREF_F_KEY_NAME); - else - snprintf(buf, sizeof(buf), - "INSERT INTO %s (%s, %s, %s) values (?3, ?1, ?2);", - PREF_TBL_NAME, PREF_F_KEY_NAME, - PREF_F_TYPE_NAME, PREF_F_DATA_NAME); - ret = _prepare_and_bind_stmt(buf, type, data, key, &stmt); + char convert_key[PREFERENCE_KEY_PATH_LEN] = {0,}; + char *chrptr = NULL; + char *pref_dir_path = NULL; - if (ret != PREFERENCE_ERROR_NONE) - return ret; + strncpy(convert_key, keyname, strlen(keyname)); - ret = sqlite3_step(stmt); - if (ret != SQLITE_DONE) { - LOGE("IO_ERROR(0x%08x): fail to write data(%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - sqlite3_finalize(stmt); + pref_dir_path = _preference_get_pref_dir_path(); + if (!pref_dir_path) + { + LOGE("_preference_get_pref_dir_path() failed."); return PREFERENCE_ERROR_IO_ERROR; } - sqlite3_finalize(stmt); + chrptr = strchr((const char*)convert_key, (int)'/'); + if(!chrptr) { + key = (const char*)convert_key; + } + else { + chrptr = strchr((const char*)convert_key, (int)'/'); + while(chrptr) { + convert_key[chrptr-convert_key] = DELIMITER; + chrptr = strchr((const char*)chrptr+1, (int)'/'); + } + key = (const char*)convert_key; + } + + snprintf(path, PREFERENCE_KEY_PATH_LEN, "%s%s", pref_dir_path, key); return PREFERENCE_ERROR_NONE; } -static int _read_data(const char *key, char *type, char *data) +static int _preference_set_key_check_pref_dir() { - int ret; - char *buf; - char **result; - int rows; - int columns; - char *errmsg; + char *pref_dir_path = NULL; + mode_t dir_mode = 0664 | 0111; - if (key == NULL || key[0] == '\0' || data == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; + pref_dir_path = _preference_get_pref_dir_path(); + if (!pref_dir_path) + { + LOGE("_preference_get_pref_dir_path() failed."); + return PREFERENCE_ERROR_IO_ERROR; } - if (pref_db == NULL) { - if (_initialize() != PREFERENCE_ERROR_NONE) { - LOGE("IO_ERROR(0x%08x) : fail to initialize db", - PREFERENCE_ERROR_IO_ERROR); + if (access(pref_dir_path, F_OK) < 0) + { + if (mkdir(pref_dir_path, dir_mode) < 0) + { + ERR("mkdir() failed(%d/%s)", errno, strerror(errno)); return PREFERENCE_ERROR_IO_ERROR; } } - buf = sqlite3_mprintf("SELECT %s, %s, %s FROM %s WHERE %s=%Q;", - PREF_F_KEY_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME, - PREF_TBL_NAME, PREF_F_KEY_NAME, key); + return PREFERENCE_ERROR_NONE; +} - if (buf == NULL) { - LOGE("IO_ERROR(0x%08x) : fail to create query string", - PREFERENCE_ERROR_IO_ERROR); +static int _preference_set_key_creation(const char* path) +{ + int fd; + mode_t temp; + temp = umask(0000); + fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + umask(temp); + if(fd == -1) { + ERR("open(rdwr,create) error: %d(%s)", errno, strerror(errno)); return PREFERENCE_ERROR_IO_ERROR; } + close(fd); - ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); - sqlite3_free(buf); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to read data (%s)", - PREFERENCE_ERROR_IO_ERROR, errmsg); - sqlite3_free(errmsg); - return PREFERENCE_ERROR_IO_ERROR; - } + return PREFERENCE_ERROR_NONE; +} - if (rows == 0) { - LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", - PREFERENCE_ERROR_NO_KEY, key); - sqlite3_free_table(result); - return PREFERENCE_ERROR_NO_KEY; - } +static int _preference_set_file_lock(int fd, short type) +{ + struct flock l; - snprintf(type, 2, "%s", result[4]); - snprintf(data, BUF_LEN, "%s", result[5]); + l.l_type = type; + l.l_start= 0; /*Start at begin*/ + l.l_whence = SEEK_SET; + l.l_len = 0; /*Do it with whole file*/ - sqlite3_free_table(result); + return fcntl(fd, F_SETLK, &l); +} - return PREFERENCE_ERROR_NONE; +static int _preference_get_pid_of_file_lock_owner(int fd, short type) +{ + struct flock l; + + l.l_type = type; + l.l_start= 0; /*Start at begin*/ + l.l_whence = SEEK_SET; + l.l_len = 0; /*Do it with whole file*/ + + if(fcntl(fd, F_GETLK, &l) < 0) { + WARN("error in getting lock info"); + return -1; + } + + if(l.l_type == F_UNLCK) + return 0; + else + return l.l_pid; } -int preference_set_int(const char *key, int value) +static int _preference_set_read_lock(int fd) { - char type[2]; - char data[BUF_LEN]; - snprintf(type, 2, "%d", PREFERENCE_TYPE_INT); - snprintf(data, BUF_LEN, "%d", value); - return _write_data(key, type, data); + return _preference_set_file_lock(fd, F_RDLCK); } -int preference_get_int(const char *key, int *value) +static int _preference_set_write_lock(int fd) { - char type[2]; - char data[BUF_LEN]; - int ret; + return _preference_set_file_lock(fd, F_WRLCK); +} - if (value == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; - } +static int _preference_set_unlock(int fd) +{ + return _preference_set_file_lock(fd, F_UNLCK); +} - ret = _read_data(key, type, data); - if (ret == PREFERENCE_ERROR_NONE) { - if (atoi(type) == PREFERENCE_TYPE_INT) - *value = atoi(data); - else { - LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", - PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); - return PREFERENCE_ERROR_INVALID_PARAMETER; +static int _preference_check_retry_err(keynode_t *keynode, int preference_errno, int io_errno, int op_type) +{ + int is_busy_err = 0; + + if (preference_errno == PREFERENCE_ERROR_FILE_OPEN) + { + switch (io_errno) + { + case ENOENT : + { + if(op_type == PREFERENCE_OP_SET) + { + int rc = 0; + char path[PREFERENCE_KEY_PATH_LEN] = {0,}; + rc = _preference_get_key_path(keynode->keyname, path); + if (rc != PREFERENCE_ERROR_NONE) { + ERR("_preference_get_key_path error"); + break; + } + + rc = _preference_set_key_check_pref_dir(); + if (rc != PREFERENCE_ERROR_NONE) { + ERR("_preference_set_key_check_pref_dir() failed."); + break; + } + + preference_errno = _preference_set_key_creation(path); + if (rc != PREFERENCE_ERROR_NONE) { + ERR("_preference_set_key_creation error : %s", path); + break; + } + INFO("%s key is created", keynode->keyname); + + is_busy_err = 1; + } + break; + } + case EAGAIN : + case EMFILE : + case ENFILE : + case ETXTBSY : + { + is_busy_err = 1; + } + } + } + else if (preference_errno == PREFERENCE_ERROR_FILE_CHMOD) + { + switch (io_errno) + { + case EINTR : + case EBADF : + { + is_busy_err = 1; + } + } + } + else if (preference_errno == PREFERENCE_ERROR_FILE_LOCK) + { + switch (io_errno) + { + case EBADF : + case EAGAIN : + case ENOLCK : + { + is_busy_err = 1; + } } } + else if (preference_errno == PREFERENCE_ERROR_FILE_WRITE) + { + switch (io_errno) + { + case 0 : + case EAGAIN : + case EINTR : + case EIO : + case ENOMEM : + { + is_busy_err = 1; + } + } + } + else if (preference_errno == PREFERENCE_ERROR_FILE_FREAD) + { + switch (io_errno) + { + case EAGAIN : + case EINTR : + case EIO : + { + is_busy_err = 1; + } + } + } + else + { + is_busy_err = 0; + } - return ret; + if (is_busy_err == 1) { + return 1; + } + else + { + ERR("key(%s), check retry err: %d/(%d/%s).",keynode->keyname, preference_errno, io_errno, strerror(io_errno)); + return 0; + } } -int preference_set_double(const char *key, double value) +static int _preference_set_key_filesys(keynode_t *keynode, int *io_errno) { - char type[2]; - char data[BUF_LEN]; - snprintf(type, 2, "%d", PREFERENCE_TYPE_DOUBLE); - snprintf(data, BUF_LEN, "%f", value); - return _write_data(key, type, data); -} + char path[PREFERENCE_KEY_PATH_LEN] = {0,}; + FILE *fp = NULL; + int ret = -1; + int func_ret = PREFERENCE_ERROR_NONE; + int err_no = 0; + char err_buf[100] = { 0, }; + int is_write_error = 0; + int retry_cnt = 0; + +retry_open : + errno = 0; + err_no = 0; + func_ret = PREFERENCE_ERROR_NONE; + + ret = _preference_get_key_path(keynode->keyname, path); + retv_if(ret != PREFERENCE_ERROR_NONE, ret); + + if( (fp = fopen(path, "r+")) == NULL ) { + func_ret = PREFERENCE_ERROR_FILE_OPEN; + err_no = errno; + goto out_return; + } -int preference_get_double(const char *key, double *value) -{ - char type[2]; - char data[BUF_LEN]; +retry : + errno = 0; + err_no = 0; + func_ret = PREFERENCE_ERROR_NONE; + + ret = _preference_set_write_lock(fileno(fp)); + if(ret == -1) { + func_ret = PREFERENCE_ERROR_FILE_LOCK; + err_no = errno; + ERR("file(%s) lock owner(%d)", + keynode->keyname, + _preference_get_pid_of_file_lock_owner(fileno(fp), F_WRLCK)); + goto out_return; + } - int ret; + /* write key type */ + ret = fwrite((void *)&(keynode->type), sizeof(int), 1, fp); + if(ret <= 0) + { + if(!errno) { + LOGW("number of written items is 0. try again"); + errno = EAGAIN; + } + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_WRITE; + goto out_unlock; + } - if (value == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; + /* write key value */ + switch(keynode->type) + { + case PREFERENCE_TYPE_INT: + ret = fwrite((void *)&(keynode->value.i), sizeof(int), 1, fp); + if(ret <= 0) is_write_error = 1; + break; + case PREFERENCE_TYPE_DOUBLE: + ret = fwrite((void *)&(keynode->value.d), sizeof(double), 1, fp); + if(ret <= 0) is_write_error = 1; + break; + case PREFERENCE_TYPE_BOOLEAN: + ret = fwrite((void *)&(keynode->value.b), sizeof(int), 1, fp); + if(ret <= 0) is_write_error = 1; + break; + case PREFERENCE_TYPE_STRING: + ret = fprintf(fp,"%s",keynode->value.s); + if(ret < strlen(keynode->value.s)) is_write_error = 1; + if (ftruncate(fileno(fp), ret) == -1) { + is_write_error = 1; + } + break; + default : + func_ret = PREFERENCE_ERROR_WRONG_TYPE; + goto out_unlock; + } + if(is_write_error) + { + if(!errno) { + LOGW("number of written items is 0. try again"); + errno = EAGAIN; + } + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_WRITE; + goto out_unlock; } - ret = _read_data(key, type, data); - if (ret == PREFERENCE_ERROR_NONE) { - if (atoi(type) == PREFERENCE_TYPE_DOUBLE) - *value = atof(data); - else { - LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", - PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); - return PREFERENCE_ERROR_INVALID_PARAMETER; + fflush(fp); + +out_unlock : + ret = _preference_set_unlock(fileno(fp)); + if(ret == -1) { + func_ret = PREFERENCE_ERROR_FILE_LOCK; + err_no = errno; + goto out_return; + } + +out_return : + if (func_ret != PREFERENCE_ERROR_NONE) + { + strerror_r(err_no, err_buf, 100); + if (_preference_check_retry_err(keynode, func_ret, err_no, PREFERENCE_OP_SET)) + { + if (retry_cnt < PREFERENCE_ERROR_RETRY_CNT) + { + WARN("_preference_set_key_filesys(%d-%s) step(%d) failed(%d / %s) retry(%d)", keynode->type, keynode->keyname, func_ret, err_no, err_buf, retry_cnt); + retry_cnt++; + usleep((retry_cnt)*PREFERENCE_ERROR_RETRY_SLEEP_UTIME); + + if (fp) + goto retry; + else + goto retry_open; + } + else + { + ERR("_preference_set_key_filesys(%d-%s) step(%d) faild(%d / %s) over the retry count.", + keynode->type, keynode->keyname, func_ret, err_no, err_buf); + } + } + else + { + ERR("_preference_set_key_filesys(%d-%s) step(%d) failed(%d / %s)\n", keynode->type, keynode->keyname, func_ret, err_no, err_buf); + } + } else { + if(retry_cnt > 0) { + DBG("_preference_set_key_filesys ok with retry cnt(%d)", retry_cnt); } } + if (fp) + { + if(func_ret == PREFERENCE_ERROR_NONE) + { + ret = fdatasync(fileno(fp)); + if(ret == -1) { + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_SYNC; + } + } + fclose(fp); + } + *io_errno = err_no; + + return func_ret; +} + +static int _preference_set_key(keynode_t *keynode) +{ + int ret = 0; + int io_errno = 0; + char err_buf[100] = { 0, }; + + ret = _preference_set_key_filesys(keynode, &io_errno); + if (ret == PREFERENCE_ERROR_NONE) + { + g_posix_errno = PREFERENCE_ERROR_NONE; + g_preference_errno = PREFERENCE_ERROR_NONE; + } + else + { + strerror_r(io_errno, err_buf, 100); + ERR("_preference_set_key(%s) step(%d) failed(%d / %s)", keynode->keyname, ret, io_errno, err_buf); + g_posix_errno = io_errno; + g_preference_errno = ret; + } + return ret; } -int preference_set_string(const char *key, const char *value) + +/* + * This function set the integer value of given key + * @param[in] key key + * @param[in] intval integer value to set + * @return 0 on success, -1 on error + */ +API int preference_set_int(const char *key, int intval) { - char type[2]; + START_TIME_CHECK + + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL"); + + int func_ret = PREFERENCE_ERROR_NONE; - snprintf(type, 2, "%d", PREFERENCE_TYPE_STRING); - if (strlen(value) > (BUF_LEN-1)) { - LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", - PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); - return PREFERENCE_ERROR_INVALID_PARAMETER; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); + + func_ret = _preference_keynode_set_keyname(pKeyNode, key); + if(func_ret != PREFERENCE_ERROR_NONE) { + _preference_keynode_free(pKeyNode); + ERR("set key name error"); + return PREFERENCE_ERROR_IO_ERROR; } - return _write_data(key, type, value); + _preference_keynode_set_value_int(pKeyNode, intval); + + if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) { + ERR("preference_set_int(%d) : key(%s/%d) error", getpid(), key, intval); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } else{ + INFO("%s(%d) success", key, intval); + } + + _preference_keynode_free(pKeyNode); + + END_TIME_CHECK + + return func_ret; } -int preference_get_string(const char *key, char **value) +/* +* This function set the boolean value of given key +* @param[in] key key +* @param[in] boolval boolean value to set + (Integer value 1 is 'True', and 0 is 'False') +* @return 0 on success, -1 on error +*/ +API int preference_set_boolean(const char *key, bool boolval) { - char type[2]; - char data[BUF_LEN]; + START_TIME_CHECK - int ret; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL"); - if (value == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; + int func_ret = PREFERENCE_ERROR_NONE; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); + + func_ret = _preference_keynode_set_keyname(pKeyNode, key); + if(func_ret != PREFERENCE_ERROR_NONE) { + _preference_keynode_free(pKeyNode); + ERR("set key name error"); + return PREFERENCE_ERROR_IO_ERROR; } + _preference_keynode_set_value_boolean(pKeyNode, boolval); - ret = _read_data(key, type, data); - if (ret == PREFERENCE_ERROR_NONE) { - if (atoi(type) == PREFERENCE_TYPE_STRING) { - *value = strdup(data); - if (value == NULL) { - LOGE("OUT_OF_MEMORY(0x%08x)", - PREFERENCE_ERROR_OUT_OF_MEMORY); - return PREFERENCE_ERROR_OUT_OF_MEMORY; - } - } else { - LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", - PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); - return PREFERENCE_ERROR_INVALID_PARAMETER; - } + if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) { + ERR("preference_set_boolean(%d) : key(%s/%d) error", getpid(), key, boolval); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } else { + INFO("%s(%d) success", key, boolval); } - return ret; + _preference_keynode_free(pKeyNode); + + END_TIME_CHECK + + return func_ret; } -int preference_set_boolean(const char *key, bool value) +/* + * This function set the double value of given key + * @param[in] key key + * @param[in] dblval double value to set + * @return 0 on success, -1 on error + */ +API int preference_set_double(const char *key, double dblval) { - char type[2]; - char data[BUF_LEN]; - snprintf(type, 2, "%d", PREFERENCE_TYPE_BOOLEAN); - snprintf(data, BUF_LEN, "%d", value); - return _write_data(key, type, data); + START_TIME_CHECK + + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL"); + + int func_ret = PREFERENCE_ERROR_NONE; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); + + func_ret = _preference_keynode_set_keyname(pKeyNode, key); + if(func_ret != PREFERENCE_ERROR_NONE) { + _preference_keynode_free(pKeyNode); + ERR("set key name error"); + return PREFERENCE_ERROR_IO_ERROR; + } + _preference_keynode_set_value_double(pKeyNode, dblval); + + if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) { + ERR("preference_set_double(%d) : key(%s/%f) error", getpid(), key, dblval); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } else { + INFO("%s(%f) success", key, dblval); + } + + _preference_keynode_free(pKeyNode); + + END_TIME_CHECK + + return func_ret; } -int preference_get_boolean(const char *key, bool *value) +/* + * This function set the string value of given key + * @param[in] key key + * @param[in] strval string value to set + * @return 0 on success, -1 on error + */ +API int preference_set_string(const char *key, const char *strval) { - char type[2]; - char data[BUF_LEN]; + START_TIME_CHECK - int ret; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL"); + retvm_if(strval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: value is NULL"); - if (value == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; + int func_ret = PREFERENCE_ERROR_NONE; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); + + func_ret = _preference_keynode_set_keyname(pKeyNode, key); + if(func_ret != PREFERENCE_ERROR_NONE) { + _preference_keynode_free(pKeyNode); + ERR("set key name error"); + return PREFERENCE_ERROR_IO_ERROR; } + _preference_keynode_set_value_string(pKeyNode, strval); - ret = _read_data(key, type, data); - if (ret == PREFERENCE_ERROR_NONE) { - if (atoi(type) == PREFERENCE_TYPE_BOOLEAN) - *value = (bool)atoi(data); - else { - LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", - PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); - return PREFERENCE_ERROR_INVALID_PARAMETER; - } + if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) { + ERR("preference_set_string(%d) : key(%s/%s) error", getpid(), key, strval); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } else { + INFO("%s(%s) success", key, strval); } - return ret; -} + _preference_keynode_free(pKeyNode); + END_TIME_CHECK -/* TODO: below operation is too heavy, let's find the light way to check. */ -int preference_is_existing(const char *key, bool *exist) + return func_ret; +} + +static int _preference_get_key_filesys(keynode_t *keynode, int* io_errno) { - int ret; - char *buf; - char **result; - int rows; - int columns; - char *errmsg; + char path[PREFERENCE_KEY_PATH_LEN] = {0,}; + int ret = -1; + int func_ret = PREFERENCE_ERROR_NONE; + char err_buf[100] = { 0, }; + int err_no = 0; + int type = 0; + FILE *fp = NULL; + int retry_cnt = 0; + int read_size = 0; + +retry_open : + errno = 0; + func_ret = PREFERENCE_ERROR_NONE; + + ret = _preference_get_key_path(keynode->keyname, path); + retv_if(ret != PREFERENCE_ERROR_NONE, ret); + + if( (fp = fopen(path, "r")) == NULL ) { + func_ret = PREFERENCE_ERROR_FILE_OPEN; + err_no = errno; + goto out_return; + } - if (key == NULL || key[0] == '\0' || exist == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; +retry : + err_no = 0; + func_ret = PREFERENCE_ERROR_NONE; + + ret = _preference_set_read_lock(fileno(fp)); + if(ret == -1) { + func_ret = PREFERENCE_ERROR_FILE_LOCK; + err_no = errno; + goto out_return; } - if (pref_db == NULL) { - if (_initialize() != PREFERENCE_ERROR_NONE) { - LOGE("IO_ERROR(0x%08x) : fail to initialize db", - PREFERENCE_ERROR_IO_ERROR); - return PREFERENCE_ERROR_IO_ERROR; + + /* read data type */ + read_size = fread((void*)&type, sizeof(int), 1, fp); + if((read_size <= 0) || (read_size > sizeof(int))) { + if(!ferror(fp)) { + LOGW("number of read items for type is 0 with false ferror. err : %d", errno); + errno = ENODATA; } + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_FREAD; + goto out_unlock; } - /* check data is exist */ - buf = sqlite3_mprintf("SELECT %s FROM %s WHERE %s=%Q;", - PREF_F_KEY_NAME, PREF_TBL_NAME, PREF_F_KEY_NAME, key); + /* read data value */ + switch(type) + { + case PREFERENCE_TYPE_INT: + { + int value_int = 0; + int read_size = 0; + read_size = fread((void*)&value_int, sizeof(int), 1, fp); + if((read_size <= 0) || (read_size > sizeof(int))) { + if(!ferror(fp)) { + LOGW("number of read items for value is wrong. err : %d", errno); + } + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_FREAD; + goto out_unlock; + } else { + _preference_keynode_set_value_int(keynode, value_int); + } - if (buf == NULL) { - LOGE("IO_ERROR(0x%08x) : fail to create query string", - PREFERENCE_ERROR_IO_ERROR); - return PREFERENCE_ERROR_IO_ERROR; + break; + } + case PREFERENCE_TYPE_DOUBLE: + { + double value_dbl = 0; + int read_size = 0; + read_size = fread((void*)&value_dbl, sizeof(double), 1, fp); + if((read_size <= 0) || (read_size > sizeof(double))) { + if(!ferror(fp)) { + LOGW("number of read items for value is wrong. err : %d", errno); + } + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_FREAD; + goto out_unlock; + } else { + _preference_keynode_set_value_double(keynode, value_dbl); + } + + break; + } + case PREFERENCE_TYPE_BOOLEAN: + { + int value_int = 0; + int read_size = 0; + read_size = fread((void*)&value_int, sizeof(int), 1, fp); + if((read_size <= 0) || (read_size > sizeof(int))) { + if(!ferror(fp)) { + LOGW("number of read items for value is wrong. err : %d", errno); + } + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_FREAD; + goto out_unlock; + } else { + _preference_keynode_set_value_boolean(keynode, value_int); + } + + break; + } + case PREFERENCE_TYPE_STRING: + { + char file_buf[BUF_LEN] = {0,}; + char *value = NULL; + int value_size = 0; + + while(fgets(file_buf, sizeof(file_buf), fp)) + { + if(value) { + value_size = value_size + strlen(file_buf); + value = (char *) realloc(value, value_size); + if(value == NULL) { + func_ret = PREFERENCE_ERROR_OUT_OF_MEMORY; + break; + } + strncat(value, file_buf, strlen(file_buf)); + } else { + value_size = strlen(file_buf) + 1; + value = (char *)malloc(value_size); + if(value == NULL) { + func_ret = PREFERENCE_ERROR_OUT_OF_MEMORY; + break; + } + memset(value, 0x00, value_size); + strncpy(value, file_buf, strlen(file_buf)); + } + } + + if(ferror(fp)) { + err_no = errno; + func_ret = PREFERENCE_ERROR_FILE_FGETS; + } else { + if(value) { + _preference_keynode_set_value_string(keynode, value); + } else { + _preference_keynode_set_value_string(keynode, ""); + } + } + if(value) + free(value); + + break; + } + default : + func_ret = PREFERENCE_ERROR_WRONG_TYPE; } - ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); - sqlite3_free(buf); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to read data(%s)", - PREFERENCE_ERROR_IO_ERROR, errmsg); - sqlite3_free(errmsg); - return PREFERENCE_ERROR_IO_ERROR; +out_unlock : + ret = _preference_set_unlock(fileno(fp)); + if(ret == -1) { + func_ret = PREFERENCE_ERROR_FILE_LOCK; + err_no = errno; + goto out_return; } - if (rows > 0) - *exist = true; - else - *exist = false; - sqlite3_free_table(result); - return PREFERENCE_ERROR_NONE; -} +out_return : + if (func_ret != PREFERENCE_ERROR_NONE) + { + strerror_r(err_no, err_buf, 100); + + if (_preference_check_retry_err(keynode, func_ret, err_no, PREFERENCE_OP_GET)) + { + if (retry_cnt < PREFERENCE_ERROR_RETRY_CNT) + { + WARN("_preference_get_key_filesys(%s) step(%d) failed(%d / %s) retry(%d)", + keynode->keyname, func_ret, err_no, err_buf, retry_cnt); + retry_cnt++; + usleep((retry_cnt)*PREFERENCE_ERROR_RETRY_SLEEP_UTIME); + + if (fp) + goto retry; + else + goto retry_open; + } + else + { + ERR("_preference_get_key_filesys(%s) step(%d) faild(%d / %s) over the retry count.", + keynode->keyname, func_ret, err_no, err_buf); + } + } + else + { + ERR("_preference_get_key_filesys(%s) step(%d) failed(%d / %s) retry(%d) ", + keynode->keyname, func_ret, err_no, err_buf, retry_cnt); + } + } else { + if(retry_cnt > 0) { + DBG("preference get filesys ok with retry cnt(%d)", retry_cnt); + } + } -static pref_changed_cb_node_t *_find_node(const char *key) -{ - pref_changed_cb_node_t *tmp_node; + if (fp) + fclose(fp); - if (key == NULL || key[0] == '\0') - return NULL; + *io_errno = err_no; - tmp_node = head; + return func_ret; +} - while (tmp_node) { - if (strcmp(tmp_node->key, key) == 0) - break; - tmp_node = tmp_node->next; +int _preference_get_key(keynode_t *keynode) +{ + int ret = 0; + int io_errno = 0; + char err_buf[100] = {0,}; + + ret = _preference_get_key_filesys(keynode, &io_errno); + if(ret == PREFERENCE_ERROR_NONE) { + g_posix_errno = PREFERENCE_ERROR_NONE; + g_preference_errno = PREFERENCE_ERROR_NONE; + } + else + { + if (io_errno == ENOENT) + ret = PREFERENCE_ERROR_NO_KEY; + else + ret = PREFERENCE_ERROR_IO_ERROR; + + strerror_r(io_errno, err_buf, 100); + ERR("_preference_get_key(%s) step(%d) failed(%d / %s)\n", keynode->keyname, ret, io_errno, err_buf); + g_posix_errno = io_errno; + g_preference_errno = ret; } - return tmp_node; + return ret; } -static int _add_node(const char *key, preference_changed_cb cb, void *user_data) +/* + * This function get the integer value of given key + * @param[in] key key + * @param[out] intval output buffer + * @return 0 on success, -1 on error + */ +API int preference_get_int(const char *key, int *intval) { - pref_changed_cb_node_t *tmp_node; + START_TIME_CHECK - if (key == NULL || key[0] == '\0' || cb == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; - } + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + retvm_if(intval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null"); + + int func_ret = PREFERENCE_ERROR_IO_ERROR; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); - tmp_node = _find_node(key); + _preference_keynode_set_keyname(pKeyNode, key); - if (tmp_node != NULL) { - tmp_node->cb = cb; - tmp_node->user_data = user_data; + func_ret = _preference_get_key(pKeyNode); + + if (func_ret != PREFERENCE_ERROR_NONE) { + ERR("preference_get_int(%d) : key(%s) error", getpid(), key); } else { - tmp_node = - (pref_changed_cb_node_t *)malloc(sizeof(pref_changed_cb_node_t)); - if (tmp_node == NULL) { - LOGE("OUT_OF_MEMORY(0x%08x)", - PREFERENCE_ERROR_OUT_OF_MEMORY); - return PREFERENCE_ERROR_OUT_OF_MEMORY; + *intval = pKeyNode->value.i; + if(pKeyNode->type == PREFERENCE_TYPE_INT) { + INFO("%s(%d) success", key, *intval); + func_ret = PREFERENCE_ERROR_NONE; + } else { + ERR("The type(%d) of keynode(%s) is not INT", pKeyNode->type, pKeyNode->keyname); + func_ret = PREFERENCE_ERROR_INVALID_PARAMETER; } + } - tmp_node->key = strdup(key); - if (tmp_node->key == NULL) { - free(tmp_node); - LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY); - return PREFERENCE_ERROR_OUT_OF_MEMORY; - } + _preference_keynode_free(pKeyNode); - if (head != NULL) - head->prev = tmp_node; - tmp_node->cb = cb; - tmp_node->user_data = user_data; - tmp_node->prev = NULL; - tmp_node->next = head; - head = tmp_node; - } + END_TIME_CHECK - return PREFERENCE_ERROR_NONE; + return func_ret; } -static int _remove_node(const char *key) +/* + * This function get the boolean value of given key + * @param[in] key key + * @param[out] boolval output buffer + * @return 0 on success, -1 on error + */ +API int preference_get_boolean(const char *key, bool *boolval) { - pref_changed_cb_node_t *tmp_node; + START_TIME_CHECK - if (key == NULL || key[0] == '\0') { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; - } + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + retvm_if(boolval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null"); - tmp_node = _find_node(key); + int func_ret = PREFERENCE_ERROR_IO_ERROR; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); - if (tmp_node == NULL) - return PREFERENCE_ERROR_NONE; + _preference_keynode_set_keyname(pKeyNode, key); - if (tmp_node->prev != NULL) - tmp_node->prev->next = tmp_node->next; - else - head = tmp_node->next; + func_ret = _preference_get_key(pKeyNode); - if (tmp_node->next != NULL) - tmp_node->next->prev = tmp_node->prev; + if (func_ret != PREFERENCE_ERROR_NONE) { + ERR("preference_get_boolean(%d) : %s error", getpid(), key); + } else { + *boolval = !!(pKeyNode->value.b); + if(pKeyNode->type == PREFERENCE_TYPE_BOOLEAN) { + INFO("%s(%d) success", key, *boolval); + func_ret = PREFERENCE_ERROR_NONE; + } else { + ERR("The type(%d) of keynode(%s) is not BOOL", pKeyNode->type, pKeyNode->keyname); + func_ret = PREFERENCE_ERROR_INVALID_PARAMETER; + } + } - if (tmp_node->key) - free(tmp_node->key); + _preference_keynode_free(pKeyNode); - free(tmp_node); + END_TIME_CHECK - return PREFERENCE_ERROR_NONE; + return func_ret; } - -static void _remove_all_node(void) +/* + * This function get the double value of given key + * @param[in] key key + * @param[out] dblval output buffer + * @return 0 on success, -1 on error + */ +API int preference_get_double(const char *key, double *dblval) { - pref_changed_cb_node_t *tmp_node; + START_TIME_CHECK - while (head) { - tmp_node = head; - head = tmp_node->next; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + retvm_if(dblval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null"); - if (tmp_node->key) - free(tmp_node->key); + int func_ret = PREFERENCE_ERROR_IO_ERROR; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); - free(tmp_node); + _preference_keynode_set_keyname(pKeyNode, key); + + func_ret = _preference_get_key(pKeyNode); + + if (func_ret != PREFERENCE_ERROR_NONE) { + ERR("preference_get_double(%d) : %s error", getpid(), key); + } else { + *dblval = pKeyNode->value.d; + + if(pKeyNode->type == PREFERENCE_TYPE_DOUBLE) { + INFO("%s(%f) success", key, *dblval); + func_ret = PREFERENCE_ERROR_NONE; + } else { + ERR("The type(%d) of keynode(%s) is not DBL", pKeyNode->type, pKeyNode->keyname); + func_ret = PREFERENCE_ERROR_INVALID_PARAMETER; + } } -} + _preference_keynode_free(pKeyNode); + + END_TIME_CHECK -static void _update_cb(void *data, int action, char const *db_name, - char const *table_name, sqlite_int64 rowid) + return func_ret; +} + +/* + * This function get the string value of given key + * @param[in] key key + * @return pointer of key value on success, NULL on error + */ +API int preference_get_string(const char *key, char **value) { - int ret; - char *buf; - char **result; - int rows; - int columns; - char *errmsg; - pref_changed_cb_node_t *tmp_node; + START_TIME_CHECK - if (action != SQLITE_UPDATE) - return; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + retvm_if(value == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null"); - if (strcmp(table_name, PREF_TBL_NAME) != 0) { - SECURE_LOGE("given table name (%s) is not same", table_name); - return; - } + int func_ret = PREFERENCE_ERROR_IO_ERROR; + keynode_t* pKeyNode = _preference_keynode_new(); + retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail"); - buf = sqlite3_mprintf("SELECT %s FROM %s WHERE rowid='%lld';", - PREF_F_KEY_NAME, PREF_TBL_NAME, rowid); - if (buf == NULL) - return; + _preference_keynode_set_keyname(pKeyNode, key); - ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); - sqlite3_free(buf); - if (ret != SQLITE_OK) { - LOGI("fail to read data(%s)", errmsg); - sqlite3_free(errmsg); - return; - } + char *tempstr = NULL; + + func_ret = _preference_get_key(pKeyNode); + + if (func_ret != PREFERENCE_ERROR_NONE) { + ERR("preference_get_string(%d) : %s error", getpid(), key); + } else { + if(pKeyNode->type == PREFERENCE_TYPE_STRING) + tempstr = pKeyNode->value.s; + else { + ERR("The type(%d) of keynode(%s) is not STR", pKeyNode->type, pKeyNode->keyname); + func_ret = PREFERENCE_ERROR_INVALID_PARAMETER; + } - if (rows == 0) { - sqlite3_free_table(result); - return; + if(tempstr) { + *value = strdup(tempstr); + INFO("%s(%s) success", key, value); + } } - tmp_node = _find_node(result[1]); + _preference_keynode_free(pKeyNode); - if (tmp_node != NULL && tmp_node->cb != NULL) - tmp_node->cb(result[1], tmp_node->user_data); + END_TIME_CHECK - sqlite3_free_table(result); + return func_ret; } - -int preference_remove(const char *key) +/* + * This function unset given key + * @param[in] key key + * @return 0 on success, -1 on error + */ +API int preference_remove(const char *key) { - int ret; - char buf[BUF_LEN]; - bool exist; - sqlite3_stmt *stmt; + START_TIME_CHECK - ret = preference_is_existing(key, &exist); - if (ret != PREFERENCE_ERROR_NONE) - return ret; + char path[PREFERENCE_KEY_PATH_LEN] = {0,}; + int ret = -1; + int err_retry = PREFERENCE_ERROR_RETRY_CNT; + int func_ret = PREFERENCE_ERROR_NONE; - if (!exist) - return PREFERENCE_ERROR_NO_KEY; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + + ret = _preference_get_key_path(key, path); + retvm_if(ret != PREFERENCE_ERROR_NONE, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is not valid"); - snprintf(buf, sizeof(buf), "DELETE FROM %s WHERE %s = ?", - PREF_TBL_NAME, PREF_F_KEY_NAME); + retvm_if(access(path, F_OK) == -1, PREFERENCE_ERROR_NO_KEY, "Error : key(%s) is not exist", key); - ret = sqlite3_prepare(pref_db, buf, -1, &stmt, NULL); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); + do { + ret = remove(path); + if(ret == -1) { + ERR("preference_remove() failed. ret=%d(%s), key(%s)", errno, strerror(errno), key); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } else { + func_ret = PREFERENCE_ERROR_NONE; + break; + } + } while(err_retry--); + + END_TIME_CHECK + + return func_ret; +} + +API int preference_remove_all(void) +{ + START_TIME_CHECK + + int ret = -1; + int err_retry = PREFERENCE_ERROR_RETRY_CNT; + int func_ret = PREFERENCE_ERROR_NONE; + DIR *dir; + struct dirent *dent = NULL; + char *pref_dir_path = NULL; + + pref_dir_path = _preference_get_pref_dir_path(); + if (!pref_dir_path) + { + LOGE("_preference_get_pref_dir_path() failed."); return PREFERENCE_ERROR_IO_ERROR; } - ret = sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - sqlite3_finalize(stmt); + dir = opendir(pref_dir_path); + if (dir == NULL) + { + LOGE("opendir() failed. pref_path: %s, error: %d(%s)", pref_dir_path, errno, strerror(errno)); return PREFERENCE_ERROR_IO_ERROR; } - ret = sqlite3_step(stmt); - if (ret != SQLITE_DONE) { - LOGE("IO_ERROR(0x%08x): fail to delete data(%d/%s)", - PREFERENCE_ERROR_IO_ERROR, - sqlite3_extended_errcode(pref_db), - sqlite3_errmsg(pref_db)); - sqlite3_finalize(stmt); - return PREFERENCE_ERROR_IO_ERROR; + while ((dent = readdir(dir))) + { + const char *entry = dent->d_name; + char keyname[PREFERENCE_KEY_PATH_LEN] = {0,}; + char path[PREFERENCE_KEY_PATH_LEN] = {0,}; + + if (entry[0] == '.') + { + continue; + } + + ret = _preference_get_key_name(entry, keyname); + if (ret != PREFERENCE_ERROR_NONE) + { + ERR("_preference_get_key_name() failed(%d)", ret); + closedir(dir); + return ret; + } + + ret = preference_unset_changed_cb(keyname); + if (ret != PREFERENCE_ERROR_NONE) + { + ERR("preference_unset_changed_cb() failed(%d)", ret); + closedir(dir); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = _preference_get_key_path(keyname, path); + if (ret != PREFERENCE_ERROR_NONE) + { + ERR("_preference_get_key_path() failed(%d)", ret); + closedir(dir); + return ret; + } + + // delete + do { + ret = remove(path); + if(ret == -1) { + ERR("preference_remove_all error: %d(%s)", errno, strerror(errno)); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } else { + func_ret = PREFERENCE_ERROR_NONE; + break; + } + } while(err_retry--); } - sqlite3_finalize(stmt); + closedir(dir); - _remove_node(key); + END_TIME_CHECK - return PREFERENCE_ERROR_NONE; + return func_ret; } - -int preference_remove_all(void) +int preference_is_existing(const char *key, bool *exist) { - int ret; - char *buf; - char *errmsg; + START_TIME_CHECK - if (pref_db == NULL) { - if (_initialize() != PREFERENCE_ERROR_NONE) { - LOGE("IO_ERROR(0x%08x) : fail to initialize db", - PREFERENCE_ERROR_IO_ERROR); - return PREFERENCE_ERROR_IO_ERROR; - } - } + char path[PREFERENCE_KEY_PATH_LEN] = {0,}; + int ret = -1; + int func_ret = PREFERENCE_ERROR_NONE; - /* insert data or update data if data already exist */ - buf = sqlite3_mprintf("DELETE FROM %s;", PREF_TBL_NAME); - if (buf == NULL) { - LOGE("IO_ERROR(0x%08x) : fail to create query string", - PREFERENCE_ERROR_IO_ERROR); - return PREFERENCE_ERROR_IO_ERROR; - } + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + retvm_if(exist == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); - ret = sqlite3_exec(pref_db, buf, NULL, NULL, &errmsg); - sqlite3_free(buf); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to delete data (%s)", - PREFERENCE_ERROR_IO_ERROR, errmsg); - sqlite3_free(errmsg); - return PREFERENCE_ERROR_IO_ERROR; + ret = _preference_get_key_path(key, path); + retv_if(ret != PREFERENCE_ERROR_NONE, ret); + + ret = access(path, F_OK); + if (ret == -1) + { + ERR("Error : key(%s) is not exist", key); + *exist = 0; + } + else + { + *exist = 1; } - _remove_all_node(); + END_TIME_CHECK - return PREFERENCE_ERROR_NONE; + return func_ret; } -int preference_set_changed_cb(const char *key, - preference_changed_cb callback, void *user_data) +API int preference_set_changed_cb(const char *key, preference_changed_cb callback, void *user_data) { - int ret; + START_TIME_CHECK + + int ret = -1; bool exist; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + retvm_if(callback == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: cb(%p)", callback); + ret = preference_is_existing(key, &exist); if (ret != PREFERENCE_ERROR_NONE) + { return ret; + } - if (!exist) { - LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", - PREFERENCE_ERROR_NO_KEY, key); + if (!exist) + { + LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key); return PREFERENCE_ERROR_NO_KEY; } - if (!is_update_hook_registered) { - sqlite3_update_hook(pref_db, _update_cb, NULL); - is_update_hook_registered = true; + if (_preference_kdb_add_notify(key, callback, user_data)) { + if(errno != 0 && errno != ENOENT) { + ERR("preference_notify_key_changed : key(%s) add notify fail", key); + return PREFERENCE_ERROR_IO_ERROR; + } } + INFO("%s noti is added", key); + + END_TIME_CHECK - return _add_node(key, callback, user_data); + return PREFERENCE_ERROR_NONE; } -int preference_unset_changed_cb(const char *key) +API int preference_unset_changed_cb(const char *key) { - int ret; + START_TIME_CHECK + + int ret = -1; bool exist; + retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null"); + ret = preference_is_existing(key, &exist); if (ret != PREFERENCE_ERROR_NONE) + { return ret; + } - if (!exist) { - LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", - PREFERENCE_ERROR_NO_KEY, key); + if (!exist) + { + LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key); return PREFERENCE_ERROR_NO_KEY; } - return _remove_node(key); + if (_preference_kdb_del_notify(key)) { + if (errno != 0 && errno != ENOENT) { + ERR("preference_unset_changed_cb() failed: key(%s) error(%d/%s)", key, errno, strerror(errno)); + return PREFERENCE_ERROR_IO_ERROR; + } + } + INFO("%s noti removed", key); + + END_TIME_CHECK + + return PREFERENCE_ERROR_NONE; } -int preference_foreach_item(preference_item_cb callback, void *user_data) + +API int preference_foreach_item(preference_item_cb callback, void *user_data) { - int ret; - char *buf; - char **result; - int rows; - int columns; - char *errmsg; - int i; + START_TIME_CHECK - if (callback == NULL) { - LOGE("INVALID_PARAMETER(0x%08x)", - PREFERENCE_ERROR_INVALID_PARAMETER); - return PREFERENCE_ERROR_INVALID_PARAMETER; - } + retvm_if(callback == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: cb(%p)", callback); - if (pref_db == NULL) { - if (_initialize() != PREFERENCE_ERROR_NONE) { - LOGE("IO_ERROR(0x%08x) : fail to initialize db", - PREFERENCE_ERROR_IO_ERROR); - return PREFERENCE_ERROR_IO_ERROR; - } - } + int ret = 0; + DIR *dir; + struct dirent *dent = NULL; + char *pref_dir_path = NULL; - buf = sqlite3_mprintf("SELECT %s FROM %s;", - PREF_F_KEY_NAME, PREF_TBL_NAME); - if (buf == NULL) { - LOGE("IO_ERROR(0x%08x) : fail to create query string", - PREFERENCE_ERROR_IO_ERROR); + pref_dir_path = _preference_get_pref_dir_path(); + if (!pref_dir_path) + { + LOGE("_preference_get_pref_dir_path() failed."); return PREFERENCE_ERROR_IO_ERROR; } - ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); - sqlite3_free(buf); - if (ret != SQLITE_OK) { - LOGE("IO_ERROR(0x%08x) : fail to read data (%s)", - PREFERENCE_ERROR_IO_ERROR, errmsg); - sqlite3_free(errmsg); + dir = opendir(pref_dir_path); + if (dir == NULL) + { + LOGE("opendir() failed. path: %s, error: %d(%s)", pref_dir_path, errno, strerror(errno)); return PREFERENCE_ERROR_IO_ERROR; } - for (i = 1; i <= rows; i++) { - if (callback(result[i], user_data) != true) - break; + while((dent = readdir(dir))) + { + const char *entry = dent->d_name; + char keyname[PREFERENCE_KEY_PATH_LEN] = {0,}; + + if (entry[0] == '.') + { + continue; + } + + ret = _preference_get_key_name(entry, keyname); + retv_if(ret != PREFERENCE_ERROR_NONE, ret); + + callback(keyname, user_data); } - sqlite3_free_table(result); + closedir(dir); + END_TIME_CHECK return PREFERENCE_ERROR_NONE; } diff --git a/preference/preference_db.c b/preference/preference_db.c new file mode 100755 index 0000000..31e9e9f --- /dev/null +++ b/preference/preference_db.c @@ -0,0 +1,915 @@ +/* + * Copyright (c) 2015 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sqlite3.h> + +#include <app_internal.h> + +#include <app_preference.h> +#include <app_preference_internal.h> + +#include <dlog.h> + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "CAPI_APPFW_APPLICATION_PREFERENCE" +#define DBG_MODE (1) + +static sqlite3 *pref_db = NULL; +static bool is_update_hook_registered = false; +static pref_changed_cb_node_t *head = NULL; + +static void _finish(void *data) +{ + if (pref_db != NULL) + { + sqlite3_close(pref_db); + pref_db = NULL; + } +} + +static int _busy_handler(void *pData, int count) +{ + if(5 - count > 0) { + LOGD("Busy Handler Called! : PID(%d) / CNT(%d)\n", getpid(), count+1); + usleep((count+1)*100000); + return 1; + } else { + LOGD("Busy Handler will be returned SQLITE_BUSY error : PID(%d) \n", getpid()); + return 0; + } +} + +static int _initialize(void) +{ + char *data_path = NULL; + char db_path[TIZEN_PATH_MAX] = {0, }; + int ret; + char *errmsg; + + if ((data_path = app_get_data_path()) == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to get data directory", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + snprintf(db_path, sizeof(db_path), "%s/%s", data_path, PREF_DB_NAME); + free(data_path); + + ret = sqlite3_open(db_path, &pref_db); + if (ret != SQLITE_OK) + { + LOGE("IO_ERROR(0x%08x) : fail to open db(%s)", PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db)); + pref_db = NULL; + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_busy_handler(pref_db, _busy_handler, NULL); + if (ret != SQLITE_OK) { + LOGW("IO_ERROR(0x%08x) : fail to register busy handler(%s)\n", PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db)); + } + + ret = sqlite3_exec(pref_db, "CREATE TABLE IF NOT EXISTS pref ( pref_key TEXT PRIMARY KEY, pref_type TEXT, pref_data TEXT)", + NULL, NULL, &errmsg); + if (ret != SQLITE_OK) + { + LOGE("IO_ERROR(0x%08x) : fail to create db table(%s)", PREFERENCE_ERROR_IO_ERROR, errmsg); + sqlite3_free(errmsg); + sqlite3_close(pref_db); + pref_db = NULL; + return PREFERENCE_ERROR_IO_ERROR; + } + + app_finalizer_add(_finish, NULL); + + return PREFERENCE_ERROR_NONE; +} + +static int _write_data(const char *key, const char *type, const char *data) +{ + int ret; + char *buf = NULL; + bool exist = false; + sqlite3_stmt *stmt; + + if (key == NULL || key[0] == '\0' || data == NULL) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + /* insert data or update data if data already exist */ + ret = preference_is_existing(key, &exist); + if (ret != PREFERENCE_ERROR_NONE) + { + return ret; + } + + // to use sqlite3_update_hook, we have to use INSERT/UPDATE operation instead of REPLACE operation + if (exist) + { + buf = sqlite3_mprintf("UPDATE %s SET %s=?, %s=? WHERE %s=?;", + PREF_TBL_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME, PREF_F_KEY_NAME); + } + else + { + buf = sqlite3_mprintf("INSERT INTO %s (%s, %s, %s) values (?, ?, ?);", + PREF_TBL_NAME, PREF_F_KEY_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME); + } + + if (buf == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_prepare(pref_db, buf, strlen(buf), &stmt, NULL); + if (ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + return PREFERENCE_ERROR_IO_ERROR; + } + + if(exist) + { + ret = sqlite3_bind_text(stmt, 1, type, strlen(type), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + ret = sqlite3_bind_text(stmt, 2, data, strlen(data), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(2) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + ret = sqlite3_bind_text(stmt, 3, key, strlen(key), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(3) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + } + else + { + ret = sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + ret = sqlite3_bind_text(stmt, 2, type, strlen(type), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(2) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + ret = sqlite3_bind_text(stmt, 3, data, strlen(data), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(3) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + } + + ret = sqlite3_step(stmt); + if (ret != SQLITE_DONE) { + LOGE("IO_ERROR(0x%08x): fail to write data(%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + + sqlite3_finalize(stmt); + if(buf) { + sqlite3_free(buf); + buf = NULL; + } + + return PREFERENCE_ERROR_NONE; +} + +//static int _read_data(const char *key, preference_type_e *type, char *data) +static int _read_data(const char *key, char *type, char *data) +{ + int ret; + char *buf; + char **result; + int rows; + int columns; + char *errmsg; + + if (key == NULL || key[0] == '\0' || data == NULL) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + if (pref_db == NULL) + { + if (_initialize() != PREFERENCE_ERROR_NONE) + { + LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + } + + buf = sqlite3_mprintf("SELECT %s, %s, %s FROM %s WHERE %s=%Q;", + PREF_F_KEY_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME, PREF_TBL_NAME, PREF_F_KEY_NAME, key); + + if (buf == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); + sqlite3_free(buf); + if (ret != SQLITE_OK) + { + LOGE("IO_ERROR(0x%08x) : fail to read data (%s)", PREFERENCE_ERROR_IO_ERROR, errmsg); + sqlite3_free(errmsg); + return PREFERENCE_ERROR_IO_ERROR; + } + + if (rows == 0) + { + LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key); + sqlite3_free_table(result); + return PREFERENCE_ERROR_NO_KEY; + } + + snprintf(type, 2, "%s", result[4]); // get type value + snprintf(data, BUF_LEN, "%s", result[5]); // get data value + + sqlite3_free_table(result); + + return PREFERENCE_ERROR_NONE; +} + + +int preference_set_int(const char *key, int value) +{ + char type[2]; + char data[BUF_LEN]; + snprintf(type, 2, "%d", PREFERENCE_TYPE_INT); + snprintf(data, BUF_LEN, "%d", value); + return _write_data(key, type, data); +} + +int preference_get_int(const char *key, int *value) +{ + char type[2]; + char data[BUF_LEN]; + int ret; + + if (value == NULL) { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + ret = _read_data(key, type, data); + if (ret == PREFERENCE_ERROR_NONE) + { + if (atoi(type) == PREFERENCE_TYPE_INT) + { + *value = atoi(data); + } + else + { + LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + } + + return ret; +} + +int preference_set_double(const char *key, double value) +{ + char type[2]; + char data[BUF_LEN]; + + locale_t loc = newlocale(LC_NUMERIC_MASK, "C", NULL); + uselocale(loc); + + snprintf(type, 2, "%d", PREFERENCE_TYPE_DOUBLE); + snprintf(data, BUF_LEN, "%f", value); + + freelocale(loc); + uselocale(LC_GLOBAL_LOCALE); + + return _write_data(key, type, data); +} + +int preference_get_double(const char *key, double *value) +{ + char type[2]; + char data[BUF_LEN]; + + int ret; + + if (value == NULL) { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + ret = _read_data(key, type, data); + if (ret == PREFERENCE_ERROR_NONE) + { + if (atoi(type) == PREFERENCE_TYPE_DOUBLE) + { + locale_t loc = newlocale(LC_NUMERIC_MASK, "C", NULL); + uselocale(loc); + + *value = atof(data); + + freelocale(loc); + uselocale(LC_GLOBAL_LOCALE); + } + else + { + LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + } + + return ret; +} + +int preference_set_string(const char *key, const char *value) +{ + char type[2]; + + snprintf(type, 2, "%d", PREFERENCE_TYPE_STRING); + if (strlen(value) > (BUF_LEN-1)) + { + LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + return _write_data(key, type, value); +} + +int preference_get_string(const char *key, char **value) +{ + char type[2]; + char data[BUF_LEN]; + + int ret; + + if (value == NULL) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + ret = _read_data(key, type, data); + if (ret == PREFERENCE_ERROR_NONE) + { + if (atoi(type) == PREFERENCE_TYPE_STRING) + { + *value = strdup(data); + if (value == NULL) + { + LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY); + return PREFERENCE_ERROR_OUT_OF_MEMORY; + } + } + else + { + LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + } + + return ret; +} + +int preference_set_boolean(const char *key, bool value) +{ + char type[2]; + char data[BUF_LEN]; + snprintf(type, 2, "%d", PREFERENCE_TYPE_BOOLEAN); + snprintf(data, BUF_LEN, "%d", value); + return _write_data(key, type, data); +} + +int preference_get_boolean(const char *key, bool *value) +{ + char type[2]; + char data[BUF_LEN]; + + int ret; + + if (value == NULL) { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + ret = _read_data(key, type, data); + if (ret == PREFERENCE_ERROR_NONE) + { + if (atoi(type) == PREFERENCE_TYPE_BOOLEAN) + { + *value = (bool)atoi(data); + } + else + { + LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type)); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + } + + return ret; +} + + +// TODO: below operation is too heavy, let's find the light way to check. +int preference_is_existing(const char *key, bool *exist) +{ + int ret; + char *buf; + char **result; + int rows; + int columns; + char *errmsg; + + if (key == NULL || key[0] == '\0' || exist == NULL) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + if (pref_db == NULL) + { + if (_initialize() != PREFERENCE_ERROR_NONE) + { + LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + } + + /* check data is exist */ + buf = sqlite3_mprintf("SELECT %s FROM %s WHERE %s=%Q;", PREF_F_KEY_NAME, PREF_TBL_NAME, PREF_F_KEY_NAME, key); + + if (buf == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); + sqlite3_free(buf); + if (ret != SQLITE_OK) + { + LOGE("IO_ERROR(0x%08x) : fail to read data(%s)", PREFERENCE_ERROR_IO_ERROR, errmsg); + sqlite3_free(errmsg); + return PREFERENCE_ERROR_IO_ERROR; + } + + if (rows > 0) + { + *exist = true; + } + else + { + *exist = false; + } + + sqlite3_free_table(result); + return PREFERENCE_ERROR_NONE; +} + +static pref_changed_cb_node_t* _find_node(const char *key) +{ + pref_changed_cb_node_t *tmp_node; + + if (key == NULL || key[0] == '\0' ) + { + return NULL; + } + + tmp_node = head; + + while (tmp_node) + { + if (strcmp(tmp_node->key, key) == 0) + { + break; + } + tmp_node = tmp_node->next; + } + + return tmp_node; +} + + +static int _add_node(const char *key, preference_changed_cb cb, void *user_data) +{ + pref_changed_cb_node_t *tmp_node; + + if (key == NULL || key[0] == '\0' || cb == NULL) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + tmp_node = _find_node(key); + + if (tmp_node != NULL) + { + tmp_node->cb = cb; + tmp_node->user_data = user_data; + } + else + { + tmp_node = (pref_changed_cb_node_t*)malloc(sizeof(pref_changed_cb_node_t)); + if (tmp_node == NULL) + { + LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY); + return PREFERENCE_ERROR_OUT_OF_MEMORY; + } + + tmp_node->key = strdup(key); + if (tmp_node->key == NULL) + { + free(tmp_node); + LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY); + return PREFERENCE_ERROR_OUT_OF_MEMORY; + } + + if (head != NULL) + head->prev = tmp_node; + tmp_node->cb = cb; + tmp_node->user_data = user_data; + tmp_node->prev = NULL; + tmp_node->next = head; + head = tmp_node; + } + + return PREFERENCE_ERROR_NONE; +} + +static int _remove_node(const char *key) +{ + pref_changed_cb_node_t *tmp_node; + + if (key == NULL || key[0] == '\0' ) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + tmp_node = _find_node(key); + + if (tmp_node == NULL) + { + return PREFERENCE_ERROR_NONE; + } + + if (tmp_node->prev != NULL) + { + tmp_node->prev->next = tmp_node->next; + } + else + { + head = tmp_node->next; + } + + if (tmp_node->next != NULL) + { + tmp_node->next->prev = tmp_node->prev; + } + + if (tmp_node->key) + { + free(tmp_node->key); + } + + free(tmp_node); + + return PREFERENCE_ERROR_NONE; +} + + +static void _remove_all_node(void) +{ + pref_changed_cb_node_t *tmp_node; + + while (head) + { + tmp_node = head; + head = tmp_node->next; + + if (tmp_node->key) + { + free(tmp_node->key); + } + + free(tmp_node); + } +} + + +static void _update_cb(void *data, int action, char const *db_name, char const *table_name, sqlite_int64 rowid) +{ + int ret; + char *buf; + char **result; + int rows; + int columns; + char *errmsg; + pref_changed_cb_node_t *tmp_node; + + // skip INSERT/DELETE event + if (action != SQLITE_UPDATE) + { + return; + } + + if (strcmp(table_name, PREF_TBL_NAME) != 0) + { + SECURE_LOGE("given table name (%s) is not same", table_name); + return; + } + + buf = sqlite3_mprintf("SELECT %s FROM %s WHERE rowid='%lld';", PREF_F_KEY_NAME, PREF_TBL_NAME, rowid); + if (buf == NULL) + { + return; + } + ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); + sqlite3_free(buf); + if (ret != SQLITE_OK) + { + LOGI("fail to read data(%s)", errmsg); + sqlite3_free(errmsg); + return; + } + + if (rows == 0) + { + sqlite3_free_table(result); + return; + } + + tmp_node = _find_node(result[1]); + + if (tmp_node != NULL && tmp_node->cb != NULL) + { + tmp_node->cb(result[1], tmp_node->user_data); + } + + sqlite3_free_table(result); +} + + +int preference_remove(const char *key) +{ + int ret; + char *buf; + bool exist; + sqlite3_stmt *stmt; + + ret = preference_is_existing(key, &exist); + if (ret != PREFERENCE_ERROR_NONE) + { + return ret; + } + + if (!exist) + { + return PREFERENCE_ERROR_NO_KEY; + } + + /* insert data or update data if data already exist */ + buf = sqlite3_mprintf("DELETE FROM %s WHERE %s = ?", + PREF_TBL_NAME, PREF_F_KEY_NAME, key); + if (buf == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_prepare(pref_db, buf, strlen(buf), &stmt, NULL); + if (ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_STATIC); + if(ret != SQLITE_OK) { + LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_step(stmt); + if (ret != SQLITE_DONE) { + LOGE("IO_ERROR(0x%08x): fail to delete data(%d/%s)", + PREFERENCE_ERROR_IO_ERROR, + sqlite3_extended_errcode(pref_db), + sqlite3_errmsg(pref_db)); + sqlite3_finalize(stmt); + return PREFERENCE_ERROR_IO_ERROR; + } + + sqlite3_finalize(stmt); + if(buf) { + sqlite3_free(buf); + buf = NULL; + } + + // if exist, remove changed cb + _remove_node(key); + + return PREFERENCE_ERROR_NONE; +} + + +int preference_remove_all(void) +{ + int ret; + char *buf; + char *errmsg; + + if (pref_db == NULL) + { + if (_initialize() != PREFERENCE_ERROR_NONE) + { + LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + } + + /* insert data or update data if data already exist */ + buf = sqlite3_mprintf("DELETE FROM %s;", PREF_TBL_NAME); + if (buf == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_exec(pref_db, buf, NULL, NULL, &errmsg); + sqlite3_free(buf); + if (ret != SQLITE_OK) + { + LOGE("IO_ERROR(0x%08x) : fail to delete data (%s)", PREFERENCE_ERROR_IO_ERROR, errmsg); + sqlite3_free(errmsg); + return PREFERENCE_ERROR_IO_ERROR; + } + + // if exist, remove changed cb + _remove_all_node(); + + return PREFERENCE_ERROR_NONE; +} + + +int preference_set_changed_cb(const char *key, preference_changed_cb callback, void *user_data) +{ + int ret; + bool exist; + + ret = preference_is_existing(key, &exist); + if (ret != PREFERENCE_ERROR_NONE) + { + return ret; + } + + if (!exist) + { + LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key); + return PREFERENCE_ERROR_NO_KEY; + } + + if (!is_update_hook_registered) + { + sqlite3_update_hook(pref_db, _update_cb, NULL); + is_update_hook_registered = true; + } + + return _add_node(key, callback, user_data); +} + +int preference_unset_changed_cb(const char *key) +{ + int ret; + bool exist; + + ret = preference_is_existing(key, &exist); + if (ret != PREFERENCE_ERROR_NONE) + { + return ret; + } + + if (!exist) + { + LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key); + return PREFERENCE_ERROR_NO_KEY; + } + + return _remove_node(key); +} + +int preference_foreach_item(preference_item_cb callback, void *user_data) +{ + int ret; + char *buf; + char **result; + int rows; + int columns; + char *errmsg; + int i; + + if (callback == NULL) + { + LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER); + return PREFERENCE_ERROR_INVALID_PARAMETER; + } + + if (pref_db == NULL) + { + if (_initialize() != PREFERENCE_ERROR_NONE) + { + LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + } + + buf = sqlite3_mprintf("SELECT %s FROM %s;", PREF_F_KEY_NAME, PREF_TBL_NAME); + if (buf == NULL) + { + LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg); + sqlite3_free(buf); + if (ret != SQLITE_OK) + { + LOGE("IO_ERROR(0x%08x) : fail to read data (%s)", PREFERENCE_ERROR_IO_ERROR, errmsg); + sqlite3_free(errmsg); + return PREFERENCE_ERROR_IO_ERROR; + } + + for (i = 1; i <= rows; i++) + { + if (callback(result[i], user_data) != true) + { + break; + } + } + + sqlite3_free_table(result); + + return PREFERENCE_ERROR_NONE; +} + diff --git a/preference/preference_inoti.c b/preference/preference_inoti.c new file mode 100755 index 0000000..dbdea63 --- /dev/null +++ b/preference/preference_inoti.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2015 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 <sys/types.h> +#include <sys/inotify.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <linux/version.h> +#include <stdlib.h> +#include <pthread.h> +#include <glib.h> + +#include <app_preference.h> +#include <app_preference_internal.h> + +#include <glib.h> + +#define INOTY_EVENT_MASK (IN_CLOSE_WRITE | IN_DELETE_SELF) + +/* inotify */ +struct noti_node { + int wd; + char *keyname; + preference_changed_cb cb; + void *cb_data; + struct noti_node *next; +}; +typedef struct noti_node noti_node_s; +static GList *g_notilist; + +static int _preference_inoti_comp_with_wd(gconstpointer a, gconstpointer b) +{ + int r; + + noti_node_s *key1 = (noti_node_s *) a; + noti_node_s *key2 = (noti_node_s *) b; + + r = key1->wd - key2->wd; + return r; +} + +static int _kdb_inoti_fd; + +static pthread_mutex_t _kdb_inoti_fd_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t _kdb_g_ns_mutex = PTHREAD_MUTEX_INITIALIZER; + +static GSource *_kdb_handler; + +static GList* _preference_copy_noti_list(GList *orig_notilist) +{ + GList *copy_notilist = NULL; + struct noti_node *n = NULL; + struct noti_node *t = NULL; + + if (!orig_notilist) + return NULL; + + orig_notilist = g_list_first(orig_notilist); + if (!orig_notilist) + return NULL; + + while (orig_notilist) { + do { + t = orig_notilist->data; + + if (t == NULL) { + WARN("noti item data is null"); + break; + } + + if ((t->keyname == NULL) || (strlen(t->keyname) == 0)) { + WARN("noti item data key name is null"); + break; + } + + n = calloc(1, sizeof(noti_node_s)); + if (n == NULL) { + ERR("_preference_copy_noti_list : calloc failed. memory full"); + break; + } + + n->keyname = strndup(t->keyname, PREFERENCE_KEY_PATH_LEN); + if (n->keyname == NULL) + { + ERR("The memory is insufficient, errno: %d (%s)", errno, strerror(errno)); + free(n); + break; + } + n->wd = t->wd; + n->cb_data = t->cb_data; + n->cb = t->cb; + + copy_notilist = g_list_append(copy_notilist, n); + } while (0); + + orig_notilist = g_list_next(orig_notilist); + } + return copy_notilist; +} + +static void _preference_free_noti_node(gpointer data) +{ + struct noti_node *n = (struct noti_node*)data; + g_free(n->keyname); + g_free(n); +} + +static void _preference_free_noti_list(GList *noti_list) +{ + g_list_free_full(noti_list, _preference_free_noti_node); +} + + +static gboolean _preference_kdb_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int fd, r, res; + struct inotify_event ie; + GList *l_notilist = NULL; + + fd = g_io_channel_unix_get_fd(src); + r = read(fd, &ie, sizeof(ie)); + + while (r > 0) { + if (ie.mask & INOTY_EVENT_MASK) { + + INFO("read event from GIOChannel. wd : %d", ie.wd); + + pthread_mutex_lock(&_kdb_g_ns_mutex); + l_notilist = _preference_copy_noti_list(g_notilist); + pthread_mutex_unlock(&_kdb_g_ns_mutex); + + if (l_notilist) { + + struct noti_node *t = NULL; + GList *noti_list = NULL; + + noti_list = g_list_first(l_notilist); + + while (noti_list) { + t = noti_list->data; + + keynode_t* keynode = _preference_keynode_new(); + if (keynode == NULL) { + ERR("key malloc fail"); + break; + } + + if ( (t) && (t->wd == ie.wd) && (t->keyname) ) { + if ((ie.mask & IN_DELETE_SELF)) + { + res = _preference_kdb_del_notify(t->keyname); + if (res != PREFERENCE_ERROR_NONE) + ERR("_preference_kdb_del_notify() failed(%d)", res); + } + else + { + res = _preference_keynode_set_keyname(keynode, t->keyname); + if (res != PREFERENCE_ERROR_NONE) { + ERR("_preference_keynode_set_keyname() failed(%d)", res); + goto out_func; + } + + res = _preference_get_key(keynode); + if (res != PREFERENCE_ERROR_NONE) + ERR("_preference_get_key() failed(%d)", res); + + INFO("key(%s) is changed. cb(%p) called", t->keyname, t->cb); + t->cb(t->keyname, t->cb_data); + } + } + else if ( (t) && (t->keyname == NULL) ) { /* for debugging */ + ERR("preference keyname is null."); + } +out_func: + _preference_keynode_free(keynode); + + noti_list = g_list_next(noti_list); + } + + _preference_free_noti_list(l_notilist); + } + } + + if (ie.len > 0) + (void) lseek(fd, ie.len, SEEK_CUR); + + r = read(fd, &ie, sizeof(ie)); + } + return TRUE; +} + +static int _preference_kdb_noti_init(void) +{ + GIOChannel *gio; + int ret = 0; + + pthread_mutex_lock(&_kdb_inoti_fd_mutex); + + if (0 < _kdb_inoti_fd) { + ERR("Error: invalid _kdb_inoti_fd"); + pthread_mutex_unlock(&_kdb_inoti_fd_mutex); + return PREFERENCE_ERROR_IO_ERROR; + } + _kdb_inoti_fd = inotify_init(); + if (_kdb_inoti_fd == -1) { + char err_buf[100] = { 0, }; + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("inotify init: %s", err_buf); + pthread_mutex_unlock(&_kdb_inoti_fd_mutex); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = fcntl(_kdb_inoti_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + char err_buf[100] = { 0, }; + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("inotify init: %s", err_buf); + pthread_mutex_unlock(&_kdb_inoti_fd_mutex); + return PREFERENCE_ERROR_IO_ERROR; + } + + ret = fcntl(_kdb_inoti_fd, F_SETFL, O_NONBLOCK); + if (ret < 0) { + char err_buf[100] = { 0, }; + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("inotify init: %s", err_buf); + pthread_mutex_unlock(&_kdb_inoti_fd_mutex); + return PREFERENCE_ERROR_IO_ERROR; + } + + pthread_mutex_unlock(&_kdb_inoti_fd_mutex); + + gio = g_io_channel_unix_new(_kdb_inoti_fd); + retvm_if(gio == NULL, -1, "Error: create a new GIOChannel"); + + g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL); + + _kdb_handler = g_io_create_watch(gio, G_IO_IN); + g_source_set_callback(_kdb_handler, (GSourceFunc) _preference_kdb_gio_cb, NULL, NULL); + g_source_attach(_kdb_handler, NULL); + g_io_channel_unref(gio); + g_source_unref(_kdb_handler); + + return PREFERENCE_ERROR_NONE; +} + +int _preference_kdb_add_notify(const char *keyname, preference_changed_cb cb, void *data) +{ + char path[PREFERENCE_KEY_PATH_LEN]; + int wd; + struct noti_node t, *n, *node; + char err_buf[ERR_LEN] = { 0, }; + int ret = 0; + GList *list = NULL; + int func_ret = PREFERENCE_ERROR_NONE; + + retvm_if((keyname == NULL || cb == NULL), PREFERENCE_ERROR_INVALID_PARAMETER, + "_preference_kdb_add_notify : Invalid argument - keyname(%s) cb(%p)", + keyname, cb); + + if (_kdb_inoti_fd <= 0) + if (_preference_kdb_noti_init()) + return PREFERENCE_ERROR_IO_ERROR; + + ret = _preference_get_key_path((char*)keyname, path); + retvm_if(ret != PREFERENCE_ERROR_NONE, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is not valid"); + + if (0 != access(path, F_OK)) { + if (errno == ENOENT) { + ERR("_preference_kdb_add_notify : Key(%s) does not exist", keyname); + return PREFERENCE_ERROR_IO_ERROR; + } + } + + wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK); + if (wd == -1) { + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("_preference_kdb_add_notify : add noti(%s)", err_buf); + return PREFERENCE_ERROR_IO_ERROR; + } + + t.wd = wd; + + pthread_mutex_lock(&_kdb_g_ns_mutex); + + list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd); + if (list) { + WARN("_preference_kdb_add_notify : key(%s) change callback(%p)", keyname, cb); + + node = list->data; + node->wd = wd; + node->cb_data = data; + node->cb = cb; + + goto out_func; + } + + n = calloc(1, sizeof(noti_node_s)); + if (n == NULL) { + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("_preference_kdb_add_notify : add noti(%s)", err_buf); + func_ret = PREFERENCE_ERROR_IO_ERROR; + goto out_func; + } + + n->keyname = strndup(keyname, PREFERENCE_KEY_PATH_LEN); + if (n->keyname == NULL) + { + ERR("The memory is insufficient, errno: %d (%s)", errno, strerror(errno)); + free(n); + goto out_func; + } + n->wd = wd; + n->cb_data = data; + n->cb = cb; + + g_notilist = g_list_append(g_notilist, n); + if (!g_notilist) { + ERR("g_list_append fail"); + } + + INFO("cb(%p) is added for %s. tot cb cnt : %d\n", cb, n->keyname, g_list_length(g_notilist)); + +out_func: + pthread_mutex_unlock(&_kdb_g_ns_mutex); + + return func_ret; +} + +int _preference_kdb_del_notify(const char *keyname) +{ + int wd = 0; + int r = 0; + struct noti_node *n = NULL; + struct noti_node t; + char path[PREFERENCE_KEY_PATH_LEN] = { 0, }; + char err_buf[ERR_LEN] = { 0, }; + int del = 0; + int ret = 0; + int func_ret = PREFERENCE_ERROR_NONE; + GList *noti_list; + + retvm_if(keyname == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: keyname(%s)", keyname); + retvm_if(_kdb_inoti_fd == 0, PREFERENCE_ERROR_NONE, "Invalid operation: not exist anything for inotify"); + + ret = _preference_get_key_path((char*)keyname, path); + retvm_if(ret != PREFERENCE_ERROR_NONE, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is not valid"); + + /* get wd */ + wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK); + if (wd == -1) { + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("Error: inotify_add_watch() [%s]: %s", path, err_buf); + return PREFERENCE_ERROR_IO_ERROR; + } + + pthread_mutex_lock(&_kdb_g_ns_mutex); + + t.wd = wd; + + noti_list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd); + if(noti_list) { + del++; + + n = noti_list->data; + g_notilist = g_list_remove(g_notilist, n); + g_free(n->keyname); + g_free(n); + + r = inotify_rm_watch(_kdb_inoti_fd, wd); + if(r == -1) { + strerror_r(errno, err_buf, sizeof(err_buf)); + ERR("Error: inotify_rm_watch [%s]: %s", keyname, err_buf); + func_ret = PREFERENCE_ERROR_IO_ERROR; + } + + INFO("key(%s) cb is removed. remained noti list total length(%d)", + keyname, g_list_length(g_notilist)); + } + + if(g_list_length(g_notilist) == 0) { + close(_kdb_inoti_fd); + _kdb_inoti_fd = 0; + + g_source_destroy(_kdb_handler); + _kdb_handler = NULL; + + g_list_free(g_notilist); + g_notilist = NULL; + + INFO("all noti list is freed"); + } + + pthread_mutex_unlock(&_kdb_g_ns_mutex); + + if(del == 0) { + errno = ENOENT; + func_ret = PREFERENCE_ERROR_IO_ERROR; + } + + return func_ret; +} |