| /** @file |
| |
| Code for class to manage configuration updates |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you 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 "FileManager.h" |
| #include "tscore/ink_platform.h" |
| #include "tscore/ink_file.h" |
| #include "ConfigManager.h" |
| #include "WebMgmtUtils.h" |
| |
| #include <vector> |
| #include <algorithm> |
| |
| FileManager::FileManager() |
| { |
| ink_mutex_init(&accessLock); |
| ink_mutex_init(&cbListLock); |
| } |
| |
| // FileManager::~FileManager |
| // |
| // There is only FileManager object in the process and it |
| // should never need to be destructed except at |
| // program exit |
| // |
| FileManager::~FileManager() |
| { |
| callbackListable *cb; |
| |
| // Let other operations finish and do not start any new ones |
| ink_mutex_acquire(&accessLock); |
| |
| for (cb = cblist.pop(); cb != nullptr; cb = cblist.pop()) { |
| delete cb; |
| } |
| for (auto &&it : bindings) { |
| delete it.second; |
| } |
| |
| ink_mutex_release(&accessLock); |
| ink_mutex_destroy(&accessLock); |
| ink_mutex_destroy(&cbListLock); |
| } |
| |
| // void FileManager::registerCallback(FileCallbackFunc func) |
| // |
| // Adds a new callback function |
| // callbacks are made whenever a configuration file has |
| // changed |
| // |
| // The callback function is responsible for free'ing |
| // the string the string it is passed |
| // |
| void |
| FileManager::registerCallback(FileCallbackFunc func) |
| { |
| callbackListable *newcb = new callbackListable(); |
| ink_assert(newcb != nullptr); |
| newcb->func = func; |
| ink_mutex_acquire(&cbListLock); |
| cblist.push(newcb); |
| ink_mutex_release(&cbListLock); |
| } |
| |
| // void FileManager::addFile(char* fileName, const configFileInfo* file_info, |
| // ConfigManager* parentConfig) |
| // |
| // for the baseFile, creates a ConfigManager object for it |
| // |
| // if file_info is not null, a WebFileEdit object is also created for |
| // the file |
| // |
| // Pointers to the new objects are stored in the bindings hashtable |
| // |
| void |
| FileManager::addFile(const char *fileName, const char *configName, bool root_access_needed, bool isRequired, |
| ConfigManager *parentConfig) |
| { |
| ink_mutex_acquire(&accessLock); |
| addFileHelper(fileName, configName, root_access_needed, isRequired, parentConfig); |
| ink_mutex_release(&accessLock); |
| } |
| |
| // caller must hold the lock |
| void |
| FileManager::addFileHelper(const char *fileName, const char *configName, bool root_access_needed, bool isRequired, |
| ConfigManager *parentConfig) |
| { |
| ink_assert(fileName != nullptr); |
| |
| ConfigManager *rb = new ConfigManager(fileName, configName, root_access_needed, isRequired, parentConfig); |
| rb->configFiles = this; |
| |
| bindings.emplace(rb->getFileName(), rb); |
| } |
| |
| // bool FileManager::getConfigManagerObj(char* fileName, ConfigManager** rbPtr) |
| // |
| // Sets rbPtr to the ConfigManager object associated |
| // with the passed in fileName. |
| // |
| // If there is no binding, false is returned |
| // |
| bool |
| FileManager::getConfigObj(const char *fileName, ConfigManager **rbPtr) |
| { |
| ink_mutex_acquire(&accessLock); |
| auto it = bindings.find(fileName); |
| bool found = it != bindings.end(); |
| ink_mutex_release(&accessLock); |
| |
| *rbPtr = found ? it->second : nullptr; |
| return found; |
| } |
| |
| // bool FileManager::fileChanged(const char* fileName) |
| // |
| // Called by the ConfigManager class whenever a config has changed |
| // Initiates callbacks |
| // |
| // |
| void |
| FileManager::fileChanged(const char *fileName, const char *configName) |
| { |
| callbackListable *cb; |
| char *filenameCopy, *confignameCopy; |
| Debug("lm", "filename changed %s", fileName); |
| ink_mutex_acquire(&cbListLock); |
| |
| for (cb = cblist.head; cb != nullptr; cb = cb->link.next) { |
| // Dup the string for each callback to be |
| // defensive in case it's modified when it's not supposed to be |
| confignameCopy = ats_strdup(configName); |
| filenameCopy = ats_strdup(fileName); |
| (*cb->func)(filenameCopy, confignameCopy); |
| ats_free(filenameCopy); |
| ats_free(confignameCopy); |
| } |
| ink_mutex_release(&cbListLock); |
| } |
| |
| // void FileManger::rereadConfig() |
| // |
| // Iterates through the list of managed files and |
| // calls ConfigManager::checkForUserUpdate on them |
| // |
| // although it is tempting, DO NOT CALL FROM SIGNAL HANDLERS |
| // This function is not Async-Signal Safe. It |
| // is thread safe |
| void |
| FileManager::rereadConfig() |
| { |
| ConfigManager *rb; |
| |
| std::vector<ConfigManager *> changedFiles; |
| std::vector<ConfigManager *> parentFileNeedChange; |
| size_t n; |
| ink_mutex_acquire(&accessLock); |
| for (auto &&it : bindings) { |
| rb = it.second; |
| // ToDo: rb->isVersions() was always true before, because numberBackups was always >= 1. So ROLLBACK_CHECK_ONLY could not |
| // happen at all... |
| if (rb->checkForUserUpdate(ROLLBACK_CHECK_AND_UPDATE)) { |
| changedFiles.push_back(rb); |
| if (rb->isChildManaged()) { |
| if (std::find(parentFileNeedChange.begin(), parentFileNeedChange.end(), rb->getParentConfig()) == |
| parentFileNeedChange.end()) { |
| parentFileNeedChange.push_back(rb->getParentConfig()); |
| } |
| } |
| } |
| } |
| |
| std::vector<ConfigManager *> childFileNeedDelete; |
| n = changedFiles.size(); |
| for (size_t i = 0; i < n; i++) { |
| if (changedFiles[i]->isChildManaged()) { |
| continue; |
| } |
| // for each parent file, if it is changed, then delete all its children |
| for (auto &&it : bindings) { |
| rb = it.second; |
| if (rb->getParentConfig() == changedFiles[i]) { |
| if (std::find(childFileNeedDelete.begin(), childFileNeedDelete.end(), rb) == childFileNeedDelete.end()) { |
| childFileNeedDelete.push_back(rb); |
| } |
| } |
| } |
| } |
| n = childFileNeedDelete.size(); |
| for (size_t i = 0; i < n; i++) { |
| bindings.erase(childFileNeedDelete[i]->getFileName()); |
| delete childFileNeedDelete[i]; |
| } |
| ink_mutex_release(&accessLock); |
| |
| n = parentFileNeedChange.size(); |
| for (size_t i = 0; i < n; i++) { |
| if (std::find(changedFiles.begin(), changedFiles.end(), parentFileNeedChange[i]) == changedFiles.end()) { |
| fileChanged(parentFileNeedChange[i]->getFileName(), parentFileNeedChange[i]->getConfigName()); |
| } |
| } |
| // INKqa11910 |
| // need to first check that enable_customizations is enabled |
| bool found; |
| int enabled = static_cast<int>(REC_readInteger("proxy.config.body_factory.enable_customizations", &found)); |
| |
| if (found && enabled) { |
| fileChanged("proxy.config.body_factory.template_sets_dir", "proxy.config.body_factory.template_sets_dir"); |
| } |
| fileChanged("proxy.config.ssl.server.ticket_key.filename", "proxy.config.ssl.server.ticket_key.filename"); |
| } |
| |
| bool |
| FileManager::isConfigStale() |
| { |
| ConfigManager *rb; |
| bool stale = false; |
| |
| ink_mutex_acquire(&accessLock); |
| for (auto &&it : bindings) { |
| rb = it.second; |
| if (rb->checkForUserUpdate(ROLLBACK_CHECK_ONLY)) { |
| stale = true; |
| break; |
| } |
| } |
| |
| ink_mutex_release(&accessLock); |
| return stale; |
| } |
| |
| // void configFileChild(const char *parent, const char *child) |
| // |
| // Add child to the bindings with parentConfig |
| void |
| FileManager::configFileChild(const char *parent, const char *child) |
| { |
| ConfigManager *parentConfig = nullptr; |
| ink_mutex_acquire(&accessLock); |
| if (auto it = bindings.find(parent); it != bindings.end()) { |
| parentConfig = it->second; |
| addFileHelper(child, "", parentConfig->rootAccessNeeded(), parentConfig->getIsRequired(), parentConfig); |
| } |
| ink_mutex_release(&accessLock); |
| } |