int exec_clean(int argc, char **argv) { struct pkgdb *db = NULL; struct pkgdb_it *it = NULL; struct pkg *p = NULL; struct sumlist *sumlist = NULL, *s, *t; FTS *fts = NULL; FTSENT *ent = NULL; struct dl_head dl = STAILQ_HEAD_INITIALIZER(dl); const char *cachedir, *sum, *name; char *paths[2], csum[PKG_FILE_CKSUM_CHARS + 1], link_buf[MAXPATHLEN]; bool all = false; bool sumloaded = false; int retcode; int ch, cnt = 0; size_t total = 0, slen; ssize_t link_len; char size[7]; struct pkg_manifest_key *keys = NULL; 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")); paths[0] = __DECONST(char*, cachedir); paths[1] = NULL; retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO); if (retcode == EPKG_ENOACCESS) { warnx("Insufficient privileges to clean old packages"); return (EX_NOPERM); } else if (retcode == EPKG_ENODB) { warnx("No package database installed. Nothing to do!"); return (EX_OK); } else if (retcode != EPKG_OK) { warnx("Error accessing the package database"); return (EX_SOFTWARE); } retcode = EX_SOFTWARE; if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) return (EX_IOERR); if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) { pkgdb_close(db); warnx("Cannot get a read lock on a database, it is locked by another process"); return (EX_TEMPFAIL); } 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 */ pkg_manifest_keys_new(&keys); while ((ent = fts_read(fts)) != NULL) { if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL) continue; if (all) { retcode = add_to_dellist(&dl, ent->fts_path); if (retcode == EPKG_OK) { total += ent->fts_statp->st_size; ++cnt; } continue; } if (sumlist == NULL && !sumloaded) { it = pkgdb_repo_search(db, "*", MATCH_GLOB, FIELD_NAME, FIELD_NONE, NULL); while (pkgdb_it_next(it, &p, PKG_LOAD_BASIC) == EPKG_OK) { pkg_get(p, PKG_CKSUM, &sum); slen = MIN(strlen(sum), PKG_FILE_CKSUM_CHARS); s = calloc(1, sizeof(struct sumlist)); memcpy(s->sum, sum, slen); s->sum[slen] = '\0'; HASH_ADD_STR(sumlist, sum, s); } } if (ent->fts_info == FTS_SL) { /* Dereference the symlink and check it for being * recognized checksum file, or delete the symlink * later. */ if ((link_len = readlink(ent->fts_name, link_buf, sizeof(link_buf))) == -1) continue; link_buf[link_len] = '\0'; name = link_buf; } else name = ent->fts_name; s = NULL; if (extract_filename_sum(name, csum)) HASH_FIND_STR(sumlist, csum, s); if (s == NULL) { retcode = add_to_dellist(&dl, ent->fts_path); if (retcode == EPKG_OK) { total += ent->fts_statp->st_size; ++cnt; } continue; } } HASH_ITER(hh, sumlist, s, t) { HASH_DEL(sumlist, s); free(s); }
static int recursive_analysis(int fd, struct pkgdb *db, const char *dir, const char *cachedir, dl_list *dl, kh_sum_t **sumlist, bool all, size_t *total) { DIR *d; struct dirent *ent; int newfd, tmpfd; char path[MAXPATHLEN], csum[PKG_FILE_CKSUM_CHARS + 1], link_buf[MAXPATHLEN]; const char *name; ssize_t link_len; size_t nbfiles = 0, added = 0; khint_t k; tmpfd = dup(fd); d = fdopendir(tmpfd); if (d == NULL) { close(tmpfd); warnx("Impossible to open the directory %s", dir); return (0); } while ((ent = readdir(d)) != NULL) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name); if (ent->d_type == DT_DIR) { nbfiles++; newfd = openat(fd, ent->d_name, O_DIRECTORY, 0); if (newfd == -1) { warnx("Impossible to open the directory %s", path); continue; } if (recursive_analysis(newfd, db, path, cachedir, dl, sumlist, all, total) == 0 || all) { add_to_dellist(fd, dl, cachedir, path); added++; } close(newfd); continue; } if (ent->d_type != DT_LNK && ent->d_type != DT_REG) continue; nbfiles++; if (all) { *total += add_to_dellist(fd, dl, cachedir, path); continue; } if (*sumlist == NULL) { *sumlist = populate_sums(db); } name = ent->d_name; if (ent->d_type == DT_LNK) { /* Dereference the symlink and check it for being * recognized checksum file, or delete the symlink * later. */ if ((link_len = readlinkat(fd, ent->d_name, link_buf, sizeof(link_buf))) == -1) continue; link_buf[link_len - 1] = '\0'; name = link_buf; } k = kh_end(*sumlist); if (extract_filename_sum(name, csum)) k = kh_get_sum(*sumlist, csum); if (k == kh_end(*sumlist)) { added++; *total += add_to_dellist(fd, dl, cachedir, path); } } closedir(d); return (nbfiles - added); }