/** * handle_overwritten: * @data: a pointer to user data (#handle_context). * @path: file name of the overwritten file. * @node: inode number of the overwritten file. * * A callback function for the directory diff calculation routine, * produces G_FILE_MONITOR_EVENT_DELETED/CREATED event pair when * an overwrite occurs in the directory (see dep-list for details). **/ static void handle_overwritten (void *udata, const char *path, ino_t inode) { handle_ctx *ctx = NULL; GFile *file = NULL; gchar *fpath = NULL; (void) inode; ctx = (handle_ctx *) udata; g_assert (udata != NULL); g_assert (ctx->sub != NULL); g_assert (ctx->monitor != NULL); fpath = _ku_path_concat (ctx->sub->filename, path); if (fpath == NULL) { KH_W ("Failed to allocate a string for a new event"); return; } file = g_file_new_for_path (fpath); g_file_monitor_emit_event (ctx->monitor, file, NULL, G_FILE_MONITOR_EVENT_DELETED); g_file_monitor_emit_event (ctx->monitor, file, NULL, G_FILE_MONITOR_EVENT_CREATED); g_free (fpath); g_object_unref (file); }
static void ih_event_callback (ik_event_t *event, inotify_sub *sub, gboolean file_event) { gchar *fullpath; GFileMonitorEvent eflags; GFile* child; GFile* other; eflags = ih_mask_to_EventFlags (event->mask); fullpath = _ih_fullpath_from_event (event, sub->dirname, file_event ? sub->filename : NULL); child = g_file_new_for_path (fullpath); g_free (fullpath); if (ih_event_is_paired_move (event) && sub->pair_moves) { const char *parent_dir = (char *) _ip_get_path_for_wd (event->pair->wd); fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL); other = g_file_new_for_path (fullpath); g_free (fullpath); eflags = G_FILE_MONITOR_EVENT_MOVED; event->pair = NULL; /* prevents the paired event to be emitted as well */ } else other = NULL; g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, other, eflags); /* For paired moves or moves whose mask has been changed from IN_MOVED_TO to * IN_CREATE, notify also that it's probably the last change to the file, * emitting CHANGES_DONE_HINT. * The first (first part of the if's guard below) is the case of a normal * move within the monitored tree and in the same mounted volume. * The latter (second part of the guard) is the case of a move within the * same mounted volume, but from a not monitored directory. * * It's not needed in cases like moves across mounted volumes as the IN_CREATE * will be followed by a IN_MODIFY and IN_CLOSE_WRITE events. * Also not needed if sub->pair_moves is set as EVENT_MOVED will be emitted * instead of EVENT_CREATED which implies no further modification will be * applied to the file * See: https://bugzilla.gnome.org/show_bug.cgi?id=640077 */ if ((!sub->pair_moves && event->is_second_in_pair && (event->mask & IN_MOVED_TO)) || (!ih_event_is_paired_move (event) && (event->original_mask & IN_MOVED_TO) && (event->mask & IN_CREATE))) { g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, NULL, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT); } g_object_unref (child); if (other) g_object_unref (other); }
/** * handle_moved: * @udata: a pointer to user data (#handle_context). * @from_path: file name of the source file. * @from_inode: inode number of the source file. * @to_path: file name of the replaced file. * @to_inode: inode number of the replaced file. * * A callback function for the directory diff calculation routine, * produces G_FILE_MONITOR_EVENT_MOVED event on a move. **/ static void handle_moved (void *udata, const char *from_path, ino_t from_inode, const char *to_path, ino_t to_inode) { handle_ctx *ctx = NULL; GFile *file = NULL; GFile *other = NULL; gchar *path = NULL; gchar *npath = NULL; (void) from_inode; (void) to_inode; ctx = (handle_ctx *) udata; g_assert (udata != NULL); g_assert (ctx->sub != NULL); g_assert (ctx->monitor != NULL); path = _ku_path_concat (ctx->sub->filename, from_path); npath = _ku_path_concat (ctx->sub->filename, to_path); if (path == NULL || npath == NULL) { KH_W ("Failed to allocate strings for event"); return; } file = g_file_new_for_path (path); other = g_file_new_for_path (npath); if (ctx->sub->pair_moves) { g_file_monitor_emit_event (ctx->monitor, file, other, G_FILE_MONITOR_EVENT_MOVED); } else { g_file_monitor_emit_event (ctx->monitor, file, NULL, G_FILE_MONITOR_EVENT_DELETED); g_file_monitor_emit_event (ctx->monitor, other, NULL, G_FILE_MONITOR_EVENT_CREATED); } g_free (path); g_free (npath); g_object_unref (file); g_object_unref (other); }
static void g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor, gpointer user_data) { GLocalFileMonitor *local_monitor = user_data; GUnixMountEntry *mount; gboolean is_mounted; GFile *file; /* Emulate unmount detection */ mount = g_unix_mount_at (local_monitor->source->dirname, NULL); is_mounted = mount != NULL; if (mount) g_unix_mount_free (mount); if (local_monitor->was_mounted != is_mounted) { if (local_monitor->was_mounted && !is_mounted) { file = g_file_new_for_path (local_monitor->source->dirname); g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED); g_object_unref (file); } local_monitor->was_mounted = is_mounted; } }
static void thunar_uca_provider_child_watch (ThunarUcaProvider *uca_provider, gint exit_status) { GFileMonitor *monitor; GFile *file; g_return_if_fail (THUNAR_UCA_IS_PROVIDER (uca_provider)); GDK_THREADS_ENTER (); /* verify that we still have a valid child_watch_path */ if (G_LIKELY (uca_provider->child_watch_path != NULL)) { /* determine the corresponding file */ file = g_file_new_for_path (uca_provider->child_watch_path); /* schedule a changed notification on the path */ monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL); if (monitor != NULL) { g_file_monitor_emit_event (monitor, file, file, G_FILE_MONITOR_EVENT_CHANGED); g_object_unref (monitor); } /* release the file */ g_object_unref (file); } thunar_uca_provider_child_watch_destroy (uca_provider, NULL); GDK_THREADS_LEAVE (); }
static void ih_not_missing_callback (inotify_sub *sub) { gchar *fullpath; GFileMonitorEvent eflags; guint32 mask; GFile* child; if (sub->filename) { fullpath = g_strdup_printf ("%s/%s", sub->dirname, sub->filename); g_warning ("Missing callback called fullpath = %s\n", fullpath); if (!g_file_test (fullpath, G_FILE_TEST_EXISTS)) { g_free (fullpath); return; } mask = IN_CREATE; } else { fullpath = g_strdup_printf ("%s", sub->dirname); mask = IN_CREATE|IN_ISDIR; } eflags = ih_mask_to_EventFlags (mask); child = g_file_new_for_path (fullpath); g_free (fullpath); g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, NULL, eflags); g_object_unref (child); }
static void ih_event_callback (ik_event_t *event, inotify_sub *sub) { gchar *fullpath; GFileMonitorEvent eflags; GFile* parent; GFile* child; eflags = ih_mask_to_EventFlags (event->mask); parent = g_file_new_for_path (sub->dirname); if (event->name) fullpath = g_strdup_printf ("%s/%s", sub->dirname, event->name); else fullpath = g_strdup_printf ("%s/", sub->dirname); child = g_file_new_for_path (fullpath); g_free (fullpath); g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, NULL, eflags); g_object_unref (child); g_object_unref (parent); }
static void node_emit_one_event(node_t *f, GList *subs, node_t *other, int event) { GList* idx; FN_W ("%s %s %d\n", __func__, NODE_NAME(f), event); #ifdef GIO_COMPILATION for (idx = subs; idx; idx = idx->next) { g_file_monitor_emit_event(G_FILE_MONITOR(idx->data), f->gfile, (other == NULL ? NULL : other->gfile), event); } #else for (idx = subs; idx; idx = idx->next) { gam_server_emit_one_event(NODE_NAME(f), gam_subscription_is_dir(idx->data), event, idx->data, 1); } #endif }
/** * _kh_file_appeared_cb: * @sub: a #kqueue_sub * * A callback function for kqueue-missing subsystem. * * Signals that a missing file has finally appeared in the filesystem. * Emits %G_FILE_MONITOR_EVENT_CREATED. **/ void _kh_file_appeared_cb (kqueue_sub *sub) { GFile* child; g_assert (sub != NULL); g_assert (sub->filename); if (!g_file_test (sub->filename, G_FILE_TEST_EXISTS)) return; child = g_file_new_for_path (sub->filename); g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), child, NULL, G_FILE_MONITOR_EVENT_CREATED); g_object_unref (child); }
static void mounts_changed (GUnixMountMonitor *mount_monitor, gpointer user_data) { GLocalDirectoryMonitor *local_monitor = user_data; GUnixMountEntry *mount; gboolean is_mounted; GFile *file; /* Emulate unmount detection */ #ifdef G_OS_WIN32 mount = NULL; /*claim everything was mounted */ is_mounted = TRUE; #else mount = g_unix_mount_at (local_monitor->dirname, NULL); is_mounted = mount != NULL; if (mount) g_unix_mount_free (mount); #endif if (local_monitor->was_mounted != is_mounted) { if (local_monitor->was_mounted && !is_mounted) { file = g_file_new_for_path (local_monitor->dirname); g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED); g_object_unref (file); } local_monitor->was_mounted = is_mounted; } }
static void CALLBACK g_win32_directory_monitor_callback (DWORD error, DWORD nBytes, LPOVERLAPPED lpOverlapped) { gulong offset; PFILE_NOTIFY_INFORMATION pfile_notify_walker; glong file_name_len; gchar *file_name; gchar *path; GFile *file; GWin32DirectoryMonitorPrivate *priv = (GWin32DirectoryMonitorPrivate *) lpOverlapped; static GFileMonitorEvent events[] = { 0, G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_ADDED */ G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_REMOVED */ G_FILE_MONITOR_EVENT_CHANGED, /* FILE_ACTION_MODIFIED */ G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_RENAMED_OLD_NAME */ G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_RENAMED_NEW_NAME */ }; /* If priv->self is NULL the GWin32DirectoryMonitor object has been destroyed. */ if (priv->self == NULL || g_file_monitor_is_cancelled (priv->self) || priv->file_notify_buffer == NULL) { g_free (priv->file_notify_buffer); g_free (priv); return; } offset = 0; do { pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)(priv->file_notify_buffer + offset); if (pfile_notify_walker->Action > 0) { file_name = g_utf16_to_utf8 (pfile_notify_walker->FileName, pfile_notify_walker->FileNameLength / sizeof(WCHAR), NULL, &file_name_len, NULL); path = g_build_filename(G_LOCAL_DIRECTORY_MONITOR (priv->self)->dirname, file_name, NULL); file = g_file_new_for_path (path); g_file_monitor_emit_event (priv->self, file, NULL, events [pfile_notify_walker->Action]); g_object_unref (file); g_free (path); g_free (file_name); } offset += pfile_notify_walker->NextEntryOffset; } while (pfile_notify_walker->NextEntryOffset); ReadDirectoryChangesW (priv->hDirectory, (gpointer)priv->file_notify_buffer, priv->buffer_allocated_bytes, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE, &priv->buffer_filled_bytes, &priv->overlapped, g_win32_directory_monitor_callback); }
static gboolean g_file_monitor_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { GFileMonitorSource *fms = (GFileMonitorSource *) source; QueuedEvent *event; GQueue event_queue; gint64 now; /* make sure the monitor still exists */ if (!fms->instance) return FALSE; now = g_source_get_time (source); /* Acquire the lock once and grab all events in one go, handling the * queued events first. This avoids strange possibilities in cases of * long delays, such as CHANGED events coming before CREATED events. * * We do this by converting the applicable pending changes into queued * events (after the ones already queued) and then stealing the entire * event queue in one go. */ g_mutex_lock (&fms->lock); /* Create events for any pending changes that are due to fire */ while (!g_sequence_is_empty (fms->pending_changes)) { GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes); PendingChange *pending = g_sequence_get (iter); /* We've gotten to a pending change that's not ready. Stop. */ if (pending_change_get_ready_time (pending, fms) > now) break; if (pending->dirty) { /* It's time to send another CHANGED and update the record */ g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL); pending->last_emission = now; pending->dirty = FALSE; g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms); } else { /* It's time to send CHANGES_DONE and remove the pending record */ g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL); g_file_monitor_source_remove_pending_change (fms, iter, pending->child); } } /* Steal the queue */ memcpy (&event_queue, &fms->event_queue, sizeof event_queue); memset (&fms->event_queue, 0, sizeof fms->event_queue); g_file_monitor_source_update_ready_time (fms); g_mutex_unlock (&fms->lock); /* We now have our list of events to deliver */ while ((event = g_queue_pop_head (&event_queue))) { /* an event handler could destroy 'instance', so check each time */ if (fms->instance) g_file_monitor_emit_event (fms->instance, event->child, event->other, event->event_type); queued_event_free (event); } return TRUE; }
/** * process_kqueue_notifications: * @gioc: unused. * @cond: unused. * @data: unused. * * Processes notifications, coming from the kqueue thread. * * Reads notifications from the command file descriptor, emits the * "changed" event on the appropriate monitor. * * A typical GIO Channel callback function. * * Returns: %TRUE **/ static gboolean process_kqueue_notifications (GIOChannel *gioc, GIOCondition cond, gpointer data) { struct kqueue_notification n; kqueue_sub *sub = NULL; GFileMonitor *monitor = NULL; GFileMonitorEvent mask = 0; g_assert (kqueue_socket_pair[0] != -1); if (!_ku_read (kqueue_socket_pair[0], &n, sizeof (struct kqueue_notification))) { KH_W ("Failed to read a kqueue notification, error %d", errno); return TRUE; } G_LOCK (hash_lock); sub = (kqueue_sub *) g_hash_table_lookup (subs_hash_table, GINT_TO_POINTER (n.fd)); G_UNLOCK (hash_lock); if (sub == NULL) { KH_W ("Got a notification for a deleted or non-existing subscription %d", n.fd); return TRUE; } monitor = G_FILE_MONITOR (sub->user_data); g_assert (monitor != NULL); if (n.flags & (NOTE_DELETE | NOTE_REVOKE)) { if (sub->deps) { dl_free (sub->deps); sub->deps = NULL; } _km_add_missing (sub); if (!(n.flags & NOTE_REVOKE)) { /* Note that NOTE_REVOKE is issued by the kqueue thread * on EV_ERROR kevent. In this case, a file descriptor is * already closed from the kqueue thread, no need to close * it manually */ _kh_cancel_sub (sub); } } if (sub->is_dir && n.flags & (NOTE_WRITE | NOTE_EXTEND)) { _kh_dir_diff (sub, monitor); n.flags &= ~(NOTE_WRITE | NOTE_EXTEND); } if (n.flags) { gboolean done = FALSE; mask = convert_kqueue_events_to_gio (n.flags, &done); if (done == TRUE) { GFile *file = g_file_new_for_path (sub->filename); g_file_monitor_emit_event (monitor, file, NULL, mask); g_object_unref (file); } } return TRUE; }