Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Gregory
8b4f29af39 add --preremove option
Allows adding and removing packages within a single transaction. This is
particularly useful for complicated conflicts that ALPM cannot resolve
on its own, e.g.:

https://archlinux.org/news/linux-firmware-2025061312fe085f-5-upgrade-requires-manual-intervention/

Currently, dealing with these situations requires doing something
complicated like bypassing dependency checking to break and then repair
the database in two separate steps. By allowing installation and removal
within a single transaction no bypass is required:

 pacman -Syu --preremove linux-firmware linux-firmware

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
2025-06-28 14:31:33 -07:00
23 changed files with 669 additions and 690 deletions

View File

@@ -292,6 +292,9 @@ Upgrade Options (apply to '-S' and '-U')[[UO]]
exclamation mark. Subsequent matches will override previous ones. A leading exclamation mark. Subsequent matches will override previous ones. A leading
literal exclamation mark or backslash needs to be escaped. literal exclamation mark or backslash needs to be escaped.
*\--preremove <package>::
Uninstall 'package' before performing the requested installation. Multiple
packages can be specified by separating them with commas.
Query Options (apply to '-Q')[[QO]] Query Options (apply to '-Q')[[QO]]
----------------------------------- -----------------------------------

View File

@@ -318,12 +318,12 @@ When to Check::
*Never*;; *Never*;;
All signature checking is suppressed, even if signatures are present. All signature checking is suppressed, even if signatures are present.
*Optional*;; *Optional* (default);;
Signatures are checked if present; absence of a signature is not an Signatures are checked if present; absence of a signature is not an
error. An invalid signature is a fatal error, as is a signature from a error. An invalid signature is a fatal error, as is a signature from a
key not in the keyring. key not in the keyring.
*Required* (default);; *Required*;;
Signatures are required; absence of a signature or an invalid signature Signatures are required; absence of a signature or an invalid signature
is a fatal error, as is a signature from a key not in the keyring. is a fatal error, as is a signature from a key not in the keyring.
@@ -349,7 +349,7 @@ level signatures for packages.
The built-in default is the following: The built-in default is the following:
-------- --------
SigLevel = Required TrustedOnly SigLevel = Optional TrustedOnly
-------- --------

View File

@@ -75,9 +75,6 @@ repo-add Options
Only add packages that are not already in the database. Warnings will be Only add packages that are not already in the database. Warnings will be
printed upon detection of existing packages, but they will not be re-added. printed upon detection of existing packages, but they will not be re-added.
*-p, \--prevent-downgrade*::
Do not add package to database if a newer version is already present
*\--include-sigs*:: *\--include-sigs*::
Include package PGP signatures in the repository database (if available) Include package PGP signatures in the repository database (if available)

View File

@@ -481,7 +481,7 @@ typedef struct _alpm_siglist_t {
* Check the PGP signature for the given package file. * Check the PGP signature for the given package file.
* @param pkg the package to check * @param pkg the package to check
* @param siglist a pointer to storage for signature results * @param siglist a pointer to storage for signature results
* @return 0 on success, -1 if an error occurred or signature is missing * @return 0 if valid, -1 if an error occurred or signature is invalid
*/ */
int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, alpm_siglist_t *siglist); int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, alpm_siglist_t *siglist);
@@ -489,7 +489,7 @@ int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, alpm_siglist_t *siglist);
* Check the PGP signature for the given database. * Check the PGP signature for the given database.
* @param db the database to check * @param db the database to check
* @param siglist a pointer to storage for signature results * @param siglist a pointer to storage for signature results
* @return 0 on success, -1 if an error occurred or signature is missing * @return 0 if valid, -1 if an error occurred or signature is invalid
*/ */
int alpm_db_check_pgp_signature(alpm_db_t *db, alpm_siglist_t *siglist); int alpm_db_check_pgp_signature(alpm_db_t *db, alpm_siglist_t *siglist);

View File

@@ -1118,34 +1118,31 @@ static int finalize_download_locations(alpm_list_t *payloads, const char *localp
filename = payload->tempfile_name; filename = payload->tempfile_name;
} }
if(filename) { /* if neither file exists then the download failed and logged an error for us */
int ret = move_file(filename, localpath); if(!filename) {
returnvalue = -1;
continue;
}
if(ret == -1) { int ret = move_file(filename, localpath);
if(payload->mtime_existing_file == 0) {
_alpm_log(payload->handle, ALPM_LOG_ERROR, _("could not move %s into %s (%s)\n"), if(ret == -1) {
filename, localpath, strerror(errno)); /* ignore error if the file already existed - only signature file was downloaded */
returnvalue = -1; if(payload->mtime_existing_file == 0) {
} _alpm_log(payload->handle, ALPM_LOG_ERROR, _("could not move %s into %s (%s)\n"),
filename, localpath, strerror(errno));
returnvalue = -1;
} }
} }
if (payload->download_signature) { if (payload->download_signature) {
char *sig_filename; const char sig_suffix[] = ".sig";
int ret; char *sig_filename = NULL;
size_t sig_filename_len = strlen(filename) + sizeof(sig_suffix);
filename = payload->destfile_name ? payload->destfile_name : payload->tempfile_name; MALLOC(sig_filename, sig_filename_len, continue);
sig_filename = _alpm_get_fullpath("", filename, ".sig"); snprintf(sig_filename, sig_filename_len, "%s%s", filename, sig_suffix);
ASSERT(sig_filename, RET_ERR(payload->handle, ALPM_ERR_MEMORY, -1)); move_file(sig_filename, localpath);
ret = move_file(sig_filename, localpath); FREE(sig_filename);
free(sig_filename);
if(ret == -1) {
sig_filename = _alpm_get_fullpath("", filename, ".sig.part");
ASSERT(sig_filename, RET_ERR(payload->handle, ALPM_ERR_MEMORY, -1));
move_file(sig_filename, localpath);
free(sig_filename);
}
} }
} }
return returnvalue; return returnvalue;
@@ -1299,7 +1296,7 @@ download_signature:
return ret; return ret;
} }
static const char *url_basename(const char *url) static char *filecache_find_url(alpm_handle_t *handle, const char *url)
{ {
const char *filebase = strrchr(url, '/'); const char *filebase = strrchr(url, '/');
@@ -1312,7 +1309,7 @@ static const char *url_basename(const char *url)
return NULL; return NULL;
} }
return filebase; return _alpm_filecache_find(handle, filebase);
} }
int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls, int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
@@ -1322,7 +1319,7 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
char *temporary_cachedir = NULL; char *temporary_cachedir = NULL;
alpm_list_t *payloads = NULL; alpm_list_t *payloads = NULL;
const alpm_list_t *i; const alpm_list_t *i;
alpm_event_t event; alpm_event_t event = {0};
CHECK_HANDLE(handle, return -1); CHECK_HANDLE(handle, return -1);
ASSERT(*fetched == NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1)); ASSERT(*fetched == NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
@@ -1334,26 +1331,9 @@ int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
for(i = urls; i; i = i->next) { for(i = urls; i; i = i->next) {
char *url = i->data; char *url = i->data;
char *filepath = NULL;
const char *urlbase = url_basename(url);
if(urlbase) {
/* attempt to find the file in our pkgcache */
filepath = _alpm_filecache_find(handle, urlbase);
if(filepath && (handle->siglevel & ALPM_SIG_PACKAGE)) {
char *sig_filename = _alpm_get_fullpath("", urlbase, ".sig");
/* if there's no .sig file then forget about the pkg file and go for download */
if(!_alpm_filecache_exists(handle, sig_filename)) {
free(filepath);
filepath = NULL;
}
free(sig_filename);
}
}
/* attempt to find the file in our pkgcache */
char *filepath = filecache_find_url(handle, url);
if(filepath) { if(filepath) {
/* the file is locally cached so add it to the output right away */ /* the file is locally cached so add it to the output right away */
alpm_list_append(fetched, filepath); alpm_list_append(fetched, filepath);

View File

@@ -776,7 +776,7 @@ static int download_files(alpm_handle_t *handle)
char * temporary_cachedir = NULL; char * temporary_cachedir = NULL;
alpm_list_t *i, *files = NULL; alpm_list_t *i, *files = NULL;
int ret = 0; int ret = 0;
alpm_event_t event; alpm_event_t event = {0};
alpm_list_t *payloads = NULL; alpm_list_t *payloads = NULL;
cachedir = _alpm_filecache_setup(handle); cachedir = _alpm_filecache_setup(handle);

View File

@@ -1,57 +0,0 @@
#!/bin/bash
#
# buildenv.sh - Check that the BUILDENV and OPTIONS arrays are valid
#
# Copyright (c) 2025 Pacman Development Team <pacman-dev@lists.archlinux.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/>.
#
[[ -n $LIBMAKEPKG_LINT_CONFIG_BUILDENV_SH ]] && return
LIBMAKEPKG_LINT_CONFIG_BUILDENV_SH=1
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
source "$MAKEPKG_LIBRARY/util/message.sh"
lint_config_functions+=('lint_buildenv')
lint_buildenv() {
local ret=0 kopt
local known_buildenv=(ccache check color distcc sign)
local known_option=(autodeps debug docs emptydirs libtool lto purge staticlibs strip zipman)
for i in "${BUILDENV[@]}"; do
for kopt in "${known_buildenv[@]}"; do
if [[ $i = "$kopt" || $i = "!$kopt" ]]; then
continue 2
fi
done
error "$(gettext "%s array contains unknown option '%s'")" "BUILDENV" "$i"
ret=1
done
for i in "${OPTIONS[@]}"; do
for kopt in "${known_option[@]}"; do
if [[ $i = "$kopt" || $i = "!$kopt" ]]; then
continue 2
fi
done
error "$(gettext "%s array contains unknown option '%s'")" "OPTIONS" "$i"
ret=1
done
}

View File

@@ -1,7 +1,6 @@
libmakepkg_module = 'lint_config' libmakepkg_module = 'lint_config'
sources = [ sources = [
'buildenv.sh.in',
'ext.sh.in', 'ext.sh.in',
'nproc.sh.in', 'nproc.sh.in',
'packager.sh.in', 'packager.sh.in',

View File

@@ -17,6 +17,7 @@ sources = [
'package_function.sh.in', 'package_function.sh.in',
'package_function_variable.sh.in', 'package_function_variable.sh.in',
'pkgbase.sh.in', 'pkgbase.sh.in',
'pkglist.sh.in',
'pkgname.sh.in', 'pkgname.sh.in',
'pkgrel.sh.in', 'pkgrel.sh.in',
'pkgver.sh.in', 'pkgver.sh.in',

View File

@@ -0,0 +1,44 @@
#!/bin/bash
#
# pkglist.sh - Check the packages selected to build exist.
#
# Copyright (c) 2014-2025 Pacman Development Team <pacman-dev@lists.archlinux.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/>.
#
[[ -n "$LIBMAKEPKG_LINT_PKGBUILD_PKGLIST_SH" ]] && return
LIBMAKEPKG_LINT_PKGBUILD_PKGLIST_SH=1
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
source "$MAKEPKG_LIBRARY/util/message.sh"
source "$MAKEPKG_LIBRARY/util/util.sh"
lint_pkgbuild_functions+=('lint_pkglist')
lint_pkglist() {
local i ret=0
for i in "${PKGLIST[@]}"; do
if ! in_array "$i" "${pkgname[@]}"; then
error "$(gettext "Requested package %s is not provided in %s")" "$i" "$BUILDFILE"
ret=1
fi
done
return $ret
}

View File

@@ -68,7 +68,6 @@ Multiple packages to add can be specified on the command line.\n")"
printf -- "$(gettext "Options:\n")" printf -- "$(gettext "Options:\n")"
printf -- "$(gettext " -n, --new only add packages that are not already in the database\n")" printf -- "$(gettext " -n, --new only add packages that are not already in the database\n")"
printf -- "$(gettext " -p, --prevent-downgrade do not add package to database if a newer version is already present\n")" printf -- "$(gettext " -p, --prevent-downgrade do not add package to database if a newer version is already present\n")"
printf -- "$(gettext " --include-sigs Include package PGP signatures in the repository database (if available)")
elif [[ $cmd == "repo-remove" ]] ; then elif [[ $cmd == "repo-remove" ]] ; then
printf -- "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename> ...\n")" printf -- "$(gettext "Usage: repo-remove [options] <path-to-db> <packagename> ...\n")"
printf -- "\n" printf -- "\n"

View File

@@ -105,11 +105,12 @@ 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_NONE; newconfig->op = PM_OP_MAIN;
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) {
newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_DATABASE; newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
newconfig->localfilesiglevel = ALPM_SIG_USE_DEFAULT; newconfig->localfilesiglevel = ALPM_SIG_USE_DEFAULT;
newconfig->remotefilesiglevel = ALPM_SIG_USE_DEFAULT; newconfig->remotefilesiglevel = ALPM_SIG_USE_DEFAULT;
} }
@@ -130,13 +131,6 @@ 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) {
@@ -156,6 +150,7 @@ int config_free(config_t *oldconfig)
FREELIST(oldconfig->noupgrade); FREELIST(oldconfig->noupgrade);
FREELIST(oldconfig->noextract); FREELIST(oldconfig->noextract);
FREELIST(oldconfig->overwrite_files); FREELIST(oldconfig->overwrite_files);
FREELIST(oldconfig->preremove);
free(oldconfig->configfile); free(oldconfig->configfile);
free(oldconfig->sysroot); free(oldconfig->sysroot);
free(oldconfig->rootdir); free(oldconfig->rootdir);

View File

@@ -44,14 +44,6 @@ 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;
@@ -142,6 +134,8 @@ typedef struct __config_t {
/* our connection to libalpm */ /* our connection to libalpm */
alpm_handle_t *handle; alpm_handle_t *handle;
alpm_list_t *preremove;
alpm_list_t *explicit_adds; alpm_list_t *explicit_adds;
alpm_list_t *explicit_removes; alpm_list_t *explicit_removes;
@@ -153,15 +147,14 @@ typedef struct __config_t {
/* Operations */ /* Operations */
enum { enum {
PM_OP_NONE = 0, PM_OP_MAIN = 1,
PM_OP_INVALID = (1 << 0), PM_OP_REMOVE,
PM_OP_REMOVE = (1 << 1), PM_OP_UPGRADE,
PM_OP_UPGRADE = (1 << 2), PM_OP_QUERY,
PM_OP_QUERY = (1 << 3), PM_OP_SYNC,
PM_OP_SYNC = (1 << 4), PM_OP_DEPTEST,
PM_OP_DEPTEST = (1 << 5), PM_OP_DATABASE,
PM_OP_DATABASE = (1 << 6), PM_OP_FILES
PM_OP_FILES = (1 << 7)
}; };
/* Long Operations */ /* Long Operations */
@@ -223,7 +216,8 @@ enum {
OP_REFRESH, OP_REFRESH,
OP_ASSUMEINSTALLED, OP_ASSUMEINSTALLED,
OP_DISABLEDLTIMEOUT, OP_DISABLEDLTIMEOUT,
OP_DISABLESANDBOX OP_DISABLESANDBOX,
OP_PREREMOVE,
}; };
/* clean method */ /* clean method */
@@ -248,9 +242,6 @@ 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,7 +10,6 @@ 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_NONE) { if(op == PM_OP_MAIN) {
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);
@@ -196,6 +196,8 @@ static void usage(int op, const char * const myname)
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n")); addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
addlist(_(" --ignoregroup <grp>\n" addlist(_(" --ignoregroup <grp>\n"
" ignore a group upgrade (can be used more than once)\n")); " ignore a group upgrade (can be used more than once)\n"));
addlist(_(" --preremove <pkg>\n"));
addlist(_(" uninstall <pkg> before performing installation\n"));
__attribute__((fallthrough)); __attribute__((fallthrough));
case PM_OP_REMOVE: case PM_OP_REMOVE:
addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n")); addlist(_(" -d, --nodeps skip dependency version checks (-dd to skip all checks)\n"));
@@ -340,25 +342,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_NONE ? PM_OP_INVALID : PM_OP_DATABASE); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break;
case 'F': case 'F':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_FILES); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_FILES); break;
case 'Q': case 'Q':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_QUERY); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
case 'R': case 'R':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_REMOVE); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
case 'S': case 'S':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_SYNC); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
case 'T': case 'T':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_DEPTEST); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
case 'U': case 'U':
if(dryrun) break; if(dryrun) break;
config->op = (config->op != PM_OP_NONE ? PM_OP_INVALID : PM_OP_UPGRADE); break; config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
case 'V': case 'V':
if(dryrun) break; if(dryrun) break;
config->version = 1; break; config->version = 1; break;
@@ -770,6 +772,9 @@ static int parsearg_upgrade(int opt)
case OP_IGNOREGROUP: case OP_IGNOREGROUP:
parsearg_util_addlist(&(config->ignoregrp)); parsearg_util_addlist(&(config->ignoregrp));
break; break;
case OP_PREREMOVE:
parsearg_util_addlist(&(config->preremove));
break;
case OP_DOWNLOADONLY: case OP_DOWNLOADONLY:
case 'w': case 'w':
config->op_s_downloadonly = 1; config->op_s_downloadonly = 1;
@@ -982,6 +987,7 @@ static int parseargs(int argc, char *argv[])
{"color", required_argument, 0, OP_COLOR}, {"color", required_argument, 0, OP_COLOR},
{"disable-download-timeout", no_argument, 0, OP_DISABLEDLTIMEOUT}, {"disable-download-timeout", no_argument, 0, OP_DISABLEDLTIMEOUT},
{"disable-sandbox", no_argument, 0, OP_DISABLESANDBOX}, {"disable-sandbox", no_argument, 0, OP_DISABLESANDBOX},
{"preremove", required_argument, 0, OP_PREREMOVE},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
@@ -996,7 +1002,7 @@ static int parseargs(int argc, char *argv[])
parsearg_op(opt, 0); parsearg_op(opt, 0);
} }
if(config->op == PM_OP_INVALID) { if(config->op == 0) {
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,7 +1130,6 @@ 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();
@@ -1277,27 +1282,24 @@ 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,8 +22,6 @@
#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 */
@@ -34,14 +32,12 @@ 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);
/* remove.c */ int sync_prepare_execute(void);
int load_remove(alpm_list_t *targets);
/* upgrade.c */ /* upgrade.c */
int load_upgrade(alpm_list_t *targets); int pacman_upgrade(alpm_list_t *targets);
/* trans.c */
int do_transaction(targets_t *targets);
#endif /* PM_PACMAN_H */ #endif /* PM_PACMAN_H */

View File

@@ -18,6 +18,7 @@
* 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>
@@ -29,6 +30,11 @@
#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;
@@ -63,16 +69,28 @@ 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; alpm_list_t *i, *data = NULL;
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;
@@ -84,6 +102,81 @@ int load_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,6 +35,7 @@
#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)
@@ -83,7 +84,7 @@ static int sync_cleandb(const char *dbpath)
/* build the full path */ /* build the full path */
len = snprintf(path, PATH_MAX, "%s%s", dbpath, dname); len = snprintf(path, PATH_MAX, "%s%s", dbpath, dname);
if(len >= PATH_MAX) { if(len > PATH_MAX) {
pm_printf(ALPM_LOG_ERROR, _("could not remove %s%s: path exceeds PATH_MAX\n"), pm_printf(ALPM_LOG_ERROR, _("could not remove %s%s: path exceeds PATH_MAX\n"),
dbpath, dname); dbpath, dname);
} }
@@ -244,7 +245,7 @@ static int sync_cleancache(int level)
/* build the full filepath */ /* build the full filepath */
len=snprintf(path, PATH_MAX, "%s%s", cachedir, ent->d_name); len=snprintf(path, PATH_MAX, "%s%s", cachedir, ent->d_name);
if(len >= PATH_MAX) { if(len > PATH_MAX) {
pm_printf(ALPM_LOG_ERROR, _("skipping %s%s: path exceeds PATH_MAX\n"), pm_printf(ALPM_LOG_ERROR, _("skipping %s%s: path exceeds PATH_MAX\n"),
cachedir, ent->d_name); cachedir, ent->d_name);
continue; continue;
@@ -363,8 +364,6 @@ static int sync_group(int level, alpm_list_t *syncs, alpm_list_t *targets)
} }
} }
if(!found) { if(!found) {
pm_printf(ALPM_LOG_ERROR,
_("package group '%s' was not found\n"), grpname);
ret = 1; ret = 1;
} }
} }
@@ -521,6 +520,398 @@ 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;
}
}
for(i = config->preremove; i; i = i->next) {
alpm_pkg_t *pkg;
const char *pname = i->data;
alpm_db_t *db_local = alpm_get_localdb(config->handle);
if((pkg = alpm_db_get_pkg(db_local, pname)) != NULL) {
if(alpm_remove_pkg(config->handle, pkg) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, "'%s': %s\n", pname, alpm_strerror(err));
retval = 1;
}
config->explicit_removes = alpm_list_add(config->explicit_removes, pkg);
}
}
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;
@@ -543,8 +934,26 @@ 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);
@@ -566,9 +975,17 @@ int pacman_sync(alpm_list_t *targets)
} }
if(targets == NULL) { if(targets == NULL) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n")); if(config->op_s_upgrade) {
return 1; /* 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;
}
} }
return 0; return sync_trans(targets);
} }

View File

@@ -1,499 +0,0 @@
/*
* 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 load_upgrade(alpm_list_t *targets) int pacman_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,6 +88,15 @@ int load_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) {
@@ -96,10 +105,23 @@ int load_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,9 +10,9 @@ lp3 = pmpkg("pkg3")
lp3.depends = [ "pkg1" ] lp3.depends = [ "pkg1" ]
self.addpkg2db("local", lp3) self.addpkg2db("local", lp3)
self.args = "-R --unneeded pkg1 pkg2" self.args = "-Ru pkg1 pkg2"
self.addrule("PACMAN_RETCODE=0") self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=pkg1") self.addrule("PKG_EXIST=pkg1")
self.addrule("!PKG_EXIST=pkg2") self.addrule("!PKG_EXIST=pkg2")
self.addrule("PKG_EXIST=pkg3") self.addrule("PKG_EXIST=pkg3")

View File

@@ -1,7 +1,6 @@
self.description = 'download remote packages with -U with a URL filename' self.description = 'download remote packages with -U with a URL filename'
self.require_capability("gpg") self.require_capability("gpg")
self.require_capability("curl") self.require_capability("curl")
self.option['SigLevel'] = ['Required']
url = self.add_simple_http_server({ url = self.add_simple_http_server({
# simple # simple

View File

@@ -115,8 +115,6 @@ def mkcfgfile(filename, root, option, db):
data = ["[options]"] data = ["[options]"]
for key, value in option.items(): for key, value in option.items():
data.extend(["%s = %s" % (key, j) for j in value]) data.extend(["%s = %s" % (key, j) for j in value])
if "SigLevel" not in option:
data.append("SigLevel = Never\n")
# Repositories # Repositories
# sort by repo name so tests can predict repo order, rather than be # sort by repo name so tests can predict repo order, rather than be