From 3179db108a83104d9de6d1d607f55f8118e92160 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 21 Apr 2021 22:47:13 +1000 Subject: Add support for multiple 'Architecture' values This allows architecture to be multivalued. On x86-64 machines, this could be something like: Architecture = x86-64-v3 x86-64 We use the first specified Architecture value in mirrorlist $arch variable replacement, as this is backwards-compatible and sane. Original-patch-by: Dan McGee Patch-updated-by: Allan McRae Signed-off-by: Allan McRae --- doc/pacman.conf.5.asciidoc | 10 ++++---- lib/libalpm/alpm.h | 30 ++++++++++++++++-------- lib/libalpm/handle.c | 30 +++++++++++++++++++----- lib/libalpm/handle.h | 2 +- lib/libalpm/trans.c | 21 ++++++++++++++--- src/pacman/conf.c | 33 +++++++++++++++++---------- src/pacman/conf.h | 4 ++-- src/pacman/pacman-conf.c | 4 ++-- src/pacman/pacman.c | 2 +- test/pacman/meson.build | 2 ++ test/pacman/tests/multiple-architectures01.py | 14 ++++++++++++ test/pacman/tests/multiple-architectures02.py | 16 +++++++++++++ 12 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 test/pacman/tests/multiple-architectures01.py create mode 100644 test/pacman/tests/multiple-architectures02.py diff --git a/doc/pacman.conf.5.asciidoc b/doc/pacman.conf.5.asciidoc index 9bd31916..11ca1e4f 100644 --- a/doc/pacman.conf.5.asciidoc +++ b/doc/pacman.conf.5.asciidoc @@ -113,9 +113,9 @@ Options general configuration options. Wildcards in the specified paths will get expanded based on linkman:glob[7] rules. -*Architecture =* auto | i686 | x86_64 | ...:: - If set, pacman will only allow installation of packages of the given - architecture (e.g. 'i686', 'x86_64', etc). The special value 'auto' will +*Architecture =* auto &| i686 &| x86_64 | ...:: + If set, pacman will only allow installation of packages with the given + architectures (e.g. 'i686', 'x86_64', etc). The special value 'auto' will use the system architecture, provided via ``uname -m''. If unset, no architecture checks are made. *NOTE*: Packages with the special architecture 'any' can always be installed, as they are meant to be @@ -252,8 +252,8 @@ number. During parsing, pacman will define the `$repo` variable to the name of the current section. This is often utilized in files specified using the 'Include' directive so all repositories can use the same mirrorfile. pacman also defines -the `$arch` variable to the value of `Architecture`, so the same mirrorfile can -even be used for different architectures. +the `$arch` variable to the first (or only) value of the `Architecture` option, +so the same mirrorfile can even be used for different architectures. *SigLevel =* ...:: Set the signature verification level for this repository. For more diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 833df829..2e99d4d8 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -2016,25 +2016,37 @@ int alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const alpm_depend_ /** @} */ -/** @name Accessors for the configured architecture - * - * libalpm will only install packages that match the configured architecture. - * The architecture does not need to match the physical architecture. - * It can just be treated as a label. +/** @name Accessors to the list of allowed architectures. + * libalpm will only install packages that match one of the configured + * architectures. The architectures do not need to match the physical + architecture. They can just be treated as a label. * @{ */ /** Returns the allowed package architecture. * @param handle the context handle - * @return the configured package architecture + * @return the configured package architectures */ -const char *alpm_option_get_arch(alpm_handle_t *handle); +alpm_list_t *alpm_option_get_architectures(alpm_handle_t *handle); -/** Sets the allowed package architecture. +/** Adds an allowed package architecture. * @param handle the context handle * @param arch the architecture to set */ -int alpm_option_set_arch(alpm_handle_t *handle, const char *arch); +int alpm_option_add_architecture(alpm_handle_t *handle, const char *arch); + +/** Sets the allowed package architecture. + * @param handle the context handle + * @param arches the architecture to set + */ +int alpm_option_set_architectures(alpm_handle_t *handle, alpm_list_t *arches); + +/** Removes an allowed package architecture. + * @param handle the context handle + * @param arch the architecture to remove + */ +int alpm_option_remove_architecture(alpm_handle_t *handle, const char *arch); + /* End of arch accessors */ /** @} */ diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index 7b8cb1da..46224a25 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -77,7 +77,7 @@ void _alpm_handle_free(alpm_handle_t *handle) FREELIST(handle->hookdirs); FREE(handle->logfile); FREE(handle->lockfile); - FREE(handle->arch); + FREELIST(handle->architectures); FREE(handle->gpgdir); FREELIST(handle->noupgrade); FREELIST(handle->noextract); @@ -276,10 +276,10 @@ alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle) return handle->assumeinstalled; } -const char SYMEXPORT *alpm_option_get_arch(alpm_handle_t *handle) +alpm_list_t SYMEXPORT *alpm_option_get_architectures(alpm_handle_t *handle) { CHECK_HANDLE(handle, return NULL); - return handle->arch; + return handle->architectures; } int SYMEXPORT alpm_option_get_checkspace(alpm_handle_t *handle) @@ -720,11 +720,29 @@ int SYMEXPORT alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const al return 0; } -int SYMEXPORT alpm_option_set_arch(alpm_handle_t *handle, const char *arch) +int SYMEXPORT alpm_option_add_architecture(alpm_handle_t *handle, const char *arch) { + handle->architectures = alpm_list_add(handle->architectures, strdup(arch)); + return 0; +} + +int SYMEXPORT alpm_option_set_architectures(alpm_handle_t *handle, alpm_list_t *arches) +{ + CHECK_HANDLE(handle, return -1); + if(handle->architectures) FREELIST(handle->architectures); + handle->architectures = alpm_list_strdup(arches); + return 0; +} + +int SYMEXPORT alpm_option_remove_architecture(alpm_handle_t *handle, const char *arch) +{ + char *vdata = NULL; CHECK_HANDLE(handle, return -1); - if(handle->arch) FREE(handle->arch); - STRDUP(handle->arch, arch, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); + handle->architectures = alpm_list_remove_str(handle->architectures, arch, &vdata); + if(vdata != NULL) { + FREE(vdata); + return 1; + } return 0; } diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h index 2d8d0f9e..52dc2125 100644 --- a/lib/libalpm/handle.h +++ b/lib/libalpm/handle.h @@ -96,7 +96,7 @@ struct __alpm_handle_t { alpm_list_t *assumeinstalled; /* List of virtual packages used to satisfy dependencies */ /* options */ - char *arch; /* Architecture of packages we should allow */ + alpm_list_t *architectures; /* Architectures of packages we should allow */ int usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */ int checkspace; /* Check disk space before installing */ char *dbext; /* Sync DB extension */ diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index c6ec7eea..939ab05a 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -71,14 +71,29 @@ static alpm_list_t *check_arch(alpm_handle_t *handle, alpm_list_t *pkgs) alpm_list_t *i; alpm_list_t *invalid = NULL; - const char *arch = handle->arch; - if(!arch) { + if(!handle->architectures) { + _alpm_log(handle, ALPM_LOG_DEBUG, "skipping architecture checks\n"); return NULL; } for(i = pkgs; i; i = i->next) { alpm_pkg_t *pkg = i->data; + alpm_list_t *j; + int found = 0; const char *pkgarch = alpm_pkg_get_arch(pkg); - if(pkgarch && strcmp(pkgarch, arch) && strcmp(pkgarch, "any")) { + + /* always allow non-architecture packages and those marked "any" */ + if(!pkgarch || strcmp(pkgarch, "any") == 0) { + continue; + } + + for(j = handle->architectures; j; j = j->next) { + if(strcmp(pkgarch, j->data) == 0) { + found = 1; + break; + } + } + + if(!found) { char *string; const char *pkgname = pkg->name; const char *pkgver = pkg->version; diff --git a/src/pacman/conf.c b/src/pacman/conf.c index cde96716..9578daa3 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -159,7 +159,7 @@ int config_free(config_t *oldconfig) FREELIST(oldconfig->cachedirs); free(oldconfig->xfercommand); free(oldconfig->print_format); - free(oldconfig->arch); + FREELIST(oldconfig->architectures); wordsplit_free(oldconfig->xfercommand_argv); free(oldconfig); @@ -394,16 +394,19 @@ cleanup: } -int config_set_arch(const char *arch) +int config_add_architecture(char *arch) { if(strcmp(arch, "auto") == 0) { struct utsname un; + char *newarch; uname(&un); - config->arch = strdup(un.machine); - } else { - config->arch = strdup(arch); + newarch = strdup(un.machine); + free(arch); + arch = newarch; } - pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", config->arch); + + pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", arch); + config->architectures = alpm_list_add(config->architectures, arch); return 0; } @@ -638,9 +641,12 @@ static int _parse_options(const char *key, char *value, } else if(strcmp(key, "HookDir") == 0) { setrepeatingoption(value, "HookDir", &(config->hookdirs)); } else if(strcmp(key, "Architecture") == 0) { - if(!config->arch) { - config_set_arch(value); + alpm_list_t *i, *arches = NULL; + setrepeatingoption(value, "Architecture", &arches); + for(i = arches; i; i = alpm_list_next(i)) { + config_add_architecture(i->data); } + alpm_list_free(arches); } else if(strcmp(key, "DBPath") == 0) { /* don't overwrite a path specified on the command line */ if(!config->dbpath) { @@ -751,17 +757,20 @@ static int _parse_options(const char *key, char *value, static char *replace_server_vars(config_t *c, config_repo_t *r, const char *s) { - if(c->arch == NULL && strstr(s, "$arch")) { + if(c->architectures == NULL && strstr(s, "$arch")) { pm_printf(ALPM_LOG_ERROR, _("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"), s, "$arch", "Architecture"); return NULL; } - if(c->arch) { + /* use first specified architecture */ + if(c->architectures) { char *temp, *replaced; + alpm_list_t *i = config->architectures; + const char *arch = i->data; - replaced = strreplace(s, "$arch", c->arch); + replaced = strreplace(s, "$arch", arch); temp = replaced; replaced = strreplace(temp, "$repo", r->name); @@ -893,7 +902,7 @@ static int setup_libalpm(void) pm_printf(ALPM_LOG_WARNING, _("no '%s' configured\n"), "XferCommand"); } - alpm_option_set_arch(handle, config->arch); + alpm_option_set_architectures(handle, config->architectures); alpm_option_set_checkspace(handle, config->checkspace); alpm_option_set_usesyslog(handle, config->usesyslog); diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 1b9fb337..316c8d0f 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -57,7 +57,6 @@ typedef struct __config_t { unsigned short usesyslog; unsigned short color; unsigned short disable_dl_timeout; - char *arch; char *print_format; /* unfortunately, we have to keep track of paths both here and in the library * because they can come from both the command line or config file, and we @@ -70,6 +69,7 @@ typedef struct __config_t { char *sysroot; alpm_list_t *hookdirs; alpm_list_t *cachedirs; + alpm_list_t *architectures; unsigned short op_q_isfile; unsigned short op_q_info; @@ -244,7 +244,7 @@ int config_free(config_t *oldconfig); void config_repo_free(config_repo_t *repo); -int config_set_arch(const char *arch); +int config_add_architecture(char *arch); int parseconfig(const char *file); int parseconfigfile(const char *file); int setdefaults(config_t *c); diff --git a/src/pacman/pacman-conf.c b/src/pacman/pacman-conf.c index f8fac75d..6ca21119 100644 --- a/src/pacman/pacman-conf.c +++ b/src/pacman/pacman-conf.c @@ -258,7 +258,7 @@ static void dump_config(void) show_list_str("NoUpgrade", config->noupgrade); show_list_str("NoExtract", config->noextract); - show_str("Architecture", config->arch); + show_list_str("Architecture", config->architectures); show_str("XferCommand", config->xfercommand); show_bool("UseSyslog", config->usesyslog); @@ -364,7 +364,7 @@ static int list_directives(void) } else if(strcasecmp(i->data, "Architecture") == 0) { - show_str("Architecture", config->arch); + show_list_str("Architecture", config->architectures); } else if(strcasecmp(i->data, "XferCommand") == 0) { show_str("XferCommand", config->xfercommand); diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index b2aabc08..7e810127 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -377,7 +377,7 @@ static int parsearg_global(int opt) { switch(opt) { case OP_ARCH: - config_set_arch(optarg); + config_add_architecture(strdup(optarg)); break; case OP_ASK: config->noask = 1; diff --git a/test/pacman/meson.build b/test/pacman/meson.build index 52ff9b9a..6c28bd7d 100644 --- a/test/pacman/meson.build +++ b/test/pacman/meson.build @@ -85,6 +85,8 @@ pacman_tests = [ 'tests/mode001.py', 'tests/mode002.py', 'tests/mode003.py', + 'tests/multiple-architectures01.py', + 'tests/multiple-architectures02.py', 'tests/noupgrade-inverted.py', 'tests/overwrite-files-match-negated.py', 'tests/overwrite-files-match.py', diff --git a/test/pacman/tests/multiple-architectures01.py b/test/pacman/tests/multiple-architectures01.py new file mode 100644 index 00000000..39f3e1f7 --- /dev/null +++ b/test/pacman/tests/multiple-architectures01.py @@ -0,0 +1,14 @@ +self.description = "Install a package (multiple Architecture options, wrong)" + +p = pmpkg("dummy") +p.files = ["bin/dummy", + "usr/man/man1/dummy.1"] +p.arch = 'i686' +self.addpkg(p) + +self.option["Architecture"] = ['i586', 'i486', 'i386'] + +self.args = "-U %s" % p.filename() + +self.addrule("PACMAN_RETCODE=1") +self.addrule("!PKG_EXIST=dummy") diff --git a/test/pacman/tests/multiple-architectures02.py b/test/pacman/tests/multiple-architectures02.py new file mode 100644 index 00000000..18625ec6 --- /dev/null +++ b/test/pacman/tests/multiple-architectures02.py @@ -0,0 +1,16 @@ +self.description = "Install a package (multiple Architecture options)" + +p = pmpkg("dummy") +p.files = ["bin/dummy", + "usr/man/man1/dummy.1"] +p.arch = 'i486' +self.addpkg(p) + +self.option["Architecture"] = ['i586', 'i486', 'i386'] + +self.args = "-U %s" % p.filename() + +self.addrule("PACMAN_RETCODE=0") +self.addrule("PKG_EXIST=dummy") +for f in p.files: + self.addrule("FILE_EXIST=%s" % f) \ No newline at end of file -- cgit v1.2.3-54-g00ecf