Alternatives framework (1/2) (WIP).

This commit is contained in:
Juan RP 2015-10-18 10:38:35 +02:00
parent c457b594aa
commit faeff38ca2
8 changed files with 642 additions and 4 deletions

246
ALTERNATIVES Normal file
View File

@ -0,0 +1,246 @@
The XBPS alternatives framework
-------------------------------
-------------------------------------------------
Package metadata stored in packages (props.plist)
-------------------------------------------------
The 'nvi' pkg declares an alternative group named 'vi':
...
<key>alternatives</key>
<dict>
<key>vi</key>
<array>
<string>/usr/bin/ex:/usr/bin/nvi</string>
<string>/usr/bin/vi:/usr/bin/nvi</string>
<string>/usr/share/man/man1/ex.1:/usr/share/man/man1/nvi.1</string>
<string>/usr/share/man/man1/vi.1:/usr/share/man/man1/nvi.1</string>
</array>
...
</dict>
...
The strings need to follow the <symlink>:<target> convention, delimited by
the ':' character.
--------------------------------------------------------------------
Package metadata stored in metadir (metadir/alternatives-0.48.plist)
--------------------------------------------------------------------
/var/db/xbps/alternatives-0.48.plist (dict):
3 packages can provide the 'vi' alternative group: ex-vi, nvi or vim.
The entry order determines the priority (first entry always wins).
After running `xbps-install -Sy nvi ex-vi vim' the order is the following:
...
<key>vi</key>
<array>
<string>nvi</string>
<string>ex-vi</string>
<string>vim</string>
</array>
...
...
If the 'nvi' pkg is removed, and there's no alternative set for `vi',
the 'ex-vi' pkg now becomes the default alternative:
...
<key>vi</key>
<array>
<string>ex-vi</string>
<string>vim</string>
</array>
...
...
The user now decides that 'vi' should be provided by the 'vim' pkg,
so that the matching entry is put into the head:
...
<key>vi</key>
<array>
<string>vim</string>
<string>ex-vi</string>
</array>
...
...
When no packages provide an alternative group, the alternative group and its associated symlinks
will be completely removed.
--------------------
xbps-alternatives(1)
--------------------
- Listing all available alternatives and current state:
$ xbps-alternatives -l
libGL
- libGL (current)
- /usr/lib/libGL.so -> /usr/lib/libGL-mesa.so
- /usr/lib/libGL.so.1 -> /usr/lib/libGL-mesa.so.1
- /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-xorg.so
- nvidia-libs
- /usr/lib/libGL.so -> /usr/lib/libGL-nvidia.so.1
- /usr/lib/libGL.so.1 -> /usr/lib/libGL-nvidia.so.1
- /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-nvidia.so
- catalyst-libs
- /usr/lib/libGL.so -> /usr/lib/libGL-fglrx.so.1
- /usr/lib/libGL.so.1 -> /usr/lib/libGL-fglrx.so.1
- /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-fglrx.so
ntpd
- openntpd (current)
- /etc/sv/ntpd -> /etc/sv/openntpd
- /usr/bin/ntpd -> /usr/bin/openntpd
- /usr/share/man/man1/ntpd.1 -> /usr/share/man/man1/openntpd.1
- ntp
- /etc/sv/ntpd -> /etc/sv/isc-ntpd
- /usr/bin/ntpd -> /usr/bin/isc-ntpd
- /usr/share/man/man1/ntpd.1 -> /usr/share/man/man1/isc-ntpd.1
- busybox
- /etc/sv/ntpd -> /etc/sv/busybox-ntpd
- /usr/bin/ntpd -> /usr/bin/busybox
sort
- coreutils (current)
- /usr/bin/sort -> /usr/bin/coreutils-sort
- /usr/share/man/man1/sort.1 -> /usr/share/man/man1/coreutils-sort.1
- busybox
- /usr/bin/sort -> /usr/bin/busybox
vi
- nvi (current)
- /usr/bin/ex -> /usr/bin/nvi
- /usr/bin/vi -> /usr/bin/nvi
- /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1
- /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1
- vim
- /usr/bin/ex -> /usr/bin/vim-ex
- /usr/bin/vi -> /usr/bin/vim
- /usr/share/man/man1/ex.1 -> /usr/share/man/man1/vim-ex.1
- /usr/share/man/man1/vi.1 -> /usr/share/man/man1/vim.1
...
- Listing available alternative groups of a package:
$ xbps-alternatives -l busybox
ntpd
- /etc/sv/ntpd -> /etc/sv/busybox-ntpd
- /usr/bin/ntpd -> /usr/bin/busybox
sort
- /usr/bin/sort -> /usr/bin/busybox
...
- Apply all alternative groups specified by the 'nvi' package (declares one group: vi):
$ xbps-alternatives -s nvi
Switched 'vi' alternatives group to 'nvi'.
Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/nvi
Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/nvi
Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1
Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1
$
- Creating a specific alternative group of a package containing multiple groups:
$ xbps-alternatives -s busybox -g ntpd
Switched 'ntpd' alternatives group to 'busybox'.
Creating 'ntpd' alternatives group symlink: /etc/sv/ntpd -> /etc/sv/busybox-ntpd
Creating 'ntpd' alternatives group symlink: /usr/bin/ntpd -> /usr/bin/busybox
$
---------------
xbps-install(1)
---------------
Installing a package with an alternatives group for the first time:
$ xbps-install -Syv vim
Name Action Version New version Download size
vim install - 1.0_1 -
Free space on disk: 49GB
[*] Downloading binary packages
[*] Verifying package integrity
vim-1.0_1: verifying SHA256 hash...
[*] Running transaction tasks
vim-1.0_1: unpacking ...
vim-1.0_1: unpacked file `./usr/bin/vim' (0 bytes)
vim-1.0_1: unpacked file `./usr/share/man/man1/vim.1' (0 bytes)
vim-1.0_1: registered 'vi' alternatives group
Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/vim
Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/vim
Creating 'vi' alternatives group symlink: /usr/bin/view -> /usr/bin/vim
Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/vim.1
Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/vim.1
Creating 'vi' alternatives group symlink: /usr/share/man/man1/view.1 -> /usr/share/man/man1/vim.1
[*] Configuring unpacked packages
vim-1.0_1: configuring ...
vim-1.0_1: installed successfully.
0 downloaded, 1 installed, 0 updated, 1 configured, 0 removed.
$
Installing a package that provides a 'vi' alternatives group (nvi):
$ xbps-install -Syv nvi
Name Action Version New version Download size
nvi install - 1.0_1 -
Free space on disk: 49GB
[*] Downloading binary packages
[*] Verifying package integrity
nvi-1.0_1: verifying SHA256 hash...
[*] Running transaction tasks
nvi-1.0_1: unpacking ...
nvi-1.0_1: unpacked file `./usr/bin/nvi' (0 bytes)
nvi-1.0_1: unpacked file `./usr/share/man/man1/nvi.1' (0 bytes)
nvi-1.0_1: registered 'vi' alternatives group
[*] Configuring unpacked packages
nvi-1.0_1: configuring ...
nvi-1.0_1: installed successfully.
0 downloaded, 1 installed, 0 updated, 1 configured, 0 removed.
$
Removing a package that was the default provider of the 'vi' alternatives group (vim):
$ xbps-remove -yv vim
Name Action Version New version Download size
vim remove 1.0_1 - -
Free space on disk: 49GB
Removing `vim-1.0_1' ...
Removing 'vi' alternatives group symlink: /usr/bin/vi
Removing 'vi' alternatives group symlink: /usr/bin/ex
Removing 'vi' alternatives group symlink: /usr/bin/view
Removing 'vi' alternatives group symlink: /usr/share/man/man1/ex.1
Removing 'vi' alternatives group symlink: /usr/share/man/man1/vi.1
Removing 'vi' alternatives group symlink: /usr/share/man/man1/view.1
vim-1.0_1: unregistered 'vi' alternatives group
Switched 'vi' alternatives group to 'nvi'
Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/nvi
Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/nvi
Creating 'vi' alternatives group symlink: /usr/bin/view -> /usr/bin/nvi
Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1
Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1
Creating 'vi' alternatives group symlink: /usr/share/man/man1/view.1 -> /usr/share/man/man1/nvi.1
Removed file `/usr/share/man/man1/vim.1'
Removed file `/usr/bin/vim'
Removed `vim-1.0_1' successfully.
0 downloaded, 0 installed, 0 updated, 0 configured, 1 removed.
$

View File

@ -102,6 +102,9 @@ usage(void)
" -s --desc Short description (max 80 characters).\n"
" -t --tags A list of tags/categories (blank separated list).\n"
" -V --version Prints XBPS release version.\n"
" --alternatives List of available alternatives this pkg provides.\n"
" This expects a blank separated list of <name>:<symlink>:<target>, e.g\n"
" 'vi:/usr/bin/vi:/usr/bin/vim foo:/usr/bin/foo:/usr/bin/blah'.\n"
" --build-options A string with the used build options.\n"
" --compression Compression format: none, gzip, bzip2, xz (default).\n"
" --shlib-provides List of provided shared libraries (blank separated list,\n"
@ -162,6 +165,72 @@ out:
xbps_object_release(array);
}
static void
process_one_alternative(const char *altgrname, const char *val)
{
xbps_dictionary_t d;
xbps_array_t a;
char *altfiles;
bool alloc = false;
if ((d = xbps_dictionary_get(pkg_propsd, "alternatives")) == NULL) {
d = xbps_dictionary_create();
assert(d);
alloc = true;
}
if ((a = xbps_dictionary_get(d, altgrname)) == NULL) {
a = xbps_array_create();
assert(a);
}
altfiles = strchr(val, ':') + 1;
assert(altfiles);
xbps_array_add_cstring(a, altfiles);
xbps_dictionary_set(d, altgrname, a);
xbps_dictionary_set(pkg_propsd, "alternatives", d);
if (alloc) {
xbps_object_release(a);
xbps_object_release(d);
}
}
static void
process_dict_of_arrays(const char *key, const char *val)
{
char *altgrname, *args, *p, *saveptr;
assert(key);
if (val == NULL)
return;
args = strdup(val);
assert(args);
if (strchr(args, ' ') == NULL) {
altgrname = strtok(args, ":");
assert(altgrname);
process_one_alternative(altgrname, val);
goto out;
}
for ((p = strtok_r(args, " ", &saveptr)); p;
(p = strtok_r(NULL, " ", &saveptr))) {
char *b;
b = strdup(p);
assert(b);
altgrname = strtok(b, ":");
assert(altgrname);
process_one_alternative(altgrname, p);
free(b);
}
out:
free(args);
}
static void
process_file(const char *file, const char *key)
{
@ -615,6 +684,7 @@ main(int argc, char **argv)
{ "shlib-requires", required_argument, NULL, '1' },
{ "build-options", required_argument, NULL, '2' },
{ "compression", required_argument, NULL, '3' },
{ "alternatives", required_argument, NULL, '4' },
{ NULL, 0, NULL, 0 }
};
struct archive *ar;
@ -624,7 +694,7 @@ main(int argc, char **argv)
const char *conflicts, *deps, *homepage, *license, *maint, *bwith;
const char *provides, *pkgver, *replaces, *reverts, *desc, *ldesc;
const char *arch, *config_files, *mutable_files, *version;
const char *buildopts, *shlib_provides, *shlib_requires;
const char *buildopts, *shlib_provides, *shlib_requires, *alternatives;
const char *compression, *tags = NULL, *srcrevs = NULL;
char *pkgname, *binpkg, *tname, *p, cwd[PATH_MAX-1];
bool quiet = false, preserve = false;
@ -634,7 +704,7 @@ main(int argc, char **argv)
arch = conflicts = deps = homepage = license = maint = compression = NULL;
provides = pkgver = replaces = reverts = desc = ldesc = bwith = NULL;
buildopts = config_files = mutable_files = shlib_provides = NULL;
shlib_requires = NULL;
alternatives = shlib_requires = NULL;
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
if (optarg && strcmp(optarg, "") == 0)
@ -716,6 +786,9 @@ main(int argc, char **argv)
case '3':
compression = optarg;
break;
case '4':
alternatives = optarg;
break;
case '?':
default:
usage();
@ -798,6 +871,7 @@ main(int argc, char **argv)
process_array("reverts", reverts);
process_array("shlib-provides", shlib_provides);
process_array("shlib-requires", shlib_requires);
process_dict_of_arrays("alternatives", alternatives);
/* save cwd */
memset(&cwd, 0, sizeof(cwd));

View File

@ -48,7 +48,7 @@
*
* This header documents the full API for the XBPS Library.
*/
#define XBPS_API_VERSION "20150603"
#define XBPS_API_VERSION "20151018"
#ifndef XBPS_VERSION
#define XBPS_VERSION "UNSET"
@ -96,6 +96,12 @@
*/
#define XBPS_PKGDB "pkgdb-0.38.plist"
/**
* @def XBPS_ALTERNATIVES
* Filename for the package alternatives database.
*/
#define XBPS_ALTERNATIVES "alternatives-0.48.plist"
/**
* @def XBPS_PKGPROPS
* Filename for package metadata property list.
@ -280,6 +286,11 @@ extern "C" {
* - XBPS_STATE_INVALID_DEP: package has an invalid dependency.
* - XBPS_STATE_SHOW_INSTALL_MSG: package must show a post-install message.
* - XBPS_STATE_SHOW_REMOVE_MSG: package must show a pre-remove message.
* - XBPS_STATE_ALTGROUP_ADDED: package has registered an alternative group.
* - XBPS_STATE_ALTGROUP_REMOVED: package has unregistered an alternative group.
* - XBPS_STATE_ALTGROUP_SWITCHED: alternative group has been switched.
* - XBPS_STATE_ALTGROUP_LINK_ADDED: link added by an alternative group.
* - XBPS_STATE_ALTGROUP_LINK_REMOVED: link removed by an alternative group.
* - XBPS_STATE_UNPACK_FILE_PRESERVED: package unpack preserved a file.
* - XBPS_STATE_PKGDB: pkgdb upgrade in progress.
* - XBPS_STATE_PKGDB_DONE: pkgdb has been upgraded successfully.
@ -328,7 +339,12 @@ typedef enum xbps_state {
XBPS_STATE_UNPACK_FILE_PRESERVED,
XBPS_STATE_PKGDB,
XBPS_STATE_PKGDB_DONE,
XBPS_STATE_TRANS_ADDPKG
XBPS_STATE_TRANS_ADDPKG,
XBPS_STATE_ALTGROUP_ADDED,
XBPS_STATE_ALTGROUP_REMOVED,
XBPS_STATE_ALTGROUP_SWITCHED,
XBPS_STATE_ALTGROUP_LINK_ADDED,
XBPS_STATE_ALTGROUP_LINK_REMOVED
} xbps_state_t;
/**
@ -514,6 +530,7 @@ struct xbps_handle {
*/
xbps_dictionary_t pkgdb_revdeps;
xbps_dictionary_t vpkgd;
xbps_dictionary_t alternatives;
/**
* @var pkgdb
*
@ -629,6 +646,10 @@ void xbps_dbg_printf_append(struct xbps_handle *, const char *, ...);
void xbps_error_printf(const char *, ...);
void xbps_warn_printf(const char *, ...);
int xbps_alternatives_flush(struct xbps_handle *xhp);
int xbps_alternatives_register(struct xbps_handle *, xbps_dictionary_t);
int xbps_alternatives_unregister(struct xbps_handle *, xbps_dictionary_t);
/**
* Initialize the XBPS library with the following steps:
*

View File

@ -42,6 +42,7 @@ OBJS += plist.o plist_find.o plist_match.o archive.o
OBJS += plist_remove.o plist_fetch.o util.o util_hash.o
OBJS += repo.o repo_pkgdeps.o repo_sync.o
OBJS += rpool.o cb_util.o proplib_wrapper.o
OBJS += package_alternatives.o
OBJS += $(EXTOBJS) $(COMPAT_SRCS)
.PHONY: all

280
lib/package_alternatives.c Normal file
View File

@ -0,0 +1,280 @@
/*-
* Copyright (c) 2015 Juan Romero Pardines.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "xbps_api_impl.h"
/*
* Alternatives framework for xbps.
*/
static char *
left(const char *str)
{
char *p;
size_t len;
p = strdup(str);
len = strlen(p) - strlen(strchr(p, ':'));
p[len] = '\0';
return p;
}
static const char *
right(const char *str)
{
return strchr(str, ':') + 1;
}
#if 0
static int
make_symlink(const char *target, const char *link)
{
char *t, *l, *tdir, *ldir;
tdir = strdup(target);
assert(tdir);
ldir = strdup(link);
assert(ldir);
}
#endif
static void
xbps_alternatives_init(struct xbps_handle *xhp)
{
char *plist;
if (xbps_object_type(xhp->alternatives) == XBPS_TYPE_DICTIONARY)
return;
plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_ALTERNATIVES);
xhp->alternatives = xbps_dictionary_internalize_from_file(plist);
free(plist);
if (xhp->alternatives == NULL)
xhp->alternatives = xbps_dictionary_create();
}
int
xbps_alternatives_flush(struct xbps_handle *xhp)
{
char *plist;
if (xbps_object_type(xhp->alternatives) != XBPS_TYPE_DICTIONARY)
return 0;
/* ... and then write dictionary to disk */
plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_ALTERNATIVES);
if (!xbps_dictionary_externalize_to_file(xhp->alternatives, plist)) {
free(plist);
return EINVAL;
}
free(plist);
return 0;
}
static int
remove_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname)
{
unsigned int i, cnt;
cnt = xbps_array_count(a);
for (i = 0; i < cnt; i++) {
xbps_string_t str;
char *l, *lnk;
str = xbps_array_get(a, i);
l = left(xbps_string_cstring_nocopy(str));
assert(l);
lnk = xbps_xasprintf("%s%s", xhp->rootdir, l);
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_LINK_REMOVED, 0, NULL,
"Removing '%s' alternatives group symlink: %s", grname, l);
unlink(lnk);
free(lnk);
free(l);
}
return 0;
}
static int
create_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname)
{
unsigned int i, cnt;
cnt = xbps_array_count(a);
for (i = 0; i < cnt; i++) {
xbps_string_t str;
char *l, *lnk;
const char *tgt;
str = xbps_array_get(a, i);
l = left(xbps_string_cstring_nocopy(str));
assert(l);
tgt = right(xbps_string_cstring_nocopy(str));
assert(tgt);
lnk = xbps_xasprintf("%s%s", xhp->rootdir, l);
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_LINK_ADDED, 0, NULL,
"Creating '%s' alternatives group symlink: %s -> %s", grname, l, tgt);
unlink(lnk);
symlink(tgt, lnk);
free(lnk);
free(l);
}
return 0;
}
int
xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
{
xbps_array_t allkeys;
xbps_dictionary_t alternatives;
const char *pkgver;
char *pkgname;
int rv = 0;
alternatives = xbps_dictionary_get(pkgd, "alternatives");
if (!xbps_dictionary_count(alternatives))
return 0;
xbps_alternatives_init(xhp);
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
if ((pkgname = xbps_pkg_name(pkgver)) == NULL)
return EINVAL;
allkeys = xbps_dictionary_all_keys(alternatives);
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
xbps_array_t array;
xbps_object_t keysym;
const char *first = NULL, *keyname;
keysym = xbps_array_get(allkeys, i);
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
array = xbps_dictionary_get(xhp->alternatives, keyname);
if (array == NULL)
continue;
xbps_array_get_cstring_nocopy(array, 0, &first);
if (strcmp(pkgname, first) == 0) {
/* this pkg is the current alternative for this group */
rv = remove_symlinks(xhp,
xbps_dictionary_get(alternatives, keyname),
keyname);
if (rv != 0)
break;
}
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_REMOVED, 0, NULL,
"%s: unregistered '%s' alternatives group", pkgver, keyname);
xbps_remove_string_from_array(array, pkgname);
if (xbps_array_count(array) == 0) {
xbps_dictionary_remove(xhp->alternatives, keyname);
} else {
xbps_dictionary_t curpkgd;
first = NULL;
xbps_array_get_cstring_nocopy(array, 0, &first);
curpkgd = xbps_pkgdb_get_pkg(xhp, first);
assert(curpkgd);
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_SWITCHED, 0, NULL,
"Switched '%s' alternatives group to '%s'", keyname, first);
alternatives = xbps_dictionary_get(curpkgd, "alternatives");
rv = create_symlinks(xhp,
xbps_dictionary_get(alternatives, keyname),
keyname);
if (rv != 0)
break;
}
}
xbps_object_release(allkeys);
free(pkgname);
return rv;
}
int
xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
{
xbps_array_t allkeys;
xbps_dictionary_t alternatives;
const char *pkgver;
char *pkgname;
int rv = 0;
alternatives = xbps_dictionary_get(pkgd, "alternatives");
if (!xbps_dictionary_count(alternatives))
return 0;
xbps_alternatives_init(xhp);
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
pkgname = xbps_pkg_name(pkgver);
if (pkgname == NULL)
return EINVAL;
allkeys = xbps_dictionary_all_keys(alternatives);
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
xbps_array_t array;
xbps_object_t keysym;
const char *keyname;
bool alloc = false;
keysym = xbps_array_get(allkeys, i);
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
array = xbps_dictionary_get(xhp->alternatives, keyname);
if (array == NULL) {
alloc = true;
array = xbps_array_create();
}
xbps_array_add_cstring(array, pkgname);
xbps_dictionary_set(xhp->alternatives, keyname, array);
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_ADDED, 0, NULL,
"%s: registered '%s' alternatives group", pkgver, keyname);
if (alloc) {
/* apply alternatives for this group */
rv = create_symlinks(xhp,
xbps_dictionary_get(alternatives, keyname),
keyname);
xbps_object_release(array);
if (rv != 0)
break;
}
}
xbps_object_release(allkeys);
free(pkgname);
return rv;
}

View File

@ -318,6 +318,10 @@ xbps_remove_pkg(struct xbps_handle *xhp, const char *pkgver, bool update)
if ((rv = xbps_cb_message(xhp, pkgd, "remove-msg")) != 0)
goto out;
/* unregister alternatives */
if ((rv = xbps_alternatives_unregister(xhp, pkgd)) != 0)
goto out;
/*
* If updating a package, we just need to execute the current
* pre-remove action target and we are done. Its files will be

View File

@ -622,6 +622,14 @@ xbps_unpack_binary_pkg(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
"%s: [unpack] failed to set state to unpacked: %s",
pkgver, strerror(rv));
}
/* register alternatives */
if ((rv = xbps_alternatives_register(xhp, pkg_repod)) != 0) {
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
rv, pkgver,
"%s: [unpack] failed to register alternatives: %s",
pkgver, strerror(rv));
}
out:
if (pkg_fd != -1)
close(pkg_fd);

View File

@ -350,6 +350,10 @@ xbps_transaction_commit(struct xbps_handle *xhp)
goto out;
}
}
/* flush changes to the alternatives framework */
if ((rv = xbps_alternatives_flush(xhp)) != 0)
goto out;
/* if there are no packages to install or update we are done */
if (!xbps_dictionary_get(xhp->transd, "total-update-pkgs") &&
!xbps_dictionary_get(xhp->transd, "total-install-pkgs"))