Ejemplo n.º 1
0
/**
 * as_pool_scan_apt:
 *
 * Scan for additional metadata in 3rd-party directories and move it to the right place.
 */
void
as_pool_scan_apt (AsPool *pool, gboolean force, GError **error)
{
	const gchar *apt_lists_dir = "/var/lib/apt/lists/";
	const gchar *appstream_yml_target = "/var/lib/app-info/yaml";
	const gchar *appstream_icons_target = "/var/lib/app-info/icons";
	g_autoptr(GPtrArray) yml_files = NULL;
	g_autoptr(GError) tmp_error = NULL;
	gboolean data_changed = FALSE;
	guint i;

	/* skip this step if the APT lists directory doesn't exist */
	if (!g_file_test (apt_lists_dir, G_FILE_TEST_IS_DIR)) {
		g_debug ("APT lists directory (%s) not found!", apt_lists_dir);
		return;
	}

	if (g_file_test (appstream_yml_target, G_FILE_TEST_IS_DIR)) {
		g_autoptr(GPtrArray) ytfiles = NULL;

		/* we can't modify the files here if we don't have write access */
		if (!as_utils_is_writable (appstream_yml_target)) {
			g_debug ("Unable to write to '%s': Can't add AppStream data from APT to the pool.", appstream_yml_target);
			return;
		}

		ytfiles = as_utils_find_files_matching (appstream_yml_target, "*", FALSE, &tmp_error);
		if (tmp_error != NULL) {
			g_warning ("Could not scan for broken symlinks in DEP-11 target: %s", tmp_error->message);
			return;
		}
		for (i = 0; i < ytfiles->len; i++) {
			const gchar *fname = (const gchar*) g_ptr_array_index (ytfiles, i);
			if (!g_file_test (fname, G_FILE_TEST_EXISTS)) {
				g_remove (fname);
				data_changed = TRUE;
			}
		}
	}

	yml_files = as_utils_find_files_matching (apt_lists_dir, "*Components-*.yml.gz", FALSE, &tmp_error);
	if (tmp_error != NULL) {
		g_warning ("Could not scan for APT-downloaded DEP-11 files: %s", tmp_error->message);
		return;
	}

	/* no data found? skip scan step */
	if (yml_files->len <= 0) {
		g_debug ("Couldn't find DEP-11 data in APT directories.");
		return;
	}

	/* We have to check if our metadata is in the target directory at all, and - if not - trigger a cache refresh.
	 * This is needed because APT is putting files with the *server* ctime/mtime into it's lists directory,
	 * and that time might be lower than the time the metadata cache was last updated, which may result
	 * in no cache update being triggered at all.
	 */
	for (i = 0; i < yml_files->len; i++) {
		g_autofree gchar *fbasename = NULL;
		g_autofree gchar *dest_fname = NULL;
		const gchar *fname = (const gchar*) g_ptr_array_index (yml_files, i);

		fbasename = g_path_get_basename (fname);
		dest_fname = g_build_filename (appstream_yml_target, fbasename, NULL);
		if (!g_file_test (dest_fname, G_FILE_TEST_EXISTS)) {
			data_changed = TRUE;
			g_debug ("File '%s' missing, cache update is needed.", dest_fname);
			break;
		}
	}

	/* get the last time we touched the database */
	if (!data_changed) {
		for (i = 0; i < yml_files->len; i++) {
			struct stat sb;
			const gchar *fname = (const gchar*) g_ptr_array_index (yml_files, i);
			if (stat (fname, &sb) < 0)
				continue;
			if (sb.st_ctime > as_pool_get_cache_age (pool)) {
				/* we need to update the cache */
				data_changed = TRUE;
				break;
			}
		}
	}

	/* no changes means nothing to do here */
	if ((!data_changed) && (!force))
		return;

	/* this is not really great, but we simply can't detect if we should remove an icons folder or not,
	 * or which specific icons we should drop from a folder.
	 * So, we hereby simply "own" the icons directory and all it's contents, anything put in there by 3rd-parties will
	 * be deleted.
	 * (And there should actually be no cases 3rd-parties put icons there on a Debian machine, since metadata in packages
	 * will land in /usr/share/app-info anyway)
	 */
	as_utils_delete_dir_recursive (appstream_icons_target);
	if (g_mkdir_with_parents (appstream_yml_target, 0755) > 0) {
		g_debug ("Unable to create '%s': %s", appstream_yml_target, g_strerror (errno));
		return;
	}

	for (i = 0; i < yml_files->len; i++) {
		g_autofree gchar *fbasename = NULL;
		g_autofree gchar *dest_fname = NULL;
		g_autofree gchar *origin = NULL;
		g_autofree gchar *file_baseprefix = NULL;
		const gchar *fname = (const gchar*) g_ptr_array_index (yml_files, i);

		fbasename = g_path_get_basename (fname);
		dest_fname = g_build_filename (appstream_yml_target, fbasename, NULL);

		if (!g_file_test (fname, G_FILE_TEST_EXISTS)) {
			/* broken symlinks in the dest will have been removed earlier */
			g_debug ("File %s is a broken symlink, skipping.", fname);
			continue;
		} else if (!g_file_test (dest_fname, G_FILE_TEST_EXISTS)) {
			/* file not found, let's symlink */
			if (symlink (fname, dest_fname) != 0) {
				g_debug ("Unable to set symlink (%s -> %s): %s",
							fname,
							dest_fname,
							g_strerror (errno));
				continue;
			}
		} else if (!g_file_test (dest_fname, G_FILE_TEST_IS_SYMLINK)) {
			/* file found, but it isn't a symlink, try to rescue */
			g_debug ("Regular file '%s' found, which doesn't belong there. Removing it.", dest_fname);
			g_remove (dest_fname);
			continue;
		}

		/* get DEP-11 data origin */
		origin = as_get_yml_data_origin (dest_fname);
		if (origin == NULL) {
			g_warning ("No origin found for file %s", fbasename);
			continue;
		}

		/* get base prefix for this file in the APT download cache */
		file_baseprefix = g_strndup (fbasename, strlen (fbasename) - strlen (g_strrstr (fbasename, "_") + 1));

		/* extract icons to their destination (if they exist at all */
		as_extract_icon_cache_tarball (appstream_icons_target,
						origin,
						file_baseprefix,
						apt_lists_dir,
						"64x64");
		as_extract_icon_cache_tarball (appstream_icons_target,
						origin,
						file_baseprefix,
						apt_lists_dir,
						"128x128");
	}

	/* ensure the cache-rebuild process notices these changes */
	as_touch_location (appstream_yml_target);
}
Ejemplo n.º 2
0
/**
 * as_pool_refresh_cache:
 * @pool: An instance of #AsPool.
 * @force: Enforce refresh, even if source data has not changed.
 *
 * Update the AppStream cache. There is normally no need to call this function manually, because cache updates are handled
 * transparently in the background.
 *
 * Returns: %TRUE if the cache was updated, %FALSE on error or if the cache update was not necessary and has been skipped.
 */
gboolean
as_pool_refresh_cache (AsPool *pool, gboolean force, GError **error)
{
	AsPoolPrivate *priv = GET_PRIVATE (pool);
	gboolean ret = FALSE;
	gboolean ret_poolupdate;
	g_autofree gchar *cache_fname = NULL;
	g_autoptr(GError) tmp_error = NULL;

	/* try to create cache directory, in case it doesn't exist */
	g_mkdir_with_parents (priv->sys_cache_path, 0755);
	if (!as_utils_is_writable (priv->sys_cache_path)) {
		g_set_error (error,
				AS_POOL_ERROR,
				AS_POOL_ERROR_TARGET_NOT_WRITABLE,
				_("Cache location '%s' is not writable."), priv->sys_cache_path);
		return FALSE;
	}

	/* collect metadata */
#ifdef HAVE_APT_SUPPORT
	/* currently, we only do something here if we are running with explicit APT support compiled in */
	as_pool_scan_apt (pool, force, &tmp_error);
	if (tmp_error != NULL) {
		/* the exact error is not forwarded here, since we might be able to partially update the cache */
		g_warning ("Error while collecting metadata: %s", tmp_error->message);
		g_error_free (tmp_error);
		tmp_error = NULL;
	}
#endif

	/* create the filename of our cache */
	cache_fname = g_strdup_printf ("%s/%s.gvz", priv->sys_cache_path, priv->locale);

	/* check if we need to refresh the cache
	 * (which is only necessary if the AppStream data has changed) */
	if (!as_pool_metadata_changed (pool)) {
		g_debug ("Data did not change, no cache refresh needed.");
		if (force) {
			g_debug ("Forcing refresh anyway.");
		} else {
			return FALSE;
		}
	}
	g_debug ("Refreshing AppStream cache");

	/* find them wherever they are */
	ret_poolupdate = as_pool_load (pool, NULL, &tmp_error);
	if (tmp_error != NULL) {
		/* the exact error is not forwarded here, since we might be able to partially update the cache */
		g_warning ("Error while updating the in-memory data pool: %s", tmp_error->message);
		g_error_free (tmp_error);
		tmp_error = NULL;
	}

	/* save the cache object */
	as_pool_save_cache_file (pool, cache_fname, &tmp_error);
	if (tmp_error != NULL) {
		/* the exact error is not forwarded here, since we might be able to partially update the cache */
		g_warning ("Error while updating the cache: %s", tmp_error->message);
		g_error_free (tmp_error);
		tmp_error = NULL;
		ret = FALSE;
	} else {
		ret = TRUE;
	}

	if (ret) {
		if (!ret_poolupdate) {
			g_set_error (error,
				AS_POOL_ERROR,
				AS_POOL_ERROR_INCOMPLETE,
				_("AppStream data pool was loaded, but some metadata was ignored due to errors."));
		}
		/* update the cache mtime, to not needlessly rebuild it again */
		as_touch_location (cache_fname);
		as_pool_check_cache_ctime (pool);
	} else {
		g_set_error (error,
				AS_POOL_ERROR,
				AS_POOL_ERROR_FAILED,
				_("AppStream cache update failed."));
	}

	return TRUE;
}