blob: da06bbc1e6042bf1c3b4855c561093931554404b [file] [log] [blame]
#include <stdexcept>
#include <cstddef>
#include <cstring>
#include <string>
#include <sys/inotify.h>
#include <unistd.h>
#include <experimental/filesystem>
#include <phosphor-logging/log.hpp>
#include "config.h"
#include "watch.hpp"
namespace openpower
{
namespace software
{
namespace updater
{
using namespace phosphor::logging;
namespace fs = std::experimental::filesystem;
Watch::Watch(sd_event* loop,
std::function<void(std::string&)> functionalCallback) :
functionalCallback(functionalCallback),
fd(inotifyInit())
{
// Create PNOR_ACTIVE_PATH if doesn't exist.
if (!fs::is_directory(PNOR_ACTIVE_PATH))
{
fs::create_directories(PNOR_ACTIVE_PATH);
}
wd = inotify_add_watch(fd(), PNOR_ACTIVE_PATH, IN_CREATE);
if (-1 == wd)
{
auto error = errno;
throw std::system_error(error,
std::generic_category(),
"Error occurred during the inotify_init1");
}
decltype(eventSource.get()) sourcePtr = nullptr;
auto rc = sd_event_add_io(loop,
&sourcePtr,
fd(),
EPOLLIN,
callback,
this);
eventSource.reset(sourcePtr);
if (0 > rc)
{
throw std::system_error(-rc,
std::generic_category(),
"Error occurred during the inotify_init1");
}
}
Watch::~Watch()
{
if ((-1 != fd()) && (-1 != wd))
{
inotify_rm_watch(fd(), wd);
}
}
int Watch::callback(sd_event_source* s,
int fd,
uint32_t revents,
void* userdata)
{
if (!(revents & EPOLLIN))
{
return 0;
}
constexpr auto maxBytes = 1024;
uint8_t buffer[maxBytes];
auto bytes = read(fd, buffer, maxBytes);
if (0 > bytes)
{
auto error = errno;
throw std::system_error(error,
std::generic_category(),
"failed to read inotify event");
}
auto offset = 0;
while (offset < bytes)
{
auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
// Update the functional association on a RO
// active image symlink change
fs::path path(PNOR_ACTIVE_PATH);
path /= event->name;
if (fs::equivalent(path, PNOR_RO_ACTIVE_PATH))
{
auto target = fs::canonical(path).string();
// Get the image <id> from the symlink target
// for example /media/ro-2a1022fe
static const auto PNOR_RO_PREFIX_LEN = strlen(PNOR_RO_PREFIX);
auto id = target.substr(PNOR_RO_PREFIX_LEN);
auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id;
static_cast<Watch*>(userdata)->functionalCallback(objPath);
}
offset += offsetof(inotify_event, name) + event->len;
}
return 0;
}
int Watch::inotifyInit()
{
auto fd = inotify_init1(IN_NONBLOCK);
if (-1 == fd)
{
auto error = errno;
throw std::system_error(error,
std::generic_category(),
"Error occurred during the inotify_init1");
}
return fd;
}
} // namespace updater
} // namespace software
} // namespace openpower