Merge branch 'morganamilo/universal-transactions' into 'master'

pacman: implement universal transactions

See merge request pacman/pacman!47
This commit is contained in:
Morgan Adamiec 2025-06-22 13:41:33 +00:00
commit 34acdd555e
10 changed files with 568 additions and 560 deletions

View file

@ -105,7 +105,7 @@ config_t *config_new(void)
return NULL; return NULL;
} }
/* defaults which may get overridden later */ /* defaults which may get overridden later */
newconfig->op = PM_OP_MAIN; newconfig->op = PM_OP_NONE;
newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING; newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING;
newconfig->configfile = strdup(CONFFILE); newconfig->configfile = strdup(CONFFILE);
if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) { if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
@ -131,6 +131,13 @@ config_t *config_new(void)
return newconfig; return newconfig;
} }
void targets_free(targets_t *targets) {
FREELIST(targets->targets);
FREELIST(targets->sync);
FREELIST(targets->upgrade);
FREELIST(targets->remove);
}
int config_free(config_t *oldconfig) int config_free(config_t *oldconfig)
{ {
if(oldconfig == NULL) { if(oldconfig == NULL) {

View file

@ -44,6 +44,14 @@ typedef struct __config_repo_t {
int siglevel_mask; int siglevel_mask;
} config_repo_t; } config_repo_t;
typedef struct __targets_t {
alpm_list_t *targets;
alpm_list_t *sync;
alpm_list_t *upgrade;
alpm_list_t *remove;
} targets_t;
typedef struct __config_t { typedef struct __config_t {
unsigned short op; unsigned short op;
unsigned short quiet; unsigned short quiet;
@ -145,14 +153,15 @@ typedef struct __config_t {
/* Operations */ /* Operations */
enum { enum {
PM_OP_MAIN = 1, PM_OP_NONE = 0,
PM_OP_REMOVE, PM_OP_INVALID = (1 << 0),
PM_OP_UPGRADE, PM_OP_REMOVE = (1 << 1),
PM_OP_QUERY, PM_OP_UPGRADE = (1 << 2),
PM_OP_SYNC, PM_OP_QUERY = (1 << 3),
PM_OP_DEPTEST, PM_OP_SYNC = (1 << 4),
PM_OP_DATABASE, PM_OP_DEPTEST = (1 << 5),
PM_OP_FILES PM_OP_DATABASE = (1 << 6),
PM_OP_FILES = (1 << 7)
}; };
/* Long Operations */ /* Long Operations */
@ -239,6 +248,9 @@ enum {
/* global config variable */ /* global config variable */
extern config_t *config; extern config_t *config;
void targets_free(targets_t *targets);
void enable_colors(int colors); void enable_colors(int colors);
config_t *config_new(void); config_t *config_new(void);
int config_free(config_t *oldconfig); int config_free(config_t *oldconfig);

View file

@ -10,6 +10,7 @@ pacman_sources = files('''
remove.c remove.c
sighandler.h sighandler.c sighandler.h sighandler.c
sync.c sync.c
trans.c
callback.h callback.c callback.h callback.c
upgrade.c upgrade.c
util.h util.c util.h util.c

View file

@ -104,7 +104,7 @@ static void usage(int op, const char * const myname)
char const *const str_opr = _("operation"); char const *const str_opr = _("operation");
/* please limit your strings to 80 characters in width */ /* please limit your strings to 80 characters in width */
if(op == PM_OP_MAIN) { if(op == PM_OP_NONE) {
printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr); printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr);
printf(_("operations:\n")); printf(_("operations:\n"));
printf(" %s {-h --help}\n", myname); printf(" %s {-h --help}\n", myname);
@ -340,25 +340,25 @@ static int parsearg_op(int opt, int dryrun)
/* operations */ /* operations */
case 'D': case 'D':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_DATABASE); break;
case 'F': case 'F':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_FILES); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_FILES); break;
case 'Q': case 'Q':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_QUERY); break;
case 'R': case 'R':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_REMOVE); break;
case 'S': case 'S':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_SYNC); break;
case 'T': case 'T':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_DEPTEST); break;
case 'U': case 'U':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break; config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_UPGRADE); break;
case 'V': case 'V':
if(dryrun) break; if(dryrun) break;
config->version = 1; break; config->version = 1; break;
@ -996,7 +996,7 @@ static int parseargs(int argc, char *argv[])
parsearg_op(opt, 0); parsearg_op(opt, 0);
} }
if(config->op == 0) { if(config->op == PM_OP_INVALID) {
pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n")); pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n"));
return 1; return 1;
} }
@ -1124,6 +1124,7 @@ int main(int argc, char *argv[])
{ {
int ret = 0; int ret = 0;
uid_t myuid = getuid(); uid_t myuid = getuid();
targets_t targets = {0};
console_cursor_hide(); console_cursor_hide();
install_segv_handler(); install_segv_handler();
@ -1276,24 +1277,27 @@ int main(int argc, char *argv[])
case PM_OP_DATABASE: case PM_OP_DATABASE:
ret = pacman_database(pm_targets); ret = pacman_database(pm_targets);
break; break;
case PM_OP_REMOVE:
ret = pacman_remove(pm_targets);
break;
case PM_OP_UPGRADE:
ret = pacman_upgrade(pm_targets);
break;
case PM_OP_QUERY: case PM_OP_QUERY:
ret = pacman_query(pm_targets); ret = pacman_query(pm_targets);
break; break;
case PM_OP_SYNC:
ret = pacman_sync(pm_targets);
break;
case PM_OP_DEPTEST: case PM_OP_DEPTEST:
ret = pacman_deptest(pm_targets); ret = pacman_deptest(pm_targets);
break; break;
case PM_OP_FILES: case PM_OP_FILES:
ret = pacman_files(pm_targets); ret = pacman_files(pm_targets);
break; break;
case PM_OP_SYNC:
targets.sync = pm_targets;
ret = do_transaction(&targets);
break;
case PM_OP_UPGRADE:
targets.upgrade = pm_targets;
ret = do_transaction(&targets);
break;
case PM_OP_REMOVE:
targets.remove = pm_targets;
ret = do_transaction(&targets);
break;
default: default:
pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n")); pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
ret = EXIT_FAILURE; ret = EXIT_FAILURE;

View file

@ -22,6 +22,8 @@
#include <alpm_list.h> #include <alpm_list.h>
#include "conf.h"
#define PACMAN_CALLER_PREFIX "PACMAN" #define PACMAN_CALLER_PREFIX "PACMAN"
/* database.c */ /* database.c */
@ -32,12 +34,14 @@ int pacman_deptest(alpm_list_t *targets);
int pacman_files(alpm_list_t *files); int pacman_files(alpm_list_t *files);
/* query.c */ /* query.c */
int pacman_query(alpm_list_t *targets); int pacman_query(alpm_list_t *targets);
/* remove.c */
int pacman_remove(alpm_list_t *targets);
/* sync.c */ /* sync.c */
int pacman_sync(alpm_list_t *targets); int pacman_sync(alpm_list_t *targets);
int sync_prepare_execute(void); /* remove.c */
int load_remove(alpm_list_t *targets);
/* upgrade.c */ /* upgrade.c */
int pacman_upgrade(alpm_list_t *targets); int load_upgrade(alpm_list_t *targets);
/* trans.c */
int do_transaction(targets_t *targets);
#endif /* PM_PACMAN_H */ #endif /* PM_PACMAN_H */

View file

@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <fnmatch.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -30,11 +29,6 @@
#include "util.h" #include "util.h"
#include "conf.h" #include "conf.h"
static int fnmatch_cmp(const void *pattern, const void *string)
{
return fnmatch(pattern, string, 0);
}
static int remove_target(const char *target) static int remove_target(const char *target)
{ {
alpm_pkg_t *pkg; alpm_pkg_t *pkg;
@ -69,28 +63,16 @@ static int remove_target(const char *target)
return 0; return 0;
} }
/** int load_remove(alpm_list_t *targets)
* @brief Remove a specified list of packages.
*
* @param targets a list of packages (as strings) to remove from the system
*
* @return 0 on success, 1 on failure
*/
int pacman_remove(alpm_list_t *targets)
{ {
int retval = 0; int retval = 0;
alpm_list_t *i, *data = NULL; alpm_list_t *i;
if(targets == NULL) { if(targets == NULL) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n")); pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1; return 1;
} }
/* Step 0: create a new transaction */
if(trans_init(config->flags, 0) == -1) {
return 1;
}
/* Step 1: add targets to the created transaction */ /* Step 1: add targets to the created transaction */
for(i = targets; i; i = alpm_list_next(i)) { for(i = targets; i; i = alpm_list_next(i)) {
char *target = i->data; char *target = i->data;
@ -102,81 +84,6 @@ int pacman_remove(alpm_list_t *targets)
} }
} }
if(retval == 1) {
goto cleanup;
}
/* Step 2: prepare the transaction based on its type, targets and flags */
if(alpm_trans_prepare(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_UNSATISFIED_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_depmissing_t *miss = i->data;
char *depstring = alpm_dep_compute_string(miss->depend);
colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
miss->causingpkg, depstring, miss->target);
free(depstring);
alpm_depmissing_free(miss);
}
break;
default:
break;
}
alpm_list_free(data);
retval = 1;
goto cleanup;
}
/* Search for holdpkg in target list */
int holdpkg = 0;
for(i = alpm_trans_get_remove(config->handle); i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(alpm_list_find(config->holdpkg, alpm_pkg_get_name(pkg), fnmatch_cmp)) {
pm_printf(ALPM_LOG_WARNING, _("%s is designated as a HoldPkg.\n"),
alpm_pkg_get_name(pkg));
holdpkg = 1;
}
}
if(holdpkg && (noyes(_("HoldPkg was found in target list. Do you want to continue?")) == 0)) {
retval = 1;
goto cleanup;
}
/* Step 3: actually perform the removal */
alpm_list_t *pkglist = alpm_trans_get_remove(config->handle);
if(pkglist == NULL) {
printf(_(" there is nothing to do\n"));
goto cleanup; /* we are done */
}
if(config->print) {
print_packages(pkglist);
goto cleanup;
}
/* print targets and ask user confirmation */
display_targets();
printf("\n");
if(yesno(_("Do you want to remove these packages?")) == 0) {
retval = 1;
goto cleanup;
}
if(alpm_trans_commit(config->handle, &data) == -1) {
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
alpm_strerror(alpm_errno(config->handle)));
retval = 1;
}
FREELIST(data);
/* Step 4: release transaction resources */
cleanup:
if(trans_release() == -1) {
retval = 1;
}
return retval; return retval;
} }

View file

@ -35,7 +35,6 @@
#include "pacman.h" #include "pacman.h"
#include "util.h" #include "util.h"
#include "package.h" #include "package.h"
#include "callback.h"
#include "conf.h" #include "conf.h"
static int unlink_verbose(const char *pathname, int ignore_missing) static int unlink_verbose(const char *pathname, int ignore_missing)
@ -520,383 +519,6 @@ static int sync_list(alpm_list_t *syncs, alpm_list_t *targets)
return ret; return ret;
} }
static alpm_db_t *get_db(const char *dbname)
{
alpm_list_t *i;
for(i = alpm_get_syncdbs(config->handle); i; i = i->next) {
alpm_db_t *db = i->data;
if(strcmp(alpm_db_get_name(db), dbname) == 0) {
return db;
}
}
return NULL;
}
static int process_pkg(alpm_pkg_t *pkg)
{
int ret = alpm_add_pkg(config->handle, pkg);
if(ret == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), alpm_strerror(err));
return 1;
}
config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
return 0;
}
static int group_exists(alpm_list_t *dbs, const char *name)
{
alpm_list_t *i;
for(i = dbs; i; i = i->next) {
alpm_db_t *db = i->data;
if(alpm_db_get_group(db, name)) {
return 1;
}
}
return 0;
}
static int process_group(alpm_list_t *dbs, const char *group, int error)
{
int ret = 0;
alpm_list_t *i;
alpm_list_t *pkgs = alpm_find_group_pkgs(dbs, group);
int count = alpm_list_count(pkgs);
if(!count) {
if(group_exists(dbs, group)) {
return 0;
}
pm_printf(ALPM_LOG_ERROR, _("target not found: %s\n"), group);
return 1;
}
if(error) {
/* we already know another target errored. there is no reason to prompt the
* user here; we already validated the group name so just move on since we
* won't actually be installing anything anyway. */
goto cleanup;
}
if(config->print == 0) {
char *array = malloc(count);
int n = 0;
const colstr_t *colstr = &config->colstr;
colon_printf(_n("There is %d member in group %s%s%s:\n",
"There are %d members in group %s%s%s:\n", count),
count, colstr->groups, group, colstr->title);
select_display(pkgs);
if(!array) {
ret = 1;
goto cleanup;
}
if(multiselect_question(array, count)) {
ret = 1;
free(array);
goto cleanup;
}
for(i = pkgs, n = 0; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(array[n++] == 0) {
continue;
}
if(process_pkg(pkg) == 1) {
ret = 1;
free(array);
goto cleanup;
}
}
free(array);
} else {
for(i = pkgs; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(process_pkg(pkg) == 1) {
ret = 1;
goto cleanup;
}
}
}
cleanup:
alpm_list_free(pkgs);
return ret;
}
static int process_targname(alpm_list_t *dblist, const char *targname,
int error)
{
alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname);
/* skip ignored packages when user says no */
if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) {
pm_printf(ALPM_LOG_WARNING, _("skipping target: %s\n"), targname);
return 0;
}
if(pkg) {
return process_pkg(pkg);
}
/* fallback on group */
return process_group(dblist, targname, error);
}
static int process_target(const char *target, int error)
{
/* process targets */
char *targstring = strdup(target);
char *targname = strchr(targstring, '/');
int ret = 0;
alpm_list_t *dblist;
if(targname && targname != targstring) {
alpm_db_t *db;
const char *dbname;
int usage;
*targname = '\0';
targname++;
dbname = targstring;
db = get_db(dbname);
if(!db) {
pm_printf(ALPM_LOG_ERROR, _("database not found: %s\n"),
dbname);
ret = 1;
goto cleanup;
}
/* explicitly mark this repo as valid for installs since
* a repo name was given with the target */
alpm_db_get_usage(db, &usage);
alpm_db_set_usage(db, usage|ALPM_DB_USAGE_INSTALL);
dblist = alpm_list_add(NULL, db);
ret = process_targname(dblist, targname, error);
alpm_list_free(dblist);
/* restore old usage so we don't possibly disturb later
* targets */
alpm_db_set_usage(db, usage);
} else {
targname = targstring;
dblist = alpm_get_syncdbs(config->handle);
ret = process_targname(dblist, targname, error);
}
cleanup:
free(targstring);
if(ret && access(target, R_OK) == 0) {
pm_printf(ALPM_LOG_WARNING,
_("'%s' is a file, did you mean %s instead of %s?\n"),
target, "-U/--upgrade", "-S/--sync");
}
return ret;
}
static int sync_trans(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *i;
/* Step 1: create a new transaction... */
if(trans_init(config->flags, 1) == -1) {
return 1;
}
/* process targets */
for(i = targets; i; i = alpm_list_next(i)) {
const char *targ = i->data;
if(process_target(targ, retval) == 1) {
retval = 1;
}
}
if(retval) {
trans_release();
return retval;
}
if(config->op_s_upgrade) {
if(!config->print) {
colon_printf(_("Starting full system upgrade...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"starting full system upgrade\n");
}
if(alpm_sync_sysupgrade(config->handle, config->op_s_upgrade >= 2) == -1) {
pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
trans_release();
return 1;
}
}
return sync_prepare_execute();
}
static void print_broken_dep(alpm_depmissing_t *miss)
{
char *depstring = alpm_dep_compute_string(miss->depend);
alpm_list_t *trans_add = alpm_trans_get_add(config->handle);
alpm_pkg_t *pkg;
if(miss->causingpkg == NULL) {
/* package being installed/upgraded has unresolved dependency */
colon_printf(_("unable to satisfy dependency '%s' required by %s\n"),
depstring, miss->target);
} else if((pkg = alpm_pkg_find(trans_add, miss->causingpkg))) {
/* upgrading a package breaks a local dependency */
colon_printf(_("installing %s (%s) breaks dependency '%s' required by %s\n"),
miss->causingpkg, alpm_pkg_get_version(pkg), depstring, miss->target);
} else {
/* removing a package breaks a local dependency */
colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
miss->causingpkg, depstring, miss->target);
}
free(depstring);
}
int sync_prepare_execute(void)
{
alpm_list_t *i, *packages, *data = NULL;
int retval = 0;
/* Step 2: "compute" the transaction based on targets and flags */
if(alpm_trans_prepare(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_PKG_INVALID_ARCH:
for(i = data; i; i = alpm_list_next(i)) {
char *pkg = i->data;
colon_printf(_("package %s does not have a valid architecture\n"), pkg);
free(pkg);
}
break;
case ALPM_ERR_UNSATISFIED_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
print_broken_dep(i->data);
alpm_depmissing_free(i->data);
}
break;
case ALPM_ERR_CONFLICTING_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_conflict_t *conflict = i->data;
/* only print reason if it contains new information */
if(conflict->reason->mod == ALPM_DEP_MOD_ANY) {
colon_printf(_("%s-%s and %s-%s are in conflict\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2));
} else {
char *reason = alpm_dep_compute_string(conflict->reason);
colon_printf(_("%s-%s and %s-%s are in conflict (%s)\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2),
reason);
free(reason);
}
alpm_conflict_free(conflict);
}
break;
default:
break;
}
retval = 1;
goto cleanup;
}
packages = alpm_trans_get_add(config->handle);
if(packages == NULL) {
/* nothing to do: just exit without complaining */
if(!config->print) {
printf(_(" there is nothing to do\n"));
}
goto cleanup;
}
/* Step 3: actually perform the operation */
if(config->print) {
print_packages(packages);
goto cleanup;
}
display_targets();
printf("\n");
int confirm;
if(config->op_s_downloadonly) {
confirm = yesno(_("Proceed with download?"));
} else {
confirm = yesno(_("Proceed with installation?"));
}
if(!confirm) {
retval = 1;
goto cleanup;
}
multibar_move_completed_up(true);
if(alpm_trans_commit(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_FILE_CONFLICTS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_fileconflict_t *conflict = i->data;
switch(conflict->type) {
case ALPM_FILECONFLICT_TARGET:
fprintf(stderr, _("%s exists in both '%s' and '%s'\n"),
conflict->file, conflict->target, conflict->ctarget);
break;
case ALPM_FILECONFLICT_FILESYSTEM:
if(conflict->ctarget[0]) {
fprintf(stderr, _("%s: %s exists in filesystem (owned by %s)\n"),
conflict->target, conflict->file, conflict->ctarget);
} else {
fprintf(stderr, _("%s: %s exists in filesystem\n"),
conflict->target, conflict->file);
}
break;
}
alpm_fileconflict_free(conflict);
}
break;
case ALPM_ERR_PKG_INVALID:
case ALPM_ERR_PKG_INVALID_CHECKSUM:
case ALPM_ERR_PKG_INVALID_SIG:
for(i = data; i; i = alpm_list_next(i)) {
char *filename = i->data;
fprintf(stderr, _("%s is invalid or corrupted\n"), filename);
free(filename);
}
break;
default:
break;
}
/* TODO: stderr? */
printf(_("Errors occurred, no packages were upgraded.\n"));
retval = 1;
goto cleanup;
}
/* Step 4: release transaction resources */
cleanup:
alpm_list_free(data);
if(trans_release() == -1) {
retval = 1;
}
return retval;
}
int pacman_sync(alpm_list_t *targets) int pacman_sync(alpm_list_t *targets)
{ {
alpm_list_t *sync_dbs = NULL; alpm_list_t *sync_dbs = NULL;
@ -919,26 +541,8 @@ int pacman_sync(alpm_list_t *targets)
return ret; return ret;
} }
if(check_syncdbs(1, 0)) {
return 1;
}
sync_dbs = alpm_get_syncdbs(config->handle); sync_dbs = alpm_get_syncdbs(config->handle);
if(config->op_s_sync) {
/* grab a fresh package list */
colon_printf(_("Synchronizing package databases...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"synchronizing package lists\n");
if(!sync_syncdbs(config->op_s_sync, sync_dbs)) {
return 1;
}
}
if(check_syncdbs(1, 1)) {
return 1;
}
/* search for a package */ /* search for a package */
if(config->op_s_search) { if(config->op_s_search) {
return sync_search(sync_dbs, targets); return sync_search(sync_dbs, targets);
@ -960,17 +564,9 @@ int pacman_sync(alpm_list_t *targets)
} }
if(targets == NULL) { if(targets == NULL) {
if(config->op_s_upgrade) { pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
/* proceed */ return 1;
} else if(config->op_s_sync) {
return 0;
} else {
/* don't proceed here unless we have an operation that doesn't require a
* target list */
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
} }
return sync_trans(targets); return 0;
} }

499
src/pacman/trans.c Normal file
View file

@ -0,0 +1,499 @@
/*
* trans.c
*
* Copyright (c) 2006-2021 Pacman Development Team <pacman-dev(a)archlinux.org>
* Copyright (c) 2002-2006 by Judd Vinet <jvinet(a)zeroflux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fnmatch.h>
#include <alpm.h>
#include <alpm_list.h>
/* pacman */
#include "pacman.h"
#include "callback.h"
#include "conf.h"
#include "util.h"
static int fnmatch_cmp(const void *pattern, const void *string)
{
return fnmatch(pattern, string, 0);
}
static void print_broken_dep(alpm_depmissing_t *miss)
{
char *depstring = alpm_dep_compute_string(miss->depend);
alpm_list_t *trans_add = alpm_trans_get_add(config->handle);
alpm_pkg_t *pkg;
if(miss->causingpkg == NULL) {
/* package being installed/upgraded has unresolved dependency */
colon_printf(_("unable to satisfy dependency '%s' required by %s\n"),
depstring, miss->target);
} else if((pkg = alpm_pkg_find(trans_add, miss->causingpkg))) {
/* upgrading a package breaks a local dependency */
colon_printf(_("installing %s (%s) breaks dependency '%s' required by %s\n"),
miss->causingpkg, alpm_pkg_get_version(pkg), depstring, miss->target);
} else {
/* removing a package breaks a local dependency */
colon_printf(_("removing %s breaks dependency '%s' required by %s\n"),
miss->causingpkg, depstring, miss->target);
}
free(depstring);
}
static void handle_trans_prepare_error(alpm_list_t *data)
{
alpm_errno_t err = alpm_errno(config->handle);
alpm_list_t *i;
pm_printf(ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_PKG_INVALID_ARCH:
for(i = data; i; i = alpm_list_next(i)) {
char *pkg = i->data;
colon_printf(_("package %s does not have a valid architecture\n"), pkg);
free(pkg);
}
break;
case ALPM_ERR_UNSATISFIED_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
print_broken_dep(i->data);
alpm_depmissing_free(i->data);
}
break;
case ALPM_ERR_CONFLICTING_DEPS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_conflict_t *conflict = i->data;
/* only print reason if it contains new information */
if(conflict->reason->mod == ALPM_DEP_MOD_ANY) {
colon_printf(_("%s-%s and %s-%s are in conflict\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2));
} else {
char *reason = alpm_dep_compute_string(conflict->reason);
colon_printf(_("%s-%s and %s-%s are in conflict (%s)\n"),
alpm_pkg_get_name(conflict->package1),
alpm_pkg_get_version(conflict->package1),
alpm_pkg_get_name(conflict->package2),
alpm_pkg_get_version(conflict->package2),
reason);
free(reason);
}
alpm_conflict_free(conflict);
}
break;
default:
break;
}
}
static void handle_trans_commit_error(alpm_list_t *data) {
alpm_errno_t err = alpm_errno(config->handle);
alpm_list_t *i;
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
alpm_strerror(err));
switch(err) {
case ALPM_ERR_FILE_CONFLICTS:
for(i = data; i; i = alpm_list_next(i)) {
alpm_fileconflict_t *conflict = i->data;
switch(conflict->type) {
case ALPM_FILECONFLICT_TARGET:
fprintf(stderr, _("%s exists in both '%s' and '%s'\n"),
conflict->file, conflict->target, conflict->ctarget);
break;
case ALPM_FILECONFLICT_FILESYSTEM:
if(conflict->ctarget[0]) {
fprintf(stderr, _("%s: %s exists in filesystem (owned by %s)\n"),
conflict->target, conflict->file, conflict->ctarget);
} else {
fprintf(stderr, _("%s: %s exists in filesystem\n"),
conflict->target, conflict->file);
}
break;
}
alpm_fileconflict_free(conflict);
}
break;
case ALPM_ERR_PKG_INVALID:
case ALPM_ERR_PKG_INVALID_CHECKSUM:
case ALPM_ERR_PKG_INVALID_SIG:
for(i = data; i; i = alpm_list_next(i)) {
char *filename = i->data;
fprintf(stderr, _("%s is invalid or corrupted\n"), filename);
free(filename);
}
break;
default:
break;
}
/* TODO: stderr? */
printf(_("Errors occurred, no packages were upgraded.\n"));
}
static int trans_prepare_execute(void)
{
alpm_list_t *i, *packages, *remove_packages, *data = NULL;
int retval = 0;
/* Step 2: "compute" the transaction based on targets and flags */
if(alpm_trans_prepare(config->handle, &data) == -1) {
handle_trans_prepare_error(data);
retval = 1;
goto cleanup;
}
packages = alpm_trans_get_add(config->handle);
remove_packages = alpm_trans_get_remove(config->handle);
if(packages == NULL && remove_packages == NULL) {
/* nothing to do: just exit without complaining */
if(!config->print) {
printf(_(" there is nothing to do\n"));
}
goto cleanup;
}
/* Search for holdpkg in target list */
int holdpkg = 0;
for(i = remove_packages; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(alpm_list_find(config->holdpkg, alpm_pkg_get_name(pkg), fnmatch_cmp)) {
pm_printf(ALPM_LOG_WARNING, _("%s is designated as a HoldPkg.\n"),
alpm_pkg_get_name(pkg));
holdpkg = 1;
}
}
if(holdpkg && (noyes(_("HoldPkg was found in target list. Do you want to continue?")) == 0)) {
retval = 1;
goto cleanup;
}
/* Step 3: actually perform the operation */
if(config->print) {
print_packages(packages);
print_packages(remove_packages);
goto cleanup;
}
display_targets();
printf("\n");
int confirm;
if(config->op_s_downloadonly) {
confirm = yesno(_("Proceed with download?"));
} else {
confirm = yesno(_("Proceed with installation?"));
}
if(!confirm) {
retval = 1;
goto cleanup;
}
multibar_move_completed_up(true);
if(alpm_trans_commit(config->handle, &data) == -1) {
handle_trans_commit_error(data);
retval = 1;
goto cleanup;
}
/* Step 4: release transaction resources */
cleanup:
alpm_list_free(data);
if(trans_release() == -1) {
retval = 1;
}
return retval;
}
static int group_exists(alpm_list_t *dbs, const char *name)
{
alpm_list_t *i;
for(i = dbs; i; i = i->next) {
alpm_db_t *db = i->data;
if(alpm_db_get_group(db, name)) {
return 1;
}
}
return 0;
}
static alpm_db_t *get_db(const char *dbname)
{
alpm_list_t *i;
for(i = alpm_get_syncdbs(config->handle); i; i = i->next) {
alpm_db_t *db = i->data;
if(strcmp(alpm_db_get_name(db), dbname) == 0) {
return db;
}
}
return NULL;
}
static int process_pkg(alpm_pkg_t *pkg)
{
int ret = alpm_add_pkg(config->handle, pkg);
if(ret == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), alpm_strerror(err));
return 1;
}
config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
return 0;
}
static int process_group(alpm_list_t *dbs, const char *group, int error)
{
int ret = 0;
alpm_list_t *i;
alpm_list_t *pkgs = alpm_find_group_pkgs(dbs, group);
int count = alpm_list_count(pkgs);
if(!count) {
if(group_exists(dbs, group)) {
return 0;
}
pm_printf(ALPM_LOG_ERROR, _("target not found: %s\n"), group);
return 1;
}
if(error) {
/* we already know another target errored. there is no reason to prompt the
* user here; we already validated the group name so just move on since we
* won't actually be installing anything anyway. */
goto cleanup;
}
if(config->print == 0) {
char *array = malloc(count);
int n = 0;
const colstr_t *colstr = &config->colstr;
colon_printf(_n("There is %d member in group %s%s%s:\n",
"There are %d members in group %s%s%s:\n", count),
count, colstr->groups, group, colstr->title);
select_display(pkgs);
if(!array) {
ret = 1;
goto cleanup;
}
if(multiselect_question(array, count)) {
ret = 1;
free(array);
goto cleanup;
}
for(i = pkgs, n = 0; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(array[n++] == 0) {
continue;
}
if(process_pkg(pkg) == 1) {
ret = 1;
free(array);
goto cleanup;
}
}
free(array);
} else {
for(i = pkgs; i; i = alpm_list_next(i)) {
alpm_pkg_t *pkg = i->data;
if(process_pkg(pkg) == 1) {
ret = 1;
goto cleanup;
}
}
}
cleanup:
alpm_list_free(pkgs);
return ret;
}
static int process_targname(alpm_list_t *dblist, const char *targname,
int error)
{
alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname);
/* skip ignored packages when user says no */
if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) {
pm_printf(ALPM_LOG_WARNING, _("skipping target: %s\n"), targname);
return 0;
}
if(pkg) {
return process_pkg(pkg);
}
/* fallback on group */
return process_group(dblist, targname, error);
}
static int process_target(const char *target, int error)
{
/* process targets */
char *targstring = strdup(target);
char *targname = strchr(targstring, '/');
int ret = 0;
alpm_list_t *dblist;
if(targname && targname != targstring) {
alpm_db_t *db;
const char *dbname;
int usage;
*targname = '\0';
targname++;
dbname = targstring;
db = get_db(dbname);
if(!db) {
pm_printf(ALPM_LOG_ERROR, _("database not found: %s\n"),
dbname);
ret = 1;
goto cleanup;
}
/* explicitly mark this repo as valid for installs since
* a repo name was given with the target */
alpm_db_get_usage(db, &usage);
alpm_db_set_usage(db, usage|ALPM_DB_USAGE_INSTALL);
dblist = alpm_list_add(NULL, db);
ret = process_targname(dblist, targname, error);
alpm_list_free(dblist);
/* restore old usage so we don't possibly disturb later
* targets */
alpm_db_set_usage(db, usage);
} else {
targname = targstring;
dblist = alpm_get_syncdbs(config->handle);
ret = process_targname(dblist, targname, error);
}
cleanup:
free(targstring);
if(ret && access(target, R_OK) == 0) {
pm_printf(ALPM_LOG_WARNING,
_("'%s' is a file, did you mean %s instead of %s?\n"),
target, "-U/--upgrade", "-S/--sync");
}
return ret;
}
static int load_sync(alpm_list_t *targets)
{
int retval = 0;
alpm_list_t *i;
if(targets == NULL && !config->op_s_upgrade && !config->op_s_sync) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
/* process targets */
for(i = targets; i; i = alpm_list_next(i)) {
const char *targ = i->data;
if(process_target(targ, retval) == 1) {
retval = 1;
}
}
return retval;
}
int do_transaction(targets_t *targets) {
int need_repos = (config->op & PM_OP_SYNC);
alpm_list_t *sync_dbs;
if(targets->targets != NULL) {
pm_printf(ALPM_LOG_ERROR, _("targets must come after operation\n"));
return 1;
}
if(check_syncdbs(need_repos, 0)) {
return 1;
}
sync_dbs = alpm_get_syncdbs(config->handle);
if(config->op_s_sync) {
/* grab a fresh package list */
colon_printf(_("Synchronizing package databases...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"synchronizing package lists\n");
if(!sync_syncdbs(config->op_s_sync, sync_dbs)) {
return 1;
}
}
if(check_syncdbs(need_repos, 1)) {
return 1;
}
if(config->op_s_clean || config->op_s_search || config->op_s_info
|| config->op_q_list || config->group) {
return pacman_sync(targets->sync);
}
/* Step 1: create a new transaction... */
if(trans_init(config->flags, 1) == -1) {
return 1;
}
if(config->op & PM_OP_SYNC && load_sync(targets->sync)) {
goto cleanup;
}
if(config->op & PM_OP_REMOVE && load_remove(targets->remove)) {
goto cleanup;
}
if(config->op & PM_OP_UPGRADE && load_upgrade(targets->upgrade)) {
goto cleanup;
}
if(config->op_s_upgrade) {
if(!config->print) {
colon_printf(_("Starting full system upgrade...\n"));
alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
"starting full system upgrade\n");
}
if(alpm_sync_sysupgrade(config->handle, config->op_s_upgrade >= 2) == -1) {
pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
trans_release();
return 1;
}
}
return trans_prepare_execute();
cleanup:
trans_release();
return 1;
}

View file

@ -65,7 +65,7 @@ static int load_packages(alpm_list_t *targets, int siglevel)
* *
* @return 0 on success, 1 on failure * @return 0 on success, 1 on failure
*/ */
int pacman_upgrade(alpm_list_t *targets) int load_upgrade(alpm_list_t *targets)
{ {
int retval = 0; int retval = 0;
alpm_list_t *remote_targets = NULL, *fetched_files = NULL; alpm_list_t *remote_targets = NULL, *fetched_files = NULL;
@ -88,15 +88,6 @@ int pacman_upgrade(alpm_list_t *targets)
if(remote_targets) { if(remote_targets) {
retval = alpm_fetch_pkgurl(config->handle, remote_targets, &fetched_files); retval = alpm_fetch_pkgurl(config->handle, remote_targets, &fetched_files);
if(retval) {
goto fail_free;
}
}
/* Step 1: create a new transaction */
if(trans_init(config->flags, 1) == -1) {
retval = 1;
goto fail_free;
} }
if(!config->print) { if(!config->print) {
@ -105,23 +96,10 @@ int pacman_upgrade(alpm_list_t *targets)
retval |= load_packages(local_targets, alpm_option_get_local_file_siglevel(config->handle)); retval |= load_packages(local_targets, alpm_option_get_local_file_siglevel(config->handle));
retval |= load_packages(fetched_files, alpm_option_get_remote_file_siglevel(config->handle)); retval |= load_packages(fetched_files, alpm_option_get_remote_file_siglevel(config->handle));
if(retval) {
goto fail_release;
}
alpm_list_free(remote_targets);
alpm_list_free(local_targets);
FREELIST(fetched_files);
/* now that targets are resolved, we can hand it all off to the sync code */
return sync_prepare_execute();
fail_release:
trans_release();
fail_free:
alpm_list_free(remote_targets); alpm_list_free(remote_targets);
alpm_list_free(local_targets); alpm_list_free(local_targets);
FREELIST(fetched_files); FREELIST(fetched_files);
return retval; return retval;
} }

View file

@ -10,7 +10,7 @@ lp3 = pmpkg("pkg3")
lp3.depends = [ "pkg1" ] lp3.depends = [ "pkg1" ]
self.addpkg2db("local", lp3) self.addpkg2db("local", lp3)
self.args = "-Ru pkg1 pkg2" self.args = "-R --unneeded pkg1 pkg2"
self.addrule("PACMAN_RETCODE=0") self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=pkg1") self.addrule("PKG_EXIST=pkg1")