int exec_clean(int argc, char **argv) { struct pkgdb *db = NULL; struct pkgdb_it *it = NULL; struct pkg *pkg = NULL; struct pkg *p = NULL; FTS *fts = NULL; FTSENT *ent = NULL; struct dl_head dl = STAILQ_HEAD_INITIALIZER(dl); const char *cachedir; char *paths[2]; char *repopath; bool dry_run = false; bool yes; int retcode = EX_SOFTWARE; int ret; int ch; pkg_config_bool(PKG_CONFIG_ASSUME_ALWAYS_YES, &yes); while ((ch = getopt(argc, argv, "nqy")) != -1) { switch (ch) { case 'n': dry_run = true; break; case 'q': quiet = true; break; case 'y': yes = true; break; default: usage_clean(); return (EX_USAGE); } } argc -= optind; argv += optind; if (pkg_config_string(PKG_CONFIG_CACHEDIR, &cachedir) != EPKG_OK) { warnx("Cannot get cachedir config entry"); return 1; } paths[0] = __DECONST(char*, cachedir); paths[1] = NULL; if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) { goto cleanup; } if ((fts = fts_open(paths, FTS_PHYSICAL, NULL)) == NULL) { warn("fts_open(%s)", cachedir); goto cleanup; } /* Build the list of out-of-date or obsolete packages */ while ((ent = fts_read(fts)) != NULL) { const char *origin, *pkgrepopath; if (ent->fts_info != FTS_F) continue; repopath = ent->fts_path + strlen(cachedir); if (repopath[0] == '/') repopath++; if (pkg_open(&pkg, ent->fts_path) != EPKG_OK) { if (!quiet) warnx("skipping %s", ent->fts_path); continue; } pkg_get(pkg, PKG_ORIGIN, &origin); it = pkgdb_search(db, origin, MATCH_EXACT, FIELD_ORIGIN, FIELD_NONE, NULL); if (it == NULL) { if (!quiet) warnx("skipping %s", ent->fts_path); continue; } if ((ret = pkgdb_it_next(it, &p, PKG_LOAD_BASIC)) == EPKG_FATAL) { if (!quiet) warnx("skipping %s", ent->fts_path); continue; } pkg_get(p, PKG_REPOPATH, &pkgrepopath); if (ret == EPKG_END) { ret = add_to_dellist(&dl, REMOVED, ent->fts_path, origin, NULL, NULL); } else if (strcmp(repopath, pkgrepopath)) { const char *newname; const char *newversion; pkg_get(p, PKG_NAME, &newname, PKG_VERSION, &newversion); ret = add_to_dellist(&dl, OUT_OF_DATE, ent->fts_path, origin, newname, newversion); } else { char local_cksum[SHA256_DIGEST_LENGTH * 2 +1]; const char *cksum; pkg_get(p, PKG_CKSUM, &cksum); if (hash_file(ent->fts_path, local_cksum) == EPKG_OK) { if (strcmp(cksum, local_cksum) != 0) { ret = add_to_dellist(&dl, CKSUM_MISMATCH, ent->fts_path, origin, NULL, NULL); } } } if (ret != EPKG_OK && ret != EPKG_END) { retcode = EX_OSERR; /* out of memory */ goto cleanup; } pkgdb_it_free(it); } if (STAILQ_EMPTY(&dl)) { if (!quiet) printf("Nothing to do.\n"); retcode = EX_OK; goto cleanup; } if (dry_run || !yes || !quiet) display_dellist(&dl, cachedir); if (!dry_run) { if (!yes) yes = query_yesno( "\nProceed with cleaning cache [y/N]: "); if (yes) retcode = delete_dellist(&dl); } else retcode = EX_OK; cleanup: free_dellist(&dl); pkg_free(pkg); pkg_free(p); if (fts != NULL) fts_close(fts); if (db != NULL) pkgdb_close(db); return (retcode); }
int exec_clean(int argc, char **argv) { struct pkgdb *db = NULL; kh_sum_t *sumlist = NULL; dl_list dl; const char *cachedir; bool all = false; int retcode; int ch; int cachefd = -1; size_t total = 0; char size[8]; char *cksum; struct pkg_manifest_key *keys = NULL; #ifdef HAVE_CAPSICUM cap_rights_t rights; #endif struct option longopts[] = { { "all", no_argument, NULL, 'a' }, { "dry-run", no_argument, NULL, 'n' }, { "quiet", no_argument, NULL, 'q' }, { "yes", no_argument, NULL, 'y' }, { NULL, 0, NULL, 0 }, }; while ((ch = getopt_long(argc, argv, "+anqy", longopts, NULL)) != -1) { switch (ch) { case 'a': all = true; break; case 'n': dry_run = true; break; case 'q': quiet = true; break; case 'y': yes = true; break; default: usage_clean(); return (EX_USAGE); } } argc -= optind; argv += optind; cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR")); cachefd = open(cachedir, O_DIRECTORY); if (cachefd == -1) { warn("Impossible to open %s", cachedir); return (EX_IOERR); } retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO); if (retcode == EPKG_ENOACCESS) { warnx("Insufficient privileges to clean old packages"); close(cachefd); return (EX_NOPERM); } else if (retcode == EPKG_ENODB) { warnx("No package database installed. Nothing to do!"); close(cachefd); return (EX_OK); } else if (retcode != EPKG_OK) { warnx("Error accessing the package database"); close(cachefd); return (EX_SOFTWARE); } retcode = EX_SOFTWARE; if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) { close(cachefd); return (EX_IOERR); } if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) { pkgdb_close(db); close(cachefd); warnx("Cannot get a read lock on a database, it is locked by " "another process"); return (EX_TEMPFAIL); } #ifdef HAVE_CAPSICUM cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_FSTATFS, CAP_FSTAT, CAP_UNLINKAT); if (cap_rights_limit(cachefd, &rights) < 0 && errno != ENOSYS ) { warn("cap_rights_limit() failed"); close(cachefd); return (EX_SOFTWARE); } if (cap_enter() < 0 && errno != ENOSYS) { warn("cap_enter() failed"); close(cachefd); return (EX_SOFTWARE); } #endif kv_init(dl); /* Build the list of out-of-date or obsolete packages */ pkg_manifest_keys_new(&keys); recursive_analysis(cachefd, db, cachedir, cachedir, &dl, &sumlist, all, &total); if (sumlist != NULL) { kh_foreach_value(sumlist, cksum, free(cksum)); kh_destroy_sum(sumlist); } if (kv_size(dl) == 0) { if (!quiet) printf("Nothing to do.\n"); retcode = EX_OK; goto cleanup; } humanize_number(size, sizeof(size), total, "B", HN_AUTOSCALE, HN_IEC_PREFIXES); if (!quiet) printf("The cleanup will free %s\n", size); if (!dry_run) { if (query_yesno(false, "\nProceed with cleaning the cache? ")) { retcode = delete_dellist(cachefd, cachedir, &dl, kv_size(dl)); } } else { retcode = EX_OK; } cleanup: pkgdb_release_lock(db, PKGDB_LOCK_READONLY); pkgdb_close(db); pkg_manifest_keys_free(keys); free_dellist(&dl); if (cachefd != -1) close(cachefd); return (retcode); }