[PATCH] pacman: implement universal transactions
This allows using -S, -U and -R in one command: pacman -S foo -R bar To make this work some breaking changes have made. Targets have to come after the operation: pacman -S foo //works pacman -Syu //works pacman -yuS //works pacman foo -S //doesn't work This could be supported with some code to copy all targets before the first operation into the first operation's target list. And -u as a short for --unneeded has been removed as it conflicts with --sysupgrade. However as -u/--sysupgrade is bound to -S, accidently doing `pacman -Ru` will not accidently cause a system upgrade. Another quirk with the UI is that -S has many non transaction related flags, -Sc -Sg -Sl -Si. These have been split off as "sync only" flags. Meaning they show up with `pacman -Si foo` but will be invalid on `pacman -R bar -Si foo`. Also when -R'ing and -S'ing the same package in some command it's treated as a full uninstall then reinstall. The backup files are .pacsave'd and the install reason is set to explicit. I feel like this behavious is good. This also allows you to wipe config files which what --nokeep was intending to solve. Other flags just have to have the op they belong to to be used for them to be valid. For example: pacman -Rn foo //works pacman -S -Rn //works pacman -Sn //doesn't work pacman -Sn -R //works We could posibly drop these flags belonging to each operation and just make them generic transaction flags. Implements FS#9694 Signed-off-by: morganamilo <morganamilo@archlinux.org> --- src/pacman/conf.c | 10 +- src/pacman/conf.h | 29 +- src/pacman/meson.build | 1 + src/pacman/pacman.c | 467 +++++++++++++++++++------------- src/pacman/pacman.h | 12 +- src/pacman/remove.c | 99 +------ src/pacman/sync.c | 403 +-------------------------- src/pacman/trans.c | 478 +++++++++++++++++++++++++++++++++ src/pacman/upgrade.c | 26 +- src/pacman/util.c | 15 +- test/pacman/tests/remove049.py | 4 +- 11 files changed, 814 insertions(+), 730 deletions(-) create mode 100644 src/pacman/trans.c diff --git a/src/pacman/conf.c b/src/pacman/conf.c index f9edf75b..9a98264c 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -105,7 +105,8 @@ config_t *config_new(void) return NULL; } /* defaults which may get overridden later */ - newconfig->op = PM_OP_MAIN; + newconfig->op = 0; + newconfig->curr_op = 0; newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING; newconfig->configfile = strdup(CONFFILE); if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) { @@ -131,6 +132,13 @@ config_t *config_new(void) 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) { if(oldconfig == NULL) { diff --git a/src/pacman/conf.h b/src/pacman/conf.h index f7916ca9..93813ef4 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -43,8 +43,17 @@ typedef struct __config_repo_t { int siglevel_mask; } 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 { unsigned short op; + unsigned short curr_op; unsigned short quiet; unsigned short verbose; unsigned short version; @@ -142,14 +151,15 @@ typedef struct __config_t { /* Operations */ enum { - PM_OP_MAIN = 1, - PM_OP_REMOVE, - PM_OP_UPGRADE, - PM_OP_QUERY, - PM_OP_SYNC, - PM_OP_DEPTEST, - PM_OP_DATABASE, - PM_OP_FILES + PM_OP_INVALID = 1 << 0, + PM_OP_REMOVE = 1 << 1, + PM_OP_UPGRADE = 1 << 2, + PM_OP_QUERY = 1 << 3, + PM_OP_SYNC = 1 << 4, + PM_OP_DEPTEST = 1 << 5, + PM_OP_DATABASE = 1 << 6, + PM_OP_FILES = 1 << 7, + PM_OP_TRANS = PM_OP_SYNC | PM_OP_UPGRADE | PM_OP_REMOVE }; /* Long Operations */ @@ -235,6 +245,9 @@ enum { /* global config variable */ extern config_t *config; + +void targets_free(targets_t *targets); + void enable_colors(int colors); config_t *config_new(void); int config_free(config_t *oldconfig); diff --git a/src/pacman/meson.build b/src/pacman/meson.build index 6926f676..6bf31495 100644 --- a/src/pacman/meson.build +++ b/src/pacman/meson.build @@ -10,6 +10,7 @@ pacman_sources = files(''' remove.c sighandler.h sighandler.c sync.c + trans.c callback.h callback.c upgrade.c util.h util.c diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index ea536a47..829747c2 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -47,7 +47,7 @@ #include "sighandler.h" /* list of targets specified on command line */ -static alpm_list_t *pm_targets; +static targets_t pm_targets; /* Used to sort the options in --help */ static int options_cmp(const void *p1, const void *p2) @@ -104,7 +104,7 @@ static void usage(int op, const char * const myname) char const *const str_opr = _("operation"); /* please limit your strings to 80 characters in width */ - if(op == PM_OP_MAIN) { + if(!op) { printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr); printf(_("operations:\n")); printf(" %s {-h --help}\n", myname); @@ -125,11 +125,10 @@ static void usage(int op, const char * const myname) addlist(_(" -c, --cascade remove packages and all packages that depend on them\n")); addlist(_(" -n, --nosave remove configuration files\n")); addlist(_(" -s, --recursive remove unnecessary dependencies\n" - " (-ss includes explicitly installed dependencies)\n")); - addlist(_(" -u, --unneeded remove unneeded packages\n")); + " (-ss includes explicitly installed dependencies)\n")); + addlist(_(" --unneeded remove unneeded packages\n")); } else if(op == PM_OP_UPGRADE) { printf("%s: %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file); - addlist(_(" --needed do not reinstall up to date packages\n")); printf("%s:\n", str_opt); } else if(op == PM_OP_QUERY) { printf("%s: %s {-Q --query} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg); @@ -163,7 +162,6 @@ static void usage(int op, const char * const myname) addlist(_(" -u, --sysupgrade upgrade installed packages (-uu enables downgrades)\n")); addlist(_(" -y, --refresh download fresh package databases from the server\n" " (-yy to force a refresh even if up to date)\n")); - addlist(_(" --needed do not reinstall up to date packages\n")); } else if(op == PM_OP_DATABASE) { printf("%s: %s {-D --database} <%s> <%s>\n", str_usg, myname, str_opt, str_pkg); printf("%s:\n", str_opt); @@ -185,29 +183,27 @@ static void usage(int op, const char * const myname) addlist(_(" --machinereadable\n" " produce machine-readable output\n")); } - switch(op) { - case PM_OP_SYNC: - case PM_OP_UPGRADE: - addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n")); - addlist(_(" --overwrite <glob>\n" - " overwrite conflicting files (can be used more than once)\n")); - addlist(_(" --asdeps install packages as non-explicitly installed\n")); - addlist(_(" --asexplicit install packages as explicitly installed\n")); - addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n")); - addlist(_(" --ignoregroup <grp>\n" - " ignore a group upgrade (can be used more than once)\n")); - __attribute__((fallthrough)); - case PM_OP_REMOVE: - addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n")); - addlist(_(" --assume-installed <package=version>\n" - " add a virtual package to satisfy dependencies\n")); - addlist(_(" --dbonly only modify database entries, not package files\n")); - addlist(_(" --noprogressbar do not show a progress bar when downloading files\n")); - addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n")); - addlist(_(" -p, --print print the targets instead of performing the operation\n")); - addlist(_(" --print-format <string>\n" - " specify how the targets should be printed\n")); - break; + if(op & (PM_OP_UPGRADE | PM_OP_SYNC)) { + addlist(_(" --needed do not reinstall up to date packages\n")); + addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n")); + addlist(_(" --overwrite <glob>\n" + " overwrite conflicting files (can be used more than once)\n")); + addlist(_(" --asdeps install packages as non-explicitly installed\n")); + addlist(_(" --asexplicit install packages as explicitly installed\n")); + addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n")); + addlist(_(" --ignoregroup <grp>\n" + " ignore a group upgrade (can be used more than once)\n")); + } + if(op & PM_OP_TRANS) { + addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n")); + addlist(_(" --assume-installed <package=version>\n" + " add a virtual package to satisfy dependencies\n")); + addlist(_(" --dbonly only modify database entries, not package files\n")); + addlist(_(" --noprogressbar do not show a progress bar when downloading files\n")); + addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n")); + addlist(_(" -p, --print print the targets instead of performing the operation\n")); + addlist(_(" --print-format <string>\n" + " specify how the targets should be printed\n")); } addlist(_(" -b, --dbpath <path> set an alternate database location\n")); @@ -301,7 +297,7 @@ static void cleanup(int ret) } /* free memory */ - FREELIST(pm_targets); + targets_free(&pm_targets); console_cursor_show(); exit(ret); } @@ -327,6 +323,24 @@ static int parsearg_util_addlist(alpm_list_t **list) return 0; } +static void set_op(int opt) +{ + int trans = (config->op & PM_OP_TRANS) != 0; + int setting_trans = (opt & PM_OP_TRANS) != 0; + + /* we allow -SS so allow -QQ for sake of consistency */ + if(config->op == opt) { + return; + } + + if(!config->op || (trans && setting_trans)) { + config->op |= opt; + config->curr_op = opt; + } else { + config->op |= PM_OP_INVALID; + } +} + /** Helper function for parsing operation from command-line arguments. * @param opt Keycode returned by getopt_long * @param dryrun If nonzero, application state is NOT changed @@ -338,25 +352,25 @@ static int parsearg_op(int opt, int dryrun) /* operations */ case 'D': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break; + set_op(PM_OP_DATABASE); break; case 'F': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_FILES); break; + set_op(PM_OP_FILES); break; case 'Q': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break; + set_op(PM_OP_QUERY); break; case 'R': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break; + set_op(PM_OP_REMOVE); break; case 'S': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break; + set_op(PM_OP_SYNC); break; case 'T': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break; + set_op(PM_OP_DEPTEST); break; case 'U': if(dryrun) break; - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break; + set_op(PM_OP_UPGRADE); break; case 'V': if(dryrun) break; config->version = 1; break; @@ -369,6 +383,7 @@ static int parsearg_op(int opt, int dryrun) return 0; } + /** Helper functions for parsing command-line arguments. * @param opt Keycode returned by getopt_long * @return 0 on success, 1 on failure @@ -619,62 +634,8 @@ static void checkargs_query(void) "--native", "--foreign"); } -/* options common to -S -R -U */ -static int parsearg_trans(int opt) -{ - switch(opt) { - case OP_NODEPS: - case 'd': - if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) { - config->flags |= ALPM_TRANS_FLAG_NODEPS; - } else { - config->flags |= ALPM_TRANS_FLAG_NODEPVERSION; - } - break; - case OP_DBONLY: - config->flags |= ALPM_TRANS_FLAG_DBONLY; - config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET; - config->flags |= ALPM_TRANS_FLAG_NOHOOKS; - break; - case OP_NOPROGRESSBAR: - config->noprogressbar = 1; - break; - case OP_NOSCRIPTLET: - config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET; - break; - case OP_PRINT: - case 'p': - config->print = 1; - break; - case OP_PRINTFORMAT: - config->print = 1; - free(config->print_format); - config->print_format = strdup(optarg); - break; - case OP_ASSUMEINSTALLED: - parsearg_util_addlist(&(config->assumeinstalled)); - break; - default: - return 1; - } - return 0; -} - -static void checkargs_trans(void) -{ - if(config->print) { - invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY, - "--print", "--dbonly"); - invalid_opt(config->flags & ALPM_TRANS_FLAG_NOSCRIPTLET, - "--print", "--noscriptlet"); - } -} - static int parsearg_remove(int opt) { - if(parsearg_trans(opt) == 0) { - return 0; - } switch(opt) { case OP_CASCADE: case 'c': @@ -693,7 +654,6 @@ static int parsearg_remove(int opt) } break; case OP_UNNEEDED: - case 'u': config->flags |= ALPM_TRANS_FLAG_UNNEEDED; break; default: @@ -704,7 +664,6 @@ static int parsearg_remove(int opt) static void checkargs_remove(void) { - checkargs_trans(); if(config->flags & ALPM_TRANS_FLAG_NOSAVE) { invalid_opt(config->print, "--nosave", "--print"); invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY, @@ -715,9 +674,6 @@ static void checkargs_remove(void) /* options common to -S -U */ static int parsearg_upgrade(int opt) { - if(parsearg_trans(opt) == 0) { - return 0; - } switch(opt) { case OP_OVERWRITE_FILES: parsearg_util_addlist(&(config->overwrite_files)); @@ -750,7 +706,6 @@ static int parsearg_upgrade(int opt) static void checkargs_upgrade(void) { - checkargs_trans(); invalid_opt(config->flags & ALPM_TRANS_FLAG_ALLDEPS && config->flags & ALPM_TRANS_FLAG_ALLEXPLICIT, "--asdeps", "--asexplicit"); @@ -758,9 +713,6 @@ static void checkargs_upgrade(void) static int parsearg_files(int opt) { - if(parsearg_trans(opt) == 0) { - return 0; - } switch(opt) { case OP_LIST: case 'l': @@ -799,6 +751,26 @@ static int parsearg_sync(int opt) if(parsearg_upgrade(opt) == 0) { return 0; } + switch(opt) { + case OP_SYSUPGRADE: + case 'u': + (config->op_s_upgrade)++; + break; + case OP_REFRESH: + case 'y': + (config->op_s_sync)++; + break; + default: + return 1; + } + return 0; +} + +static int parsearg_sync_only(int opt) +{ + if(parsearg_sync(opt) == 0) { + return 0; + } switch(opt) { case OP_CLEAN: case 'c': @@ -840,7 +812,12 @@ static int parsearg_sync(int opt) static void checkargs_sync(void) { - checkargs_upgrade(); + /* no conflicts currently */ +} + +static void checkargs_sync_only(void) +{ + checkargs_sync(); if(config->op_s_clean) { invalid_opt(config->group, "--clean", "--groups"); invalid_opt(config->op_s_info, "--clean", "--info"); @@ -870,6 +847,89 @@ static void checkargs_sync(void) } } +/* options common to -S -R -U */ +static int parsearg_trans(int opt) +{ + if(config->op & (PM_OP_SYNC | PM_OP_UPGRADE) && parsearg_upgrade(opt) == 0) { + return 0; + } + if(config->op & PM_OP_SYNC && parsearg_sync(opt) == 0) { + return 0; + } + if(config->op & PM_OP_REMOVE && parsearg_remove(opt) == 0) { + return 0; + } + switch(opt) { + case OP_NODEPS: + case 'd': + if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) { + config->flags |= ALPM_TRANS_FLAG_NODEPS; + } else { + config->flags |= ALPM_TRANS_FLAG_NODEPVERSION; + } + break; + case OP_DBONLY: + config->flags |= ALPM_TRANS_FLAG_DBONLY; + config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET; + config->flags |= ALPM_TRANS_FLAG_NOHOOKS; + break; + case OP_NOPROGRESSBAR: + config->noprogressbar = 1; + break; + case OP_NOSCRIPTLET: + config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET; + break; + case OP_PRINT: + case 'p': + config->print = 1; + break; + case OP_PRINTFORMAT: + config->print = 1; + free(config->print_format); + config->print_format = strdup(optarg); + break; + case OP_ASSUMEINSTALLED: + parsearg_util_addlist(&(config->assumeinstalled)); + break; + default: + return 1; + } + return 0; +} + +static void checkargs_trans(void) +{ + if(config->op & (PM_OP_SYNC | PM_OP_UPGRADE)) { + checkargs_upgrade(); + } + if(config->op & PM_OP_SYNC) { + checkargs_sync(); + } + if(config->op & PM_OP_REMOVE) { + checkargs_remove(); + } + + if(config->print) { + invalid_opt(config->flags & ALPM_TRANS_FLAG_DBONLY, + "--print", "--dbonly"); + invalid_opt(config->flags & ALPM_TRANS_FLAG_NOSCRIPTLET, + "--print", "--noscriptlet"); + } +} + +static void add_target(char *targ) +{ + if(config->curr_op & PM_OP_SYNC) { + pm_targets.sync = alpm_list_add(pm_targets.sync, targ); + } else if(config->curr_op & PM_OP_REMOVE) { + pm_targets.remove = alpm_list_add(pm_targets.remove, targ); + } else if(config->curr_op & PM_OP_UPGRADE) { + pm_targets.upgrade = alpm_list_add(pm_targets.upgrade, targ); + } else { + pm_targets.targets = alpm_list_add(pm_targets.targets, targ); + } +} + /** Parse command-line arguments for each operation. * @param argc argc * @param argv argv @@ -880,7 +940,7 @@ static int parseargs(int argc, char *argv[]) int opt; int option_index = 0; int result; - const char *optstring = "DFQRSTUVb:cdefghiklmnopqr:stuvwxy"; + const char *optstring = "-DFQRSTUVb:cdefghiklmnopqr:stuvwxy"; static const struct option opts[] = { {"database", no_argument, 0, 'D'}, @@ -955,6 +1015,11 @@ static int parseargs(int argc, char *argv[]) while((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { if(opt == 0) { continue; + } else if(opt == 1) { + char *targ = strdup(optarg); + /* add the target to our target array */ + add_target(targ); + continue; } else if(opt == '?') { /* unknown option, getopt printed an error */ return 1; @@ -962,8 +1027,8 @@ static int parseargs(int argc, char *argv[]) parsearg_op(opt, 0); } - if(config->op == 0) { - pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n")); + if(config->op & PM_OP_INVALID) { + pm_printf(ALPM_LOG_ERROR, _("these operations can not be used together\n")); return 1; } if(config->help) { @@ -978,7 +1043,7 @@ static int parseargs(int argc, char *argv[]) /* parse all other options */ optind = 1; while((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { - if(opt == 0) { + if(opt == 0 || opt == 1) { continue; } else if(opt == '?') { /* this should have failed during first pass already */ @@ -995,14 +1060,9 @@ static int parseargs(int argc, char *argv[]) case PM_OP_QUERY: result = parsearg_query(opt); break; - case PM_OP_REMOVE: - result = parsearg_remove(opt); - break; case PM_OP_SYNC: - result = parsearg_sync(opt); - break; - case PM_OP_UPGRADE: - result = parsearg_upgrade(opt); + /* handle -Si -Sg etc if -S is the only trans op */ + result = parsearg_sync_only(opt); break; case PM_OP_FILES: result = parsearg_files(opt); @@ -1016,6 +1076,13 @@ static int parseargs(int argc, char *argv[]) continue; } + if(config->op & PM_OP_TRANS) { + result = parsearg_trans(opt); + } + if(result == 0) { + continue; + } + /* fall back to global options */ result = parsearg_global(opt); if(result != 0) { @@ -1032,7 +1099,7 @@ static int parseargs(int argc, char *argv[]) while(optind < argc) { /* add the target to our target array */ - pm_targets = alpm_list_add(pm_targets, strdup(argv[optind])); + add_target(strdup(argv[optind])); optind++; } @@ -1044,7 +1111,7 @@ static int parseargs(int argc, char *argv[]) /* no conflicting options */ break; case PM_OP_SYNC: - checkargs_sync(); + checkargs_sync_only(); break; case PM_OP_QUERY: checkargs_query(); @@ -1061,10 +1128,83 @@ static int parseargs(int argc, char *argv[]) default: break; } + if(config->op & PM_OP_TRANS) { + checkargs_trans(); + } return 0; } +static void parse_stdin(alpm_list_t **targets) +{ + if(!isatty(fileno(stdin))) { + int target_found = 0; + char *vdata, *line = NULL; + size_t line_size = 0; + ssize_t nread; + + /* remove the '-' from the list */ + *targets = alpm_list_remove_str(*targets, "-", &vdata); + free(vdata); + + while((nread = getline(&line, &line_size, stdin)) != -1) { + if(line[nread - 1] == '\n') { + /* remove trailing newline */ + line[nread - 1] = '\0'; + } + if(line[0] == '\0') { + /* skip empty lines */ + continue; + } + if(!alpm_list_append_strdup(targets, line)) { + break; + } + target_found = 1; + } + free(line); + + if(ferror(stdin)) { + pm_printf(ALPM_LOG_ERROR, + _("failed to read arguments from stdin: (%s)\n"), strerror(errno)); + cleanup(EXIT_FAILURE); + } + + if(!freopen(ctermid(NULL), "r", stdin)) { + pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"), + strerror(errno)); + } + + if(!target_found) { + pm_printf(ALPM_LOG_ERROR, _("argument '-' specified with empty stdin\n")); + cleanup(1); + } + } else { + /* do not read stdin from terminal */ + pm_printf(ALPM_LOG_ERROR, _("argument '-' specified without input on stdin\n")); + cleanup(1); + } +} + + +static void parse_stdins(void) +{ + alpm_list_t **lists[4] = {&pm_targets.targets, &pm_targets.sync, &pm_targets.upgrade, &pm_targets.remove}; + int replaced = 0; + + for(int i = 0; i < 4; i++) { + alpm_list_t **list = lists[i]; + if(alpm_list_find_str(*list, "-")) { + if(replaced) { + pm_printf(ALPM_LOG_ERROR, _("argument '-' can not be specified multiple times\n")); + cleanup(1); + } + parse_stdin(list); + replaced++; + } + } +} + + /** Print command line to logfile. * @param argc * @param argv @@ -1138,54 +1278,7 @@ int main(int argc, char *argv[]) } /* we support reading targets from stdin if a cmdline parameter is '-' */ - if(alpm_list_find_str(pm_targets, "-")) { - if(!isatty(fileno(stdin))) { - int target_found = 0; - char *vdata, *line = NULL; - size_t line_size = 0; - ssize_t nread; - - /* remove the '-' from the list */ - pm_targets = alpm_list_remove_str(pm_targets, "-", &vdata); - free(vdata); - - while((nread = getline(&line, &line_size, stdin)) != -1) { - if(line[nread - 1] == '\n') { - /* remove trailing newline */ - line[nread - 1] = '\0'; - } - if(line[0] == '\0') { - /* skip empty lines */ - continue; - } - if(!alpm_list_append_strdup(&pm_targets, line)) { - break; - } - target_found = 1; - } - free(line); - - if(ferror(stdin)) { - pm_printf(ALPM_LOG_ERROR, - _("failed to read arguments from stdin: (%s)\n"), strerror(errno)); - cleanup(EXIT_FAILURE); - } - - if(!freopen(ctermid(NULL), "r", stdin)) { - pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"), - strerror(errno)); - } - - if(!target_found) { - pm_printf(ALPM_LOG_ERROR, _("argument '-' specified with empty stdin\n")); - cleanup(1); - } - } else { - /* do not read stdin from terminal */ - pm_printf(ALPM_LOG_ERROR, _("argument '-' specified without input on stdin\n")); - cleanup(1); - } - } + parse_stdins(); if(config->sysroot && (chroot(config->sysroot) != 0 || chdir("/") != 0)) { pm_printf(ALPM_LOG_ERROR, @@ -1233,7 +1326,10 @@ int main(int argc, char *argv[]) printf("Lock File : %s\n", alpm_option_get_lockfile(config->handle)); printf("Log File : %s\n", alpm_option_get_logfile(config->handle)); printf("GPG Dir : %s\n", alpm_option_get_gpgdir(config->handle)); - list_display("Targets :", pm_targets, 0); + list_display("Targets :", pm_targets.targets, 0); + list_display("Sync :", pm_targets.sync, 0); + list_display("Upgrade :", pm_targets.upgrade, 0); + list_display("Remove :", pm_targets.remove, 0); } /* Log command line */ @@ -1242,31 +1338,26 @@ int main(int argc, char *argv[]) } /* start the requested operation */ - switch(config->op) { - case PM_OP_DATABASE: - ret = pacman_database(pm_targets); - 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: - ret = pacman_query(pm_targets); - break; - case PM_OP_SYNC: - ret = pacman_sync(pm_targets); - break; - case PM_OP_DEPTEST: - ret = pacman_deptest(pm_targets); - break; - case PM_OP_FILES: - ret = pacman_files(pm_targets); - break; - default: - pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n")); - ret = EXIT_FAILURE; + if(config->op & PM_OP_TRANS) { + ret = do_transaction(&pm_targets); + } else { + switch(config->op) { + case PM_OP_DATABASE: + ret = pacman_database(pm_targets.targets); + break; + case PM_OP_QUERY: + ret = pacman_query(pm_targets.targets); + break; + case PM_OP_DEPTEST: + ret = pacman_deptest(pm_targets.targets); + break; + case PM_OP_FILES: + ret = pacman_files(pm_targets.targets); + break; + default: + pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n")); + ret = EXIT_FAILURE; + } } cleanup(ret); diff --git a/src/pacman/pacman.h b/src/pacman/pacman.h index 2d5229ed..b5a1061a 100644 --- a/src/pacman/pacman.h +++ b/src/pacman/pacman.h @@ -22,6 +22,8 @@ #include <alpm_list.h> +#include "conf.h" + #define PACMAN_CALLER_PREFIX "PACMAN" /* database.c */ @@ -32,12 +34,14 @@ int pacman_deptest(alpm_list_t *targets); int pacman_files(alpm_list_t *files); /* query.c */ int pacman_query(alpm_list_t *targets); -/* remove.c */ -int pacman_remove(alpm_list_t *targets); /* sync.c */ int pacman_sync(alpm_list_t *targets); -int sync_prepare_execute(void); +/* remove.c */ +int load_remove(alpm_list_t *targets); /* 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 */ diff --git a/src/pacman/remove.c b/src/pacman/remove.c index 98f8bcda..ff11b6e6 100644 --- a/src/pacman/remove.c +++ b/src/pacman/remove.c @@ -18,7 +18,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <fnmatch.h> #include <stdlib.h> #include <stdio.h> @@ -30,11 +29,6 @@ #include "util.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) { alpm_pkg_t *pkg; @@ -69,28 +63,16 @@ static int remove_target(const char *target) return 0; } -/** - * @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 load_remove(alpm_list_t *targets) { int retval = 0; - alpm_list_t *i, *data = NULL; + alpm_list_t *i; if(targets == NULL) { pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n")); 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 */ for(i = targets; i; i = alpm_list_next(i)) { 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; } + diff --git a/src/pacman/sync.c b/src/pacman/sync.c index 1f2c8cba..b72fdec3 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -35,7 +35,6 @@ #include "pacman.h" #include "util.h" #include "package.h" -#include "callback.h" #include "conf.h" static int unlink_verbose(const char *pathname, int ignore_missing) @@ -510,376 +509,6 @@ static int sync_list(alpm_list_t *syncs, alpm_list_t *targets) 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 and %s are in conflict\n"), - conflict->package1, conflict->package2); - } else { - char *reason = alpm_dep_compute_string(conflict->reason); - colon_printf(_("%s and %s are in conflict (%s)\n"), - conflict->package1, 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) { alpm_list_t *sync_dbs = NULL; @@ -902,26 +531,8 @@ int pacman_sync(alpm_list_t *targets) return ret; } - if(check_syncdbs(1, 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(1, 1)) { - return 1; - } - /* search for a package */ if(config->op_s_search) { return sync_search(sync_dbs, targets); @@ -943,17 +554,9 @@ int pacman_sync(alpm_list_t *targets) } if(targets == NULL) { - if(config->op_s_upgrade) { - /* proceed */ - } 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; - } + pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n")); + return 1; } - return sync_trans(targets); + return 0; } diff --git a/src/pacman/trans.c b/src/pacman/trans.c new file mode 100644 index 00000000..62c7fb4d --- /dev/null +++ b/src/pacman/trans.c @@ -0,0 +1,478 @@ +/* + * trans.c + * + * Copyright (c) 2006-2021 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2002-2006 by Judd Vinet <jvinet@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 int sync_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) { + 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 and %s are in conflict\n"), + conflict->package1, conflict->package2); + } else { + char *reason = alpm_dep_compute_string(conflict->reason); + colon_printf(_("%s and %s are in conflict (%s)\n"), + conflict->package1, conflict->package2, reason); + free(reason); + } + alpm_conflict_free(conflict); + } + break; + default: + break; + } + 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) { + 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; +} + +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 sync_prepare_execute(); + +cleanup: + trans_release(); + return 1; +} diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c index 0691856f..b0fa7b1d 100644 --- a/src/pacman/upgrade.c +++ b/src/pacman/upgrade.c @@ -65,7 +65,7 @@ static int load_packages(alpm_list_t *targets, int siglevel) * * @return 0 on success, 1 on failure */ -int pacman_upgrade(alpm_list_t *targets) +int load_upgrade(alpm_list_t *targets) { int retval = 0; alpm_list_t *remote_targets = NULL, *fetched_files = NULL; @@ -88,38 +88,16 @@ int pacman_upgrade(alpm_list_t *targets) if(remote_targets) { 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; } printf(_("loading packages...\n")); 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)); - 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(local_targets); FREELIST(fetched_files); return retval; } + diff --git a/src/pacman/util.c b/src/pacman/util.c index 53833d6f..f1b8fc40 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -106,16 +106,17 @@ int needs_root(void) if(config->sysroot) { return 1; } + if(config->op & PM_OP_SYNC) { + return (config->op_s_clean || config->op_s_sync || + (!config->group && !config->op_s_info && !config->op_q_list && + !config->op_s_search && !config->print)); + } + if(config->op & PM_OP_TRANS) { + return !config->print; + } switch(config->op) { case PM_OP_DATABASE: return !config->op_q_check; - case PM_OP_UPGRADE: - case PM_OP_REMOVE: - return !config->print; - case PM_OP_SYNC: - return (config->op_s_clean || config->op_s_sync || - (!config->group && !config->op_s_info && !config->op_q_list && - !config->op_s_search && !config->print)); case PM_OP_FILES: return config->op_s_sync; default: diff --git a/test/pacman/tests/remove049.py b/test/pacman/tests/remove049.py index a0336295..a147847a 100644 --- a/test/pacman/tests/remove049.py +++ b/test/pacman/tests/remove049.py @@ -10,9 +10,9 @@ lp3.depends = [ "pkg1" ] self.addpkg2db("local", lp3) -self.args = "-Ru pkg1 pkg2" +self.args = "-R --unneeded pkg1 pkg2" self.addrule("PACMAN_RETCODE=0") self.addrule("PKG_EXIST=pkg1") self.addrule("!PKG_EXIST=pkg2") -self.addrule("PKG_EXIST=pkg3") \ No newline at end of file +self.addrule("PKG_EXIST=pkg3") -- 2.35.1
participants (1)
-
morganamilo