1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
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
30 namespace {
31 constexpr uint32_t MAX_BUF_SIZE = 1024;
32 } // namespace
33
PluginWatcher(const PluginManagerPtr & pluginManager)34 PluginWatcher::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 HILOG_INFO(LOG_CORE, "%s:inotify_init1 failed! inotifyFd_ : %d", __func__, inotifyFd_);
40 } else {
41 monitorThread_ = std::thread(&PluginWatcher::Monitor, this);
42 }
43 }
44
~PluginWatcher()45 PluginWatcher::~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
ScanPlugins(const std::string & pluginDir)58 bool 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 HILOG_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 HILOG_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
WatchPlugins(const std::string & pluginDir)87 bool 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 HILOG_INFO(LOG_CORE, "%s:inotify_add_watch add directory %s failed!", __func__, pluginDir.c_str());
95 return false;
96 }
97 HILOG_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
MonitorIsSet()103 bool 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 HILOG_ERROR(LOG_CORE, "%s:memset_s error!", __func__);
141 }
142 return true;
143 }
144
Monitor()145 void 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
OnPluginAdded(const std::string & pluginPath)169 void PluginWatcher::OnPluginAdded(const std::string& pluginPath)
170 {
171 auto pluginManager = pluginManager_.lock();
172 if (pluginManager != nullptr) {
173 if (pluginManager->AddPlugin(pluginPath)) {
174 HILOG_INFO(LOG_CORE, "%s:plugin %s add success!", __func__, pluginPath.c_str());
175 } else {
176 HILOG_INFO(LOG_CORE, "%s:pluginPath %s add failed!", __func__, pluginPath.c_str());
177 }
178 } else {
179 HILOG_INFO(LOG_CORE, "%s:weak_ptr pluginManager lock failed!", __func__);
180 }
181 }
182
OnPluginRemoved(const std::string & pluginPath)183 void PluginWatcher::OnPluginRemoved(const std::string& pluginPath)
184 {
185 auto pluginManager = pluginManager_.lock();
186 if (pluginManager != nullptr) {
187 if (pluginManager->RemovePlugin(pluginPath)) {
188 HILOG_INFO(LOG_CORE, "%s:pluginPath %s remove success!", __func__, pluginPath.c_str());
189 } else {
190 HILOG_INFO(LOG_CORE, "%s:pluginPath %s remove failed!", __func__, pluginPath.c_str());
191 }
192 } else {
193 HILOG_INFO(LOG_CORE, "%s:weak_ptr pluginManager lock failed!", __func__);
194 }
195 }