static CachedDir * cached_dir_lookup (const char *canonical) { CachedDir *dir; char **split; int i; if (dir_cache == NULL) dir_cache = cached_dir_new_full ("/", (GFunc) clear_cache, &dir_cache); dir = dir_cache; g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR); menu_verbose ("Looking up cached dir \"%s\"\n", canonical); split = g_strsplit (canonical + 1, "/", -1); i = 0; while (split[i] != NULL) { CachedDir *subdir; subdir = cached_dir_add_subdir (dir, split[i], NULL); dir = subdir; ++i; } g_strfreev (split); g_assert (dir != NULL); return dir; }
static gboolean cached_dir_load_entries_recursive (CachedDir *dir, const char *dirname) { DIR *dp; struct dirent *dent; GString *fullpath; gsize fullpath_len; g_assert (dir != NULL); if (dir->have_read_entries) return TRUE; menu_verbose ("Attempting to read entries from %s (full path %s)\n", dir->name, dirname); dp = opendir (dirname); if (dp == NULL) { menu_verbose ("Unable to list directory \"%s\"\n", dirname); return FALSE; } cached_dir_ensure_monitor (dir, dirname); fullpath = g_string_new (dirname); if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR) g_string_append_c (fullpath, G_DIR_SEPARATOR); fullpath_len = fullpath->len; while ((dent = readdir (dp)) != NULL) { /* ignore . and .. */ if (dent->d_name[0] == '.' && (dent->d_name[1] == '\0' || (dent->d_name[1] == '.' && dent->d_name[2] == '\0'))) continue; g_string_append (fullpath, dent->d_name); if (g_str_has_suffix (dent->d_name, ".desktop") || g_str_has_suffix (dent->d_name, ".directory")) { cached_dir_add_entry (dir, dent->d_name, fullpath->str); } else /* Try recursing */ { cached_dir_add_subdir (dir, dent->d_name, fullpath->str); } g_string_truncate (fullpath, fullpath_len); } closedir (dp); g_string_free (fullpath, TRUE); dir->have_read_entries = TRUE; return TRUE; }
static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir) { gboolean handled = FALSE; char *basename; char *dirname; menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", dir->name, path, event == MENU_MONITOR_EVENT_CREATED ? ("created") : event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); dirname = g_path_get_dirname (path); basename = g_path_get_basename (path); dir = cached_dir_lookup (dirname); if (g_str_has_suffix (basename, ".desktop") || g_str_has_suffix (basename, ".directory")) { switch (event) { case MENU_MONITOR_EVENT_CREATED: case MENU_MONITOR_EVENT_CHANGED: handled = cached_dir_update_entry (dir, basename, path); break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_entry (dir, basename); break; default: g_assert_not_reached (); break; } } else /* Try recursing */ { switch (event) { case MENU_MONITOR_EVENT_CREATED: handled = cached_dir_add_subdir (dir, basename, path) != NULL; break; case MENU_MONITOR_EVENT_CHANGED: break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_subdir (dir, basename); break; default: g_assert_not_reached (); break; } } g_free (basename); g_free (dirname); if (handled) { /* CHANGED events don't change the set of desktop entries */ if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED) { _entry_directory_list_empty_desktop_cache (); } cached_dir_queue_monitor_event (dir); } }
static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir) { gboolean handled = FALSE; gboolean retry_changes = FALSE; char *basename; char *dirname; dirname = g_path_get_dirname (path); basename = g_path_get_basename (path); dir = cached_dir_lookup (dirname); cached_dir_add_reference (dir); if (g_str_has_suffix (basename, ".desktop") || g_str_has_suffix (basename, ".directory")) { switch (event) { case MENU_MONITOR_EVENT_CREATED: case MENU_MONITOR_EVENT_CHANGED: handled = cached_dir_update_entry (dir, basename, path); break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_entry (dir, basename); break; default: g_assert_not_reached (); break; } } else if (g_strcmp0 (basename, "mimeinfo.cache") == 0) { /* The observed file notifies when a new desktop file is added * (but fails to load) go something like: * * NOTIFY: foo.desktop * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache * * Additionally, the failure is not upon trying to read the file, * but attempting to get its GAppInfo (g_desktop_app_info_new_from_filename() * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around * and read the file as a keyfile *first*, it succeeds. If you then try * to run g_desktop_app_info_new_from_keyfile(), *then* it fails. * * The theory here is there is a race condition where app info (which includes * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately * after the app is installed. * * What we do here is, when a desktop fails to load, we add it to a temporary * list. We wait until mimeinfo.cache changes, then retry that desktop file, * which succeeds this second time. * * Note: An alternative fix (presented more as a proof than a suggestion) is to * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay * for one second. This also avoids the issue (but it remains a race condition). */ GSList *iter; menu_verbose ("mimeinfo changed, checking for failed entries\n"); for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next) { const gchar *retry_path = iter->data; menu_verbose ("retrying %s\n", retry_path); char *retry_basename = g_path_get_basename (retry_path); if (cached_dir_update_entry (dir, retry_basename, retry_path)) retry_changes = TRUE; g_free (retry_basename); } g_slist_free_full (dir->retry_later_desktop_entries, g_free); dir->retry_later_desktop_entries = NULL; handled = retry_changes; } else /* Try recursing */ { switch (event) { case MENU_MONITOR_EVENT_CREATED: handled = cached_dir_add_subdir (dir, basename, path) != NULL; break; case MENU_MONITOR_EVENT_CHANGED: break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_subdir (dir, basename); break; default: g_assert_not_reached (); break; } } g_free (basename); g_free (dirname); if (handled) { menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", dir->name, path, event == MENU_MONITOR_EVENT_CREATED ? ("created") : event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); /* CHANGED events don't change the set of desktop entries, unless it's the mimeinfo.cache file changing */ if (retry_changes || (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)) { _entry_directory_list_empty_desktop_cache (); } cached_dir_queue_monitor_event (dir); } cached_dir_remove_reference (dir); }