diff --git a/bin/xbps-remove/clean-cache.c b/bin/xbps-remove/clean-cache.c index ce62c259..dbdc983f 100644 --- a/bin/xbps-remove/clean-cache.c +++ b/bin/xbps-remove/clean-cache.c @@ -64,22 +64,23 @@ binpkg_parse(char *buf, size_t bufsz, const char *path, const char **pkgver, con return 0; } +struct cleaner_data { + bool dry; + bool uninstalled; +}; + static int cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj, const char *key UNUSED, void *arg, bool *done UNUSED) { char buf[PATH_MAX]; - xbps_dictionary_t repo_pkgd; + xbps_dictionary_t pkgd; const char *binpkg, *rsha256; const char *binpkgver, *binpkgarch; - bool drun = false; + struct cleaner_data *data = arg; int r; - /* Extract drun (dry-run) flag from arg*/ - if (arg != NULL) - drun = *(bool*)arg; - binpkg = xbps_string_cstring_nocopy(obj); r = binpkg_parse(buf, sizeof(buf), binpkg, &binpkgver, &binpkgarch); if (r < 0) { @@ -96,9 +97,13 @@ cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj, * Remove binary pkg if it's not registered in any repository * or if hash doesn't match. */ - repo_pkgd = xbps_rpool_get_pkg(xhp, binpkgver); - if (repo_pkgd) { - xbps_dictionary_get_cstring_nocopy(repo_pkgd, + if (data->uninstalled) { + pkgd = xbps_pkgdb_get_pkg(xhp, binpkgver); + } else { + pkgd = xbps_rpool_get_pkg(xhp, binpkgver); + } + if (pkgd) { + xbps_dictionary_get_cstring_nocopy(pkgd, "filename-sha256", &rsha256); r = xbps_file_sha256_check(binpkg, rsha256); if (r == 0) { @@ -111,13 +116,13 @@ cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj, } } snprintf(buf, sizeof(buf), "%s.sig", binpkg); - if (!drun && unlink(binpkg) == -1) { + if (!data->dry && unlink(binpkg) == -1) { xbps_error_printf("Failed to remove `%s': %s\n", binpkg, strerror(errno)); } else { printf("Removed %s from cachedir (obsolete)\n", binpkg); } - if (!drun && unlink(buf) == -1 && errno != ENOENT) { + if (!data->dry && unlink(buf) == -1 && errno != ENOENT) { xbps_error_printf("Failed to remove `%s': %s\n", buf, strerror(errno)); } @@ -126,7 +131,7 @@ cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj, } int -clean_cachedir(struct xbps_handle *xhp, bool drun) +clean_cachedir(struct xbps_handle *xhp, bool uninstalled, bool drun) { xbps_array_t array = NULL; DIR *dirp; @@ -158,7 +163,11 @@ clean_cachedir(struct xbps_handle *xhp, bool drun) (void)closedir(dirp); if (xbps_array_count(array)) { - rv = xbps_array_foreach_cb_multi(xhp, array, NULL, cleaner_cb, (void*)&drun); + struct cleaner_data data = { + .dry = drun, + .uninstalled = uninstalled, + }; + rv = xbps_array_foreach_cb_multi(xhp, array, NULL, cleaner_cb, (void*)&data); xbps_object_release(array); } return rv; diff --git a/bin/xbps-remove/defs.h b/bin/xbps-remove/defs.h index 0788812c..5b39ba85 100644 --- a/bin/xbps-remove/defs.h +++ b/bin/xbps-remove/defs.h @@ -27,6 +27,6 @@ #define _XBPS_REMOVE_DEFS_H_ /* From clean-cache.c */ -int clean_cachedir(struct xbps_handle *, bool drun); +int clean_cachedir(struct xbps_handle *, bool uninstalled, bool drun); #endif /* !_XBPS_REMOVE_DEFS_H_ */ diff --git a/bin/xbps-remove/main.c b/bin/xbps-remove/main.c index 60df2976..7c24d614 100644 --- a/bin/xbps-remove/main.c +++ b/bin/xbps-remove/main.c @@ -53,7 +53,8 @@ usage(bool fail) " -f, --force Force package files removal\n" " -h, --help Show usage\n" " -n, --dry-run Dry-run mode\n" - " -O, --clean-cache Remove obsolete packages in cachedir\n" + " -O, --clean-cache Remove outdated packages from the cache\n" + " If specified twice, also remove uninstalled packages\n" " -o, --remove-orphans Remove package orphans\n" " -R, --recursive Recursively remove dependencies\n" " -r, --rootdir Full path to rootdir\n" @@ -179,12 +180,13 @@ main(int argc, char **argv) struct xbps_handle xh; const char *rootdir, *cachedir, *confdir; int c, flags, rv; - bool yes, drun, recursive, clean_cache, orphans; + bool yes, drun, recursive, orphans; int maxcols, missing; + int clean_cache = 0; rootdir = cachedir = confdir = NULL; flags = rv = 0; - drun = recursive = clean_cache = yes = orphans = false; + drun = recursive = yes = orphans = false; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch (c) { @@ -210,7 +212,7 @@ main(int argc, char **argv) drun = true; break; case 'O': - clean_cache = true; + clean_cache++; break; case 'o': orphans = true; @@ -236,7 +238,7 @@ main(int argc, char **argv) /* NOTREACHED */ } } - if (!clean_cache && !orphans && (argc == optind)) { + if (clean_cache == 0 && !orphans && (argc == optind)) { usage(true); /* NOTREACHED */ } @@ -263,8 +265,8 @@ main(int argc, char **argv) maxcols = get_maxcols(); - if (clean_cache) { - rv = clean_cachedir(&xh, drun); + if (clean_cache > 0) { + rv = clean_cachedir(&xh, clean_cache > 1, drun); if (!orphans || rv) exit(rv);; } diff --git a/bin/xbps-remove/xbps-remove.1 b/bin/xbps-remove/xbps-remove.1 index a2aa7a64..1b07bd39 100644 --- a/bin/xbps-remove/xbps-remove.1 +++ b/bin/xbps-remove/xbps-remove.1 @@ -79,7 +79,9 @@ Show the help message. Dry-run mode. Show what actions would be done but don't do anything. The current output prints 6 arguments: " ". .It Fl O, Fl -clean-cache -Cleans cache directory removing obsolete binary packages. +Cleans cache directory removing outdated binary packages. +If specified twice, +also remove packages that are not installed from the cache. .It Fl o, Fl -remove-orphans Removes installed package orphans that were installed automatically (as dependencies) and are not currently dependencies of any installed package. diff --git a/tests/xbps/xbps-remove/basic_test.sh b/tests/xbps/xbps-remove/basic_test.sh index 0f9fd577..716fb4eb 100755 --- a/tests/xbps/xbps-remove/basic_test.sh +++ b/tests/xbps/xbps-remove/basic_test.sh @@ -53,13 +53,15 @@ clean_cache_head() { } clean_cache_body() { - mkdir -p repo pkg_A/B/C + mkdir -p repo pkg_A/B/C pkg_B touch pkg_A/ cd repo xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A atf_check_equal $? 0 xbps-create -A noarch -n A-1.0_2 -s "A pkg" ../pkg_A atf_check_equal $? 0 + xbps-create -A noarch -n B-1.0_1 -s "B pkg" ../pkg_B + atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 cd .. @@ -69,12 +71,15 @@ clean_cache_body() { cp repo/*.xbps root/var/cache/xbps atf_check_equal $? 0 echo "repository=https://localhost/" >root/etc/xbps.d/localrepo.conf + xbps-install -r root -C etc/xbps.d -R repo -dvy B xbps-remove -r root -C etc/xbps.d -dvO atf_check_equal $? 0 test -f root/var/cache/xbps/A-1.0_2.noarch.xbps atf_check_equal $? 0 test -f root/var/cache/xbps/A-1.0_1.noarch.xbps atf_check_equal $? 1 + test -f root/var/cache/xbps/B-1.0_1.noarch.xbps + atf_check_equal $? 0 } atf_test_case clean_cache_dry_run @@ -136,10 +141,46 @@ clean_cache_dry_run_perm_body() { atf_check_equal "$out" "Removed A-1.0_1.noarch.xbps from cachedir (obsolete)" } +clean_cache_uninstalled_head() { + atf_set "descr" "xbps-remove(1): clean uninstalled package from cache" +} + +clean_cache_uninstalled_body() { + mkdir -p repo pkg_A/B/C pkg_B + touch pkg_A/ + cd repo + xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n A-1.0_2 -s "A pkg" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n B-1.0_1 -s "B pkg" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + mkdir -p root/etc/xbps.d root/var/db/xbps/https___localhost_ root/var/cache/xbps + cp repo/*-repodata root/var/db/xbps/https___localhost_ + atf_check_equal $? 0 + cp repo/*.xbps root/var/cache/xbps + atf_check_equal $? 0 + echo "repository=https://localhost/" >root/etc/xbps.d/localrepo.conf + xbps-install -r root -C etc/xbps.d -R repo -dvy B + atf_check_equal $? 0 + xbps-remove -r root -C etc/xbps.d -dvOO + atf_check_equal $? 0 + test -f root/var/cache/xbps/A-1.0_2.noarch.xbps + atf_check_equal $? 1 + test -f root/var/cache/xbps/A-1.0_1.noarch.xbps + atf_check_equal $? 1 + test -f root/var/cache/xbps/B-1.0_1.noarch.xbps + atf_check_equal $? 0 +} + atf_init_test_cases() { atf_add_test_case remove_directory atf_add_test_case remove_orphans atf_add_test_case clean_cache atf_add_test_case clean_cache_dry_run atf_add_test_case clean_cache_dry_run_perm + atf_add_test_case clean_cache_uninstalled }