/* * file_watcher.c * This file is part of Network-inador * * Copyright (C) 2025 - Félix Arreola Rodríguez * * Network-inador is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Network-inador is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Network-inador; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "file_watcher.h" #include "network-inador-private.h" static void network_inador_file_watcher_deliver_event (NetworkInadorHandle *handle, struct inotify_event *event, INotifyWD *inotwd) { char path[4096]; if (event->name[0] == 0) { snprintf (path, sizeof (path), "%s", inotwd->path); } else { snprintf (path, sizeof (path), "%s/%s", inotwd->path, event->name); } /* Este es el que pidió cierre de escritura */ if (event->mask & IN_CLOSE_WRITE) { if (inotwd->close_write_cb != NULL) { inotwd->close_write_cb (handle, path, inotwd->close_write_data); } } if ((event->mask & IN_IGNORED) || (event->mask & IN_DELETE_SELF) || (event->mask & IN_MOVE_SELF)) { /* El evento fué eliminado, quitar de la lista de wds */ handle->file_watcher.wds = f_list_remove (handle->file_watcher.wds, inotwd); } } static void network_inador_file_watcher_inotify_read_cb (void *data, int fd, int condition) { NetworkInadorHandle *handle = (NetworkInadorHandle *) data; int res, g, n; struct inotify_event events[20]; FList *p, *next; INotifyWD *inotwd; inotify_read_again: res = read (handle->file_watcher.inotify_fd, events, sizeof (events)); /* Nada que leer */ if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return; if (res <= 0) { /* El inotify fué cerrado, cancelar la vigilancia y quitar el source */ handle->ops.input_remove (handle->file_watcher.inotify_source); handle->file_watcher.inotify_source = 0; f_list_free_full (handle->file_watcher.wds, free); handle->file_watcher.has_inotify = FALSE; close (handle->file_watcher.inotify_fd); handle->file_watcher.inotify_fd = -1; return; } n = res / sizeof (events[0]); for (g = 0; g < n; g++) { p = handle->file_watcher.wds; while (p != NULL) { next = p->next; inotwd = (INotifyWD *) p->data; if (inotwd->inotify_wd == events[g].wd) { network_inador_file_watcher_deliver_event (handle, &events[g], inotwd); break; } p = next; } } if (res == sizeof (events)) goto inotify_read_again; } void network_inador_file_watcher_init (NetworkInadorHandle *handle) { int fd; if (handle->ops.input_add != NULL) { /* Como tenemos vigilancia de fds, podemos intentar inicializar el inotify */ fd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC); if (fd < 0) { handle->file_watcher.has_inotify = FALSE; handle->file_watcher.inotify_fd = -1; return; } handle->file_watcher.inotify_source = handle->ops.input_add (fd, POLLIN, network_inador_file_watcher_inotify_read_cb, handle); handle->file_watcher.has_inotify = TRUE; handle->file_watcher.inotify_fd = fd; } } void network_inador_file_watcher_add_file (NetworkInadorHandle *handle, const char *path, FileWatcherCloseWriteCB close_write_cb, void *close_write_data) { int res, wd; INotifyWD *inotwd; if (handle->file_watcher.has_inotify == FALSE) return; wd = inotify_add_watch (handle->file_watcher.inotify_fd, path, IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MOVE_SELF); if (wd < 0) return; /* No lo pude agregar */ inotwd = (INotifyWD *) malloc (sizeof (INotifyWD)); if (inotwd == NULL) { inotify_rm_watch (handle->file_watcher.inotify_fd, wd); return; } memset (inotwd, 0, sizeof (INotifyWD)); inotwd->inotify_wd = wd; inotwd->close_write_cb = close_write_cb; inotwd->close_write_data = close_write_data; strncpy (inotwd->path, path, sizeof (inotwd->path)); handle->file_watcher.wds = f_list_append (handle->file_watcher.wds, inotwd); } void network_inador_file_watcher_cleanup (NetworkInadorHandle *handle) { if (handle->file_watcher.has_inotify) { close (handle->file_watcher.inotify_fd); handle->file_watcher.inotify_fd = -1; if (handle->file_watcher.inotify_source != 0 && handle->ops.input_remove != NULL) { handle->ops.input_remove (handle->file_watcher.inotify_source); handle->file_watcher.inotify_source = 0; } } }