summaryrefslogtreecommitdiff
path: root/src/app_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/app_cache.c')
-rw-r--r--src/app_cache.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/app_cache.c b/src/app_cache.c
new file mode 100644
index 0000000..7ea5356
--- /dev/null
+++ b/src/app_cache.c
@@ -0,0 +1,193 @@
+/* app_cache.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation. See http://www.gnu.org/ for details.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "apk_defines.h"
+#include "apk_applet.h"
+#include "apk_database.h"
+#include "apk_package.h"
+#include "apk_print.h"
+#include "apk_solver.h"
+
+#define CACHE_CLEAN BIT(0)
+#define CACHE_DOWNLOAD BIT(1)
+
+struct cache_ctx {
+ unsigned short solver_flags;
+};
+
+static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
+{
+ struct cache_ctx *cctx = (struct cache_ctx *) ctx;
+
+ switch (optch) {
+ case 'u':
+ cctx->solver_flags |= APK_SOLVERF_UPGRADE;
+ break;
+ case 'l':
+ cctx->solver_flags |= APK_SOLVERF_LATEST;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+static const struct apk_option options_applet[] = {
+ { 'u', "upgrade" },
+ { 'l', "latest" },
+};
+
+static const struct apk_option_group optgroup_applet = {
+ .name = "Cache",
+ .options = options_applet,
+ .num_options = ARRAY_SIZE(options_applet),
+ .parse = option_parse_applet,
+};
+
+struct progress {
+ size_t done, total;
+};
+
+static void progress_cb(void *ctx, size_t bytes_done)
+{
+ struct progress *prog = (struct progress *) ctx;
+ apk_print_progress(prog->done + bytes_done, prog->total);
+}
+
+static int cache_download(struct cache_ctx *cctx, struct apk_database *db)
+{
+ struct apk_changeset changeset = {};
+ struct apk_change *change;
+ struct apk_package *pkg;
+ struct apk_repository *repo;
+ struct progress prog = { 0, 0 };
+ int r, ret = 0;
+
+ r = apk_solver_solve(db, cctx->solver_flags, db->world, &changeset);
+ if (r < 0) {
+ apk_error("Unable to select packages. Run apk fix.");
+ return r;
+ }
+
+ foreach_array_item(change, changeset.changes) {
+ pkg = change->new_pkg;
+ if ((pkg != NULL) && !(pkg->repos & db->local_repos))
+ prog.total += pkg->size;
+ }
+
+ foreach_array_item(change, changeset.changes) {
+ pkg = change->new_pkg;
+ if ((pkg == NULL) || (pkg->repos & db->local_repos))
+ continue;
+
+ repo = apk_db_select_repo(db, pkg);
+ if (repo == NULL)
+ continue;
+
+ r = apk_cache_download(db, repo, pkg, APK_SIGN_VERIFY_IDENTITY, 0,
+ progress_cb, &prog);
+ if (r && r != -EALREADY) {
+ apk_error(PKG_VER_FMT ": %s", PKG_VER_PRINTF(pkg), apk_error_str(r));
+ ret++;
+ }
+ prog.done += pkg->size;
+ }
+
+ return ret;
+}
+
+static void cache_clean_item(struct apk_database *db, int dirfd, const char *name, struct apk_package *pkg)
+{
+ char tmp[PATH_MAX];
+ apk_blob_t b;
+ int i;
+
+ if (strcmp(name, "installed") == 0) return;
+
+ if (pkg) {
+ if ((apk_flags & APK_PURGE) && pkg->ipkg == NULL) goto delete;
+ if (pkg->repos & db->local_repos & ~BIT(APK_REPOSITORY_CACHED)) goto delete;
+ if (pkg->ipkg == NULL && !(pkg->repos & ~BIT(APK_REPOSITORY_CACHED))) goto delete;
+ return;
+ }
+
+ b = APK_BLOB_STR(name);
+ for (i = 0; i < db->num_repos; i++) {
+ /* Check if this is a valid index */
+ apk_repo_format_cache_index(APK_BLOB_BUF(tmp), &db->repos[i]);
+ if (apk_blob_compare(b, APK_BLOB_STR(tmp)) == 0) return;
+ }
+
+delete:
+ if (apk_verbosity >= 2)
+ apk_message("deleting %s", name);
+ if (!(apk_flags & APK_SIMULATE)) {
+ if (unlinkat(dirfd, name, 0) < 0 && errno == EISDIR)
+ unlinkat(dirfd, name, AT_REMOVEDIR);
+ }
+}
+
+static int cache_clean(struct apk_database *db)
+{
+ return apk_db_cache_foreach_item(db, cache_clean_item);
+}
+
+static int cache_main(void *ctx, struct apk_database *db, struct apk_string_array *args)
+{
+ struct cache_ctx *cctx = (struct cache_ctx *) ctx;
+ char *arg;
+ int r = 0, actions = 0;
+
+ if (args->num != 1)
+ return -EINVAL;
+
+ arg = args->item[0];
+ if (strcmp(arg, "sync") == 0)
+ actions = CACHE_CLEAN | CACHE_DOWNLOAD;
+ else if (strcmp(arg, "clean") == 0)
+ actions = CACHE_CLEAN;
+ else if (strcmp(arg, "download") == 0)
+ actions = CACHE_DOWNLOAD;
+ else
+ return -EINVAL;
+
+ if (!apk_db_cache_active(db)) {
+ apk_error("Package cache is not enabled.\n");
+ r = 2;
+ goto err;
+ }
+
+ if (r == 0 && (actions & CACHE_CLEAN))
+ r = cache_clean(db);
+ if (r == 0 && (actions & CACHE_DOWNLOAD))
+ r = cache_download(cctx, db);
+err:
+ return r;
+}
+
+static struct apk_applet apk_cache = {
+ .name = "cache",
+ .arguments = "sync | clean | download",
+ .open_flags = APK_OPENF_READ|APK_OPENF_NO_SCRIPTS|APK_OPENF_CACHE_WRITE,
+ .command_groups = APK_COMMAND_GROUP_SYSTEM,
+ .context_size = sizeof(struct cache_ctx),
+ .optgroups = { &optgroup_global, &optgroup_applet },
+ .main = cache_main,
+};
+
+APK_DEFINE_APPLET(apk_cache);