int exec_transaction(struct xbps_handle *xhp, int maxcols, bool yes, bool drun) { xbps_array_t array; struct transaction *trans; uint64_t fsize = 0, isize = 0; char freesize[8], instsize[8]; int rv = 0; trans = calloc(1, sizeof(*trans)); if (trans == NULL) return ENOMEM; if ((rv = xbps_transaction_prepare(xhp)) != 0) { if (rv == ENODEV) { array = xbps_dictionary_get(xhp->transd, "missing_deps"); if (xbps_array_count(array)) { /* missing dependencies */ print_array(array); fprintf(stderr, "Transaction aborted due to unresolved dependencies.\n"); } } else if (rv == ENOEXEC) { array = xbps_dictionary_get(xhp->transd, "missing_shlibs"); if (xbps_array_count(array)) { /* missing shlibs */ print_array(array); fprintf(stderr, "Transaction aborted due to unresolved shlibs.\n"); } } else if (rv == EAGAIN) { /* conflicts */ array = xbps_dictionary_get(xhp->transd, "conflicts"); print_array(array); fprintf(stderr, "Transaction aborted due to conflicting packages.\n"); } else if (rv == ENOSPC) { /* not enough free space */ xbps_dictionary_get_uint64(xhp->transd, "total-installed-size", &isize); if (xbps_humanize_number(instsize, (int64_t)isize) == -1) { xbps_error_printf("humanize_number2 returns " "%s\n", strerror(errno)); return -1; } xbps_dictionary_get_uint64(xhp->transd, "disk-free-size", &fsize); if (xbps_humanize_number(freesize, (int64_t)fsize) == -1) { xbps_error_printf("humanize_number2 returns " "%s\n", strerror(errno)); return -1; } fprintf(stderr, "Transaction aborted due to insufficient disk " "space (need %s, got %s free).\n", instsize, freesize); } else { xbps_dbg_printf(xhp, "Empty transaction dictionary: %s\n", strerror(errno)); } goto out; } #ifdef FULL_DEBUG xbps_dbg_printf(xhp, "Dictionary before transaction happens:\n"); xbps_dbg_printf_append(xhp, "%s", xbps_dictionary_externalize(xhp->transd)); #endif trans->xhp = xhp; trans->d = xhp->transd; trans->iter = xbps_array_iter_from_dict(xhp->transd, "packages"); assert(trans->iter); /* * dry-run mode, show what would be done but don't run anything. */ if (drun) { show_actions(trans->iter); goto out; } /* * Show download/installed size for the transaction. */ if ((rv = show_transaction_sizes(trans, maxcols)) != 0) goto out; /* * Ask interactively (if -y not set). */ if (!yes && !yesno("Do you want to continue?")) { printf("Aborting!\n"); goto out; } /* * It's time to run the transaction! */ if ((rv = xbps_transaction_commit(xhp)) == 0) { printf("\n%u downloaded, %u installed, %u updated, " "%u configured, %u removed.\n", trans->dl_pkgcnt, trans->inst_pkgcnt, trans->up_pkgcnt, trans->cf_pkgcnt + trans->inst_pkgcnt, trans->rm_pkgcnt); } out: if (trans->iter) xbps_object_iterator_release(trans->iter); if (trans) free(trans); return rv; }
static int show_transaction_sizes(struct transaction *trans, int cols) { uint64_t dlsize = 0, instsize = 0, rmsize = 0, disk_free_size = 0; char size[8]; if (!print_trans_colmode(trans, cols)) { /* * Show the list of packages that will be downloaded, installed, updated, * removed or configured. */ xbps_dictionary_get_uint32(trans->d, "total-download-pkgs", &trans->dl_pkgcnt); if (trans->dl_pkgcnt) { printf("%u package%s will be downloaded:\n", trans->dl_pkgcnt, trans->dl_pkgcnt == 1 ? "" : "s"); show_package_list(trans, NULL, cols); printf("\n"); } xbps_dictionary_get_uint32(trans->d, "total-install-pkgs", &trans->inst_pkgcnt); if (trans->inst_pkgcnt) { printf("%u package%s will be installed:\n", trans->inst_pkgcnt, trans->inst_pkgcnt == 1 ? "" : "s"); show_package_list(trans, "install", cols); printf("\n"); } xbps_dictionary_get_uint32(trans->d, "total-update-pkgs", &trans->up_pkgcnt); if (trans->up_pkgcnt) { printf("%u package%s will be updated:\n", trans->up_pkgcnt, trans->up_pkgcnt == 1 ? "" : "s"); show_package_list(trans, "update", cols); printf("\n"); } xbps_dictionary_get_uint32(trans->d, "total-configure-pkgs", &trans->cf_pkgcnt); if (trans->cf_pkgcnt) { printf("%u package%s will be configured:\n", trans->cf_pkgcnt, trans->cf_pkgcnt == 1 ? "" : "s"); show_package_list(trans, "configure", cols); printf("\n"); } xbps_dictionary_get_uint32(trans->d, "total-remove-pkgs", &trans->rm_pkgcnt); if (trans->rm_pkgcnt) { printf("%u package%s will be removed:\n", trans->rm_pkgcnt, trans->rm_pkgcnt == 1 ? "" : "s"); show_package_list(trans, "remove", cols); printf("\n"); } } /* * Show total download/installed/removed size for all required packages. */ xbps_dictionary_get_uint64(trans->d, "total-download-size", &dlsize); xbps_dictionary_get_uint64(trans->d, "total-installed-size", &instsize); xbps_dictionary_get_uint64(trans->d, "total-removed-size", &rmsize); xbps_dictionary_get_uint64(trans->d, "disk-free-size", &disk_free_size); if (dlsize || instsize || rmsize || disk_free_size) printf("\n"); if (dlsize) { if (xbps_humanize_number(size, (int64_t)dlsize) == -1) { xbps_error_printf("humanize_number returns " "%s\n", strerror(errno)); return -1; } printf("Size to download: %6s\n", size); } if (instsize) { if (xbps_humanize_number(size, (int64_t)instsize) == -1) { xbps_error_printf("humanize_number2 returns " "%s\n", strerror(errno)); return -1; } printf("Size required on disk: %6s\n", size); } if (rmsize) { if (xbps_humanize_number(size, (int64_t)rmsize) == -1) { xbps_error_printf("humanize_number3 returns " "%s\n", strerror(errno)); return -1; } printf("Size freed on disk: %6s\n", size); } if (disk_free_size) { if (xbps_humanize_number(size, (int64_t)disk_free_size) == -1) { xbps_error_printf("humanize_number3 returns " "%s\n", strerror(errno)); return -1; } printf("Free space on disk: %6s\n", size); } printf("\n"); return 0; }
xbps_array_t xbps_find_pkg_obsoletes(struct xbps_handle *xhp, xbps_dictionary_t instd, xbps_dictionary_t newd) { xbps_array_t instfiles, newfiles, obsoletes; /* These are symlinks in Void and must not be removed */ const char *basesymlinks[] = { "./bin", "./sbin", "./lib", "./lib32", "./lib64", "./usr/lib32", "./usr/lib64", "./var/run", }; int rv = 0; assert(xbps_object_type(instd) == XBPS_TYPE_DICTIONARY); assert(xbps_object_type(newd) == XBPS_TYPE_DICTIONARY); obsoletes = xbps_array_create(); assert(obsoletes); instfiles = merge_filelist(instd); if (xbps_array_count(instfiles) == 0) { /* nothing to check if current pkg does not own any file */ xbps_object_release(instfiles); return obsoletes; } newfiles = merge_filelist(newd); /* * Iterate over files list from installed package. */ for (unsigned int i = 0; i < xbps_array_count(instfiles); i++) { xbps_object_t obj, obj2; xbps_string_t oldstr, newstr; struct stat st; uint64_t mtime = 0; const char *oldhash; char file[PATH_MAX]; bool found = false; obj = xbps_array_get(instfiles, i); if (xbps_object_type(obj) != XBPS_TYPE_DICTIONARY) { /* ignore unexistent files */ continue; } oldstr = xbps_dictionary_get(obj, "file"); if (oldstr == NULL) continue; snprintf(file, sizeof(file), ".%s", xbps_string_cstring_nocopy(oldstr)); if (xbps_dictionary_get_cstring_nocopy(obj, "sha256", &oldhash)) { rv = xbps_file_hash_check(file, oldhash); if (rv == ENOENT || rv == ERANGE) { /* Skip unexistent and files that do not * match the hash. */ continue; } } /* * Check if current file is available in new pkg filelist. */ for (unsigned int x = 0; x < xbps_array_count(newfiles); x++) { obj2 = xbps_array_get(newfiles, x); newstr = xbps_dictionary_get(obj2, "file"); assert(newstr); /* * Skip files with same path. */ if (xbps_string_equals(oldstr, newstr)) { found = true; break; } } if (found) { continue; } /* * Make sure to not remove any symlink of root directory. */ for (uint8_t x = 0; x < __arraycount(basesymlinks); x++) { if (strcmp(file, basesymlinks[x]) == 0) { found = true; xbps_dbg_printf(xhp, "[obsoletes] ignoring " "%s removal\n", file); break; } } if (found) { continue; } /* * Finally check if file mtime on disk matched what * the installed pkg has stored. */ if (xbps_dictionary_get_uint64(obj, "mtime", &mtime)) { if (lstat(file, &st) == -1) { xbps_dbg_printf(xhp, "[obsoletes] lstat failed " "for %s: %s\n", file, strerror(errno)); continue; } if (mtime != (uint64_t)st.st_mtime) continue; xbps_dbg_printf(xhp, "[obsoletes] %s: matched mtime, adding obsolete.\n", file); } /* * Obsolete found, add onto the array. */ xbps_dbg_printf(xhp, "found obsolete: %s\n", file); xbps_array_add_cstring(obsoletes, file); } xbps_object_release(instfiles); xbps_object_release(newfiles); return obsoletes; }
static int show_transaction_sizes(struct transaction *trans, int cols) { uint64_t dlsize = 0, instsize = 0, rmsize = 0; char size[8]; /* * Show the list of packages that will be installed. */ if (xbps_dictionary_get_uint32(trans->d, "total-install-pkgs", &trans->inst_pkgcnt)) { printf("%u package%s will be installed:\n", trans->inst_pkgcnt, trans->inst_pkgcnt == 1 ? "" : "s"); show_package_list(trans->iter, "install", cols); printf("\n"); } if (xbps_dictionary_get_uint32(trans->d, "total-update-pkgs", &trans->up_pkgcnt)) { printf("%u package%s will be updated:\n", trans->up_pkgcnt, trans->up_pkgcnt == 1 ? "" : "s"); show_package_list(trans->iter, "update", cols); printf("\n"); } if (xbps_dictionary_get_uint32(trans->d, "total-configure-pkgs", &trans->cf_pkgcnt)) { printf("%u package%s will be configured:\n", trans->cf_pkgcnt, trans->cf_pkgcnt == 1 ? "" : "s"); show_package_list(trans->iter, "configure", cols); printf("\n"); } if (xbps_dictionary_get_uint32(trans->d, "total-remove-pkgs", &trans->rm_pkgcnt)) { printf("%u package%s will be removed:\n", trans->rm_pkgcnt, trans->rm_pkgcnt == 1 ? "" : "s"); show_package_list(trans->iter, "remove", cols); printf("\n"); } /* * Show total download/installed/removed size for all required packages. */ xbps_dictionary_get_uint64(trans->d, "total-download-size", &dlsize); xbps_dictionary_get_uint64(trans->d, "total-installed-size", &instsize); xbps_dictionary_get_uint64(trans->d, "total-removed-size", &rmsize); if (dlsize || instsize || rmsize) printf("\n"); if (dlsize) { if (xbps_humanize_number(size, (int64_t)dlsize) == -1) { xbps_error_printf("humanize_number returns " "%s\n", strerror(errno)); return -1; } printf("Total download size:\t%6s\n", size); } if (instsize) { if (xbps_humanize_number(size, (int64_t)instsize) == -1) { xbps_error_printf("humanize_number2 returns " "%s\n", strerror(errno)); return -1; } printf("Total installed size:\t%6s\n", size); } if (rmsize) { if (xbps_humanize_number(size, (int64_t)rmsize) == -1) { xbps_error_printf("humanize_number3 returns " "%s\n", strerror(errno)); return -1; } printf("Total freed size:\t%6s\n", size); } printf("\n"); return 0; }
static int compute_transaction_stats(struct xbps_handle *xhp) { xbps_dictionary_t pkg_metad; xbps_object_iterator_t iter; xbps_object_t obj; struct statvfs svfs; uint64_t rootdir_free_size, tsize, dlsize, instsize, rmsize; uint32_t inst_pkgcnt, up_pkgcnt, cf_pkgcnt, rm_pkgcnt, dl_pkgcnt; const char *tract, *pkgver, *repo; inst_pkgcnt = up_pkgcnt = cf_pkgcnt = rm_pkgcnt = dl_pkgcnt = 0; rootdir_free_size = tsize = dlsize = instsize = rmsize = 0; iter = xbps_array_iter_from_dict(xhp->transd, "packages"); if (iter == NULL) return EINVAL; while ((obj = xbps_object_iterator_next(iter)) != NULL) { bool preserve = false; /* * Count number of pkgs to be removed, configured, * installed and updated. */ xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); xbps_dictionary_get_cstring_nocopy(obj, "transaction", &tract); xbps_dictionary_get_cstring_nocopy(obj, "repository", &repo); xbps_dictionary_get_bool(obj, "preserve", &preserve); if (strcmp(tract, "configure") == 0) { cf_pkgcnt++; continue; } else if (strcmp(tract, "install") == 0) { inst_pkgcnt++; } else if (strcmp(tract, "update") == 0) { up_pkgcnt++; } else if (strcmp(tract, "remove") == 0) { rm_pkgcnt++; } tsize = 0; if ((strcmp(tract, "install") == 0) || (strcmp(tract, "update") == 0)) { xbps_dictionary_get_uint64(obj, "installed_size", &tsize); instsize += tsize; if (xbps_repository_is_remote(repo) && !xbps_binpkg_exists(xhp, obj)) { xbps_dictionary_get_uint64(obj, "filename-size", &tsize); /* signature file: 512 bytes */ tsize += 512; dlsize += tsize; instsize += tsize; dl_pkgcnt++; xbps_dictionary_set_bool(obj, "download", true); } } /* * If removing or updating a package, get installed_size * from pkg's metadata dictionary. */ if ((strcmp(tract, "remove") == 0) || ((strcmp(tract, "update") == 0) && !preserve)) { char *pkgname; pkgname = xbps_pkg_name(pkgver); assert(pkgname); pkg_metad = xbps_pkgdb_get_pkg(xhp, pkgname); free(pkgname); if (pkg_metad == NULL) continue; xbps_dictionary_get_uint64(pkg_metad, "installed_size", &tsize); rmsize += tsize; } } xbps_object_iterator_release(iter); if (instsize > rmsize) { instsize -= rmsize; rmsize = 0; } else if (rmsize > instsize) { rmsize -= instsize; instsize = 0; } else { instsize = rmsize = 0; } if (!xbps_dictionary_set_uint32(xhp->transd, "total-install-pkgs", inst_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-update-pkgs", up_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-configure-pkgs", cf_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-remove-pkgs", rm_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint32(xhp->transd, "total-download-pkgs", dl_pkgcnt)) return EINVAL; if (!xbps_dictionary_set_uint64(xhp->transd, "total-installed-size", instsize)) return EINVAL; if (!xbps_dictionary_set_uint64(xhp->transd, "total-download-size", dlsize)) return EINVAL; if (!xbps_dictionary_set_uint64(xhp->transd, "total-removed-size", rmsize)) return EINVAL; /* Get free space from target rootdir: return ENOSPC if there's not enough space */ if (statvfs(xhp->rootdir, &svfs) == -1) { xbps_dbg_printf(xhp, "%s: statvfs failed: %s\n", __func__, strerror(errno)); return 0; } /* compute free space on disk */ rootdir_free_size = svfs.f_bfree * svfs.f_bsize; if (!xbps_dictionary_set_uint64(xhp->transd, "disk-free-size", rootdir_free_size)) return EINVAL; if (instsize > rootdir_free_size) return ENOSPC; return 0; }