106f6ba60Sopenharmony_ci/*
206f6ba60Sopenharmony_ci * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
306f6ba60Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
406f6ba60Sopenharmony_ci * you may not use this file except in compliance with the License.
506f6ba60Sopenharmony_ci * You may obtain a copy of the License at
606f6ba60Sopenharmony_ci *
706f6ba60Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
806f6ba60Sopenharmony_ci *
906f6ba60Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1006f6ba60Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1106f6ba60Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1206f6ba60Sopenharmony_ci * See the License for the specific language governing permissions and
1306f6ba60Sopenharmony_ci * limitations under the License.
1406f6ba60Sopenharmony_ci */
1506f6ba60Sopenharmony_ci
1606f6ba60Sopenharmony_ci#include "plugin_watcher.h"
1706f6ba60Sopenharmony_ci
1806f6ba60Sopenharmony_ci#include <climits>
1906f6ba60Sopenharmony_ci#include <cstdio>
2006f6ba60Sopenharmony_ci#include <cstring>
2106f6ba60Sopenharmony_ci#include <dirent.h>
2206f6ba60Sopenharmony_ci#include <pthread.h>
2306f6ba60Sopenharmony_ci#include <sys/inotify.h>
2406f6ba60Sopenharmony_ci#include <unistd.h>
2506f6ba60Sopenharmony_ci
2606f6ba60Sopenharmony_ci#include "logging.h"
2706f6ba60Sopenharmony_ci#include "plugin_manager.h"
2806f6ba60Sopenharmony_ci#include "securec.h"
2906f6ba60Sopenharmony_ci
3006f6ba60Sopenharmony_cinamespace {
3106f6ba60Sopenharmony_ciconstexpr uint32_t MAX_BUF_SIZE = 1024;
3206f6ba60Sopenharmony_ci} // namespace
3306f6ba60Sopenharmony_ci
3406f6ba60Sopenharmony_ciPluginWatcher::PluginWatcher(const PluginManagerPtr& pluginManager)
3506f6ba60Sopenharmony_ci    : inotifyFd_(-1), pluginManager_(pluginManager), runMonitor_(true)
3606f6ba60Sopenharmony_ci{
3706f6ba60Sopenharmony_ci    inotifyFd_ = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
3806f6ba60Sopenharmony_ci    if (inotifyFd_ < 0) {
3906f6ba60Sopenharmony_ci        PROFILER_LOG_INFO(LOG_CORE, "%s:inotify_init1 failed! inotifyFd_ : %d", __func__, inotifyFd_);
4006f6ba60Sopenharmony_ci    } else {
4106f6ba60Sopenharmony_ci        monitorThread_ = std::thread([this] { this->Monitor(); });
4206f6ba60Sopenharmony_ci    }
4306f6ba60Sopenharmony_ci}
4406f6ba60Sopenharmony_ci
4506f6ba60Sopenharmony_ciPluginWatcher::~PluginWatcher()
4606f6ba60Sopenharmony_ci{
4706f6ba60Sopenharmony_ci    runMonitor_ = false;
4806f6ba60Sopenharmony_ci    for (auto it = wdToDir_.begin(); it != wdToDir_.end(); ++it) {
4906f6ba60Sopenharmony_ci        inotify_rm_watch(inotifyFd_, it->first);
5006f6ba60Sopenharmony_ci    }
5106f6ba60Sopenharmony_ci
5206f6ba60Sopenharmony_ci    if (inotifyFd_ != -1) {
5306f6ba60Sopenharmony_ci        close(inotifyFd_);
5406f6ba60Sopenharmony_ci    }
5506f6ba60Sopenharmony_ci    monitorThread_.join();
5606f6ba60Sopenharmony_ci}
5706f6ba60Sopenharmony_ci
5806f6ba60Sopenharmony_cibool PluginWatcher::ScanPlugins(const std::string& pluginDir)
5906f6ba60Sopenharmony_ci{
6006f6ba60Sopenharmony_ci    DIR* dir = nullptr;
6106f6ba60Sopenharmony_ci    struct dirent* entry = nullptr;
6206f6ba60Sopenharmony_ci    char fullpath[PATH_MAX + 1] = {0};
6306f6ba60Sopenharmony_ci    realpath(pluginDir.c_str(), fullpath);
6406f6ba60Sopenharmony_ci    PROFILER_LOG_INFO(LOG_CORE, "%s:scan plugin from directory %s", __func__, fullpath);
6506f6ba60Sopenharmony_ci    dir = opendir(fullpath);
6606f6ba60Sopenharmony_ci    if (dir == nullptr) {
6706f6ba60Sopenharmony_ci        return false;
6806f6ba60Sopenharmony_ci    }
6906f6ba60Sopenharmony_ci    while (true) {
7006f6ba60Sopenharmony_ci        entry = readdir(dir);
7106f6ba60Sopenharmony_ci        if (!entry) {
7206f6ba60Sopenharmony_ci            PROFILER_LOG_INFO(LOG_CORE, "%s:readdir finish!", __func__);
7306f6ba60Sopenharmony_ci            break;
7406f6ba60Sopenharmony_ci        }
7506f6ba60Sopenharmony_ci        std::string fileName = entry->d_name;
7606f6ba60Sopenharmony_ci        if (entry->d_type & DT_REG) {
7706f6ba60Sopenharmony_ci            size_t pos = fileName.rfind(".so");
7806f6ba60Sopenharmony_ci            if (pos != std::string::npos && (pos == fileName.length() - strlen(".so"))) {
7906f6ba60Sopenharmony_ci                OnPluginAdded(std::string(fullpath) + '/' + fileName);
8006f6ba60Sopenharmony_ci            }
8106f6ba60Sopenharmony_ci        }
8206f6ba60Sopenharmony_ci    }
8306f6ba60Sopenharmony_ci    closedir(dir);
8406f6ba60Sopenharmony_ci    return true;
8506f6ba60Sopenharmony_ci}
8606f6ba60Sopenharmony_ci
8706f6ba60Sopenharmony_cibool PluginWatcher::WatchPlugins(const std::string& pluginDir)
8806f6ba60Sopenharmony_ci{
8906f6ba60Sopenharmony_ci    char fullpath[PATH_MAX + 1] = {0};
9006f6ba60Sopenharmony_ci    realpath(pluginDir.c_str(), fullpath);
9106f6ba60Sopenharmony_ci
9206f6ba60Sopenharmony_ci    int wd = inotify_add_watch(inotifyFd_, fullpath, IN_ALL_EVENTS);
9306f6ba60Sopenharmony_ci    if (wd < 0) {
9406f6ba60Sopenharmony_ci        PROFILER_LOG_INFO(LOG_CORE, "%s:inotify_add_watch add directory %s failed!", __func__, pluginDir.c_str());
9506f6ba60Sopenharmony_ci        return false;
9606f6ba60Sopenharmony_ci    }
9706f6ba60Sopenharmony_ci    PROFILER_LOG_INFO(LOG_CORE, "%s:inotify_add_watch add directory %s success!", __func__, fullpath);
9806f6ba60Sopenharmony_ci    std::lock_guard<std::mutex> guard(mtx_);
9906f6ba60Sopenharmony_ci    wdToDir_.insert(std::pair<int, std::string>(wd, std::string(fullpath)));
10006f6ba60Sopenharmony_ci    return true;
10106f6ba60Sopenharmony_ci}
10206f6ba60Sopenharmony_ci
10306f6ba60Sopenharmony_cibool PluginWatcher::MonitorIsSet()
10406f6ba60Sopenharmony_ci{
10506f6ba60Sopenharmony_ci    const struct inotify_event* event = nullptr;
10606f6ba60Sopenharmony_ci    char buffer[MAX_BUF_SIZE] = {'\0'};
10706f6ba60Sopenharmony_ci    char* ptr = nullptr;
10806f6ba60Sopenharmony_ci
10906f6ba60Sopenharmony_ci    ssize_t readLength = read(inotifyFd_, buffer, MAX_BUF_SIZE);
11006f6ba60Sopenharmony_ci    if (readLength == -1) {
11106f6ba60Sopenharmony_ci        return false;
11206f6ba60Sopenharmony_ci    }
11306f6ba60Sopenharmony_ci    for (ptr = buffer; ptr < buffer + readLength; ptr += sizeof(struct inotify_event) + event->len) {
11406f6ba60Sopenharmony_ci        event = reinterpret_cast<const struct inotify_event*>(ptr);
11506f6ba60Sopenharmony_ci        std::unique_lock<std::mutex> guard(mtx_, std::adopt_lock);
11606f6ba60Sopenharmony_ci        const std::string& pluginDir = wdToDir_[event->wd];
11706f6ba60Sopenharmony_ci        guard.unlock();
11806f6ba60Sopenharmony_ci        if (event->mask & IN_ISDIR) {
11906f6ba60Sopenharmony_ci            continue;
12006f6ba60Sopenharmony_ci        }
12106f6ba60Sopenharmony_ci        std::string fileName = event->name;
12206f6ba60Sopenharmony_ci        size_t pos = fileName.rfind(".so");
12306f6ba60Sopenharmony_ci        if ((pos == std::string::npos) || (pos != fileName.length() - strlen(".so"))) {
12406f6ba60Sopenharmony_ci            continue;
12506f6ba60Sopenharmony_ci        }
12606f6ba60Sopenharmony_ci        switch (event->mask) {
12706f6ba60Sopenharmony_ci            case IN_CLOSE_WRITE:
12806f6ba60Sopenharmony_ci            case IN_MOVED_TO:
12906f6ba60Sopenharmony_ci                OnPluginAdded(pluginDir + '/' + fileName);
13006f6ba60Sopenharmony_ci                break;
13106f6ba60Sopenharmony_ci            case IN_DELETE:
13206f6ba60Sopenharmony_ci            case IN_MOVED_FROM:
13306f6ba60Sopenharmony_ci                OnPluginRemoved(pluginDir + '/' + fileName);
13406f6ba60Sopenharmony_ci                break;
13506f6ba60Sopenharmony_ci            default:
13606f6ba60Sopenharmony_ci                break;
13706f6ba60Sopenharmony_ci        }
13806f6ba60Sopenharmony_ci    }
13906f6ba60Sopenharmony_ci    if (memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)) != 0) {
14006f6ba60Sopenharmony_ci        PROFILER_LOG_ERROR(LOG_CORE, "%s:memset_s error!", __func__);
14106f6ba60Sopenharmony_ci    }
14206f6ba60Sopenharmony_ci    return true;
14306f6ba60Sopenharmony_ci}
14406f6ba60Sopenharmony_ci
14506f6ba60Sopenharmony_civoid PluginWatcher::Monitor()
14606f6ba60Sopenharmony_ci{
14706f6ba60Sopenharmony_ci    struct timeval time;
14806f6ba60Sopenharmony_ci
14906f6ba60Sopenharmony_ci    pthread_setname_np(pthread_self(), "PluginWatcher");
15006f6ba60Sopenharmony_ci    while (runMonitor_) {
15106f6ba60Sopenharmony_ci        fd_set rFds;
15206f6ba60Sopenharmony_ci        FD_ZERO(&rFds);
15306f6ba60Sopenharmony_ci        FD_SET(inotifyFd_, &rFds);
15406f6ba60Sopenharmony_ci        time.tv_sec = 1;
15506f6ba60Sopenharmony_ci        time.tv_usec = 0;
15606f6ba60Sopenharmony_ci        int ret = select(inotifyFd_ + 1, &rFds, nullptr, nullptr, &time);
15706f6ba60Sopenharmony_ci        if (ret < 0) {
15806f6ba60Sopenharmony_ci            continue;
15906f6ba60Sopenharmony_ci        } else if (!ret) {
16006f6ba60Sopenharmony_ci            continue;
16106f6ba60Sopenharmony_ci        } else if (FD_ISSET(inotifyFd_, &rFds)) {
16206f6ba60Sopenharmony_ci            if (!MonitorIsSet()) {
16306f6ba60Sopenharmony_ci                continue;
16406f6ba60Sopenharmony_ci            }
16506f6ba60Sopenharmony_ci        }
16606f6ba60Sopenharmony_ci    }
16706f6ba60Sopenharmony_ci}
16806f6ba60Sopenharmony_ci
16906f6ba60Sopenharmony_civoid PluginWatcher::OnPluginAdded(const std::string& pluginPath)
17006f6ba60Sopenharmony_ci{
17106f6ba60Sopenharmony_ci    auto pluginManager = pluginManager_.lock();
17206f6ba60Sopenharmony_ci    if (pluginManager != nullptr) {
17306f6ba60Sopenharmony_ci        if (pluginManager->AddPlugin(pluginPath)) {
17406f6ba60Sopenharmony_ci            PROFILER_LOG_INFO(LOG_CORE, "%s:plugin %s add success!", __func__, pluginPath.c_str());
17506f6ba60Sopenharmony_ci        } else {
17606f6ba60Sopenharmony_ci            PROFILER_LOG_INFO(LOG_CORE, "%s:pluginPath %s add failed!", __func__, pluginPath.c_str());
17706f6ba60Sopenharmony_ci        }
17806f6ba60Sopenharmony_ci    } else {
17906f6ba60Sopenharmony_ci        PROFILER_LOG_INFO(LOG_CORE, "%s:weak_ptr pluginManager lock failed!", __func__);
18006f6ba60Sopenharmony_ci    }
18106f6ba60Sopenharmony_ci}
18206f6ba60Sopenharmony_ci
18306f6ba60Sopenharmony_civoid PluginWatcher::OnPluginRemoved(const std::string& pluginPath)
18406f6ba60Sopenharmony_ci{
18506f6ba60Sopenharmony_ci    auto pluginManager = pluginManager_.lock();
18606f6ba60Sopenharmony_ci    if (pluginManager != nullptr) {
18706f6ba60Sopenharmony_ci        if (pluginManager->RemovePlugin(pluginPath)) {
18806f6ba60Sopenharmony_ci            PROFILER_LOG_INFO(LOG_CORE, "%s:pluginPath %s remove success!", __func__, pluginPath.c_str());
18906f6ba60Sopenharmony_ci        } else {
19006f6ba60Sopenharmony_ci            PROFILER_LOG_INFO(LOG_CORE, "%s:pluginPath %s remove failed!", __func__, pluginPath.c_str());
19106f6ba60Sopenharmony_ci        }
19206f6ba60Sopenharmony_ci    } else {
19306f6ba60Sopenharmony_ci        PROFILER_LOG_INFO(LOG_CORE, "%s:weak_ptr pluginManager lock failed!", __func__);
19406f6ba60Sopenharmony_ci    }
19506f6ba60Sopenharmony_ci}