8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek Copyright (C) 2016 Red Hat
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek This program is free software; you can redistribute it and/or modify
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek it under the terms of the GNU General Public License as published by
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek the Free Software Foundation; either version 3 of the License, or
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek (at your option) any later version.
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek This program is distributed in the hope that it will be useful,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek but WITHOUT ANY WARRANTY; without even the implied warranty of
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek GNU General Public License for more details.
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek You should have received a copy of the GNU General Public License
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek along with this program. If not, see <http://www.gnu.org/licenses/>.
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek/* For parent directories, we want to know if a file was moved there or
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * created there
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek#define PARENT_DIR_MASK (IN_CREATE | IN_MOVED_TO)
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek/* This structure is recreated if we need to rewatch the file and/or
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek struct tevent_fd *tfd; /* Activity on the fd */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek struct snotify_ctx *snctx; /* Pointer up to the main snotify struct */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* In case we're also watching the parent directory, otherwise -1.
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * We keep the variable here and not in snctx so that we're able
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * to catch even changes to the parent directory
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* The file watch */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek/* This is what we call when an event we're interested in arrives */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek/* One instance of a callback. We hoard the inotify notifications
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * until timer fires in caught_flags
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* The full path of the file we're watching,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * its file and directory components */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Private pointer passed to the callback */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* A singleton callback dispatcher */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Internal snotify flags */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* The caller might decide to batch the updates and receive
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * them all together with a delay
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* We keep the structure that actually does the work
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * separately to be able to reinitialize it when the
346d6d8bf5fdb446921d754c07c8a7d913a048d5René Genz * file is recreated or moved to the directory
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic void debug_flags(uint32_t flags, const char *file)
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_LIBS, "Inotify event: %s on %s\n", msgbuf, file);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic void snotify_process_callbacks(struct tevent_context *ev,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek snctx = talloc_get_type(ptr, struct snotify_ctx);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic struct snotify_dispatcher *create_dispatcher(struct snotify_ctx *snctx)
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek disp = talloc_zero(snctx, struct snotify_dispatcher);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Running a timer with delay %ld.%ld\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek disp->te = tevent_add_timer(snctx->ev, disp, tv,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n");
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic struct snotify_dispatcher *get_dispatcher(struct snotify_ctx *snctx)
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_INTERNAL, "Reusing existing dispatcher\n");
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic errno_t dispatch_event(struct snotify_ctx *snctx,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Dispatched an event with combined flags 0x%X\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic errno_t process_dir_event(struct snotify_ctx *snctx,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_ALL, "inotify name: %s\n", in_event->name);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek || strcmp(in_event->name, snctx->base_name) != 0) {
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_FUNC, "Not interested in %s\n", in_event->name);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "received notification for watched file [%s] under %s\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* file the event for the file to see if the caller is interested in it */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Tells the outer loop to re-initialize flags once the loop is finished.
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * However, finish reading all the events first to make sure we don't
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic errno_t process_file_event(struct snotify_ctx *snctx,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Will reopen moved or deleted file %s\n", snctx->filename);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Notify caller of the event, don't quit */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "received notification for watched file %s\n", snctx->filename);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic errno_t snotify_rewatch(struct snotify_ctx *snctx);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic void snotify_internal_cb(struct tevent_context *ev,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek char ev_buf[sizeof(struct inotify_event) + PATH_MAX];
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek snctx = talloc_get_type(data, struct snotify_ctx);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek len = read(snctx->wctx->inotify_fd, ev_buf, sizeof(ev_buf));
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Cannot read inotify_event [%d]: %s\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_INTERNAL, "All inotify events processed\n");
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek if ((size_t) len < sizeof(struct inotify_event)) {
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Did not even read the required amount of data, move on.. */
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek ptr += sizeof(struct inotify_event) + in_event->len) {
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek //debug_flags(in_event->mask, in_event->name);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Continue with the loop and read all the events from
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * this descriptor first, then rewatch when done
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Failed to process inotify event\n");
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek } else if (snctx->wctx->file_wd == in_event->wd) {
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Failed to process inotify event\n");
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Failed to re-set watch");
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek wctx = talloc_get_type(memptr, struct snotify_watch_ctx);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* We don't need to close the watches explicitly. man 7 inotify says:
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * When all file descriptors referring to an inotify instance
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * have been closed (using close(2)), the underlying object
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * and its resources are freed for reuse by the kernel; all
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * associated watches are automatically freed.
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic errno_t copy_filenames(struct snotify_ctx *snctx,
c53997720b231ad61af435a3124c7ecd731fc99bLukas Slebodnik strncpy(fcopy, filename, sizeof(fcopy) - 1);
c53997720b231ad61af435a3124c7ecd731fc99bLukas Slebodnik strncpy(fcopy, filename, sizeof(fcopy) - 1);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek snctx->filename = talloc_strdup(snctx, filename);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic struct snotify_watch_ctx *snotify_watch(struct snotify_ctx *snctx,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek wctx = talloc_zero(snctx, struct snotify_watch_ctx);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek talloc_set_destructor((TALLOC_CTX *)wctx, watch_ctx_destructor);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek wctx->inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "inotify_init1 failed: %d: %s\n", ret, strerror(ret));
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_INTERNAL, "Opened inotify fd %d\n", wctx->inotify_fd);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek wctx->tfd = tevent_add_fd(snctx->ev, wctx, wctx->inotify_fd,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Cannot add tevent fd watch for %s\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek wctx->file_wd = inotify_add_watch(wctx->inotify_fd, snctx->filename, mask);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek if (ret != ENOENT || (!(snctx->snotify_flags & SNOTIFY_WATCH_DIR))) {
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "inotify_add_watch failed [%d]: %s\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek DEBUG(SSSDBG_TRACE_INTERNAL, "Opened file watch %d\n", wctx->file_wd);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek if (snctx->snotify_flags & SNOTIFY_WATCH_DIR) {
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek /* Create a watch for the parent directory. This is useful for cases
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * where we start watching a file before it's created, but still want
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek * a notification when the file is moved in
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek wctx->dir_wd = inotify_add_watch(wctx->inotify_fd,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "inotify_add_watch failed [%d]: %s\n",
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstatic errno_t snotify_rewatch(struct snotify_ctx *snctx)
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek snctx->wctx = snotify_watch(snctx, snctx->cb.mask);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozekstruct snotify_ctx *_snotify_create(TALLOC_CTX *mem_ctx,
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek snctx = talloc_zero(mem_ctx, struct snotify_ctx);
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "Added a watch for %s with inotify flags 0x%X "
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "internal flags 0x%X "
8cfb42e1985550e99585d311f68087d414932806Jakub Hrozek "using function %s after delay %ld.%ld\n",