diff --git a/ALTERNATIVES b/ALTERNATIVES deleted file mode 100644 index 89d666f8..00000000 --- a/ALTERNATIVES +++ /dev/null @@ -1,246 +0,0 @@ -The XBPS alternatives framework -------------------------------- - -------------------------------------------------- -Package metadata stored in packages (props.plist) -------------------------------------------------- - -The 'nvi' pkg declares an alternative group named 'vi': - -... -alternatives - - vi - - /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 - - ... - -... - -The strings need to follow the : 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: - -... - vi - - nvi - ex-vi - vim - - ... -... - -If the 'nvi' pkg is removed, and there's no alternative set for `vi', -the 'ex-vi' pkg now becomes the default alternative: - -... - vi - - ex-vi - vim - - ... -... - -The user now decides that 'vi' should be provided by the 'vim' pkg, -so that the matching entry is put into the head: - -... - vi - - vim - ex-vi - - ... -... - -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. - $ diff --git a/NEWS b/NEWS index 36f160f3..df9f4446 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ xbps-0.48 (???): + * xbps-alternatives(1): new utility to list or set alternatives provided by + packages. This effectively implements a functional alternatives framework + to switch easily a default provider via symbolic links. + * xbps-{install,reconfigure,remove}(1): do not log to console when the syslog option (xbps.d(5)) is enabled. Messages are send to stderr/stdout, so that logging to the console duplicates them. Close #123 diff --git a/bin/Makefile b/bin/Makefile index 1815c56e..5a71d10b 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,6 +1,7 @@ -include ../config.mk -SUBDIRS = xbps-create +SUBDIRS += xbps-alternatives +SUBDIRS += xbps-create SUBDIRS += xbps-dgraph SUBDIRS += xbps-install SUBDIRS += xbps-pkgdb diff --git a/bin/xbps-alternatives/Makefile b/bin/xbps-alternatives/Makefile new file mode 100644 index 00000000..c80f4d5b --- /dev/null +++ b/bin/xbps-alternatives/Makefile @@ -0,0 +1,6 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-alternatives + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-alternatives/main.c b/bin/xbps-alternatives/main.c new file mode 100644 index 00000000..d73acfc1 --- /dev/null +++ b/bin/xbps-alternatives/main.c @@ -0,0 +1,252 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-alternatives [OPTIONS] MODE\n\n" + "OPTIONS\n" + " -C --config Path to confdir (xbps.d)\n" + " -d --debug Debug mode shown to stderr\n" + " -g --group Group of alternatives to match\n" + " -h --help Print usage help\n" + " -r --rootdir Full path to rootdir\n" + " -v --verbose Verbose messages\n" + " -V --version Show XBPS version\n" + "MODE\n" + " -l --list [PKG] List all alternatives or from PKG\n" + " -s --set PKG Set alternatives for PKG\n\n"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static int +state_cb(const struct xbps_state_cb_data *xscd, void *cbd _unused) +{ + bool slog = false; + + if ((xscd->xhp->flags & XBPS_FLAG_DISABLE_SYSLOG) == 0) { + slog = true; + openlog("xbps-alternatives", 0, LOG_USER); + } + if (xscd->desc) { + printf("%s\n", xscd->desc); + if (slog) + syslog(LOG_NOTICE, "%s", xscd->desc); + } + return 0; +} + +static void +list_pkg_alternatives(xbps_dictionary_t pkgd, bool print_key) +{ + xbps_dictionary_t pkg_alternatives; + xbps_array_t allkeys; + + pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives"); + if (pkg_alternatives == NULL) + return; + + allkeys = xbps_dictionary_all_keys(pkg_alternatives); + for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) { + xbps_object_t keysym; + xbps_array_t array; + const char *keyname; + + keysym = xbps_array_get(allkeys, i); + keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); + array = xbps_dictionary_get_keysym(pkg_alternatives, keysym); + + if (print_key) + printf("%s\n", keyname); + + for (unsigned int x = 0; x < xbps_array_count(array); x++) { + const char *str; + + xbps_array_get_cstring_nocopy(array, x, &str); + printf(" - %s\n", str); + } + } + xbps_object_release(allkeys); +} + +static int +list_alternatives(struct xbps_handle *xhp, const char *pkgname) +{ + xbps_dictionary_t alternatives, pkgd; + xbps_array_t allkeys; + + (void)xbps_pkgdb_get_pkg(xhp, "foo"); + + if (pkgname) { + /* list alternatives for pkgname */ + if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkgname)) == NULL) + return ENOENT; + + list_pkg_alternatives(pkgd, true); + return 0; + } + assert(xhp->pkgdb); + + alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_"); + if (alternatives == NULL) + return ENOENT; + + 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; + + keysym = xbps_array_get(allkeys, i); + keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); + array = xbps_dictionary_get_keysym(alternatives, keysym); + + printf("%s\n", keyname); + for (unsigned int x = 0; x < xbps_array_count(array); x++) { + const char *str; + + xbps_array_get_cstring_nocopy(array, x, &str); + printf(" - %s%s\n", str, x == 0 ? " (current)" : ""); + pkgd = xbps_pkgdb_get_pkg(xhp, str); + assert(pkgd); + list_pkg_alternatives(pkgd, false); + } + } + xbps_object_release(allkeys); + + return 0; +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "C:dg:hls:r:Vv"; + const struct option longopts[] = { + { "config", required_argument, NULL, 'C' }, + { "debug", no_argument, NULL, 'd' }, + { "group", required_argument, NULL, 'g' }, + { "help", no_argument, NULL, 'h' }, + { "list", no_argument, NULL, 'l' }, + { "set", required_argument, NULL, 's' }, + { "rootdir", required_argument, NULL, 'r' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + struct xbps_handle xh; + const char *confdir, *rootdir, *group, *pkg; + int c, rv, flags = 0; + bool list_mode = false, set_mode = false; + + confdir = rootdir = group = pkg = NULL; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'C': + confdir = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'g': + group = optarg; + break; + case 'h': + usage(false); + /* NOTREACHED */ + case 'l': + list_mode = true; + break; + case 's': + set_mode = true; + pkg = optarg; + break; + case 'r': + rootdir = optarg; + break; + case 'v': + flags |= XBPS_FLAG_VERBOSE; + break; + case 'V': + printf("%s\n", XBPS_RELVER); + exit(EXIT_SUCCESS); + case '?': + default: + usage(true); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (!list_mode && !set_mode) + usage(true); + else if (argc && list_mode) + pkg = *argv; + + memset(&xh, 0, sizeof(xh)); + xh.state_cb = state_cb; + if (rootdir) + xbps_strlcpy(xh.rootdir, rootdir, sizeof(xh.rootdir)); + if (confdir) + xbps_strlcpy(xh.confdir, confdir, sizeof(xh.confdir)); + + xh.flags = flags; + + /* initialize xbps */ + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + if (set_mode) { + /* in set mode pkgdb must be locked and flushed on success */ + if ((rv = xbps_pkgdb_lock(&xh)) != 0) { + fprintf(stderr, "failed to lock pkgdb: %s\n", strerror(rv)); + exit(EXIT_FAILURE); + } + if ((rv = xbps_alternatives_set(&xh, pkg, group)) == 0) + rv = xbps_pkgdb_update(&xh, true, true); + } else if (list_mode) { + /* list alternative groups */ + rv = list_alternatives(&xh, pkg); + } + + xbps_end(&xh); + exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/bin/xbps-alternatives/xbps-alternatives.1 b/bin/xbps-alternatives/xbps-alternatives.1 new file mode 100644 index 00000000..784c44f4 --- /dev/null +++ b/bin/xbps-alternatives/xbps-alternatives.1 @@ -0,0 +1,89 @@ +.Dd October 30, 2015 +.Dt XBPS-ALTERNATIVES 1 +.Sh NAME +.Nm xbps-alternatives +.Nd XBPS utility to handle alternatives +.Sh SYNOPSIS +.Nm xbps-alternatives +.Op OPTIONS +.Ar MODE +.Sh DESCRIPTION +The +.Nm +utility lists or sets the alternatives provided by installed packages. +Alternatives are classified by groups, and a group contains a number +of symbolic links which are applied when the group is set. +.Pp +When a package is installed its alternative groups are registered in the package database (pkgdb). +.Pp +When a package is removed its alternative groups are unregistered from the package database (pkgdb). +If there are no more alternative groups, the package database removes the references. If there were +alternative groups registered previously, the previous package is set as default provider. +.Sh OPTIONS +.Bl -tag -width -x +.It Fl C, Fl -config Ar dir +Specifies a path to the XBPS configuration directory. +If the first character is not '\/' then it's a relative path of +.Ar rootdir . +.It Fl d, Fl -debug +Enables extra debugging shown to stderr. +.It Fl g, Fl -group +Alternative group name to match. To be used with the +.Ar set +mode. +.It Fl h, Fl -help +Show the help message. +.It Fl r, Fl -rootdir Ar dir +Specifies a full path for the target root directory. +.It Fl v, Fl -verbose +Enables verbose messages. +.It Fl V, Fl -version +Show the version information. +.El +.Sh MODE +Only one of the following modes can be used at a time. +.Bl -tag -width -x +.It Fl l, Fl -list Op PKG +Lists all current alternatives or only from +.Ar PKG . +.It Fl s, Fl -set Ar PKG Op -g Ar group +Set alternative groups specified by +.Ar PKG +or just a specific group, if the +.Fl g Fl -group +option is set. +.El +.Sh FILES +.Bl -tag -width /var/db/xbps/.-files.plist +.It Ar /etc/xbps.d +Default configuration directory. +.It Ar /usr/share/xbps.d +Default system configuration directory. +.It Ar /var/db/xbps/.-files.plist +Package files metadata. +.It Ar /var/db/xbps/pkgdb-0.38.plist +Default package database (0.38 format). Keeps track of installed packages and properties. +.It Ar /var/cache/xbps +Default cache directory to store downloaded binary packages. +.El +.Sh SEE ALSO +.Xr xbps-checkvers 1 , +.Xr xbps-create 1 , +.Xr xbps-dgraph 1 , +.Xr xbps-fbulk 1 , +.Xr xbps-install 1 , +.Xr xbps-pkgdb 1 , +.Xr xbps-query 1 , +.Xr xbps-reconfigure 1 , +.Xr xbps-remove 1 , +.Xr xbps-rindex 1 , +.Xr xbps-uchroot 1 , +.Xr xbps-uunshare 1 , +.Xr xbps.d 5 +.Sh AUTHORS +.An Juan Romero Pardines +.Sh BUGS +Probably, but I try to make this not happen. Use it under your own +responsibility and enjoy your life. +.Pp +Report bugs at https://github.com/voidlinux/xbps/issues diff --git a/include/xbps.h.in b/include/xbps.h.in index c632dfab..20398d02 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -48,7 +48,7 @@ * * This header documents the full API for the XBPS Library. */ -#define XBPS_API_VERSION "20151018" +#define XBPS_API_VERSION "20151030" #ifndef XBPS_VERSION #define XBPS_VERSION "UNSET" @@ -96,12 +96,6 @@ */ #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. @@ -530,7 +524,6 @@ struct xbps_handle { */ xbps_dictionary_t pkgdb_revdeps; xbps_dictionary_t vpkgd; - xbps_dictionary_t alternatives; /** * @var pkgdb * @@ -646,7 +639,7 @@ 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_set(struct xbps_handle *, const char *, const char *); int xbps_alternatives_register(struct xbps_handle *, xbps_dictionary_t); int xbps_alternatives_unregister(struct xbps_handle *, xbps_dictionary_t); diff --git a/lib/package_alternatives.c b/lib/package_alternatives.c index 96a00e13..02b42e37 100644 --- a/lib/package_alternatives.c +++ b/lib/package_alternatives.c @@ -32,7 +32,7 @@ #include "xbps_api_impl.h" /* - * Alternatives framework for xbps. + * XXX TODO: relative symlinks. */ static char * left(const char *str) @@ -53,55 +53,6 @@ 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) { @@ -162,26 +113,92 @@ create_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname) return 0; } +int +xbps_alternatives_set(struct xbps_handle *xhp, const char *pkgname, + const char *group) +{ + xbps_array_t allkeys; + xbps_dictionary_t alternatives, pkg_alternatives, pkgd; + const char *pkgver; + int rv = 0; + + assert(xhp); + assert(pkgname); + + alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_"); + if (alternatives == NULL) + return ENOENT; + + pkgd = xbps_pkgdb_get_pkg(xhp, pkgname); + if (pkgd == NULL) + return ENOENT; + + pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives"); + if (!xbps_dictionary_count(pkg_alternatives)) + return ENOENT; + + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + + allkeys = xbps_dictionary_all_keys(pkg_alternatives); + for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) { + xbps_array_t array; + xbps_object_t keysym; + xbps_string_t kstr; + const char *keyname; + + keysym = xbps_array_get(allkeys, i); + keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); + + if (group && strcmp(keyname, group)) { + rv = ENOENT; + continue; + } + + array = xbps_dictionary_get(alternatives, keyname); + if (array == NULL) + continue; + + /* put this alternative group at the head */ + xbps_remove_string_from_array(array, pkgname); + kstr = xbps_string_create_cstring(pkgname); + xbps_array_add_first(array, kstr); + xbps_object_release(kstr); + + /* apply the alternatives group */ + xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_ADDED, 0, NULL, + "%s: applying '%s' alternatives group", pkgver, keyname); + rv = create_symlinks(xhp, xbps_dictionary_get(pkg_alternatives, keyname), keyname); + if (rv != 0) + break; + } + xbps_object_release(allkeys); + return rv; +} + int xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) { xbps_array_t allkeys; - xbps_dictionary_t alternatives; + xbps_dictionary_t alternatives, pkg_alternatives; const char *pkgver; char *pkgname; int rv = 0; - alternatives = xbps_dictionary_get(pkgd, "alternatives"); - if (!xbps_dictionary_count(alternatives)) + assert(xhp); + + alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_"); + if (alternatives == NULL) return 0; - xbps_alternatives_init(xhp); + pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives"); + if (!xbps_dictionary_count(pkg_alternatives)) + return 0; xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); if ((pkgname = xbps_pkg_name(pkgver)) == NULL) return EINVAL; - allkeys = xbps_dictionary_all_keys(alternatives); + allkeys = xbps_dictionary_all_keys(pkg_alternatives); for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) { xbps_array_t array; xbps_object_t keysym; @@ -190,7 +207,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) keysym = xbps_array_get(allkeys, i); keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); - array = xbps_dictionary_get(xhp->alternatives, keyname); + array = xbps_dictionary_get(alternatives, keyname); if (array == NULL) continue; @@ -198,7 +215,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) if (strcmp(pkgname, first) == 0) { /* this pkg is the current alternative for this group */ rv = remove_symlinks(xhp, - xbps_dictionary_get(alternatives, keyname), + xbps_dictionary_get(pkg_alternatives, keyname), keyname); if (rv != 0) break; @@ -207,7 +224,7 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) "%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); + xbps_dictionary_remove(alternatives, keyname); } else { xbps_dictionary_t curpkgd; @@ -217,9 +234,9 @@ xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd) 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"); + pkg_alternatives = xbps_dictionary_get(curpkgd, "alternatives"); rv = create_symlinks(xhp, - xbps_dictionary_get(alternatives, keyname), + xbps_dictionary_get(pkg_alternatives, keyname), keyname); if (rv != 0) break; @@ -236,23 +253,35 @@ int xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd) { xbps_array_t allkeys; - xbps_dictionary_t alternatives; + xbps_dictionary_t alternatives, pkg_alternatives; const char *pkgver; char *pkgname; int rv = 0; - alternatives = xbps_dictionary_get(pkgd, "alternatives"); - if (!xbps_dictionary_count(alternatives)) + assert(xhp); + + if (xhp->pkgdb == NULL) + return EINVAL; + + pkg_alternatives = xbps_dictionary_get(pkgd, "alternatives"); + if (!xbps_dictionary_count(pkg_alternatives)) return 0; - xbps_alternatives_init(xhp); + alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_"); + if (alternatives == NULL) { + alternatives = xbps_dictionary_create(); + xbps_dictionary_set(xhp->pkgdb, "_XBPS_ALTERNATIVES_", alternatives); + xbps_object_release(alternatives); + } + alternatives = xbps_dictionary_get(xhp->pkgdb, "_XBPS_ALTERNATIVES_"); + assert(alternatives); xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); pkgname = xbps_pkg_name(pkgver); if (pkgname == NULL) return EINVAL; - allkeys = xbps_dictionary_all_keys(alternatives); + allkeys = xbps_dictionary_all_keys(pkg_alternatives); for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) { xbps_array_t array; xbps_object_t keysym; @@ -262,19 +291,19 @@ xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd) keysym = xbps_array_get(allkeys, i); keyname = xbps_dictionary_keysym_cstring_nocopy(keysym); - array = xbps_dictionary_get(xhp->alternatives, keyname); + array = xbps_dictionary_get(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_dictionary_set(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), + xbps_dictionary_get(pkg_alternatives, keyname), keyname); xbps_object_release(array); if (rv != 0) diff --git a/lib/pkgdb.c b/lib/pkgdb.c index 011bc7a6..ded8ff66 100644 --- a/lib/pkgdb.c +++ b/lib/pkgdb.c @@ -208,6 +208,7 @@ xbps_pkgdb_init(struct xbps_handle *xhp) xbps_dbg_printf(xhp, "[pkgdb] pkgdb_map_vpkgs %s\n", strerror(rv)); return rv; } + assert(xhp->pkgdb); xbps_dbg_printf(xhp, "[pkgdb] initialized ok.\n"); return 0; diff --git a/lib/plist.c b/lib/plist.c index 9eb6ab64..ef18b06c 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008-2013 Juan Romero Pardines. + * Copyright (c) 2008-2015 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,6 +66,9 @@ array_foreach_thread(void *arg) if (xbps_object_type(thd->dict) == XBPS_TYPE_DICTIONARY) { pkgd = xbps_dictionary_get_keysym(thd->dict, obj); key = xbps_dictionary_keysym_cstring_nocopy(obj); + /* ignore internal objs */ + if (strncmp(key, "_XBPS_", 6) == 0) + continue; } else { pkgd = obj; key = NULL; @@ -148,6 +151,9 @@ xbps_array_foreach_cb(struct xbps_handle *xhp, if (xbps_object_type(dict) == XBPS_TYPE_DICTIONARY) { pkgd = xbps_dictionary_get_keysym(dict, obj); key = xbps_dictionary_keysym_cstring_nocopy(obj); + /* ignore internal objs */ + if (strncmp(key, "_XBPS_", 6) == 0) + continue; } else { pkgd = obj; key = NULL; diff --git a/lib/transaction_commit.c b/lib/transaction_commit.c index d50c7bd1..b018e5d3 100644 --- a/lib/transaction_commit.c +++ b/lib/transaction_commit.c @@ -350,10 +350,6 @@ 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")) diff --git a/lib/transaction_ops.c b/lib/transaction_ops.c index cf3b882c..a2dbd2ce 100644 --- a/lib/transaction_ops.c +++ b/lib/transaction_ops.c @@ -260,7 +260,8 @@ xbps_transaction_update_packages(struct xbps_handle *xhp) while ((obj = xbps_object_iterator_next(iter))) { hold = false; pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj); - xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) + continue; xbps_dictionary_get_bool(pkgd, "hold", &hold); if (hold) { xbps_dbg_printf(xhp, "[rpool] package `%s' " diff --git a/lib/transaction_shlibs.c b/lib/transaction_shlibs.c index 592dde11..a979f1a1 100644 --- a/lib/transaction_shlibs.c +++ b/lib/transaction_shlibs.c @@ -84,7 +84,9 @@ collect_shlibs(struct xbps_handle *xhp, xbps_array_t pkgs, bool req) while ((obj = xbps_object_iterator_next(iter))) { char *pkgname; - xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + if (!xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver)) + continue; + pkgname = xbps_pkg_name(pkgver); assert(pkgname); xbps_dictionary_set(pd, pkgname, obj); diff --git a/tests/xbps/Kyuafile b/tests/xbps/Kyuafile index 1572eba4..326f6960 100644 --- a/tests/xbps/Kyuafile +++ b/tests/xbps/Kyuafile @@ -3,6 +3,7 @@ syntax("kyuafile", 1) test_suite("xbps") include('libxbps/Kyuafile') +include('xbps-alternatives/Kyuafile') include('xbps-checkvers/Kyuafile') include('xbps-create/Kyuafile') include('xbps-install/Kyuafile') diff --git a/tests/xbps/Makefile b/tests/xbps/Makefile index af8ae620..8171e1bb 100644 --- a/tests/xbps/Makefile +++ b/tests/xbps/Makefile @@ -1,5 +1,5 @@ -include ../../config.mk -SUBDIRS = common libxbps xbps-checkvers xbps-create xbps-install xbps-query xbps-rindex xbps-uhelper +SUBDIRS = common libxbps xbps-alternatives xbps-checkvers xbps-create xbps-install xbps-query xbps-rindex xbps-uhelper include ../../mk/subdir.mk diff --git a/tests/xbps/xbps-alternatives/Kyuafile b/tests/xbps/xbps-alternatives/Kyuafile new file mode 100644 index 00000000..1a1d609e --- /dev/null +++ b/tests/xbps/xbps-alternatives/Kyuafile @@ -0,0 +1,4 @@ +syntax("kyuafile", 1) + +test_suite("xbps-alternatives") +atf_test_program{name="main"} diff --git a/tests/xbps/xbps-alternatives/Makefile b/tests/xbps/xbps-alternatives/Makefile new file mode 100644 index 00000000..e6da7521 --- /dev/null +++ b/tests/xbps/xbps-alternatives/Makefile @@ -0,0 +1,8 @@ +TOPDIR = ../../.. +-include $(TOPDIR)/config.mk + +TESTSHELL = main +TESTSSUBDIR = xbps/xbps-alternatives +EXTRA_FILES = Kyuafile + +include $(TOPDIR)/mk/test.mk diff --git a/tests/xbps/xbps-alternatives/main.sh b/tests/xbps/xbps-alternatives/main.sh new file mode 100644 index 00000000..4e8e4c41 --- /dev/null +++ b/tests/xbps/xbps-alternatives/main.sh @@ -0,0 +1,270 @@ +#! /usr/bin/env atf-sh + +atf_test_case register_one + +register_one_head() { + atf_set "descr" "xbps-alternatives: register one pkg wth an alternatives group" +} +register_one_body() { + mkdir -p repo pkg_A/usr/bin + touch pkg_A/usr/bin/fileA + cd repo + xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv A + atf_check_equal $? 0 + rv=1 + if [ -e root/usr/bin/fileA ]; then + lnk=$(readlink root/usr/bin/file) + if [ "$lnk" = "/usr/bin/fileA" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 +} + +atf_test_case register_multi + +register_multi_head() { + atf_set "descr" "xbps-alternatives: register multiple pkgs with an alternatives group" +} +register_multi_body() { + mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin + touch pkg_A/usr/bin/fileA pkg_B/usr/bin/fileB + cd repo + xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileB" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv A + atf_check_equal $? 0 + rv=1 + if [ -e root/usr/bin/fileA ]; then + lnk=$(readlink root/usr/bin/file) + if [ "$lnk" = "/usr/bin/fileA" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + + xbps-install -r root --repository=repo -ydv B + atf_check_equal $? 0 + rv=1 + if [ -e root/usr/bin/fileA -a -e root/usr/bin/fileB ]; then + lnk=$(readlink root/usr/bin/file) + if [ "$lnk" = "/usr/bin/fileA" ]; then + rv=0 + fi + echo "B lnk: $lnk" + fi + atf_check_equal $rv 0 +} + +atf_test_case unregister_one + +unregister_one_head() { + atf_set "descr" "xbps-alternatives: unregister one pkg with an alternatives group" +} +unregister_one_body() { + mkdir -p repo pkg_A/usr/bin + touch pkg_A/usr/bin/fileA + cd repo + xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv A + atf_check_equal $? 0 + xbps-remove -r root -yvd A + rv=1 + if [ ! -L root/usr/bin/file -a ! -e root/usr/bin/fileA ]; then + rv=0 + fi + atf_check_equal $rv 0 +} + +atf_test_case unregister_multi + +unregister_multi_head() { + atf_set "descr" "xbps-alternatives: unregister multiple pkgs with an alternatives group" +} +unregister_multi_body() { + mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin + touch pkg_A/usr/bin/fileA pkg_B/usr/bin/fileB + cd repo + xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileA" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "file:/usr/bin/file:/usr/bin/fileB" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv A + atf_check_equal $? 0 + + if [ -e root/usr/bin/fileA ]; then + lnk=$(readlink root/usr/bin/file) + if [ "$lnk" = "/usr/bin/fileA" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + xbps-remove -r root -yvd A + rv=1 + if [ ! -L root/usr/bin/file -a ! -e root/usr/bin/fileA ]; then + rv=0 + fi + atf_check_equal $rv 0 + + xbps-install -r root --repository=repo -ydv B + atf_check_equal $? 0 + + if [ -e root/usr/bin/fileB ]; then + lnk=$(readlink root/usr/bin/file) + if [ "$lnk" = "/usr/bin/fileB" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + + xbps-remove -r root -yvd B + rv=1 + if [ ! -L root/usr/bin/file -a ! -e root/usr/bin/fileB ]; then + rv=0 + fi + atf_check_equal $rv 0 +} + +atf_test_case set_pkg + +set_pkg_head() { + atf_set "descr" "xbps-alternatives: set all alternative groups from pkg" +} +set_pkg_body() { + mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin + touch pkg_A/usr/bin/A1 pkg_A/usr/bin/A2 pkg_B/usr/bin/B1 pkg_B/usr/bin/B2 + cd repo + xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "1:/usr/bin/1:/usr/bin/A1 2:/usr/bin/2:/usr/bin/A2" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "1:/usr/bin/1:/usr/bin/B1 2:/usr/bin/2:/usr/bin/B2" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv A B + atf_check_equal $? 0 + xbps-alternatives -r root -s B + atf_check_equal $? 0 + + rv=1 + if [ -e root/usr/bin/B1 ]; then + lnk=$(readlink root/usr/bin/1) + if [ "$lnk" = "/usr/bin/B1" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + + rv=1 + if [ -e root/usr/bin/B2 ]; then + lnk=$(readlink root/usr/bin/2) + if [ "$lnk" = "/usr/bin/B2" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + + xbps-alternatives -r root -s A + atf_check_equal $? 0 + + rv=1 + if [ -e root/usr/bin/A1 ]; then + lnk=$(readlink root/usr/bin/1) + if [ "$lnk" = "/usr/bin/A1" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + + rv=1 + if [ -e root/usr/bin/A2 ]; then + lnk=$(readlink root/usr/bin/2) + if [ "$lnk" = "/usr/bin/A2" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 +} + +atf_test_case set_pkg_group + +set_pkg_group_head() { + atf_set "descr" "xbps-alternatives: set one alternative group from pkg" +} +set_pkg_group_body() { + mkdir -p repo pkg_A/usr/bin pkg_B/usr/bin + touch pkg_A/usr/bin/A1 pkg_A/usr/bin/A2 pkg_B/usr/bin/B1 pkg_B/usr/bin/B2 + cd repo + xbps-create -A noarch -n A-1.1_1 -s "A pkg" --alternatives "1:/usr/bin/1:/usr/bin/A1 2:/usr/bin/2:/usr/bin/A2" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n B-1.1_1 -s "B pkg" --alternatives "1:/usr/bin/1:/usr/bin/B1 2:/usr/bin/2:/usr/bin/B2" ../pkg_B + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + + xbps-install -r root --repository=repo -ydv A B + atf_check_equal $? 0 + xbps-alternatives -r root -s B -g 2 + atf_check_equal $? 0 + + rv=1 + if [ -e root/usr/bin/B1 ]; then + lnk=$(readlink root/usr/bin/1) + if [ "$lnk" = "/usr/bin/A1" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + + rv=1 + if [ -e root/usr/bin/B2 ]; then + lnk=$(readlink root/usr/bin/2) + if [ "$lnk" = "/usr/bin/B2" ]; then + rv=0 + fi + echo "A lnk: $lnk" + fi + atf_check_equal $rv 0 + +} + +atf_init_test_cases() { + atf_add_test_case register_one + atf_add_test_case register_multi + atf_add_test_case unregister_one + atf_add_test_case unregister_multi + atf_add_test_case set_pkg + atf_add_test_case set_pkg_group +}