Alternatives framework (1/2) (WIP).
This commit is contained in:
parent
c457b594aa
commit
faeff38ca2
246
ALTERNATIVES
Normal file
246
ALTERNATIVES
Normal 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.
|
||||
$
|
@ -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));
|
||||
|
@ -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:
|
||||
*
|
||||
|
@ -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
280
lib/package_alternatives.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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"))
|
||||
|
Loading…
x
Reference in New Issue
Block a user