1/* 2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "plugin_watcher.h" 17 18#include <climits> 19#include <cstdio> 20#include <cstring> 21#include <dirent.h> 22#include <pthread.h> 23#include <sys/inotify.h> 24#include <unistd.h> 25 26#include "logging.h" 27#include "plugin_manager.h" 28#include "securec.h" 29 30namespace { 31constexpr uint32_t MAX_BUF_SIZE = 1024; 32} // namespace 33 34PluginWatcher::PluginWatcher(const PluginManagerPtr& pluginManager) 35 : inotifyFd_(-1), pluginManager_(pluginManager), runMonitor_(true) 36{ 37 inotifyFd_ = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); 38 if (inotifyFd_ < 0) { 39 PROFILER_LOG_INFO(LOG_CORE, "%s:inotify_init1 failed! inotifyFd_ : %d", __func__, inotifyFd_); 40 } else { 41 monitorThread_ = std::thread([this] { this->Monitor(); }); 42 } 43} 44 45PluginWatcher::~PluginWatcher() 46{ 47 runMonitor_ = false; 48 for (auto it = wdToDir_.begin(); it != wdToDir_.end(); ++it) { 49 inotify_rm_watch(inotifyFd_, it->first); 50 } 51 52 if (inotifyFd_ != -1) { 53 close(inotifyFd_); 54 } 55 monitorThread_.join(); 56} 57 58bool PluginWatcher::ScanPlugins(const std::string& pluginDir) 59{ 60 DIR* dir = nullptr; 61 struct dirent* entry = nullptr; 62 char fullpath[PATH_MAX + 1] = {0}; 63 realpath(pluginDir.c_str(), fullpath); 64 PROFILER_LOG_INFO(LOG_CORE, "%s:scan plugin from directory %s", __func__, fullpath); 65 dir = opendir(fullpath); 66 if (dir == nullptr) { 67 return false; 68 } 69 while (true) { 70 entry = readdir(dir); 71 if (!entry) { 72 PROFILER_LOG_INFO(LOG_CORE, "%s:readdir finish!", __func__); 73 break; 74 } 75 std::string fileName = entry->d_name; 76 if (entry->d_type & DT_REG) { 77 size_t pos = fileName.rfind(".so"); 78 if (pos != std::string::npos && (pos == fileName.length() - strlen(".so"))) { 79 OnPluginAdded(std::string(fullpath) + '/' + fileName); 80 } 81 } 82 } 83 closedir(dir); 84 return true; 85} 86 87bool PluginWatcher::WatchPlugins(const std::string& pluginDir) 88{ 89 char fullpath[PATH_MAX + 1] = {0}; 90 realpath(pluginDir.c_str(), fullpath); 91 92 int wd = inotify_add_watch(inotifyFd_, fullpath, IN_ALL_EVENTS); 93 if (wd < 0) { 94 PROFILER_LOG_INFO(LOG_CORE, "%s:inotify_add_watch add directory %s failed!", __func__, pluginDir.c_str()); 95 return false; 96 } 97 PROFILER_LOG_INFO(LOG_CORE, "%s:inotify_add_watch add directory %s success!", __func__, fullpath); 98 std::lock_guard<std::mutex> guard(mtx_); 99 wdToDir_.insert(std::pair<int, std::string>(wd, std::string(fullpath))); 100 return true; 101} 102 103bool PluginWatcher::MonitorIsSet() 104{ 105 const struct inotify_event* event = nullptr; 106 char buffer[MAX_BUF_SIZE] = {'\0'}; 107 char* ptr = nullptr; 108 109 ssize_t readLength = read(inotifyFd_, buffer, MAX_BUF_SIZE); 110 if (readLength == -1) { 111 return false; 112 } 113 for (ptr = buffer; ptr < buffer + readLength; ptr += sizeof(struct inotify_event) + event->len) { 114 event = reinterpret_cast<const struct inotify_event*>(ptr); 115 std::unique_lock<std::mutex> guard(mtx_, std::adopt_lock); 116 const std::string& pluginDir = wdToDir_[event->wd]; 117 guard.unlock(); 118 if (event->mask & IN_ISDIR) { 119 continue; 120 } 121 std::string fileName = event->name; 122 size_t pos = fileName.rfind(".so"); 123 if ((pos == std::string::npos) || (pos != fileName.length() - strlen(".so"))) { 124 continue; 125 } 126 switch (event->mask) { 127 case IN_CLOSE_WRITE: 128 case IN_MOVED_TO: 129 OnPluginAdded(pluginDir + '/' + fileName); 130 break; 131 case IN_DELETE: 132 case IN_MOVED_FROM: 133 OnPluginRemoved(pluginDir + '/' + fileName); 134 break; 135 default: 136 break; 137 } 138 } 139 if (memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)) != 0) { 140 PROFILER_LOG_ERROR(LOG_CORE, "%s:memset_s error!", __func__); 141 } 142 return true; 143} 144 145void PluginWatcher::Monitor() 146{ 147 struct timeval time; 148 149 pthread_setname_np(pthread_self(), "PluginWatcher"); 150 while (runMonitor_) { 151 fd_set rFds; 152 FD_ZERO(&rFds); 153 FD_SET(inotifyFd_, &rFds); 154 time.tv_sec = 1; 155 time.tv_usec = 0; 156 int ret = select(inotifyFd_ + 1, &rFds, nullptr, nullptr, &time); 157 if (ret < 0) { 158 continue; 159 } else if (!ret) { 160 continue; 161 } else if (FD_ISSET(inotifyFd_, &rFds)) { 162 if (!MonitorIsSet()) { 163 continue; 164 } 165 } 166 } 167} 168 169void PluginWatcher::OnPluginAdded(const std::string& pluginPath) 170{ 171 auto pluginManager = pluginManager_.lock(); 172 if (pluginManager != nullptr) { 173 if (pluginManager->AddPlugin(pluginPath)) { 174 PROFILER_LOG_INFO(LOG_CORE, "%s:plugin %s add success!", __func__, pluginPath.c_str()); 175 } else { 176 PROFILER_LOG_INFO(LOG_CORE, "%s:pluginPath %s add failed!", __func__, pluginPath.c_str()); 177 } 178 } else { 179 PROFILER_LOG_INFO(LOG_CORE, "%s:weak_ptr pluginManager lock failed!", __func__); 180 } 181} 182 183void PluginWatcher::OnPluginRemoved(const std::string& pluginPath) 184{ 185 auto pluginManager = pluginManager_.lock(); 186 if (pluginManager != nullptr) { 187 if (pluginManager->RemovePlugin(pluginPath)) { 188 PROFILER_LOG_INFO(LOG_CORE, "%s:pluginPath %s remove success!", __func__, pluginPath.c_str()); 189 } else { 190 PROFILER_LOG_INFO(LOG_CORE, "%s:pluginPath %s remove failed!", __func__, pluginPath.c_str()); 191 } 192 } else { 193 PROFILER_LOG_INFO(LOG_CORE, "%s:weak_ptr pluginManager lock failed!", __func__); 194 } 195}