/* * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "sa_common.h" #include "sa_types.h" #include "input_file.h" #include #define EVENT_NAME_MAX 256 #define EVENT_SIZE (sizeof(struct inotify_event)) #define EVENT_BUF_LEN (512 * (EVENT_SIZE + EVENT_NAME_MAX)) #define MAXLINE 1024 static volatile int cond_cnt = 0; /* a global count of the number of threads finished working. It will be protected by mutex and changes to it will be signalled to the main thread via cond */ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static void get_applied_file_name(char *file_path, char *applied_file_path, int buf_len) { char buf[256]; char *ptr = NULL; int len; // find the position of the file name before the extension('.') if ((file_path != NULL) && (applied_file_path != NULL) && (buf_len > 0)) { ptr = strchr(file_path, '.'); len = strlen(file_path); if ((ptr != NULL) && (len > 5)) { strncpy(buf, file_path, len - 5); buf[len - 5] = 0; } else { _E("fail to get file name"); } snprintf(applied_file_path, buf_len, "%s%s", buf, "_applied.json"); //_D("applied_file_path : %s", applied_file_path); } else { _E("file path is NULL ~~"); } } static int compare_config_files(char *file_path, char *applied_file_path) { FILE *fp1, *fp2; int ch1, ch2, same; int ret = -1; unsigned long l; /* open first file */ if ((fp1 = fopen(file_path, "rb")) == NULL) { _E("Cannot open first file."); return -1; } /* open second file */ if ((fp2 = fopen(applied_file_path, "rb")) == NULL) { _E("Cannot open second file."); fclose(fp1); return -1; } if (fp1 != NULL && fp2 != NULL) { l = 0; same = 1; /* compare the files */ while (!feof(fp1)) { ch1 = fgetc(fp1); if (ferror(fp1)) { _E("Error reading first file."); exit(1); } ch2 = fgetc(fp2); if (ferror(fp2)) { _E("Error reading second file."); exit(1); } if (ch1 != ch2) { //_D("Files differ at byte number %lu, %c, %c", l, ch1, ch2); same = 0; break; } l++; } if (same) { //_D("Files are identical."); ret = 0; } else { //_D("Files are not identical."); ret = 1; } fclose(fp1); fclose(fp2); } return ret; } static int update_file(char *file_path) { FILE *dst; FILE *src; int readlen; unsigned char buf[MAXLINE]; char applied_file_path[256]; /* open first file */ if ((src = fopen(file_path, "rb")) == NULL) { _E("Cannot open first file."); return -1; } /* open second file */ get_applied_file_name(file_path, applied_file_path, sizeof(applied_file_path)); if ((dst = fopen(applied_file_path, "wb")) == NULL) { _E("Cannot open second file."); fclose(src); return -1; } while (1) { readlen = fread(buf, 1, MAXLINE, src); if (readlen != MAXLINE) { if (ferror(src)) _E("Error reading %s", file_path); else if (feof(src)) { _D("EOF found"); if (readlen > 0) fwrite(buf, 1, readlen, dst); } break; } else fwrite(buf, 1, readlen, dst); } fclose(src); fclose(dst); return 1; } static int call_config_setting_cb(char *file_path, int (*callbackFuntion) (void *)) { int ret = 0; _D("call setup callback function ~~~"); if (callbackFuntion != NULL) { if (callbackFuntion((void *)file_path) == SA_ERROR_NONE) { _D("complete update_file ~~~~~"); ret = update_file(file_path); } else { ret = -1; _E("setting error !!"); } } return ret; } static int file_check(char *file_path) { // Here, check the condition if we need to configure the network or system setting. // compare the contents of two configuration files (config.json and config_applied.json) // and call callbackfunction if needed to be updated. _D("start to check config file ~~~ (%s)", file_path); int flag_update = FALSE; int ret; char applied_file_path[256]; if (access(file_path, F_OK) == 0) { // get allpied file name to be compared. get_applied_file_name(file_path, applied_file_path, sizeof(applied_file_path)); // 1. if applied file is not exist, call callbackfunction to update; if (access(applied_file_path, F_OK) == -1) { //_D("1. if the applied file is not existed, call setup callback function"); flag_update = TRUE; } else { //_D("2. else, the applied file exists, then compare two config files ~~~"); ret = compare_config_files(file_path, applied_file_path); if (ret == 1) { flag_update = TRUE; } else if (ret == 0) { //_D("file is the same !!!"); flag_update = FALSE; } else { _E("there are some errors in compare_config_files function."); flag_update = -1; } } _D("flag_update (%d)", flag_update); } else { flag_update = FALSE; _D("There's no config file(%s)", file_path); } return flag_update; } static void *__config_main_loop(void *arg) { int fd = 0; int wd = 0; int i = 0; sa_file_check_thread_s *param = NULL; char *buffer = NULL; int flagUpdate = 0; _D("__config_main_loop start"); param = (sa_file_check_thread_s *) arg; if (param == NULL) { _D("param or challback is null for event"); return NULL; } _D("checking file : %s", param->config_file_path); // 1. first check the configuration file. flagUpdate = file_check(param->config_file_path); if (flagUpdate == 1) { // Apply the modified setting value to system and update the applied json file. call_config_setting_cb(param->config_file_path, param->file_state_cb); } else if (flagUpdate == -1) { _E("file checking error !!"); //return NULL; } _D("thread [%d] is done. Send cond_signal", param->id); pthread_mutex_lock(&mutex); cond_cnt++; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); // 2. start to monitoring the configuration file. fd = inotify_init(); if (fd < 0) { _E("inotify_init error"); return NULL; } wd = inotify_add_watch(fd, param->config_file_path, IN_CLOSE_WRITE); if (wd < 0) { _E("inotify_add_watch fail"); close(fd); return NULL; } buffer = (char *)malloc(EVENT_BUF_LEN); if (buffer != NULL) { memset(buffer, 0x00, EVENT_BUF_LEN); // Start callack _D("Registerd Callback Triggered"); while (1) { int length = 0; _D("start listening ~~~"); length = read(fd, buffer, EVENT_BUF_LEN); _D("event buffer read done(%d)", length); if (length <= 0) { _E("read error"); break; } i = 0; while (i < length && i < (EVENT_BUF_LEN - EVENT_SIZE)) { struct inotify_event *event = (struct inotify_event *)&buffer[i]; _D("event->len(%d), event->mask(%d)", event->len, event->mask); if (event->mask & IN_CLOSE_WRITE) { if (event->mask & IN_ISDIR) { _D("The directory was created"); } else { _D("callback function is called !!!!"); // Apply the modified setting value to system and update the applied json file. call_config_setting_cb(param->config_file_path, param->file_state_cb); break; } } i += EVENT_SIZE + event->len; } //_D("event buffer parse done~~~"); } } else { _E("buffer is NULL"); } _D("__config_main_loop close"); inotify_rm_watch(fd, wd); close(fd); if (buffer != NULL) free(buffer); return NULL; } void *sa_inputfile_thread(void *user_data) { sa_main_thread_s *main_param = (sa_main_thread_s *) user_data; sa_file_check_thread_s **th_param = NULL; sa_error_e ret = SA_ERROR_NONE; int index; pthread_t *p_thread = NULL; if (main_param == NULL) { _E("parameter is NULL !!!"); return FALSE; } cond_cnt = 0; _D("sa_inputfile_thread>>> config_file_num[%d]", main_param->config_file_num); if (main_param->config_file_num > 0) { // alloc p_thread p_thread = (pthread_t *) malloc(main_param->config_file_num * sizeof(pthread_t)); if (p_thread != NULL) { th_param = (sa_file_check_thread_s **) malloc(main_param->config_file_num * sizeof(sa_file_check_thread_s *)); if (th_param != NULL) { // 1. create a new thread to monitoring modification of setting file. for (index = 0; index < main_param->config_file_num; index++) { p_thread[index] = 0; if (access(main_param->config_file_path[index], F_OK) == 0) { th_param[index] = (sa_file_check_thread_s *) malloc(sizeof(sa_file_check_thread_s)); if (th_param[index] != NULL) { th_param[index]->config_file_path = (char *) malloc(strlen(main_param->config_file_path[index]) + 1); if (th_param[index]->config_file_path != NULL) { memcpy(th_param[index]->config_file_path, main_param->config_file_path[index], strlen(main_param->config_file_path[index]) + 1); th_param[index]->file_state_cb = main_param->setup_cb; // ****************** Start thread to create in order to receive event *************** // _D("Create monitoring thread"); th_param[index]->id = index; if (pthread_create(&p_thread[index], NULL, &__config_main_loop, (void *)th_param[index]) < 0) { _E("thread create error for checking configuration"); ret = SA_ERROR_NOT_AVAILABLE; //break; } else { _D("thread created (%d)", index); } } else { _E("memory alloc fail !!!"); ret = SA_ERROR_NOT_AVAILABLE; break; } } } else { _D("No Configuration file (%s)", main_param->config_file_path[index]); ret = SA_ERROR_INVALID_PARAMETER; } } // we're going to test "done" so we need the mutex for safety pthread_mutex_lock(&mutex); do { _D("[thread main] cond_cnt is %d which is < %d so waiting on cond", cond_cnt, main_param->config_file_num); /* block this thread until another thread signals cond. While blocked, the mutex is released, then re-aquired before this thread is woken up and the call returns. */ pthread_cond_wait(&cond, &mutex); } while (cond_cnt < main_param->config_file_num); _D("[thread main] done == %d so everyone is done", main_param->config_file_num); pthread_mutex_unlock(&mutex); if (main_param->completed_notify_cb != NULL) main_param->completed_notify_cb(); else _D("no sd_notify() call !!!"); for (index = 0; index < main_param->config_file_num; index++) { if (p_thread[index]) { //_D("pthread_join(%d) index); pthread_join(p_thread[index], NULL); } } // free the allocated memory.. for (index = 0; index < main_param->config_file_num; index++) { if (th_param[index] != NULL) { if (th_param[index]->config_file_path != NULL) free(th_param[index]->config_file_path); } free(th_param[index]); } free(th_param); } free(p_thread); } else _E("memory alloc faile !!!"); } else _D("There's no config file to update ~~~"); // if return value is "FALSE", function stop // if return value is "TRUE", function restart return FALSE; } sa_file_state_e sa_inputfile_is_file_exist(char *file_name) { sa_file_state_e ret = SA_FILE_STATE_NOT_EXISTED; if (file_name && access(file_name, F_OK) != -1) ret = SA_FILE_STATE_EXIST; else ret = SA_FILE_STATE_NOT_EXISTED; return ret; } int sa_generate_folder(char *folder_path) { int ret = 0; // check folder exists or not if (folder_path != NULL) { if (access(folder_path, F_OK) != -1) { _D("%s folder existes !!", folder_path); ret = -1; } else { // create folder _D("folder create (%s)", folder_path); if (mkdir(folder_path, 0644) == -1) { _E("directory create error !!!"); ret = -1; } } } else { _E("folder path is NULL !!"); ret = -1; } return ret; }