+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed 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 "launchpad-process-pool/file_monitor.hh"
+
+#include "lib/common/inc/launchpad_common.h"
+
+namespace launchpad {
+
+namespace fs = std::filesystem;
+
+gboolean FileMonitor::OnEventReceived(GIOChannel* channel,
+ GIOCondition cond,
+ gpointer user_data) {
+ char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
+ char* ptr;
+ ssize_t len;
+ struct inotify_event* event;
+ auto event_listener = reinterpret_cast<FileMonitor*>(user_data);
+ int fd = g_io_channel_unix_get_fd(channel);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ for (ptr = buf; ptr < buf + len;
+ ptr += sizeof(struct inotify_event) + event->len) {
+ event = reinterpret_cast<struct inotify_event*>(ptr);
+ char* nptr = ptr + sizeof(struct inotify_event) + event->len;
+ if (nptr > buf + len)
+ break;
+
+ if (!event->len || event_listener->file_name_ != event->name)
+ continue;
+
+ if (event->mask & IN_CREATE) {
+ if (event_listener->listener_)
+ event_listener->listener_->OnFileChanged(FileMonitorEvent::Created);
+ } else if (event->mask & IN_MODIFY) {
+ if (event_listener->listener_)
+ event_listener->listener_->OnFileChanged(FileMonitorEvent::Modified);
+ } else if (event->mask & IN_DELETE) {
+ if (event_listener->listener_)
+ event_listener->listener_->OnFileChanged(FileMonitorEvent::Deleted);
+ }
+
+ return G_SOURCE_CONTINUE;
+ }
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+FileMonitor::FileMonitor(const std::string_view file_path,
+ FileMonitor::IEvent* listener)
+ : listener_(listener) {
+ fs::path path(file_path);
+ parent_path_ = path.parent_path();
+ file_name_ = path.filename();
+ if (!fs::exists(parent_path_)) {
+ if (!fs::create_directories(parent_path_)) {
+ _E("Failed to create directory. %s", parent_path_.c_str());
+ return;
+ }
+ }
+
+ if (fs::exists(path)) {
+ if (listener_)
+ listener_->OnFileChanged(FileMonitor::FileMonitorEvent::Created);
+ }
+
+ fd_ = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (fd_ == -1) {
+ int ret = errno;
+ _E("Failed to inotify_init. errno: %d", ret);
+ disposed_ = true;
+ return;
+ }
+
+ wd_ = inotify_add_watch(fd_, parent_path_.c_str(),
+ IN_CREATE | IN_MODIFY | IN_DELETE);
+ if (wd_ == -1) {
+ int ret = errno;
+ _E("Failed to inotify_add_watch. errno: %d", ret);
+ Dispose();
+ return;
+ }
+
+ channel_ = g_io_channel_unix_new(fd_);
+ if (channel_ == nullptr) {
+ _E("Failed to create GIO channel");
+ Dispose();
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(channel_, false);
+ tag_ = g_io_add_watch(channel_, G_IO_IN, OnEventReceived, this);
+ if (tag_ == 0) {
+ _E("Failed to add watch");
+ Dispose();
+ return;
+ }
+
+ disposed_ = false;
+}
+
+FileMonitor::~FileMonitor() {
+ Dispose();
+}
+
+void FileMonitor::Dispose() {
+ if (disposed_)
+ return;
+
+ if (tag_) {
+ g_source_remove(tag_);
+ tag_ = 0;
+ }
+
+ if (channel_ != nullptr) {
+ g_io_channel_unref(channel_);
+ channel_ = nullptr;
+ }
+
+ if (wd_ > 0) {
+ inotify_rm_watch(fd_, wd_);
+ wd_ = 0;
+ }
+
+ if (fd_ > 0) {
+ close(fd_);
+ fd_ = 0;
+ }
+
+ disposed_ = true;
+}
+
+} // namespace launchpad