mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-04 18:28:12 +02:00
Otherwise, the following warning is observed with musl headers, which translates to an error on Android. external/musl/include/sys/poll.h:1:2: error: redirecting incorrect #include <sys/poll.h> to <poll.h> [-Werror,-W#warnings] warning redirecting incorrect #include <sys/poll.h> to <poll.h> Reviewed-by: Eric Engestrom <eric@igalia.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39279>
254 lines
7.5 KiB
C
254 lines
7.5 KiB
C
/*
|
|
* Copyright © 2025 Igalia S.L.
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "os_file_notify.h"
|
|
#include "detect_os.h"
|
|
#include "log.h"
|
|
#include "u_thread.h"
|
|
|
|
#if DETECT_OS_LINUX
|
|
#include <sys/eventfd.h>
|
|
#include <sys/inotify.h>
|
|
|
|
#ifdef HAVE_POLL_H
|
|
#include <poll.h>
|
|
#else
|
|
#include <sys/poll.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdatomic.h>
|
|
#include <stdlib.h>
|
|
|
|
struct os_file_notifier {
|
|
int ifd;
|
|
int file_wd, dir_wd;
|
|
int efd;
|
|
os_file_notify_cb cb;
|
|
void *data;
|
|
thrd_t thread;
|
|
atomic_bool quit;
|
|
const char *filename;
|
|
char file_path[PATH_MAX], dir_path[PATH_MAX];
|
|
};
|
|
|
|
#define INOTIFY_BUF_LEN ((10 * (sizeof(struct inotify_event) + NAME_MAX + 1)))
|
|
|
|
static int
|
|
os_file_notifier_thread(void *data)
|
|
{
|
|
os_file_notifier_t notifier = data;
|
|
char buf[INOTIFY_BUF_LEN]
|
|
__attribute__((aligned(__alignof__(struct inotify_event))));
|
|
ssize_t len;
|
|
|
|
u_thread_setname("File Notifier");
|
|
|
|
/* To ensure the callback sets the file up to the initial state. */
|
|
if (access(notifier->file_path, F_OK) != -1) {
|
|
notifier->cb(notifier->data, notifier->file_path, false, false, false);
|
|
} else {
|
|
/* The file doesn't exist, we cannot watch it. */
|
|
notifier->cb(notifier->data, notifier->file_path, false, true, false);
|
|
}
|
|
|
|
while (!notifier->quit) {
|
|
/* Poll on the inotify file descriptor and the eventfd file descriptor. */
|
|
struct pollfd fds[] = {
|
|
{notifier->ifd, POLLIN, 0},
|
|
{notifier->efd, POLLIN, 0},
|
|
};
|
|
if (poll(fds, ARRAY_SIZE(fds), -1) == -1) {
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
continue;
|
|
|
|
mesa_logw("Failed to poll on file notifier FDs: %s",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (fds[1].revents & POLLIN) {
|
|
/* The eventfd is used to wake up the thread when the notifier is
|
|
* destroyed. */
|
|
eventfd_t val;
|
|
eventfd_read(notifier->efd, &val);
|
|
if (val == 1)
|
|
return 0; /* Quit the thread. */
|
|
}
|
|
|
|
len = read(notifier->ifd, buf, sizeof(buf));
|
|
if (len == -1) {
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
continue;
|
|
|
|
mesa_logw("Failed to read inotify events: %s", strerror(errno));
|
|
return -1;
|
|
} else {
|
|
for (char *ptr = buf; ptr < buf + len;
|
|
ptr += ((struct inotify_event *)ptr)->len +
|
|
sizeof(struct inotify_event)) {
|
|
struct inotify_event *event = (struct inotify_event *)ptr;
|
|
|
|
bool created = false, deleted = false, dir_deleted = false;
|
|
if (event->wd == notifier->dir_wd) {
|
|
/* Check if the event is about the directory itself or the
|
|
* file. */
|
|
if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {
|
|
/* The directory was deleted/moved, we cannot watch it or
|
|
* the file anymore. */
|
|
dir_deleted = true;
|
|
} else if (strcmp(event->name, notifier->filename) == 0) {
|
|
/* If it's a file event, ensure that the event is about the
|
|
* file we are watching. */
|
|
if (event->mask & IN_CREATE) {
|
|
created = true;
|
|
|
|
/* The file was just created, add a watch to it. */
|
|
notifier->file_wd = inotify_add_watch(
|
|
notifier->ifd, notifier->file_path, IN_CLOSE_WRITE);
|
|
if (notifier->file_wd == -1) {
|
|
mesa_logw("Failed to add inotify watch for file");
|
|
return -1;
|
|
}
|
|
} else if (event->mask & IN_DELETE) {
|
|
deleted = true;
|
|
|
|
/* The file was deleted, we cannot watch it anymore. */
|
|
inotify_rm_watch(notifier->ifd, notifier->file_wd);
|
|
notifier->file_wd = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
notifier->cb(notifier->data, notifier->file_path, created,
|
|
deleted, dir_deleted);
|
|
if (dir_deleted)
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
os_file_notifier_t
|
|
os_file_notifier_create(const char *file_path, os_file_notify_cb cb,
|
|
void *data, const char **error_str)
|
|
{
|
|
#define RET_ERR(s) \
|
|
do { \
|
|
if (error_str) { \
|
|
*error_str = s; \
|
|
} \
|
|
goto cleanup; \
|
|
} while (0)
|
|
|
|
os_file_notifier_t notifier = calloc(1, sizeof(struct os_file_notifier));
|
|
if (!notifier)
|
|
RET_ERR("Failed to allocate memory for file notifier");
|
|
notifier->ifd = -1;
|
|
notifier->efd = -1;
|
|
|
|
size_t path_len = strlen(file_path);
|
|
if (path_len == 0)
|
|
RET_ERR("File path is empty");
|
|
else if (path_len >= PATH_MAX)
|
|
RET_ERR("File path is longer than PATH_MAX");
|
|
|
|
memcpy(notifier->file_path, file_path, path_len + 1);
|
|
|
|
notifier->ifd = inotify_init1(IN_NONBLOCK);
|
|
if (notifier->ifd == -1)
|
|
RET_ERR("Failed to initialize inotify");
|
|
|
|
notifier->file_wd =
|
|
inotify_add_watch(notifier->ifd, notifier->file_path, IN_CLOSE_WRITE);
|
|
if (notifier->file_wd == -1 && errno != ENOENT)
|
|
RET_ERR("Failed to add inotify watch for file");
|
|
|
|
/* Determine the parent directory path of the file. */
|
|
char *last_slash = strrchr(notifier->file_path, '/');
|
|
if (last_slash) {
|
|
size_t len = last_slash - notifier->file_path;
|
|
memcpy(notifier->dir_path, notifier->file_path, len);
|
|
notifier->dir_path[len] = 0;
|
|
notifier->filename = last_slash + 1;
|
|
} else {
|
|
notifier->dir_path[0] = '.';
|
|
notifier->dir_path[1] = 0;
|
|
notifier->filename = notifier->file_path;
|
|
}
|
|
|
|
notifier->dir_wd =
|
|
inotify_add_watch(notifier->ifd, notifier->dir_path,
|
|
IN_CREATE | IN_MOVE | IN_DELETE | IN_DELETE_SELF |
|
|
IN_MOVE_SELF | IN_ONLYDIR);
|
|
if (notifier->dir_wd == -1) {
|
|
if (errno == ENOENT)
|
|
RET_ERR("The folder containing the watched file doesn't exist");
|
|
RET_ERR("Failed to add inotify watch for directory");
|
|
}
|
|
|
|
notifier->efd = eventfd(0, EFD_NONBLOCK);
|
|
if (notifier->efd == -1)
|
|
RET_ERR("Failed to create eventfd");
|
|
|
|
notifier->cb = cb;
|
|
notifier->data = data;
|
|
|
|
if (u_thread_create(¬ifier->thread, os_file_notifier_thread,
|
|
notifier) != 0)
|
|
RET_ERR("Failed to create file notifier thread");
|
|
|
|
return notifier;
|
|
|
|
cleanup:
|
|
if (notifier) {
|
|
if (notifier->ifd != -1)
|
|
close(notifier->ifd);
|
|
if (notifier->efd != -1)
|
|
close(notifier->efd);
|
|
free(notifier);
|
|
}
|
|
return NULL;
|
|
|
|
#undef RET_ERR
|
|
}
|
|
|
|
void
|
|
os_file_notifier_destroy(os_file_notifier_t notifier)
|
|
{
|
|
if (!notifier)
|
|
return;
|
|
|
|
notifier->quit = true;
|
|
eventfd_write(notifier->efd, 1);
|
|
thrd_join(notifier->thread, NULL);
|
|
|
|
close(notifier->ifd);
|
|
close(notifier->efd);
|
|
free(notifier);
|
|
}
|
|
|
|
#else /* !DETECT_OS_LINUX */
|
|
|
|
os_file_notifier_t
|
|
os_file_notifier_create(const char *file_path, os_file_notify_cb cb,
|
|
void *data, const char **error_str)
|
|
{
|
|
(void)file_path;
|
|
(void)cb;
|
|
(void)data;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
os_file_notifier_destroy(os_file_notifier_t notifier)
|
|
{
|
|
(void)notifier;
|
|
}
|
|
|
|
#endif
|