From b05ce9fe57322d309baf63987b16313233afbbaf Mon Sep 17 00:00:00 2001 From: Juan RP Date: Fri, 2 Nov 2012 15:04:25 +0100 Subject: [PATCH] New utilities: xbps-{install,pkgdb,query,reconfigure,rindex} (WIP). --- .gitignore | 10 +- bin/Makefile | 13 +- bin/xbps-install/Makefile | 9 + bin/xbps-install/defs.h | 69 +++++ bin/xbps-install/fetch_cb.c | 176 +++++++++++++ bin/xbps-install/main.c | 242 +++++++++++++++++ bin/xbps-install/question.c | 110 ++++++++ bin/xbps-install/state_cb.c | 160 ++++++++++++ bin/xbps-install/transaction.c | 331 ++++++++++++++++++++++++ bin/xbps-install/unpack_cb.c | 63 +++++ bin/xbps-install/util.c | 71 +++++ bin/xbps-pkgdb/Makefile | 11 + bin/xbps-pkgdb/check.c | 188 ++++++++++++++ bin/xbps-pkgdb/check_pkg_automatic.c | 77 ++++++ bin/xbps-pkgdb/check_pkg_files.c | 153 +++++++++++ bin/xbps-pkgdb/check_pkg_requiredby.c | 222 ++++++++++++++++ bin/xbps-pkgdb/check_pkg_rundeps.c | 82 ++++++ bin/xbps-pkgdb/check_pkg_symlinks.c | 123 +++++++++ bin/xbps-pkgdb/check_pkg_unneeded.c | 65 +++++ bin/xbps-pkgdb/defs.h | 50 ++++ bin/xbps-pkgdb/main.c | 128 +++++++++ bin/xbps-query/Makefile | 9 + bin/xbps-query/defs.h | 68 +++++ bin/xbps-query/list.c | 238 +++++++++++++++++ bin/xbps-query/main.c | 247 ++++++++++++++++++ bin/xbps-query/ownedby.c | 209 +++++++++++++++ bin/xbps-query/search.c | 175 +++++++++++++ bin/xbps-query/show-deps.c | 94 +++++++ bin/xbps-query/show-info-files.c | 259 +++++++++++++++++++ bin/xbps-reconfigure/Makefile | 8 + bin/xbps-reconfigure/main.c | 134 ++++++++++ bin/xbps-remove/Makefile | 12 + bin/xbps-remove/main.c | 347 +++++++++++++++++++++++++ bin/xbps-rindex/Makefile | 8 + bin/xbps-rindex/common.c | 70 +++++ bin/xbps-rindex/defs.h | 45 ++++ bin/xbps-rindex/index-files.c | 359 ++++++++++++++++++++++++++ bin/xbps-rindex/index.c | 325 +++++++++++++++++++++++ bin/xbps-rindex/main.c | 114 ++++++++ bin/xbps-rindex/remove-obsoletes.c | 119 +++++++++ 40 files changed, 5188 insertions(+), 5 deletions(-) create mode 100644 bin/xbps-install/Makefile create mode 100644 bin/xbps-install/defs.h create mode 100644 bin/xbps-install/fetch_cb.c create mode 100644 bin/xbps-install/main.c create mode 100644 bin/xbps-install/question.c create mode 100644 bin/xbps-install/state_cb.c create mode 100644 bin/xbps-install/transaction.c create mode 100644 bin/xbps-install/unpack_cb.c create mode 100644 bin/xbps-install/util.c create mode 100644 bin/xbps-pkgdb/Makefile create mode 100644 bin/xbps-pkgdb/check.c create mode 100644 bin/xbps-pkgdb/check_pkg_automatic.c create mode 100644 bin/xbps-pkgdb/check_pkg_files.c create mode 100644 bin/xbps-pkgdb/check_pkg_requiredby.c create mode 100644 bin/xbps-pkgdb/check_pkg_rundeps.c create mode 100644 bin/xbps-pkgdb/check_pkg_symlinks.c create mode 100644 bin/xbps-pkgdb/check_pkg_unneeded.c create mode 100644 bin/xbps-pkgdb/defs.h create mode 100644 bin/xbps-pkgdb/main.c create mode 100644 bin/xbps-query/Makefile create mode 100644 bin/xbps-query/defs.h create mode 100644 bin/xbps-query/list.c create mode 100644 bin/xbps-query/main.c create mode 100644 bin/xbps-query/ownedby.c create mode 100644 bin/xbps-query/search.c create mode 100644 bin/xbps-query/show-deps.c create mode 100644 bin/xbps-query/show-info-files.c create mode 100644 bin/xbps-reconfigure/Makefile create mode 100644 bin/xbps-reconfigure/main.c create mode 100644 bin/xbps-remove/Makefile create mode 100644 bin/xbps-remove/main.c create mode 100644 bin/xbps-rindex/Makefile create mode 100644 bin/xbps-rindex/common.c create mode 100644 bin/xbps-rindex/defs.h create mode 100644 bin/xbps-rindex/index-files.c create mode 100644 bin/xbps-rindex/index.c create mode 100644 bin/xbps-rindex/main.c create mode 100644 bin/xbps-rindex/remove-obsoletes.c diff --git a/.gitignore b/.gitignore index d8760cd0..e6633138 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,15 @@ config.h config.mk bin/xbps-bin/xbps-bin bin/xbps-repo/xbps-repo -bin/xbps-uhelper/xbps-uhelper -bin/xbps-dgraph/xbps-dgraph bin/xbps-create/xbps-create +bin/xbps-dgraph/xbps-dgraph +bin/xbps-install/xbps-install +bin/xbps-pkgdb/xbps-pkgdb +bin/xbps-query/xbps-query +bin/xbps-reconfigure/xbps-reconfigure +bin/xbps-remove/xbps-remove +bin/xbps-rindex/xbps-rindex +bin/xbps-uhelper/xbps-uhelper *.static *.so* *.o diff --git a/bin/Makefile b/bin/Makefile index e012a577..1a5cb373 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,9 +1,16 @@ -include ../config.mk -SUBDIRS = xbps-bin +SUBDIRS += xbps-bin SUBDIRS += xbps-repo -SUBDIRS += xbps-dgraph -SUBDIRS += xbps-uhelper + SUBDIRS += xbps-create +SUBDIRS += xbps-dgraph +SUBDIRS += xbps-install +SUBDIRS += xbps-pkgdb +SUBDIRS += xbps-query +SUBDIRS += xbps-reconfigure +SUBDIRS += xbps-remove +SUBDIRS += xbps-rindex +SUBDIRS += xbps-uhelper include ../mk/subdir.mk diff --git a/bin/xbps-install/Makefile b/bin/xbps-install/Makefile new file mode 100644 index 00000000..c38bb0d7 --- /dev/null +++ b/bin/xbps-install/Makefile @@ -0,0 +1,9 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-install +OBJS = main.o transaction.o main.o question.o fetch_cb.o state_cb.o +OBJS += unpack_cb.o util.o +#MAN = $(BIN).8 + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-install/defs.h b/bin/xbps-install/defs.h new file mode 100644 index 00000000..d24ac52f --- /dev/null +++ b/bin/xbps-install/defs.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2009-2012 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. + */ + +#ifndef _XBPS_INSTALL_DEFS_H_ +#define _XBPS_INSTALL_DEFS_H_ + +#include +#include + +struct xferstat { + struct timeval start; + struct timeval last; +}; + +/* from transaction.c */ +int install_new_pkg(struct xbps_handle *, const char *, bool); +int update_pkg(struct xbps_handle *, const char *); +int dist_upgrade(struct xbps_handle *, size_t, bool, bool); +int exec_transaction(struct xbps_handle *, size_t, bool, bool); + +/* from question.c */ +bool yesno(const char *, ...); +bool noyes(const char *, ...); + +/* from fetch_cb.c */ +void fetch_file_progress_cb(struct xbps_handle *, + struct xbps_fetch_cb_data *, + void *); + +/* from state_cb.c */ +void state_cb(struct xbps_handle *, + struct xbps_state_cb_data *, + void *); + +/* from unpack_cb.c */ +void unpack_progress_cb_verbose(struct xbps_handle *, + struct xbps_unpack_cb_data *, + void *); +void unpack_progress_cb(struct xbps_handle *, + struct xbps_unpack_cb_data *, + void *); + +/* From util.c */ +void print_package_line(const char *, size_t, bool); +size_t get_maxcols(void); + +#endif /* !_XBPS_INSTALL_DEFS_H_ */ diff --git a/bin/xbps-install/fetch_cb.c b/bin/xbps-install/fetch_cb.c new file mode 100644 index 00000000..bfe8ba40 --- /dev/null +++ b/bin/xbps-install/fetch_cb.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2009-2012 Juan Romero Pardines + * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * 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. + * + * From FreeBSD fetch(8): + * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.84.2.1 2009/08/03 08:13:06 kensmith Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "defs.h" +#include "config.h" + +static void +get_time(struct timeval *tvp) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + (void)clock_gettime(CLOCK_MONOTONIC, &ts); + tvp->tv_sec = ts.tv_sec; + tvp->tv_usec = ts.tv_nsec / 1000; +#else + (void)gettimeofday(tvp, NULL); +#endif +} + +/* + * Compute and display ETA + */ +static const char * +stat_eta(struct xbps_fetch_cb_data *xfpd, void *cbdata) +{ + struct xferstat *xfer = cbdata; + static char str[16]; + long elapsed, eta; + off_t received, expected; + + if (xfpd->file_size == -1) + return "unknown"; + + elapsed = xfer->last.tv_sec - xfer->start.tv_sec; + received = xfpd->file_dloaded - xfpd->file_offset; + expected = xfpd->file_size - xfpd->file_dloaded; + eta = (long)(elapsed * (long)expected / (long)received); + if (eta > 3600) + snprintf(str, sizeof str, "%02ldh%02ldm", + eta / 3600, (eta % 3600) / 60); + else + snprintf(str, sizeof str, "%02ldm%02lds", + eta / 60, eta % 60); + + return str; +} + +static inline bool +compare_double(const double a, const double b) +{ + const double precision = 0.00001; + + if ((a - precision) < b && (a + precision) > b) + return true; + else + return false; +} + +/* + * Compute and display transfer rate + */ +static const char * +stat_bps(struct xbps_fetch_cb_data *xfpd, void *cbdata) +{ + struct xferstat *xfer = cbdata; + static char str[16]; + char size[8]; + double delta, bps; + + delta = (xfer->last.tv_sec + (xfer->last.tv_usec / 1.e6)) + - (xfer->start.tv_sec + (xfer->start.tv_usec / 1.e6)); + if (compare_double(delta, 0.0001)) { + snprintf(str, sizeof str, "-- stalled --"); + } else { + bps = + ((double)(xfpd->file_dloaded - xfpd->file_offset) / delta); + (void)xbps_humanize_number(size, (int64_t)bps); + snprintf(str, sizeof str, "%s/s", size); + } + return str; +} + +/* + * Update the stats display + */ +static void +stat_display(struct xbps_fetch_cb_data *xfpd, void *cbdata) +{ + struct xferstat *xfer = cbdata; + struct timeval now; + char totsize[8], recvsize[8]; + int percentage; + + get_time(&now); + if (now.tv_sec <= xfer->last.tv_sec) + return; + xfer->last = now; + + if (xfpd->file_size == -1) { + percentage = 0; + snprintf(totsize, 3, "0B"); + } else { + percentage = (int)((double)(100.0 * + (double)xfpd->file_dloaded) / (double)xfpd->file_size); + (void)xbps_humanize_number(totsize, (int64_t)xfpd->file_size); + } + (void)xbps_humanize_number(recvsize, (int64_t)xfpd->file_dloaded); + fprintf(stderr, "%s: %s [%d%% of %s] %s ETA: %s\033[K\r", + xfpd->file_name, recvsize, percentage, totsize, + stat_bps(xfpd, xfer), stat_eta(xfpd, xfer)); +} + +void +fetch_file_progress_cb(struct xbps_handle *xhp, + struct xbps_fetch_cb_data *xfpd, + void *cbdata) +{ + struct xferstat *xfer = cbdata; + char size[8]; + + (void)xhp; + + if (xfpd->cb_start) { + /* start transfer stats */ + get_time(&xfer->start); + xfer->last.tv_sec = xfer->last.tv_usec = 0; + } else if (xfpd->cb_update) { + /* update transfer stats */ + stat_display(xfpd, xfer); + } else if (xfpd->cb_end) { + /* end transfer stats */ + (void)xbps_humanize_number(size, (int64_t)xfpd->file_dloaded); + fprintf(stderr,"Downloaded %s for %s [avg rate: %s]\033[K\n", + size, xfpd->file_name, stat_bps(xfpd, xfer)); + } +} diff --git a/bin/xbps-install/main.c b/bin/xbps-install/main.c new file mode 100644 index 00000000..2638d272 --- /dev/null +++ b/bin/xbps-install/main.c @@ -0,0 +1,242 @@ +/*- + * Copyright (c) 2008-2012 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 + +#include +#include "defs.h" + +static struct xbps_handle xh; + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-install [OPTIONS] [PKGNAME...]\n\n" + "OPTIONS\n" + " -A --automatic Set automatic installation mode\n" + " -C --config Full path to configuration file\n" + " -c --cachedir Full path to cachedir\n" + " -d --debug Debug mode shown to stderr\n" + " -f --force Force package installation\n" + " -h --help Print help usage\n" + " -n --dry-run Dry-run mode\n" + " -p --print-format Print format for dry-run mode\n" + " -R --repository Default repository to be used if config not set\n" + " -r --rootdir Full path to rootdir\n" + " -s --skip-sync Skip remote repository index sync\n" + " -u --update Update target package(s)\n" + " -v --verbose Verbose messages\n" + " -y --yes Assume yes to all questions\n" + " -V --version Show XBPS version\n"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void __attribute__((noreturn)) +cleanup_sighandler(int signum) +{ + xbps_end(&xh); + _exit(signum); +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "AC:c:dfhnp:R:r:suVvy"; + const struct option longopts[] = { + { "automatic", no_argument, NULL, 'A' }, + { "config", required_argument, NULL, 'C' }, + { "cachedir", required_argument, NULL, 'c' }, + { "debug", no_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "dry-run", no_argument, NULL, 'n' }, + { "print-format", required_argument, NULL, 'p' }, + { "repository", required_argument, NULL, 'R' }, + { "rootdir", required_argument, NULL, 'r' }, + { "skip-sync", no_argument, NULL, 's' }, + { "update", no_argument, NULL, 'u' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "yes", no_argument, NULL, 'y' }, + { NULL, 0, NULL, 0 } + }; + struct xferstat xfer; + struct sigaction sa; + const char *rootdir, *cachedir, *conffile, *defrepo, *pformat; + int i, c, flags, rv; + bool skip_sync, yes, reinstall, drun, update; + size_t maxcols; + + rootdir = cachedir = conffile = defrepo = pformat = NULL; + flags = rv = 0; + skip_sync = yes = reinstall = drun = update = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'A': + flags |= XBPS_FLAG_INSTALL_AUTO; + break; + case 'C': + conffile = optarg; + break; + case 'c': + cachedir = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'f': + reinstall = true; + break; + case 'h': + usage(false); + /* NOTREACHED */ + case 'n': + drun = true; + break; + case 'p': + pformat = optarg; + break; + case 'R': + defrepo = optarg; + break; + case 'r': + rootdir = optarg; + break; + case 's': + skip_sync = true; + break; + case 'u': + update = true; + break; + case 'v': + flags |= XBPS_FLAG_VERBOSE; + break; + case 'V': + printf("%s\n", XBPS_RELVER); + exit(EXIT_SUCCESS); + case 'y': + yes = true; + break; + case '?': + default: + usage(true); + /* NOTREACHED */ + } + } + if (!update && (argc == optind)) + usage(true); + + /* + * Initialize libxbps. + */ + memset(&xh, 0, sizeof(xh)); + xh.state_cb = state_cb; + xh.fetch_cb = fetch_file_progress_cb; + xh.fetch_cb_data = &xfer; + xh.rootdir = rootdir; + xh.cachedir = cachedir; + xh.conffile = conffile; + xh.flags = flags; + xh.repository = defrepo; + if (flags & XBPS_FLAG_VERBOSE) + xh.unpack_cb = unpack_progress_cb_verbose; + else + xh.unpack_cb = unpack_progress_cb; + + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + /* + * Register a signal handler to clean up resources used by libxbps. + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup_sighandler; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + maxcols = get_maxcols(); + /* + * Check that we have write permission on rootdir, metadir + * and cachedir. + */ + if ((!drun && ((access(xh.rootdir, W_OK) == -1) || + (access(xh.metadir, W_OK) == -1) || + (access(xh.cachedir, W_OK) == -1)))) { + if (errno != ENOENT) { + fprintf(stderr, "Not enough permissions on " + "rootdir/cachedir/metadir: %s\n", + strerror(errno)); + rv = errno; + goto out; + } + } + + /* Sync remote repository index files by default */ + if (!skip_sync || !drun) { + rv = xbps_rpool_sync(&xh, NULL); + if (rv != 0) + goto out; + } + + if (update && !argc) { + /* Update all installed packages */ + rv = dist_upgrade(&xh, maxcols, yes, drun); + } else if (update && argc) { + /* Update target packages */ + for (i = optind; i < argc; i++) { + rv = update_pkg(&xh, argv[i]); + if (rv != 0) + goto out; + } + rv = exec_transaction(&xh, maxcols, yes, drun); + } else if (!update && argc) { + /* Install target packages */ + for (i = optind; i < argc; i++) { + rv = install_new_pkg(&xh, argv[i], reinstall); + if (rv != 0) + goto out; + } + rv = exec_transaction(&xh, maxcols, yes, drun); + } + +out: + xbps_end(&xh); + exit(rv); +} diff --git a/bin/xbps-install/question.c b/bin/xbps-install/question.c new file mode 100644 index 00000000..6d85a6fc --- /dev/null +++ b/bin/xbps-install/question.c @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2008-2010 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 "defs.h" + +static char * +strtrim(char *str) +{ + char *pch = str; + + if (str == NULL || *str == '\0') + return str; + + while (isspace((unsigned char)*pch)) + pch++; + + if (pch != str) + memmove(str, pch, (strlen(pch) + 1)); + + if (*str == '\0') + return str; + + pch = (str + (strlen(str) - 1)); + while (isspace((unsigned char)*pch)) + pch--; + + *++pch = '\0'; + + return str; +} + +static bool +question(bool preset, const char *fmt, va_list ap) +{ + char response[32]; + + vfprintf(stderr, fmt, ap); + if (preset) + fprintf(stderr, " %s ", "[YES/no]"); + else + fprintf(stderr, " %s ", "[yes/NO]"); + + if (fgets(response, sizeof(response), stdin)) { + (void)strtrim(response); + if (strlen(response) == 0) + return preset; + + if (strcasecmp(response, "yes") == 0) + return true; + else if (strcasecmp(response, "no") == 0) + return false; + } + return false; +} + +bool +yesno(const char *fmt, ...) +{ + va_list ap; + bool res; + + va_start(ap, fmt); + res = question(1, fmt, ap); + va_end(ap); + + return res; +} + +bool +noyes(const char *fmt, ...) +{ + va_list ap; + bool res; + + va_start(ap, fmt); + res = question(0, fmt, ap); + va_end(ap); + + return res; +} diff --git a/bin/xbps-install/state_cb.c b/bin/xbps-install/state_cb.c new file mode 100644 index 00000000..217c0a56 --- /dev/null +++ b/bin/xbps-install/state_cb.c @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 2011-2012 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 "defs.h" + +void +state_cb(struct xbps_handle *xhp, + struct xbps_state_cb_data *xscd, + void *cbdata) +{ + prop_dictionary_t pkgd; + const char *version; + bool syslog_enabled = false; + + (void)cbdata; + + if (xhp->flags & XBPS_FLAG_SYSLOG) { + syslog_enabled = true; + openlog("xbps-bin", LOG_CONS, LOG_USER); + } + + switch (xscd->state) { + /* notifications */ + case XBPS_STATE_TRANS_DOWNLOAD: + printf("[*] Downloading binary packages\n"); + break; + case XBPS_STATE_TRANS_VERIFY: + printf("[*] Verifying binary package integrity\n"); + break; + case XBPS_STATE_TRANS_RUN: + printf("[*] Running transaction tasks\n"); + break; + case XBPS_STATE_TRANS_CONFIGURE: + printf("[*] Configuring unpacked packages\n"); + break; + case XBPS_STATE_REPOSYNC: + case XBPS_STATE_DOWNLOAD: + case XBPS_STATE_VERIFY: + case XBPS_STATE_CONFIG_FILE: + if (xscd->desc != NULL) + printf("%s\n", xscd->desc); + break; + case XBPS_STATE_REMOVE: + printf("Removing `%s-%s' ...\n", xscd->pkgname, xscd->version); + break; + case XBPS_STATE_CONFIGURE: + printf("Configuring `%s-%s' ...\n", xscd->pkgname, + xscd->version); + break; + case XBPS_STATE_REGISTER: + case XBPS_STATE_UNREGISTER: + /* empty */ + break; + case XBPS_STATE_UNPACK: + printf("Unpacking `%s-%s' ...\n", xscd->pkgname, xscd->version); + break; + case XBPS_STATE_INSTALL: + printf("Installing `%s-%s' ...\n", + xscd->pkgname, xscd->version); + break; + case XBPS_STATE_UPDATE: + pkgd = xbps_find_pkg_dict_installed(xhp, + xscd->pkgname, false); + prop_dictionary_get_cstring_nocopy(pkgd, "version", &version); + printf("Updating `%s' (`%s' to `%s') ...\n", xscd->pkgname, + version, xscd->version); + break; + /* success */ + case XBPS_STATE_REMOVE_FILE: + case XBPS_STATE_REMOVE_FILE_OBSOLETE: + if (xhp->flags & XBPS_FLAG_VERBOSE) + printf("%s\n", xscd->desc); + else { + printf("%s\n", xscd->desc); + printf("\033[1A\033[K"); + } + break; + case XBPS_STATE_INSTALL_DONE: + printf("Installed `%s-%s' successfully.\n", + xscd->pkgname, xscd->version); + if (syslog_enabled) + syslog(LOG_NOTICE, "Installed `%s-%s' successfully " + "(rootdir: %s).", xscd->pkgname, xscd->version, + xhp->rootdir); + break; + case XBPS_STATE_UPDATE_DONE: + printf("Updated `%s' to `%s' successfully.\n", + xscd->pkgname, xscd->version); + if (syslog_enabled) + syslog(LOG_NOTICE, "Updated `%s' to `%s' successfully " + "(rootdir: %s).", xscd->pkgname, xscd->version, + xhp->rootdir); + break; + case XBPS_STATE_REMOVE_DONE: + printf("Removed `%s-%s' successfully.\n", + xscd->pkgname, xscd->version); + if (syslog_enabled) + syslog(LOG_NOTICE, "Removed `%s-%s' successfully " + "(rootdir: %s).", xscd->pkgname, xscd->version, + xhp->rootdir); + break; + /* errors */ + case XBPS_STATE_UNPACK_FAIL: + case XBPS_STATE_UPDATE_FAIL: + case XBPS_STATE_CONFIGURE_FAIL: + case XBPS_STATE_REGISTER_FAIL: + case XBPS_STATE_UNREGISTER_FAIL: + case XBPS_STATE_REMOVE_FAIL: + case XBPS_STATE_VERIFY_FAIL: + case XBPS_STATE_DOWNLOAD_FAIL: + case XBPS_STATE_REPOSYNC_FAIL: + case XBPS_STATE_CONFIG_FILE_FAIL: + xbps_error_printf("%s\n", xscd->desc); + if (syslog_enabled) + syslog(LOG_ERR, "%s", xscd->desc); + break; + case XBPS_STATE_REMOVE_FILE_FAIL: + case XBPS_STATE_REMOVE_FILE_HASH_FAIL: + case XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL: + /* Ignore errors due to not empty directories */ + if (xscd->err == ENOTEMPTY) + return; + + xbps_error_printf("%s\n", xscd->desc); + if (syslog_enabled) + syslog(LOG_ERR, "%s", xscd->desc); + break; + default: + xbps_dbg_printf(xhp, + "unknown state %d\n", xscd->state); + break; + } +} diff --git a/bin/xbps-install/transaction.c b/bin/xbps-install/transaction.c new file mode 100644 index 00000000..a8e14a70 --- /dev/null +++ b/bin/xbps-install/transaction.c @@ -0,0 +1,331 @@ +/*- + * Copyright (c) 2009-2012 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 +#include "compat.h" +#include "defs.h" +#include "../xbps-repo/defs.h" + +struct transaction { + prop_dictionary_t d; + prop_object_iterator_t iter; + uint32_t inst_pkgcnt; + uint32_t up_pkgcnt; + uint32_t cf_pkgcnt; + uint32_t rm_pkgcnt; +}; + +static void +show_missing_deps(prop_array_t a) +{ + size_t i; + const char *str; + + fprintf(stderr, "Unable to locate some required packages:\n"); + for (i = 0; i < prop_array_count(a); i++) { + prop_array_get_cstring_nocopy(a, i, &str); + fprintf(stderr, " %s\n", str); + } +} + +static void +show_conflicts(prop_array_t a) +{ + size_t i; + const char *str; + + fprintf(stderr, "Conflicting packages were found:\n"); + for (i = 0; i < prop_array_count(a); i++) { + prop_array_get_cstring_nocopy(a, i, &str); + fprintf(stderr, " %s\n", str); + } +} + +static void +show_actions(prop_object_iterator_t iter) +{ + prop_object_t obj; + const char *repoloc, *trans, *pkgname, *version, *fname, *arch; + + repoloc = trans = pkgname = version = fname = arch = NULL; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "transaction", &trans); + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + printf("%s %s %s", pkgname, trans, version); + prop_dictionary_get_cstring_nocopy(obj, "repository", &repoloc); + prop_dictionary_get_cstring_nocopy(obj, "filename", &fname); + prop_dictionary_get_cstring_nocopy(obj, "architecture", &arch); + if (repoloc && fname && arch) + printf(" %s %s %s", repoloc, fname, arch); + + printf("\n"); + } +} + +static void +show_package_list(prop_object_iterator_t iter, const char *match, size_t cols) +{ + prop_object_t obj; + const char *pkgver, *tract; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + prop_dictionary_get_cstring_nocopy(obj, "transaction", &tract); + if (strcmp(match, tract)) + continue; + print_package_line(pkgver, cols, false); + } + prop_object_iterator_reset(iter); + print_package_line(NULL, cols, true); +} + +static int +show_transaction_sizes(struct transaction *trans, size_t cols) +{ + uint64_t dlsize = 0, instsize = 0, rmsize = 0; + char size[8]; + + /* + * Show the list of packages that will be installed. + */ + if (prop_dictionary_get_uint32(trans->d, "total-install-pkgs", + &trans->inst_pkgcnt)) { + printf("%u package%s will be installed:\n", + trans->inst_pkgcnt, trans->inst_pkgcnt == 1 ? "" : "s"); + show_package_list(trans->iter, "install", cols); + printf("\n"); + } + if (prop_dictionary_get_uint32(trans->d, "total-update-pkgs", + &trans->up_pkgcnt)) { + printf("%u package%s will be updated:\n", + trans->up_pkgcnt, trans->up_pkgcnt == 1 ? "" : "s"); + show_package_list(trans->iter, "update", cols); + printf("\n"); + } + if (prop_dictionary_get_uint32(trans->d, "total-configure-pkgs", + &trans->cf_pkgcnt)) { + printf("%u package%s will be configured:\n", + trans->cf_pkgcnt, trans->cf_pkgcnt == 1 ? "" : "s"); + show_package_list(trans->iter, "configure", cols); + printf("\n"); + } + if (prop_dictionary_get_uint32(trans->d, "total-remove-pkgs", + &trans->rm_pkgcnt)) { + printf("%u package%s will be removed:\n", + trans->rm_pkgcnt, trans->rm_pkgcnt == 1 ? "" : "s"); + show_package_list(trans->iter, "remove", cols); + printf("\n"); + } + /* + * Show total download/installed/removed size for all required packages. + */ + printf("\n"); + prop_dictionary_get_uint64(trans->d, "total-download-size", &dlsize); + if (dlsize > 0) { + if (xbps_humanize_number(size, (int64_t)dlsize) == -1) { + xbps_error_printf("humanize_number returns " + "%s\n", strerror(errno)); + return -1; + } + printf("Total download size:\t%6s\n", size); + } + prop_dictionary_get_uint64(trans->d, "total-installed-size", + &instsize); + if (instsize > 0) { + if (xbps_humanize_number(size, (int64_t)instsize) == -1) { + xbps_error_printf("humanize_number2 returns " + "%s\n", strerror(errno)); + return -1; + } + printf("Total installed size:\t%6s\n", size); + } + prop_dictionary_get_uint64(trans->d, "total-removed-size", &rmsize); + if (rmsize > 0) { + if (xbps_humanize_number(size, (int64_t)rmsize) == -1) { + xbps_error_printf("humanize_number3 returns " + "%s\n", strerror(errno)); + return -1; + } + printf("Total freed size:\t%6s\n", size); + } + printf("\n"); + + return 0; +} + +int +dist_upgrade(struct xbps_handle *xhp, size_t cols, bool yes, bool drun) +{ + int rv = 0; + + if ((rv = xbps_transaction_update_packages(xhp)) != 0) { + if (rv == ENOENT) { + printf("No packages currently registered.\n"); + return 0; + } else if (rv == EEXIST) { + return 0; + } else if (rv == ENOTSUP) { + fprintf(stderr, "No repositories currently " + "registered!\n"); + return -1; + } else { + fprintf(stderr, "Unexpected error %s\n", + strerror(rv)); + return -1; + } + } + + return exec_transaction(xhp, cols, yes, drun); +} + +int +install_new_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall) +{ + int rv; + + if ((rv = xbps_transaction_install_pkg(xhp, pkg, reinstall)) != 0) { + if (rv == EEXIST) { + printf("Package `%s' already installed.\n", pkg); + } else if (rv == ENOENT) { + fprintf(stderr, "Unable to locate '%s' in " + "repository pool.\n", pkg); + } else if (rv == ENOTSUP) { + fprintf(stderr, "No repositories " + "currently registered!\n"); + } else { + fprintf(stderr, "Unexpected error: %s\n", + strerror(rv)); + rv = -1; + } + } + return rv; +} + +int +update_pkg(struct xbps_handle *xhp, const char *pkgname) +{ + int rv; + + rv = xbps_transaction_update_pkg(xhp, pkgname); + if (rv == EEXIST) + printf("Package '%s' is up to date.\n", pkgname); + else if (rv == ENOENT) + fprintf(stderr, "Package '%s' not found in " + "repository pool.\n", pkgname); + else if (rv == ENODEV) + printf("Package '%s' not installed.\n", pkgname); + else if (rv == ENOTSUP) + fprintf(stderr, "No repositories currently registered!\n"); + else if (rv != 0) { + fprintf(stderr, "Unexpected error: %s\n", strerror(rv)); + return -1; + } + return rv; +} + +int +exec_transaction(struct xbps_handle *xhp, size_t maxcols, bool yes, bool drun) +{ + prop_array_t mdeps, cflicts; + struct transaction *trans; + int rv = 0; + + trans = calloc(1, sizeof(*trans)); + if (trans == NULL) + return ENOMEM; + + if ((rv = xbps_transaction_prepare(xhp)) != 0) { + if (rv == ENODEV) { + mdeps = + prop_dictionary_get(xhp->transd, "missing_deps"); + /* missing packages */ + show_missing_deps(mdeps); + goto out; + } else if (rv == EAGAIN) { + /* conflicts */ + cflicts = prop_dictionary_get(xhp->transd, "conflicts"); + show_conflicts(cflicts); + goto out; + } + xbps_dbg_printf(xhp, "Empty transaction dictionary: %s\n", + strerror(errno)); + return rv; + } + xbps_dbg_printf(xhp, "Dictionary before transaction happens:\n"); + xbps_dbg_printf_append(xhp, "%s", + prop_dictionary_externalize(xhp->transd)); + + trans->d = xhp->transd; + trans->iter = xbps_array_iter_from_dict(xhp->transd, "packages"); + assert(trans->iter); + + /* + * dry-run mode, show what would be done but don't run anything. + */ + if (drun) { + show_actions(trans->iter); + goto out; + } + /* + * Show download/installed size for the transaction. + */ + if ((rv = show_transaction_sizes(trans, maxcols)) != 0) + goto out; + /* + * Ask interactively (if -y not set). + */ + if (!yes && !yesno("Do you want to continue?")) { + printf("Aborting!\n"); + goto out; + } + /* + * It's time to run the transaction! + */ + if ((rv = xbps_transaction_commit(xhp)) == 0) { + printf("\n %u installed, %u updated, " + "%u configured, %u removed.\n", trans->inst_pkgcnt, + trans->up_pkgcnt, trans->cf_pkgcnt + trans->inst_pkgcnt, + trans->rm_pkgcnt); + } +out: + if (trans->iter) + prop_object_iterator_release(trans->iter); + if (trans) + free(trans); + + return rv; +} diff --git a/bin/xbps-install/unpack_cb.c b/bin/xbps-install/unpack_cb.c new file mode 100644 index 00000000..e6c25f37 --- /dev/null +++ b/bin/xbps-install/unpack_cb.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2008-2011 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 "defs.h" + +void +unpack_progress_cb_verbose(struct xbps_handle *xhp, + struct xbps_unpack_cb_data *xpd, + void *cbdata) +{ + (void)xhp; + (void)cbdata; + + if (xpd->entry == NULL || xpd->entry_total_count <= 0) + return; + + printf("%s: unpacked %sfile `%s' (%" PRIi64 " bytes)\n", + xpd->pkgver, + xpd->entry_is_conf ? "configuration " : "", xpd->entry, + xpd->entry_size); +} + +void +unpack_progress_cb(struct xbps_handle *xhp, + struct xbps_unpack_cb_data *xpd, + void *cbdata) +{ + (void)xhp; + (void)cbdata; + + if (xpd->entry_total_count <= 0) + return; + + printf("%s: unpacked %zd of %zd files...\n", + xpd->pkgver, xpd->entry_extract_count, xpd->entry_total_count); + printf("\033[1A\033[K"); +} diff --git a/bin/xbps-install/util.c b/bin/xbps-install/util.c new file mode 100644 index 00000000..dccf7701 --- /dev/null +++ b/bin/xbps-install/util.c @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 2008-2012 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 "defs.h" + +size_t +get_maxcols(void) +{ + struct winsize ws; + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) == 0) + return ws.ws_col; + + return 80; +} + +void +print_package_line(const char *str, size_t maxcols, bool reset) +{ + static size_t cols; + static bool first; + + if (reset) { + cols = 0; + first = false; + return; + } + cols += strlen(str) + 4; + if (cols <= maxcols) { + if (first == false) { + printf(" "); + first = true; + } + } else { + printf("\n "); + cols = strlen(str) + 4; + } + printf("%s ", str); +} diff --git a/bin/xbps-pkgdb/Makefile b/bin/xbps-pkgdb/Makefile new file mode 100644 index 00000000..d4441d25 --- /dev/null +++ b/bin/xbps-pkgdb/Makefile @@ -0,0 +1,11 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-pkgdb +OBJS = main.o check.o check_pkg_automatic.o check_pkg_files.o +OBJS += check_pkg_requiredby.o check_pkg_rundeps.o +OBJS += check_pkg_symlinks.o check_pkg_unneeded.o + +#MAN = $(BIN).8 + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-pkgdb/check.c b/bin/xbps-pkgdb/check.c new file mode 100644 index 00000000..e0e4e2f7 --- /dev/null +++ b/bin/xbps-pkgdb/check.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2009-2012 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 "defs.h" + +struct checkpkg { + size_t totalpkgs; + size_t npkgs; + size_t nbrokenpkgs; + bool flush; +}; + +static int +cb_pkg_integrity(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *done) +{ + struct checkpkg *cpkg = arg; + const char *pkgname, *version; + bool flush = false; + + (void)done; + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + printf("[%zu/%zu] checking %s-%s ...\n", + cpkg->npkgs, cpkg->totalpkgs, pkgname, version); + if (check_pkg_integrity(xhp, obj, pkgname, false, &flush) != 0) + cpkg->nbrokenpkgs++; + else + printf("\033[1A\033[K"); + + + if (flush && !cpkg->flush) + cpkg->flush = flush; + cpkg->npkgs++; + return 0; +} + +int +check_pkg_integrity_all(struct xbps_handle *xhp) +{ + struct checkpkg cpkg; + int rv; + + memset(&cpkg, 0, sizeof(cpkg)); + /* force an update to get total pkg count */ + (void)xbps_pkgdb_update(xhp, false); + cpkg.totalpkgs = prop_array_count(xhp->pkgdb); + + (void)xbps_pkgdb_foreach_cb(xhp, cb_pkg_integrity, &cpkg); + if (cpkg.flush) { + if ((rv = xbps_pkgdb_update(xhp, true)) != 0) { + xbps_error_printf("failed to write pkgdb: %s\n", + strerror(rv)); + return rv; + } + } + printf("%zu package%s processed: %zu broken.\n", cpkg.npkgs, + cpkg.npkgs == 1 ? "" : "s", cpkg.nbrokenpkgs); + return 0; +} + +int +check_pkg_integrity(struct xbps_handle *xhp, + prop_dictionary_t pkgd, + const char *pkgname, + bool flush, + bool *setflush) +{ + prop_dictionary_t opkgd, propsd, filesd; + int rv = 0; + bool pkgdb_update = false, broken = false; + + propsd = filesd = opkgd = NULL; + + /* find real pkg by name */ + if (pkgd == NULL) { + opkgd = xbps_find_pkg_dict_installed(xhp, pkgname, false); + if (opkgd == NULL) { + /* find virtual pkg by name */ + opkgd = xbps_find_virtualpkg_dict_installed(xhp, + pkgname, false); + } + if (opkgd == NULL) { + printf("Package %s is not installed.\n", pkgname); + return 0; + } + } + /* + * Check for props.plist metadata file. + */ + propsd = xbps_dictionary_from_metadata_plist(xhp, pkgname, XBPS_PKGPROPS); + if (propsd == NULL) { + xbps_error_printf("%s: unexistent %s or invalid metadata " + "file.\n", pkgname, XBPS_PKGPROPS); + broken = true; + goto out; + } else if (prop_dictionary_count(propsd) == 0) { + xbps_error_printf("%s: incomplete %s metadata file.\n", + pkgname, XBPS_PKGPROPS); + broken = true; + goto out; + } + /* + * Check for files.plist metadata file. + */ + filesd = xbps_dictionary_from_metadata_plist(xhp, pkgname, XBPS_PKGFILES); + if (filesd == NULL) { + xbps_error_printf("%s: unexistent %s or invalid metadata " + "file.\n", pkgname, XBPS_PKGFILES); + broken = true; + goto out; + } + +#define RUN_PKG_CHECK(x, name, arg, arg2) \ +do { \ + rv = check_pkg_##name(x, pkgname, arg, arg2); \ + if (rv) \ + broken = true; \ + else if (rv == -1) { \ + xbps_error_printf("%s: the %s test " \ + "returned error!\n", pkgname, #name); \ + goto out; \ + } \ +} while (0) + + /* Execute pkg checks */ + RUN_PKG_CHECK(xhp, files, filesd, &pkgdb_update); + RUN_PKG_CHECK(xhp, symlinks, filesd, &pkgdb_update); + RUN_PKG_CHECK(xhp, rundeps, propsd, &pkgdb_update); + RUN_PKG_CHECK(xhp, requiredby, pkgd ? pkgd : opkgd, &pkgdb_update); + RUN_PKG_CHECK(xhp, autoinstall, pkgd ? pkgd : opkgd, &pkgdb_update); + RUN_PKG_CHECK(xhp, unneeded, pkgd ? pkgd : opkgd, &pkgdb_update); + + if (flush && pkgdb_update) { + if (!xbps_pkgdb_replace_pkgd(xhp, opkgd, pkgname, false, true)) { + rv = EINVAL; + goto out; + } + } + if (setflush && pkgdb_update) + *setflush = true; + +#undef RUN_PKG_CHECK + +out: + if (prop_object_type(filesd) == PROP_TYPE_DICTIONARY) + prop_object_release(filesd); + if (prop_object_type(propsd) == PROP_TYPE_DICTIONARY) + prop_object_release(propsd); + if (broken) + return 1; + + return rv; +} diff --git a/bin/xbps-pkgdb/check_pkg_automatic.c b/bin/xbps-pkgdb/check_pkg_automatic.c new file mode 100644 index 00000000..561ecb9c --- /dev/null +++ b/bin/xbps-pkgdb/check_pkg_automatic.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2011-2012 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 "defs.h" + +/* + * Checks package integrity of an installed package. + * The following task is accomplished in this file: + * + * o Check if package was installed manually, but currently + * other packages are depending on it. This package shall be + * changed to automatic mode, i.e installed as dependency of + * those packages. + * + * Returns 0 if test ran successfully, 1 otherwise and -1 on error. + */ +int +check_pkg_autoinstall(struct xbps_handle *xhp, + const char *pkgname, + void *arg, + bool *pkgdb_update) +{ + prop_dictionary_t pkgd = arg; + prop_array_t reqby; + bool autoinst = false; + + (void)xhp; + /* + * Check if package has been installed manually but any other + * package is currently depending on it; in that case the package + * must be in automatic mode. + */ + if (prop_dictionary_get_bool(pkgd, "automatic-install", &autoinst)) { + reqby = prop_dictionary_get(pkgd, "requiredby"); + if (reqby != NULL && prop_array_count(reqby) && !autoinst) { + /* pkg has reversedeps and was installed manually */ + prop_dictionary_set_bool(pkgd, + "automatic-install", true); + *pkgdb_update = true; + printf("%s: changed to automatic install mode.\n", + pkgname); + } + } + return 0; +} diff --git a/bin/xbps-pkgdb/check_pkg_files.c b/bin/xbps-pkgdb/check_pkg_files.c new file mode 100644 index 00000000..bf661c9c --- /dev/null +++ b/bin/xbps-pkgdb/check_pkg_files.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 2011-2012 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 "defs.h" + +/* + * Checks package integrity of an installed package. + * The following tasks are processed in that order: + * + * o Check for missing installed files. + * + * o Check the hash for all installed files, except + * configuration files (which is expected if they are modified). + * + * Return 0 if test ran successfully, 1 otherwise and -1 on error. + */ +int +check_pkg_files(struct xbps_handle *xhp, + const char *pkgname, + void *arg, + bool *pkgdb_update) +{ + prop_array_t array; + prop_object_t obj; + prop_object_iterator_t iter; + prop_dictionary_t pkg_filesd = arg; + const char *file, *sha256; + char *path; + int rv = 0; + bool mutable, broken = false, test_broken = false; + + (void)pkgdb_update; + + array = prop_dictionary_get(pkg_filesd, "files"); + if (array != NULL && prop_array_count(array) > 0) { + iter = xbps_array_iter_from_dict(pkg_filesd, "files"); + if (iter == NULL) + return -1; + + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + path = xbps_xasprintf("%s/%s", xhp->rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + return -1; + } + prop_dictionary_get_cstring_nocopy(obj, + "sha256", &sha256); + rv = xbps_file_hash_check(path, sha256); + switch (rv) { + case 0: + break; + case ENOENT: + xbps_error_printf("%s: unexistent file %s.\n", + pkgname, file); + test_broken = true; + break; + case ERANGE: + mutable = false; + prop_dictionary_get_bool(obj, + "mutable", &mutable); + if (!mutable) { + xbps_error_printf("%s: hash mismatch " + "for %s.\n", pkgname, file); + test_broken = true; + } + break; + default: + xbps_error_printf( + "%s: can't check `%s' (%s)\n", + pkgname, file, strerror(rv)); + break; + } + free(path); + } + prop_object_iterator_release(iter); + } + if (test_broken) { + xbps_error_printf("%s: files check FAILED.\n", pkgname); + test_broken = false; + broken = true; + } + + /* + * Check for missing configuration files. + */ + array = prop_dictionary_get(pkg_filesd, "conf_files"); + if (array != NULL && prop_array_count(array) > 0) { + iter = xbps_array_iter_from_dict(pkg_filesd, "conf_files"); + if (iter == NULL) + return -1; + + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + path = xbps_xasprintf("%s/%s", xhp->rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + return -1; + } + if ((rv = access(path, R_OK)) == -1) { + if (errno == ENOENT) { + xbps_error_printf( + "%s: unexistent file %s\n", + pkgname, file); + test_broken = true; + } else + xbps_error_printf( + "%s: can't check `%s' (%s)\n", + pkgname, file, + strerror(errno)); + } + free(path); + } + prop_object_iterator_release(iter); + } + if (test_broken) { + xbps_error_printf("%s: conf files check FAILED.\n", pkgname); + broken = true; + } + + return broken; +} diff --git a/bin/xbps-pkgdb/check_pkg_requiredby.c b/bin/xbps-pkgdb/check_pkg_requiredby.c new file mode 100644 index 00000000..fdaefebf --- /dev/null +++ b/bin/xbps-pkgdb/check_pkg_requiredby.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2011-2012 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 "defs.h" + +struct check_reqby_data { + prop_dictionary_t pkgd; + prop_array_t pkgd_reqby; + const char *pkgname; + const char *pkgver; + bool pkgd_reqby_alloc; +}; + +static int +check_reqby_pkg_cb(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *done) +{ + struct check_reqby_data *crd = arg; + prop_array_t curpkg_rdeps, provides; + prop_dictionary_t curpkg_propsd; + prop_string_t curpkgver; + const char *curpkgn; + + (void)done; + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &curpkgn); + /* skip same pkg */ + if (strcmp(curpkgn, crd->pkgname) == 0) + return 0; + + /* + * Internalize current pkg props dictionary from its + * installed metadata directory. + */ + curpkg_propsd = + xbps_dictionary_from_metadata_plist(xhp, curpkgn, XBPS_PKGPROPS); + if (curpkg_propsd == NULL) { + xbps_error_printf("%s: missing %s metadata file!\n", + curpkgn, XBPS_PKGPROPS); + return -1; + } + curpkg_rdeps = + prop_dictionary_get(curpkg_propsd, "run_depends"); + if (curpkg_rdeps == NULL) { + /* package has no rundeps, skip */ + prop_object_release(curpkg_propsd); + return 0; + } + /* + * Check for pkgpattern match with real packages... + */ + if (!xbps_match_pkgdep_in_array(curpkg_rdeps, crd->pkgver)) { + /* + * ... otherwise check if package provides any virtual + * package and is matched against any object in + * run_depends. + */ + provides = prop_dictionary_get(obj, "provides"); + if (provides == NULL) { + /* doesn't provide any virtual pkg */ + prop_object_release(curpkg_propsd); + return 0; + } + if (!xbps_match_any_virtualpkg_in_rundeps(curpkg_rdeps, + provides)) { + /* doesn't match any virtual pkg */ + prop_object_release(curpkg_propsd); + return 0; + } + } + crd->pkgd_reqby = prop_dictionary_get(crd->pkgd, "requiredby"); + curpkgver = prop_dictionary_get(curpkg_propsd, "pkgver"); + if (crd->pkgd_reqby != NULL) { + /* + * Now check that current pkgver has been registered into + * its requiredby array. + */ + if (xbps_match_string_in_array(crd->pkgd_reqby, + prop_string_cstring_nocopy(curpkgver))) { + /* + * Current package already requires our package, + * this is good so skip it. + */ + prop_object_release(curpkg_propsd); + return 0; + } + } else { + /* + * Missing requiredby array object, create it. + */ + crd->pkgd_reqby = prop_array_create(); + if (crd->pkgd_reqby == NULL) { + prop_object_release(curpkg_propsd); + return -1; + } + crd->pkgd_reqby_alloc = true; + } + /* + * Added pkgdep into pkg's requiredby array. + */ + if (!prop_array_add(crd->pkgd_reqby, curpkgver)) { + prop_object_release(curpkg_propsd); + return -1; + } + printf("%s: added missing requiredby entry for %s.\n\n", + crd->pkgver, prop_string_cstring_nocopy(curpkgver)); + prop_object_release(curpkg_propsd); + return 1; +} + +/* + * Removes unused entries in pkg's requiredby array. + */ +static bool +remove_stale_entries_in_reqby(struct xbps_handle *xhp, + struct check_reqby_data *crd) +{ + prop_array_t reqby; + prop_dictionary_t pkgd; + const char *str; + size_t i; + bool needs_update = false; + + reqby = prop_dictionary_get(crd->pkgd, "requiredby"); + if (reqby == NULL || prop_array_count(reqby) == 0) + return false; + + crd->pkgd_reqby = prop_dictionary_get(crd->pkgd, "requiredby"); + + for (i = 0; i < prop_array_count(reqby); i++) { + prop_array_get_cstring_nocopy(reqby, i, &str); + if ((pkgd = xbps_pkgdb_get_pkgd_by_pkgver(xhp, str)) != NULL) + continue; + printf("%s: found stale entry in requiredby `%s' (fixed)\n", + crd->pkgver, str); + if (xbps_remove_string_from_array(xhp, crd->pkgd_reqby, str)) + needs_update = true; + } + if (needs_update) { + prop_dictionary_set(crd->pkgd, "requiredby", crd->pkgd_reqby); + printf("%s: requiredby fix done!\n\n", crd->pkgver); + return true; + } + return false; +} + +/* + * Checks package integrity of an installed package. + * The following task is accomplished in this file: + * + * o Check for missing reverse dependencies (aka requiredby) + * entries in pkg's pkgdb dictionary. + * + * Returns 0 if test ran successfully, 1 otherwise and -1 on error. + */ +int +check_pkg_requiredby(struct xbps_handle *xhp, + const char *pkgname, + void *arg, + bool *pkgdb_update) +{ + prop_dictionary_t pkgd = arg; + struct check_reqby_data crd; + int rv; + + crd.pkgd = pkgd; + crd.pkgd_reqby = NULL; + crd.pkgd_reqby_alloc = false; + crd.pkgname = pkgname; + prop_dictionary_get_cstring_nocopy(pkgd, "pkgver", &crd.pkgver); + + /* missing reqby entries in pkgs */ + rv = xbps_pkgdb_foreach_cb(xhp, check_reqby_pkg_cb, &crd); + if (rv < 0) { + return rv; + } else if (rv == 1) { + *pkgdb_update = true; + prop_dictionary_set(pkgd, "requiredby", crd.pkgd_reqby); + if (crd.pkgd_reqby_alloc) + prop_object_release(crd.pkgd_reqby); + + printf("%s: requiredby fix done!\n\n", crd.pkgver); + } + /* remove stale entries in pkg's reqby */ + if (remove_stale_entries_in_reqby(xhp, &crd)) + *pkgdb_update = true; + + return 0; +} diff --git a/bin/xbps-pkgdb/check_pkg_rundeps.c b/bin/xbps-pkgdb/check_pkg_rundeps.c new file mode 100644 index 00000000..77b80c6f --- /dev/null +++ b/bin/xbps-pkgdb/check_pkg_rundeps.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2011-2012 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 "defs.h" + +/* + * Checks package integrity of an installed package. + * The following task is accomplished in this file: + * + * o Check for missing run time dependencies. + * + * Returns 0 if test ran successfully, 1 otherwise and -1 on error. + */ + +int +check_pkg_rundeps(struct xbps_handle *xhp, + const char *pkgname, + void *arg, + bool *pkgdb_update) +{ + prop_dictionary_t pkg_propsd = arg; + prop_object_t obj; + prop_object_iterator_t iter; + const char *reqpkg; + bool test_broken = false; + + (void)pkgdb_update; + + if (!xbps_pkg_has_rundeps(pkg_propsd)) + return 0; + + iter = xbps_array_iter_from_dict(pkg_propsd, "run_depends"); + if (iter == NULL) + return -1; + + while ((obj = prop_object_iterator_next(iter))) { + reqpkg = prop_string_cstring_nocopy(obj); + if (reqpkg == NULL) { + prop_object_iterator_release(iter); + return -1; + } + if (xbps_check_is_installed_pkg_by_pattern(xhp, reqpkg) <= 0) { + xbps_error_printf("%s: dependency not satisfied: %s\n", + pkgname, reqpkg); + test_broken = true; + } + } + prop_object_iterator_release(iter); + return test_broken; +} diff --git a/bin/xbps-pkgdb/check_pkg_symlinks.c b/bin/xbps-pkgdb/check_pkg_symlinks.c new file mode 100644 index 00000000..125c0d48 --- /dev/null +++ b/bin/xbps-pkgdb/check_pkg_symlinks.c @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2011-2012 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 +#include "defs.h" + +/* + * Checks package integrity of an installed package. + * The following task is accomplished in this file: + * + * o Check for target file in symlinks, so that we can check that + * they have not been modified. + * + * returns 0 if test ran successfully, 1 otherwise and -1 on error. + */ +int +check_pkg_symlinks(struct xbps_handle *xhp, + const char *pkgname, + void *arg, + bool *pkgdb_update) +{ + prop_array_t array; + prop_object_t obj; + prop_object_iterator_t iter; + prop_dictionary_t pkg_filesd = arg; + const char *file, *tgt = NULL; + char *path, *buf, *buf2, *buf3, *dname, *path_target; + bool broken = false, test_broken = false; + + (void)pkgdb_update; + + array = prop_dictionary_get(pkg_filesd, "links"); + if ((prop_object_type(array) == PROP_TYPE_ARRAY) && + prop_array_count(array) > 0) { + iter = xbps_array_iter_from_dict(pkg_filesd, "links"); + if (iter == NULL) + return -1; + + while ((obj = prop_object_iterator_next(iter))) { + if (!prop_dictionary_get_cstring_nocopy(obj, "target", &tgt)) + continue; + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + if (strcmp(tgt, "") == 0) { + xbps_warn_printf("%s: `%s' symlink with " + "empty target object!\n", pkgname, file); + continue; + } + path = xbps_xasprintf("%s/%s", xhp->rootdir, file); + if (path == NULL) + return -1; + + if ((buf = realpath(path, NULL)) == NULL) { + xbps_error_printf("%s: broken symlink `%s': " + "%s\n", pkgname, file, strerror(errno)); + test_broken = true; + continue; + } + if (strncmp(tgt, "../", 3) == 0) { + /* relative symlink target */ + dname = dirname(path); + buf2 = xbps_xasprintf("%s/%s", dname, tgt); + assert(buf2); + buf3 = realpath(buf2, NULL); + assert(buf3); + free(buf2); + path_target = buf3; + } else { + path_target = buf; + } + if (strcmp(buf, path_target)) { + xbps_error_printf("%s: modified symlink `%s' " + "points to: `%s' (shall be: `%s')\n", + pkgname, file, buf, path_target); + test_broken = true; + } + free(buf); + free(path); + if (buf3) + free(buf3); + + path = buf = buf2 = buf3 = NULL; + + } + prop_object_iterator_release(iter); + } + if (test_broken) { + xbps_error_printf("%s: symlinks check FAILED.\n", pkgname); + broken = true; + } + return broken; +} diff --git a/bin/xbps-pkgdb/check_pkg_unneeded.c b/bin/xbps-pkgdb/check_pkg_unneeded.c new file mode 100644 index 00000000..1a5a6292 --- /dev/null +++ b/bin/xbps-pkgdb/check_pkg_unneeded.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2012 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 "defs.h" + +/* + * Checks package integrity of an installed package. + * The following task is accomplished in this file: + * + * o Check if pkg dictionary from pkgdb contains "unneeded" objects, + * and remove them if that was true. + */ +int +check_pkg_unneeded(struct xbps_handle *xhp, + const char *pkgname, + void *arg, + bool *pkgdb_update) +{ + prop_dictionary_t pkgd = arg; + + (void)pkgname; + (void)xhp; + + if (prop_dictionary_get(pkgd, "remove-and-update")) { + *pkgdb_update = true; + prop_dictionary_remove(pkgd, "remove-and-update"); + } + if (prop_dictionary_get(pkgd, "transaction")) { + *pkgdb_update = true; + prop_dictionary_remove(pkgd, "transaction"); + } + return 0; +} diff --git a/bin/xbps-pkgdb/defs.h b/bin/xbps-pkgdb/defs.h new file mode 100644 index 00000000..88ad541d --- /dev/null +++ b/bin/xbps-pkgdb/defs.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2012 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. + */ + +#ifndef _XBPS_PKGDB_DEFS_H_ +#define _XBPS_PKGDB_DEFS_H_ + +#include +#include + +/* from check.c */ +int check_pkg_integrity(struct xbps_handle *, + prop_dictionary_t, + const char *, + bool, + bool *); +int check_pkg_integrity_all(struct xbps_handle *); + +#define CHECK_PKG_DECL(type) \ +int check_pkg_##type (struct xbps_handle *, const char *, void *, bool *) + +CHECK_PKG_DECL(autoinstall); +CHECK_PKG_DECL(unneeded); +CHECK_PKG_DECL(files); +CHECK_PKG_DECL(rundeps); +CHECK_PKG_DECL(symlinks); +CHECK_PKG_DECL(requiredby); + +#endif /* !_XBPS_PKGDB_DEFS_H_ */ diff --git a/bin/xbps-pkgdb/main.c b/bin/xbps-pkgdb/main.c new file mode 100644 index 00000000..46a02e4a --- /dev/null +++ b/bin/xbps-pkgdb/main.c @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2012 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 "defs.h" + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-pkgdb [OPTIONS] [PKGNAME...]\n\n" + "OPTIONS\n" + " -a --all Process all packages\n" + " -C --config Full path to configuration file\n" + " -d --debug Debug mode shown to stderr\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"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "aC:dhr:Vv"; + const struct option longopts[] = { + { "all", no_argument, NULL, 'a' }, + { "config", required_argument, NULL, 'C' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "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 *conffile = NULL, *rootdir = NULL; + int c, i, rv, flags = 0; + bool all = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'a': + all = true; + break; + case 'C': + conffile = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'h': + usage(false); + /* NOTREACHED */ + 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 */ + } + } + if (!all && (argc == optind)) + usage(true); + + memset(&xh, 0, sizeof(xh)); + xh.rootdir = rootdir; + xh.conffile = conffile; + xh.flags = flags; + + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + if (all) { + rv = check_pkg_integrity_all(&xh); + } else { + for (i = optind; i < argc; i++) { + rv = check_pkg_integrity(&xh, NULL, argv[i], + true, NULL); + if (rv != 0) + fprintf(stderr, "Failed to check " + "`%s': %s\n", argv[i], strerror(rv)); + } + } + + xbps_end(&xh); + exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/bin/xbps-query/Makefile b/bin/xbps-query/Makefile new file mode 100644 index 00000000..df838a5d --- /dev/null +++ b/bin/xbps-query/Makefile @@ -0,0 +1,9 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-query +OBJS = main.o list.o show-deps.o show-info-files.o +OBJS += ownedby.o search.o +#MAN = $(BIN).8 + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-query/defs.h b/bin/xbps-query/defs.h new file mode 100644 index 00000000..9a0b0b64 --- /dev/null +++ b/bin/xbps-query/defs.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2009-2012 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. + */ + +#ifndef _XBPS_QUERY_DEFS_H_ +#define _XBPS_QUERY_DEFS_H_ + +#include + +/* from show-deps.c */ +int show_pkg_deps(struct xbps_handle *, const char *); +int show_pkg_revdeps(struct xbps_handle *, const char *); +int repo_show_pkg_deps(struct xbps_handle *, const char *); + +/* from show-info-files.c */ +void show_pkg_info(prop_dictionary_t); +void show_pkg_info_one(prop_dictionary_t, const char *); +int show_pkg_info_from_metadir(struct xbps_handle *, const char *, + const char *); +int show_pkg_files(prop_dictionary_t); +int show_pkg_files_from_metadir(struct xbps_handle *, const char *); +int repo_show_pkg_files(struct xbps_handle *, const char *); +int repo_show_pkg_info(struct xbps_handle *, const char *, const char *); +int repo_show_pkg_namedesc(struct xbps_handle *, prop_object_t, void *, + bool *); + +/* from ownedby.c */ +int ownedby(struct xbps_handle *, int, char **); +int repo_ownedby(struct xbps_handle *, int, char **); + +/* From list.c */ +size_t get_maxcols(void); +int list_strings_sep_in_array(struct xbps_handle *, + prop_object_t, void *, bool *); +size_t find_longest_pkgver(struct xbps_handle *, prop_object_t); + +int list_pkgs_in_dict(struct xbps_handle *, prop_object_t, void *, bool *); +int list_manual_pkgs(struct xbps_handle *, prop_object_t, void *, bool *); +int list_orphans(struct xbps_handle *); +int list_pkgs_pkgdb(struct xbps_handle *); + +int repo_list(struct xbps_handle *); + +/* from search.c */ +int repo_search(struct xbps_handle *, int, char **); + +#endif /* !_XBPS_QUERY_DEFS_H_ */ diff --git a/bin/xbps-query/list.c b/bin/xbps-query/list.c new file mode 100644 index 00000000..1eb997a5 --- /dev/null +++ b/bin/xbps-query/list.c @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 2008-2012 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 "defs.h" + +struct list_pkgver_cb { + size_t pkgver_len; + size_t maxcols; +}; + +size_t +get_maxcols(void) +{ + struct winsize ws; + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) == 0) + return ws.ws_col; + + return 80; +} + +int +list_pkgs_in_dict(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *loop_done) +{ + struct list_pkgver_cb *lpc = arg; + const char *pkgver, *short_desc, *arch; + char *tmp = NULL, *out = NULL; + size_t i, len = 0; + bool chkarch; + + (void)xhp; + (void)loop_done; + + chkarch = prop_dictionary_get_cstring_nocopy(obj, "architecture", &arch); + if (chkarch && !xbps_pkg_arch_match(xhp, arch, NULL)) + return 0; + + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + prop_dictionary_get_cstring_nocopy(obj, "short_desc", &short_desc); + if (!pkgver && !short_desc) + return EINVAL; + + tmp = calloc(1, lpc->pkgver_len + 1); + assert(tmp); + memcpy(tmp, pkgver, lpc->pkgver_len); + for (i = strlen(tmp); i < lpc->pkgver_len; i++) + tmp[i] = ' '; + + tmp[i] = '\0'; + len = strlen(tmp) + strlen(short_desc) + 1; + if (len > lpc->maxcols) { + out = malloc(lpc->maxcols); + assert(out); + snprintf(out, lpc->maxcols-2, "%s %s", tmp, short_desc); + strncat(out, "...", lpc->maxcols); + printf("%s\n", out); + free(out); + } else { + printf("%s %s\n", tmp, short_desc); + } + free(tmp); + + return 0; +} + +int +list_manual_pkgs(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *loop_done) +{ + const char *pkgver; + bool automatic = false; + + (void)xhp; + (void)arg; + (void)loop_done; + + prop_dictionary_get_bool(obj, "automatic-install", &automatic); + if (automatic == false) { + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + printf("%s\n", pkgver); + } + + return 0; +} + +int +list_orphans(struct xbps_handle *xhp) +{ + prop_array_t orphans; + prop_object_iterator_t iter; + prop_object_t obj; + const char *pkgver; + + orphans = xbps_find_pkg_orphans(xhp, NULL); + if (orphans == NULL) + return EINVAL; + + if (prop_array_count(orphans) == 0) + return 0; + + iter = prop_array_iterator(orphans); + if (iter == NULL) + return ENOMEM; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + printf("%s\n", pkgver); + } + prop_object_iterator_release(iter); + + return 0; +} + +int +list_pkgs_pkgdb(struct xbps_handle *xhp) +{ + struct list_pkgver_cb lpc; + + lpc.pkgver_len = find_longest_pkgver(xhp, NULL); + lpc.maxcols = get_maxcols(); + + return xbps_pkgdb_foreach_cb(xhp, list_pkgs_in_dict, &lpc); +} + +static int +repo_list_uri_cb(struct xbps_handle *xhp, + struct xbps_rpool_index *rpi, + void *arg, + bool *done) +{ + (void)xhp; + (void)arg; + (void)done; + + printf("%s (%zu packages)\n", rpi->uri, + (size_t)prop_array_count(rpi->repo)); + + return 0; +} + +int +repo_list(struct xbps_handle *xhp) +{ + int rv; + + rv = xbps_rpool_foreach(xhp, repo_list_uri_cb, NULL); + if (rv != 0 && rv != ENOTSUP) { + fprintf(stderr, "Failed to initialize rpool: %s\n", + strerror(rv)); + return rv; + } + return 0; +} + +static int +_find_longest_pkgver_cb(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *loop_done) +{ + size_t *len = arg; + const char *pkgver; + + (void)xhp; + (void)loop_done; + + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + if (*len == 0 || strlen(pkgver) > *len) + *len = strlen(pkgver); + + return 0; +} + +size_t +find_longest_pkgver(struct xbps_handle *xhp, prop_object_t o) +{ + size_t len = 0; + + if (prop_object_type(o) == PROP_TYPE_ARRAY) + (void)xbps_callback_array_iter(xhp, o, + _find_longest_pkgver_cb, &len); + else + (void)xbps_pkgdb_foreach_cb(xhp, + _find_longest_pkgver_cb, &len); + + return len; +} + +int +list_strings_sep_in_array(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *loop_done) +{ + const char *sep = arg; + + (void)xhp; + (void)loop_done; + + printf("%s%s\n", sep ? sep : "", prop_string_cstring_nocopy(obj)); + + return 0; +} diff --git a/bin/xbps-query/main.c b/bin/xbps-query/main.c new file mode 100644 index 00000000..d3dd4ee2 --- /dev/null +++ b/bin/xbps-query/main.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2008-2012 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 "defs.h" + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-query [OPTIONS...] [PKGNAME]\n" + "\nOPTIONS\n" + " -C --config Full path to configuration file\n" + " -c --cachedir Full path to cachedir\n" + " -D --defrepo Default repository to be used if config not set\n" + " -d --debug Debug mode shown to stderr\n" + " -h --help Print help usage\n" + " -R --repository Enable repository mode\n" + " -r --rootdir Full path to rootdir\n" + " -V --version Show XBPS version\n" + " -v --verbose Verbose messages\n" + "\nMODE [only one mode may be specified]\n" + " -l --list-pkgs List available packages\n" + " -L --list-repos List working repositories\n" + " -m --list-manual-pkgs List packages installed explicitly\n" + " -M --list-orphans List package orphans\n" + " -o --ownedby PATTERN(s) Search for packages owning PATTERN(s)\n" + " -s --search PATTERN(s) Search for packages matching PATTERN(s)\n" + " -f --files Show files for PKGNAME\n" + " -p --property PROP,... Show properties for PKGNAME\n" + " -x --deps Show dependencies for PKGNAME\n" + " -X --revdeps Show reverse dependencies for PKGNAME\n"); + + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "C:c:D:dfhLlmMop:Rr:sVvXx"; + const struct option longopts[] = { + { "config", required_argument, NULL, 'C' }, + { "cachedir", required_argument, NULL, 'c' }, + { "defrepo", required_argument, NULL, 'D' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "list-repos", no_argument, NULL, 'L' }, + { "list-pkgs", no_argument, NULL, 'l' }, + { "list-manual-pkgs", no_argument, NULL, 'm' }, + { "list-orphans", no_argument, NULL, 'M' }, + { "ownedby", no_argument, NULL, 'o' }, + { "property", required_argument, NULL, 'p' }, + { "repository-mode", no_argument, NULL, 'R' }, + { "rootdir", required_argument, NULL, 'r' }, + { "search", no_argument, NULL, 's' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { "files", no_argument, NULL, 'f' }, + { "deps", no_argument, NULL, 'x' }, + { "revdeps", no_argument, NULL, 'X' }, + { NULL, 0, NULL, 0 }, + }; + struct xbps_handle xh; + const char *rootdir, *cachedir, *conffile, *props, *defrepo; + int c, flags, rv; + bool list_pkgs, list_repos, orphans, own; + bool list_manual, show_prop, show_files, show_deps, show_rdeps; + bool show, search, repo_mode, opmode; + + rootdir = cachedir = conffile = defrepo = props = NULL; + flags = rv = c = 0; + list_pkgs = list_repos = orphans = search = own = false; + list_manual = show_prop = show_files = search = false; + show = show_deps = show_rdeps = false; + repo_mode = opmode = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'C': + conffile = optarg; + break; + case 'c': + cachedir = optarg; + break; + case 'D': + defrepo = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'f': + show_files = opmode = true; + break; + case 'h': + usage(false); + /* NOTREACHED */ + case 'L': + list_repos = opmode = true; + break; + case 'l': + list_pkgs = opmode = true; + break; + case 'm': + list_manual = opmode = true; + break; + case 'M': + orphans = opmode = true; + break; + case 'o': + own = opmode = true; + break; + case 'p': + props = optarg; + show_prop = opmode = true; + break; + case 'R': + repo_mode = true; + break; + case 'r': + rootdir = optarg; + break; + case 's': + search = opmode = true; + break; + case 'v': + flags |= XBPS_FLAG_VERBOSE; + break; + case 'V': + printf("%s\n", XBPS_RELVER); + exit(EXIT_SUCCESS); + case 'x': + show_deps = opmode = true; + break; + case 'X': + show_rdeps = opmode = true; + break; + case '?': + usage(true); + /* NOTREACHED */ + } + } + if (!opmode && argc > optind) + show = true; + else if (!opmode && argc == optind) + usage(true); + + /* + * Initialize libxbps. + */ + memset(&xh, 0, sizeof(xh)); + xh.rootdir = rootdir; + xh.cachedir = cachedir; + xh.conffile = conffile; + xh.flags = flags; + xh.repository = defrepo; + + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + if (list_repos) { + /* list repositories */ + rv = repo_list(&xh); + + } else if (list_manual) { + /* list manual pkgs */ + rv = xbps_pkgdb_foreach_cb(&xh, list_manual_pkgs, NULL); + + } else if (list_pkgs) { + /* list available pkgs */ + rv = list_pkgs_pkgdb(&xh); + + } else if (orphans) { + /* list pkg orphans */ + rv = list_orphans(&xh); + + } else if (own) { + /* ownedby mode */ + if (repo_mode) + rv = repo_ownedby(&xh, argc - optind, argv + optind); + else + rv = ownedby(&xh, argc - optind, argv + optind); + + } else if (search) { + /* search mode */ + rv = repo_search(&xh, argc - optind, argv + optind); + + } else if (show || show_prop) { + /* show mode */ + if (repo_mode) + rv = repo_show_pkg_info(&xh, argv[optind], props); + else + rv = show_pkg_info_from_metadir(&xh, + argv[optind], props); + + } else if (show_files) { + /* show-files mode */ + if (repo_mode) + rv = repo_show_pkg_files(&xh, argv[optind]); + else + rv = show_pkg_files_from_metadir(&xh, argv[optind]); + + } else if (show_deps) { + /* show-deps mode */ + if (repo_mode) + rv = repo_show_pkg_deps(&xh, argv[optind]); + else + rv = show_pkg_deps(&xh, argv[optind]); + + } else if (show_rdeps) { + /* show-rdeps mode */ + rv = show_pkg_revdeps(&xh, argv[optind]); + + } + + xbps_end(&xh); + exit(rv); +} diff --git a/bin/xbps-query/ownedby.c b/bin/xbps-query/ownedby.c new file mode 100644 index 00000000..6e582271 --- /dev/null +++ b/bin/xbps-query/ownedby.c @@ -0,0 +1,209 @@ +/*- + * Copyright (c) 2010-2012 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 "defs.h" + +static int +match_files_by_pattern(prop_dictionary_t pkg_filesd, + prop_dictionary_keysym_t key, + int npatterns, + char **patterns, + const char *pkgname) +{ + prop_object_iterator_t iter; + prop_array_t array; + prop_object_t obj; + const char *keyname, *filestr, *typestr; + int i; + + keyname = prop_dictionary_keysym_cstring_nocopy(key); + array = prop_dictionary_get_keysym(pkg_filesd, key); + + if (strcmp(keyname, "files") == 0) + typestr = "regular file"; + else if (strcmp(keyname, "dirs") == 0) + typestr = "directory"; + else if (strcmp(keyname, "links") == 0) + typestr = "link"; + else + typestr = "configuration file"; + + iter = prop_array_iterator(array); + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &filestr); + for (i = 0; i < npatterns; i++) { + if ((xbps_pkgpattern_match(filestr, patterns[i])) || + (strcmp(filestr, patterns[i]) == 0)) + printf("%s: %s (%s)\n", pkgname, filestr, typestr); + } + } + prop_object_iterator_release(iter); + + return 0; +} + +int +ownedby(struct xbps_handle *xhp, int npatterns, char **patterns) +{ + prop_dictionary_t pkg_filesd; + prop_array_t files_keys; + DIR *dirp; + struct dirent *dp; + char *path; + int rv = 0; + unsigned int i, count; + + path = xbps_xasprintf("%s/metadata", xhp->metadir); + if (path == NULL) + return -1; + + if ((dirp = opendir(path)) == NULL) { + free(path); + return -1; + } + + while ((dp = readdir(dirp)) != NULL) { + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0)) + continue; + + pkg_filesd = xbps_dictionary_from_metadata_plist(xhp, + dp->d_name, XBPS_PKGFILES); + if (pkg_filesd == NULL) { + if (errno == ENOENT) + continue; + rv = -1; + break; + } + files_keys = prop_dictionary_all_keys(pkg_filesd); + count = prop_array_count(files_keys); + for (i = 0; i < count; i++) { + rv = match_files_by_pattern(pkg_filesd, + prop_array_get(files_keys, i), + npatterns, patterns, dp->d_name); + if (rv == -1) + break; + } + prop_object_release(files_keys); + prop_object_release(pkg_filesd); + if (rv == -1) + break; + } + (void)closedir(dirp); + free(path); + + return rv; +} + +struct ffdata { + int npatterns; + char **patterns; + const char *repouri; +}; + +static void +repo_match_files_by_pattern(struct xbps_handle *xhp, + prop_dictionary_t pkg_filesd, + struct ffdata *ffd) +{ + prop_array_t array; + const char *filestr, *pkgver, *arch; + size_t i; + int x; + + prop_dictionary_get_cstring_nocopy(pkg_filesd, "architecture", &arch); + if (!xbps_pkg_arch_match(xhp, arch, NULL)) + return; + + array = prop_dictionary_get(pkg_filesd, "files"); + for (i = 0; i < prop_array_count(array); i++) { + prop_array_get_cstring_nocopy(array, i, &filestr); + for (x = 0; x < ffd->npatterns; x++) { + if ((xbps_pkgpattern_match(filestr, ffd->patterns[x])) || + (strcmp(filestr, ffd->patterns[x]) == 0)) { + prop_dictionary_get_cstring_nocopy(pkg_filesd, + "pkgver", &pkgver); + printf("%s: %s (%s)\n", + pkgver, filestr, ffd->repouri); + } + } + } +} + +static int +repo_ownedby_cb(struct xbps_handle *xhp, + struct xbps_rpool_index *rpi, + void *arg, + bool *done) +{ + prop_array_t idxfiles; + struct ffdata *ffd = arg; + char *plist; + unsigned int i; + + (void)done; + + if ((plist = xbps_pkg_index_files_plist(xhp, rpi->uri)) == NULL) + return ENOMEM; + + if ((idxfiles = prop_array_internalize_from_zfile(plist)) == NULL) { + free(plist); + if (errno == ENOENT) { + fprintf(stderr, "%s: index-files missing! " + "ignoring...\n", rpi->uri); + return 0; + } + return errno; + } + free(plist); + ffd->repouri = rpi->uri; + + for (i = 0; i < prop_array_count(idxfiles); i++) + repo_match_files_by_pattern(xhp, + prop_array_get(idxfiles, i), ffd); + + prop_object_release(idxfiles); + return 0; +} + +int +repo_ownedby(struct xbps_handle *xhp, int npatterns, char **patterns) +{ + struct ffdata ffd; + + ffd.npatterns = npatterns; + ffd.patterns = patterns; + + return xbps_rpool_foreach(xhp, repo_ownedby_cb, &ffd); +} diff --git a/bin/xbps-query/search.c b/bin/xbps-query/search.c new file mode 100644 index 00000000..8413009d --- /dev/null +++ b/bin/xbps-query/search.c @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 2008-2012 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. + */ + +#ifdef HAVE_STRCASESTR +# define _GNU_SOURCE /* for strcasestr(3) */ +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "defs.h" + +struct repo_search_data { + int npatterns; + char **patterns; + void *arg; + size_t pkgver_len; + size_t maxcols; +}; + +static int +repo_longest_pkgver(struct xbps_handle *xhp, + struct xbps_rpool_index *rpi, + void *arg, + bool *done) +{ + size_t *len = arg, olen = 0; + + (void)done; + + if (*len == 0) { + *len = find_longest_pkgver(xhp, rpi->repo); + return 0; + } + olen = find_longest_pkgver(xhp, rpi->repo); + if (olen > *len) + *len = olen; + + return 0; +} + +static size_t +repo_find_longest_pkgver(struct xbps_handle *xhp) +{ + size_t len = 0; + + xbps_rpool_foreach(xhp, repo_longest_pkgver, &len); + + return len; +} + +static int +show_pkg_namedesc(struct xbps_handle *xhp, + prop_object_t obj, + void *arg, + bool *loop_done) +{ + struct repo_search_data *rsd = arg; + const char *pkgver, *pkgname, *desc, *arch, *inststr; + char *tmp = NULL, *out = NULL; + size_t x, len; + int i; + + (void)xhp; + (void)loop_done; + + prop_dictionary_get_cstring_nocopy(obj, "architecture", &arch); + if (!xbps_pkg_arch_match(xhp, arch, NULL)) + return 0; + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); + prop_dictionary_get_cstring_nocopy(obj, "short_desc", &desc); + + for (i = 0; i < rsd->npatterns; i++) { + if ((xbps_pkgpattern_match(pkgver, rsd->patterns[i]) == 1) || + (xbps_pkgpattern_match(desc, rsd->patterns[i]) == 1) || + (strcasecmp(pkgname, rsd->patterns[i]) == 0) || + (strcasestr(pkgver, rsd->patterns[i])) || + (strcasestr(desc, rsd->patterns[i]))) { + tmp = calloc(1, rsd->pkgver_len + 1); + assert(tmp); + memcpy(tmp, pkgver, rsd->pkgver_len); + for (x = strlen(tmp); x < rsd->pkgver_len; x++) + tmp[x] = ' '; + + tmp[x] = '\0'; + if (xbps_pkgdb_get_pkgd_by_pkgver(xhp, pkgver)) + inststr = "[*]"; + else + inststr = "[-]"; + + len = strlen(inststr) + strlen(tmp) + strlen(desc) + 1; + if (len > rsd->maxcols) { + out = malloc(rsd->maxcols+1); + assert(out); + snprintf(out, rsd->maxcols-3, "%s %s %s", + inststr, tmp, desc); + strncat(out, "...", rsd->maxcols); + out[rsd->maxcols+1] = '\0'; + printf("%s\n", out); + free(out); + } else { + printf("%s %s %s\n", inststr, tmp, desc); + } + } + } + + return 0; +} +static int +repo_search_pkgs_cb(struct xbps_handle *xhp, + struct xbps_rpool_index *rpi, + void *arg, + bool *done) +{ + struct repo_search_data *rsd = arg; + (void)done; + + (void)xbps_callback_array_iter(xhp, rpi->repo, show_pkg_namedesc, rsd); + + return 0; +} + +int +repo_search(struct xbps_handle *xhp, int npatterns, char **patterns) +{ + struct repo_search_data rsd; + int rv; + + rsd.npatterns = npatterns; + rsd.patterns = patterns; + rsd.pkgver_len = repo_find_longest_pkgver(xhp); + rsd.maxcols = get_maxcols(); + + rv = xbps_rpool_foreach(xhp, repo_search_pkgs_cb, &rsd); + if (rv != 0 && rv != ENOTSUP) + fprintf(stderr, "Failed to initialize rpool: %s\n", + strerror(rv)); + + return rv; +} diff --git a/bin/xbps-query/show-deps.c b/bin/xbps-query/show-deps.c new file mode 100644 index 00000000..489245a5 --- /dev/null +++ b/bin/xbps-query/show-deps.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2009-2012 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 "defs.h" + +int +show_pkg_deps(struct xbps_handle *xhp, const char *pkgname) +{ + prop_dictionary_t propsd; + int rv = 0; + + assert(pkgname != NULL); + + /* + * Check for props.plist metadata file. + */ + propsd = xbps_dictionary_from_metadata_plist(xhp, + pkgname, XBPS_PKGPROPS); + if (propsd == NULL) + return errno; + + rv = xbps_callback_array_iter_in_dict(xhp, propsd, "run_depends", + list_strings_sep_in_array, NULL); + prop_object_release(propsd); + + return rv; +} + +int +show_pkg_revdeps(struct xbps_handle *xhp, const char *pkgname) +{ + prop_dictionary_t pkgd; + int rv = 0; + + pkgd = xbps_find_virtualpkg_dict_installed(xhp, pkgname, false); + if (pkgd == NULL) { + pkgd = xbps_find_pkg_dict_installed(xhp, pkgname, false); + if (pkgd == NULL) + return 0; + } + rv = xbps_callback_array_iter_in_dict(xhp, pkgd, "requiredby", + list_strings_sep_in_array, NULL); + + return rv; +} + +int +repo_show_pkg_deps(struct xbps_handle *xhp, const char *pattern) +{ + prop_dictionary_t pkgd; + + if (xbps_pkgpattern_version(pattern)) + pkgd = xbps_rpool_find_pkg(xhp, pattern, true, false); + else + pkgd = xbps_rpool_find_pkg(xhp, pattern, false, true); + + if (pkgd == NULL) + return errno; + + (void)xbps_callback_array_iter_in_dict(xhp, pkgd, + "run_depends", list_strings_sep_in_array, NULL); + + return 0; +} diff --git a/bin/xbps-query/show-info-files.c b/bin/xbps-query/show-info-files.c new file mode 100644 index 00000000..39ce9164 --- /dev/null +++ b/bin/xbps-query/show-info-files.c @@ -0,0 +1,259 @@ +/*- + * Copyright (c) 2008-2012 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 "defs.h" + +static void +print_value_obj(const char *keyname, prop_object_t obj, bool raw) +{ + const char *value; + size_t i; + char size[8]; + + switch (prop_object_type(obj)) { + case PROP_TYPE_STRING: + if (!raw) + printf("%s: ", keyname); + printf("%s\n", prop_string_cstring_nocopy(obj)); + break; + case PROP_TYPE_NUMBER: + if (!raw) + printf("%s: ", keyname); + if (xbps_humanize_number(size, + (int64_t)prop_number_unsigned_integer_value(obj)) == -1) + printf("%ju\n", + prop_number_unsigned_integer_value(obj)); + else + printf("%s\n", size); + break; + case PROP_TYPE_BOOL: + if (!raw) + printf("%s: ", keyname); + printf("%s\n", prop_bool_true(obj) ? "yes" : "no"); + break; + case PROP_TYPE_ARRAY: + if (!raw) + printf("%s:\n", keyname); + for (i = 0; i < prop_array_count(obj); i++) { + prop_array_get_cstring_nocopy(obj, i, &value); + printf("%s%s%s", !raw ? "\t" : "", value, + !raw ? "\n" : " "); + } + if (raw) + printf("\n"); + break; + default: + xbps_warn_printf("unknown obj type (key %s)\n", + keyname); + break; + } +} + +void +show_pkg_info_one(prop_dictionary_t d, const char *keys) +{ + prop_object_t obj; + char *key, *p, *saveptr; + + if (strchr(keys, ',') == NULL) { + obj = prop_dictionary_get(d, keys); + if (obj == NULL) + return; + print_value_obj(keys, obj, true); + return; + } + key = strdup(keys); + if (key == NULL) + abort(); + for ((p = strtok_r(key, ",", &saveptr)); p; + (p = strtok_r(NULL, ",", &saveptr))) { + obj = prop_dictionary_get(d, p); + if (obj == NULL) + continue; + print_value_obj(p, obj, true); + } + free(key); +} + +void +show_pkg_info(prop_dictionary_t dict) +{ + prop_array_t all_keys; + prop_object_t obj, keysym; + const char *keyname; + size_t i; + + all_keys = prop_dictionary_all_keys(dict); + for (i = 0; i < prop_array_count(all_keys); i++) { + keysym = prop_array_get(all_keys, i); + keyname = prop_dictionary_keysym_cstring_nocopy(keysym); + obj = prop_dictionary_get_keysym(dict, keysym); + /* ignore run_depends, it's shown via 'show-deps' */ + if (strcmp(keyname, "run_depends") == 0) + continue; + + print_value_obj(keyname, obj, false); + } + prop_object_release(all_keys); +} + +int +show_pkg_files(prop_dictionary_t filesd) +{ + prop_array_t array, allkeys; + prop_object_t obj; + prop_dictionary_keysym_t ksym; + const char *keyname, *file; + size_t i, x; + + allkeys = prop_dictionary_all_keys(filesd); + for (i = 0; i < prop_array_count(allkeys); i++) { + ksym = prop_array_get(allkeys, i); + keyname = prop_dictionary_keysym_cstring_nocopy(ksym); + if (strcmp(keyname, "dirs") == 0) + continue; + + array = prop_dictionary_get(filesd, keyname); + if (array == NULL || prop_array_count(array) == 0) + continue; + + for (x = 0; x < prop_array_count(array); x++) { + obj = prop_array_get(array, x); + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + printf("%s", file); + if (prop_dictionary_get_cstring_nocopy(obj, + "target", &file)) + printf(" -> %s", file); + + printf("\n"); + } + } + prop_object_release(allkeys); + + return 0; +} + +int +show_pkg_info_from_metadir(struct xbps_handle *xhp, + const char *pkgname, + const char *option) +{ + prop_dictionary_t d, pkgdb_d; + const char *instdate, *pname; + bool autoinst; + + d = xbps_dictionary_from_metadata_plist(xhp, pkgname, XBPS_PKGPROPS); + if (d == NULL) + return EINVAL; + + prop_dictionary_get_cstring_nocopy(d, "pkgname", &pname); + pkgdb_d = xbps_pkgdb_get_pkgd(xhp, pname, false); + if (pkgdb_d == NULL) { + prop_object_release(d); + return EINVAL; + } + if (prop_dictionary_get_cstring_nocopy(pkgdb_d, + "install-date", &instdate)) + prop_dictionary_set_cstring_nocopy(d, "install-date", + instdate); + + if (prop_dictionary_get_bool(pkgdb_d, "automatic-install", &autoinst)) + prop_dictionary_set_bool(d, "automatic-install", autoinst); + + if (option == NULL) + show_pkg_info(d); + else + show_pkg_info_one(d, option); + + prop_object_release(d); + return 0; +} + +int +show_pkg_files_from_metadir(struct xbps_handle *xhp, const char *pkgname) +{ + prop_dictionary_t d; + int rv = 0; + + d = xbps_dictionary_from_metadata_plist(xhp, pkgname, XBPS_PKGFILES); + if (d == NULL) + return EINVAL; + + rv = show_pkg_files(d); + prop_object_release(d); + + return rv; +} + +int +repo_show_pkg_info(struct xbps_handle *xhp, + const char *pattern, + const char *option) +{ + prop_dictionary_t pkgd; + + if (xbps_pkgpattern_version(pattern)) + pkgd = xbps_rpool_find_pkg(xhp, pattern, true, false); + else + pkgd = xbps_rpool_find_pkg(xhp, pattern, false, true); + + if (pkgd == NULL) + return errno; + + if (option) + show_pkg_info_one(pkgd, option); + else + show_pkg_info(pkgd); + + return 0; +} + +int +repo_show_pkg_files(struct xbps_handle *xhp, const char *pkg) +{ + prop_dictionary_t pkgd; + + pkgd = xbps_rpool_dictionary_metadata_plist(xhp, pkg, + "./files.plist"); + if (pkgd == NULL) { + if (errno != ENOTSUP && errno != ENOENT) { + fprintf(stderr, "Unexpected error: %s\n", + strerror(errno)); + return errno; + } + } + + return show_pkg_files(pkgd); +} diff --git a/bin/xbps-reconfigure/Makefile b/bin/xbps-reconfigure/Makefile new file mode 100644 index 00000000..eb32edb8 --- /dev/null +++ b/bin/xbps-reconfigure/Makefile @@ -0,0 +1,8 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-reconfigure +OBJS = main.o ../xbps-install/state_cb.o +#MAN = $(BIN).8 + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-reconfigure/main.c b/bin/xbps-reconfigure/main.c new file mode 100644 index 00000000..aa0cb6dc --- /dev/null +++ b/bin/xbps-reconfigure/main.c @@ -0,0 +1,134 @@ +/*- + * Copyright (c) 2012 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 "../xbps-bin/defs.h" + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-reconfigure [OPTIONS] [PKGNAME...]\n\n" + "OPTIONS\n" + " -a --all Process all packages\n" + " -C --config Full path to configuration file\n" + " -d --debug Debug mode shown to stderr\n" + " -f --force Force reconfiguration\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"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "aC:dfhr:Vv"; + const struct option longopts[] = { + { "all", no_argument, NULL, 'a' }, + { "config", required_argument, NULL, 'C' }, + { "debug", no_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "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 *conffile = NULL, *rootdir = NULL; + int c, i, rv, flags = 0; + bool all = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'a': + all = true; + break; + case 'C': + conffile = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'f': + flags |= XBPS_FLAG_FORCE_CONFIGURE; + break; + case 'h': + usage(false); + /* NOTREACHED */ + 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 */ + } + } + if (!all && (argc == optind)) + usage(true); + + memset(&xh, 0, sizeof(xh)); + xh.state_cb = state_cb; + xh.rootdir = rootdir; + xh.conffile = conffile; + xh.flags = flags; + + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + if (all) { + rv = xbps_configure_packages(&xh, true); + } else { + for (i = optind; i < argc; i++) { + rv = xbps_configure_pkg(&xh, argv[i], + true, false, true); + if (rv != 0) + fprintf(stderr, "Failed to reconfigure " + "`%s': %s\n", argv[i], strerror(rv)); + } + } + + xbps_end(&xh); + exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/bin/xbps-remove/Makefile b/bin/xbps-remove/Makefile new file mode 100644 index 00000000..2884eee7 --- /dev/null +++ b/bin/xbps-remove/Makefile @@ -0,0 +1,12 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-remove +OBJS = main.o +OBJS += ../xbps-install/question.o +OBJS += ../xbps-install/util.o +OBJS += ../xbps-install/transaction.o +OBJS += ../xbps-install/state_cb.o +#MAN = $(BIN).8 + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-remove/main.c b/bin/xbps-remove/main.c new file mode 100644 index 00000000..604e3674 --- /dev/null +++ b/bin/xbps-remove/main.c @@ -0,0 +1,347 @@ +/*- + * Copyright (c) 2008-2012 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 +#include + +#include +#include "../xbps-install/defs.h" + +static struct xbps_handle xh; + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-remove [OPTIONS] [PKGNAME...]\n\n" + "OPTIONS\n" + " -C --config Full path to configuration file\n" + " -c --cachedir Full path to cachedir\n" + " -d --debug Debug mode shown to stderr\n" + " -f --force Force package files removal\n" + " -h --help Print help usage\n" + " -i --ignore-revdeps Ignore reverse deps\n" + " -n --dry-run Dry-run mode\n" + " -O --clean-cache Remove obsolete packages in cachedir\n" + " -o --remove-orphans Remove package orphans\n" + " -p --print-format Print format for dry-run mode\n" + " -R --recursive Recursively remove dependencies\n" + " -r --rootdir Full path to rootdir\n" + " -v --verbose Verbose messages\n" + " -y --yes Assume yes to all questions\n" + " -V --version Show XBPS version\n"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void __attribute__((noreturn)) +cleanup_sighandler(int signum) +{ + xbps_end(&xh); + _exit(signum); +} + +static int +cachedir_clean(struct xbps_handle *xhp) +{ + prop_dictionary_t pkg_propsd, repo_pkgd; + DIR *dirp; + struct dirent *dp; + const char *pkgver, *rsha256; + char *binpkg, *ext; + int rv = 0; + + if ((dirp = opendir(xhp->cachedir)) == NULL) + return 0; + + while ((dp = readdir(dirp)) != NULL) { + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0)) + continue; + + /* only process xbps binary packages, ignore something else */ + if ((ext = strrchr(dp->d_name, '.')) == NULL) + continue; + if (strcmp(ext, ".xbps")) { + printf("ignoring unknown file: %s\n", dp->d_name); + continue; + } + /* Internalize props.plist dictionary from binary pkg */ + binpkg = xbps_xasprintf("%s/%s", xhp->cachedir, dp->d_name); + assert(binpkg != NULL); + pkg_propsd = xbps_dictionary_metadata_plist_by_url(binpkg, + "./props.plist"); + if (pkg_propsd == NULL) { + xbps_error_printf("Failed to read from %s: %s\n", + dp->d_name, strerror(errno)); + free(binpkg); + rv = errno; + break; + } + prop_dictionary_get_cstring_nocopy(pkg_propsd, "pkgver", &pkgver); + /* + * Remove binary pkg if it's not registered in any repository + * or if hash doesn't match. + */ + repo_pkgd = xbps_rpool_find_pkg_exact(xhp, pkgver); + if (repo_pkgd) { + prop_dictionary_get_cstring_nocopy(repo_pkgd, + "filename-sha256", &rsha256); + if (xbps_file_hash_check(binpkg, rsha256) == ERANGE) { + printf("Removed %s from cachedir (sha256 mismatch)\n", + dp->d_name); + if (unlink(binpkg) == -1) + fprintf(stderr, "Failed to remove " + "`%s': %s\n", binpkg, + strerror(errno)); + } + free(binpkg); + continue; + } + printf("Removed %s from cachedir (obsolete)\n", dp->d_name); + if (unlink(binpkg) == -1) + fprintf(stderr, "Failed to remove `%s': %s\n", + binpkg, strerror(errno)); + free(binpkg); + } + closedir(dirp); + return rv; +} + +static int +remove_pkg(struct xbps_handle *xhp, const char *pkgname, size_t cols, + bool recursive) +{ + prop_dictionary_t pkgd; + prop_array_t reqby; + const char *pkgver; + size_t x; + int rv; + + rv = xbps_transaction_remove_pkg(xhp, pkgname, recursive); + if (rv == EEXIST) { + /* pkg has revdeps */ + pkgd = xbps_find_pkg_dict_installed(xhp, pkgname, false); + prop_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + reqby = prop_dictionary_get(pkgd, "requiredby"); + printf("WARNING: %s IS REQUIRED BY %u PACKAGE%s:\n\n", + pkgver, prop_array_count(reqby), + prop_array_count(reqby) > 1 ? "S" : ""); + for (x = 0; x < prop_array_count(reqby); x++) { + prop_array_get_cstring_nocopy(reqby, x, &pkgver); + print_package_line(pkgver, cols, false); + } + printf("\n\n"); + print_package_line(NULL, cols, true); + return rv; + } else if (rv == ENOENT) { + printf("Package `%s' is not currently installed.\n", pkgname); + return 0; + } else if (rv != 0) { + xbps_error_printf("Failed to queue `%s' for removing: %s\n", + pkgname, strerror(rv)); + return rv; + } + + return 0; +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "C:c:dfhinOop:Rr:vVy"; + struct option longopts[] = { + { "config", required_argument, NULL, 'C' }, + { "cachedir", required_argument, NULL, 'c' }, + { "debug", no_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "ignore-revdeps", no_argument, NULL, 'i' }, + { "dry-run", no_argument, NULL, 'n' }, + { "clean-cache", no_argument, NULL, 'O' }, + { "remove-orphans", no_argument, NULL, 'o' }, + { "print-format", required_argument, NULL, 'p' }, + { "recursive", no_argument, NULL, 'R' }, + { "rootdir", required_argument, NULL, 'r' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "yes", no_argument, NULL, 'y' }, + { NULL, 0, NULL, 0 } + }; + struct sigaction sa; + const char *rootdir, *cachedir, *conffile, *pformat; + int i, c, flags, rv; + bool yes, drun, recursive, ignore_revdeps, clean_cache; + bool orphans, reqby_force; + size_t maxcols; + + rootdir = cachedir = conffile = pformat = NULL; + flags = rv = 0; + drun = recursive = ignore_revdeps = clean_cache = false; + reqby_force = yes = orphans = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'C': + conffile = optarg; + break; + case 'c': + cachedir = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'f': + flags |= XBPS_FLAG_FORCE_REMOVE_FILES; + break; + case 'h': + usage(false); + /* NOTREACHED */ + case 'i': + ignore_revdeps = true; + break; + case 'n': + drun = true; + break; + case 'O': + clean_cache = true; + break; + case 'o': + orphans = true; + break; + case 'p': + pformat = optarg; + break; + case 'R': + recursive = true; + break; + case 'r': + rootdir = optarg; + break; + case 'v': + flags |= XBPS_FLAG_VERBOSE; + break; + case 'V': + printf("%s\n", XBPS_RELVER); + exit(EXIT_SUCCESS); + case 'y': + yes = true; + break; + case '?': + default: + usage(true); + /* NOTREACHED */ + } + } + if (!clean_cache && !orphans && (argc == optind)) + usage(true); + + /* + * Initialize libxbps. + */ + memset(&xh, 0, sizeof(xh)); + xh.state_cb = state_cb; + xh.rootdir = rootdir; + xh.cachedir = cachedir; + xh.conffile = conffile; + xh.flags = flags; + + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + /* + * Register a signal handler to clean up resources used by libxbps. + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanup_sighandler; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + maxcols = get_maxcols(); + /* + * Check that we have write permission on rootdir, metadir + * and cachedir. + */ + if ((!drun && ((access(xh.rootdir, W_OK) == -1) || + (access(xh.metadir, W_OK) == -1) || + (access(xh.cachedir, W_OK) == -1)))) { + if (errno != ENOENT) { + fprintf(stderr, "Not enough permissions on " + "rootdir/cachedir/metadir: %s\n", + strerror(errno)); + rv = errno; + goto out; + } + } + + if (clean_cache) { + rv = cachedir_clean(&xh); + if (rv != 0) + goto out; + } + + if (orphans) { + if ((rv = xbps_transaction_autoremove_pkgs(&xh)) != 0) { + if (rv != ENOENT) { + fprintf(stderr, "Failed to remove package " + "orphans: %s\n", strerror(rv)); + goto out; + } + } + } + + for (i = optind; i < argc; i++) { + rv = remove_pkg(&xh, argv[i], maxcols, recursive); + if (rv == 0) + continue; + else if (rv != EEXIST) + goto out; + else + reqby_force = true; + } + if (reqby_force && !ignore_revdeps) { + rv = EINVAL; + goto out; + } + + if (orphans || argc) + rv = exec_transaction(&xh, maxcols, yes, drun); + +out: + xbps_end(&xh); + exit(rv); +} diff --git a/bin/xbps-rindex/Makefile b/bin/xbps-rindex/Makefile new file mode 100644 index 00000000..58e8fe6e --- /dev/null +++ b/bin/xbps-rindex/Makefile @@ -0,0 +1,8 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-rindex +OBJS = main.o index.o index-files.o remove-obsoletes.o common.o +#MAN = $(BIN).8 + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-rindex/common.c b/bin/xbps-rindex/common.c new file mode 100644 index 00000000..868b1696 --- /dev/null +++ b/bin/xbps-rindex/common.c @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 2012 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 +#include "defs.h" + +int +remove_pkg(const char *repodir, const char *arch, const char *file) +{ + char *filepath; + int rv; + + /* Remove real binpkg */ + filepath = xbps_xasprintf("%s/%s/%s", repodir, arch, file); + assert(filepath); + if (remove(filepath) == -1) { + rv = errno; + xbps_error_printf("failed to remove old binpkg `%s': %s\n", + file, strerror(rv)); + free(filepath); + return rv; + } + free(filepath); + + /* Remove symlink to binpkg */ + filepath = xbps_xasprintf("%s/%s", repodir, file); + assert(filepath); + if (remove(filepath) == -1) { + rv = errno; + xbps_error_printf("failed to remove old binpkg `%s': %s\n", + file, strerror(rv)); + free(filepath); + return rv; + } + free(filepath); + + return 0; +} diff --git a/bin/xbps-rindex/defs.h b/bin/xbps-rindex/defs.h new file mode 100644 index 00000000..9b863eb6 --- /dev/null +++ b/bin/xbps-rindex/defs.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2012 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. + */ + +#ifndef _XBPS_RINDEX_DEFS_H_ +#define _XBPS_RINDEX_DEFS_H_ + +#include + +/* From common.c */ +int remove_pkg(const char *, const char *, const char *); + +/* From index.c */ +int index_add(struct xbps_handle *, int, char **); +int index_clean(struct xbps_handle *, const char *); + +/* From index-files.c */ +int index_files_add(struct xbps_handle *, int, char **); +int index_files_clean(struct xbps_handle *, const char *); + +/* From remove-obsoletes.c */ +int remove_obsoletes(struct xbps_handle *, const char *); + +#endif /* !_XBPS_RINDEX_DEFS_H_ */ diff --git a/bin/xbps-rindex/index-files.c b/bin/xbps-rindex/index-files.c new file mode 100644 index 00000000..ef88b2b2 --- /dev/null +++ b/bin/xbps-rindex/index-files.c @@ -0,0 +1,359 @@ +/*- + * Copyright (c) 2012 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 "defs.h" + +int +index_files_clean(struct xbps_handle *xhp, const char *repodir) +{ + prop_object_t obj; + prop_array_t idx, idxfiles, obsoletes; + char *plist, *plistf, *pkgver, *str; + const char *p, *arch, *ipkgver, *iarch; + size_t x, i; + int rv = 0; + bool flush = false; + + plist = plistf = pkgver = str = NULL; + idx = idxfiles = obsoletes = NULL; + + /* Internalize index-files.plist if found */ + if ((plistf = xbps_pkg_index_files_plist(xhp, repodir)) == NULL) + return EINVAL; + if ((idxfiles = prop_array_internalize_from_zfile(plistf)) == NULL) { + free(plistf); + return 0; + } + /* Internalize index.plist */ + if ((plist = xbps_pkg_index_plist(xhp, repodir)) == NULL) { + rv = EINVAL; + goto out; + } + if ((idx = prop_array_internalize_from_zfile(plist)) == NULL) { + rv = EINVAL; + goto out; + } + printf("Cleaning `%s' index-files, please wait...\n", repodir); + /* + * Iterate over index-files array to find obsolete entries. + */ + obsoletes = prop_array_create(); + assert(obsoletes); + + for (x = 0; x < prop_array_count(idxfiles); x++) { + obj = prop_array_get(idxfiles, x); + prop_dictionary_get_cstring_nocopy(obj, "pkgver", &ipkgver); + prop_dictionary_get_cstring_nocopy(obj, "architecture", &iarch); + if (xbps_find_pkg_in_array_by_pkgver(xhp, idx, ipkgver, iarch)) { + /* pkg found, do nothing */ + continue; + } + if ((str = xbps_xasprintf("%s,%s", ipkgver, iarch)) == NULL) { + rv = ENOMEM; + goto out; + } + if (!prop_array_add_cstring(obsoletes, str)) { + free(str); + rv = EINVAL; + goto out; + } + free(str); + } + /* + * Iterate over the obsoletes and array and remove entries + * from index-files array. + */ + for (i = 0; i < prop_array_count(obsoletes); i++) { + prop_array_get_cstring_nocopy(obsoletes, i, &p); + pkgver = strdup(p); + for (x = 0; x < strlen(p); x++) { + if ((pkgver[x] = p[x]) == ',') { + pkgver[x] = '\0'; + break; + } + } + arch = strchr(p, ',') + 1; + if (!xbps_remove_pkg_from_array_by_pkgver( + xhp, idxfiles, pkgver, arch)) { + free(pkgver); + rv = EINVAL; + goto out; + } + printf("index-files: removed obsolete entry `%s' " + "(%s)\n", pkgver, arch); + free(pkgver); + flush = true; + } + /* Externalize index-files array to plist when necessary */ + if (flush && !prop_array_externalize_to_zfile(idxfiles, plistf)) + rv = errno; + + printf("index-files: %u packages registered.\n", + prop_array_count(idxfiles)); + +out: + if (obsoletes) + prop_object_release(obsoletes); + if (idx) + prop_object_release(idx); + if (idxfiles) + prop_object_release(idxfiles); + if (plist) + free(plist); + if (plistf) + free(plistf); + + return rv; +} + +int +index_files_add(struct xbps_handle *xhp, int argc, char **argv) +{ + prop_array_t idxfiles = NULL; + prop_object_t obj, fileobj; + prop_dictionary_t pkgprops, pkg_filesd, pkgd; + prop_array_t files, pkg_cffiles, pkg_files, pkg_links; + const char *binpkg, *pkgver, *arch; + char *plist, *repodir, *p; + size_t x; + int i, rv = 0; + bool found, flush; + + found = flush = false; + plist = repodir = p = NULL; + obj = fileobj = NULL; + pkgprops = pkg_filesd = pkgd = NULL; + files = NULL; + + if ((p = strdup(argv[0])) == NULL) { + rv = ENOMEM; + goto out; + } + repodir = dirname(p); + if ((plist = xbps_pkg_index_files_plist(xhp, repodir)) == NULL) { + rv = ENOMEM; + goto out; + } + /* + * Internalize index-files.plist if found and process argv. + */ + if ((idxfiles = prop_array_internalize_from_zfile(plist)) == NULL) { + if (errno == ENOENT) { + idxfiles = prop_array_create(); + assert(idxfiles); + } else { + rv = errno; + goto out; + } + } + + for (i = 0; i < argc; i++) { + found = false; + pkgprops = xbps_dictionary_metadata_plist_by_url(argv[i], + "./props.plist"); + if (pkgprops == NULL) { + fprintf(stderr, "index-files: cannot internalize " + "%s props.plist: %s\n", argv[i], strerror(errno)); + continue; + } + prop_dictionary_get_cstring_nocopy(pkgprops, + "filename", &binpkg); + prop_dictionary_get_cstring_nocopy(pkgprops, + "pkgver", &pkgver); + prop_dictionary_get_cstring_nocopy(pkgprops, + "architecture", &arch); + + if (xbps_find_pkg_in_array_by_pkgver(xhp, idxfiles, + pkgver, arch)) { + fprintf(stderr, "index-files: skipping `%s' (%s), " + "already registered.\n", pkgver, arch); + prop_object_release(pkgprops); + pkgprops = NULL; + continue; + } + + /* internalize files.plist from binary package archive */ + pkg_filesd = xbps_dictionary_metadata_plist_by_url(argv[i], + "./files.plist"); + if (pkg_filesd == NULL) { + prop_object_release(pkgprops); + rv = EINVAL; + goto out; + } + + /* Find out if binary pkg stored in index contain any file */ + pkg_cffiles = prop_dictionary_get(pkg_filesd, "conf_files"); + if (pkg_cffiles != NULL && prop_array_count(pkg_cffiles)) + found = true; + else + pkg_cffiles = NULL; + + pkg_files = prop_dictionary_get(pkg_filesd, "files"); + if (pkg_files != NULL && prop_array_count(pkg_files)) + found = true; + else + pkg_files = NULL; + + pkg_links = prop_dictionary_get(pkg_filesd, "links"); + if (pkg_links != NULL && prop_array_count(pkg_links)) + found = true; + else + pkg_links = NULL; + + /* If pkg does not contain any file, ignore it */ + if (!found) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + continue; + } + /* create pkg dictionary */ + if ((pkgd = prop_dictionary_create()) == NULL) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + rv = EINVAL; + goto out; + } + /* add pkgver and architecture objects into pkg dictionary */ + if (!prop_dictionary_set_cstring(pkgd, "architecture", arch)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + if (!prop_dictionary_set_cstring(pkgd, "pkgver", pkgver)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + /* add files array obj into pkg dictionary */ + if ((files = prop_array_create()) == NULL) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + if (!prop_dictionary_set(pkgd, "files", files)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(files); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + /* add conf_files in pkgd */ + if (pkg_cffiles != NULL) { + for (x = 0; x < prop_array_count(pkg_cffiles); x++) { + obj = prop_array_get(pkg_cffiles, x); + fileobj = prop_dictionary_get(obj, "file"); + if (!prop_array_add(files, fileobj)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(files); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + } + } + /* add files array in pkgd */ + if (pkg_files != NULL) { + for (x = 0; x < prop_array_count(pkg_files); x++) { + obj = prop_array_get(pkg_files, x); + fileobj = prop_dictionary_get(obj, "file"); + if (!prop_array_add(files, fileobj)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(files); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + } + } + /* add links array in pkgd */ + if (pkg_links != NULL) { + for (x = 0; x < prop_array_count(pkg_links); x++) { + obj = prop_array_get(pkg_links, x); + fileobj = prop_dictionary_get(obj, "file"); + if (!prop_array_add(files, fileobj)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(files); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + } + } + /* add pkgd into the index-files array */ + if (!prop_array_add(idxfiles, pkgd)) { + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(files); + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + flush = true; + printf("index-files: added `%s' (%s)\n", pkgver, arch); + prop_object_release(pkgprops); + prop_object_release(pkg_filesd); + prop_object_release(files); + prop_object_release(pkgd); + pkgprops = pkg_filesd = pkgd = NULL; + files = NULL; + } + + if (flush && !prop_array_externalize_to_zfile(idxfiles, plist)) { + fprintf(stderr, "failed to externalize %s: %s\n", + plist, strerror(errno)); + rv = errno; + } + printf("index-files: %u packages registered.\n", + prop_array_count(idxfiles)); + +out: + if (p) + free(p); + if (plist) + free(plist); + if (idxfiles) + prop_object_release(idxfiles); + + return rv; +} diff --git a/bin/xbps-rindex/index.c b/bin/xbps-rindex/index.c new file mode 100644 index 00000000..3509971a --- /dev/null +++ b/bin/xbps-rindex/index.c @@ -0,0 +1,325 @@ +/*- + * Copyright (c) 2012 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 +#include "defs.h" + +/* + * Removes stalled pkg entries in repository's index.plist file, if any + * binary package cannot be read (unavailable, not enough perms, etc). + */ +int +index_clean(struct xbps_handle *xhp, const char *repodir) +{ + prop_array_t array; + prop_dictionary_t pkgd; + const char *filen, *pkgver, *arch; + char *plist; + size_t i, idx = 0; + int rv = 0; + bool flush = false; + + if ((plist = xbps_pkg_index_plist(xhp, repodir)) == NULL) + return -1; + + array = prop_array_internalize_from_zfile(plist); + if (array == NULL) { + if (errno != ENOENT) { + xbps_error_printf("xbps-repo: cannot read `%s': %s\n", + plist, strerror(errno)); + free(plist); + return -1; + } else { + free(plist); + return 0; + } + } + if (chdir(repodir) == -1) { + fprintf(stderr, "cannot chdir to %s: %s\n", + repodir, strerror(errno)); + rv = errno; + goto out; + } + printf("Cleaning `%s' index, please wait...\n", repodir); + +again: + for (i = idx; i < prop_array_count(array); i++) { + pkgd = prop_array_get(array, i); + prop_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + prop_dictionary_get_cstring_nocopy(pkgd, "filename", &filen); + prop_dictionary_get_cstring_nocopy(pkgd, "architecture", &arch); + if (access(filen, R_OK) == -1) { + printf("index: removed obsolete entry `%s' (%s)\n", + pkgver, arch); + prop_array_remove(array, i); + flush = true; + idx = i; + goto again; + } + } + if (flush && !prop_array_externalize_to_zfile(array, plist)) { + rv = errno; + goto out; + } + printf("index: %u packages registered.\n", prop_array_count(array)); +out: + free(plist); + prop_object_release(array); + + return rv; +} + +/* + * Adds a binary package into the index and removes old binary package + * and entry when it's necessary. + */ +int +index_add(struct xbps_handle *xhp, int argc, char **argv) +{ + prop_array_t idx = NULL; + prop_dictionary_t newpkgd = NULL, curpkgd; + struct stat st; + const char *pkgname, *version, *regver, *oldfilen, *oldpkgver; + const char *arch, *oldarch; + char *sha256, *filen, *repodir, *buf, *buf2; + char *tmpfilen = NULL, *tmprepodir = NULL, *plist = NULL; + int i, ret = 0, rv = 0; + bool flush = false; + + if ((tmprepodir = strdup(argv[0])) == NULL) { + rv = ENOMEM; + goto out; + } + repodir = dirname(tmprepodir); + + /* Internalize plist file or create it if doesn't exist */ + if ((plist = xbps_pkg_index_plist(xhp, repodir)) == NULL) + return -1; + + if ((idx = prop_array_internalize_from_zfile(plist)) == NULL) { + if (errno != ENOENT) { + xbps_error_printf("xbps-repo: cannot read `%s': %s\n", + plist, strerror(errno)); + rv = -1; + goto out; + } else { + idx = prop_array_create(); + assert(idx); + } + } + + /* + * Process all packages specified in argv. + */ + for (i = 0; i < argc; i++) { + if ((tmpfilen = strdup(argv[i])) == NULL) { + rv = ENOMEM; + goto out; + } + filen = basename(tmpfilen); + /* + * Read metadata props plist dictionary from binary package. + */ + newpkgd = xbps_dictionary_metadata_plist_by_url(argv[i], + "./props.plist"); + if (newpkgd == NULL) { + xbps_error_printf("failed to read %s metadata for `%s'," + " skipping!\n", XBPS_PKGPROPS, argv[i]); + free(tmpfilen); + continue; + } + prop_dictionary_get_cstring_nocopy(newpkgd, "pkgname", + &pkgname); + prop_dictionary_get_cstring_nocopy(newpkgd, "version", + &version); + prop_dictionary_get_cstring_nocopy(newpkgd, "architecture", + &arch); + /* + * Check if this package exists already in the index, but first + * checking the version. If current package version is greater + * than current registered package, update the index; otherwise + * pass to the next one. + */ + curpkgd = + xbps_find_pkg_in_array_by_name(xhp, idx, pkgname, arch); + if (curpkgd == NULL) { + if (errno && errno != ENOENT) { + prop_object_release(newpkgd); + free(tmpfilen); + rv = errno; + goto out; + } + } else { + prop_dictionary_get_cstring_nocopy(curpkgd, + "filename", &oldfilen); + prop_dictionary_get_cstring_nocopy(curpkgd, + "pkgver", &oldpkgver); + prop_dictionary_get_cstring_nocopy(curpkgd, + "architecture", &oldarch); + prop_dictionary_get_cstring_nocopy(curpkgd, + "version", ®ver); + ret = xbps_cmpver(version, regver); + if (ret == 0) { + /* Same version */ + fprintf(stderr, "index: skipping `%s-%s' " + "(%s), already registered.\n", + pkgname, version, arch); + prop_object_release(newpkgd); + free(tmpfilen); + continue; + } else if (ret == -1) { + /* + * Index version is greater, remove current + * package. + */ + buf = xbps_xasprintf("`%s' (%s)", + oldpkgver, oldarch); + assert(buf); + rv = remove_pkg(repodir, + oldarch, oldfilen); + if (rv != 0) { + prop_object_release(newpkgd); + free(tmpfilen); + free(buf); + goto out; + } + printf("index: removed obsolete binpkg %s.\n", buf); + free(buf); + prop_object_release(newpkgd); + free(tmpfilen); + continue; + } + /* + * Current package version is greater than + * index version. + */ + buf = xbps_xasprintf("`%s' (%s)", oldpkgver, oldarch); + assert(buf); + buf2 = strdup(oldpkgver); + assert(buf2); + rv = remove_pkg(repodir, oldarch, oldfilen); + if (rv != 0) { + free(buf); + free(buf2); + prop_object_release(newpkgd); + free(tmpfilen); + goto out; + } + if (!xbps_remove_pkg_from_array_by_pkgver(xhp, idx, + buf2, oldarch)) { + xbps_error_printf("failed to remove %s " + "from plist index: %s\n", buf, + strerror(errno)); + rv = errno; + free(buf); + free(buf2); + prop_object_release(newpkgd); + free(tmpfilen); + goto out; + } + free(buf2); + printf("index: removed obsolete entry/binpkg %s.\n", buf); + free(buf); + } + /* + * We have the dictionary now, add the required + * objects for the index. + */ + if (!prop_dictionary_set_cstring(newpkgd, "filename", filen)) { + rv = errno; + prop_object_release(newpkgd); + free(tmpfilen); + goto out; + } + if ((sha256 = xbps_file_hash(argv[i])) == NULL) { + rv = errno; + prop_object_release(newpkgd); + free(tmpfilen); + goto out; + } + if (!prop_dictionary_set_cstring(newpkgd, "filename-sha256", + sha256)) { + free(sha256); + prop_object_release(newpkgd); + free(tmpfilen); + rv = errno; + goto out; + } + free(sha256); + if (stat(argv[i], &st) == -1) { + prop_object_release(newpkgd); + free(tmpfilen); + rv = errno; + goto out; + } + if (!prop_dictionary_set_uint64(newpkgd, "filename-size", + (uint64_t)st.st_size)) { + prop_object_release(newpkgd); + free(tmpfilen); + rv = errno; + goto out; + } + /* + * Add new pkg dictionary into the index. + */ + if (!prop_array_add(idx, newpkgd)) { + prop_object_release(newpkgd); + free(tmpfilen); + rv = EINVAL; + goto out; + } + flush = true; + printf("index: added `%s-%s' (%s).\n", pkgname, version, arch); + free(tmpfilen); + prop_object_release(newpkgd); + } + + if (flush && !prop_array_externalize_to_zfile(idx, plist)) { + xbps_error_printf("failed to externalize plist: %s\n", + strerror(errno)); + rv = errno; + } + printf("index: %u packages registered.\n", prop_array_count(idx)); + +out: + if (tmprepodir) + free(tmprepodir); + if (plist) + free(plist); + if (idx) + prop_object_release(idx); + + return rv; +} diff --git a/bin/xbps-rindex/main.c b/bin/xbps-rindex/main.c new file mode 100644 index 00000000..b02a18af --- /dev/null +++ b/bin/xbps-rindex/main.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2012 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 "defs.h" + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-rindex [OPTIONS] MODE ARGUMENTS\n\n" + "OPTIONS\n" + " -h --help Show help usage\n" + " -V --version Show XBPS version\n\n" + "MODE\n" + " -a --add ... Add package(s) to repository index\n" + " -c --clean Cleans obsolete entries in repository index\n" + " -r --remove-obsoletes Removes obsolete packages from repository\n\n"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "achrV"; + struct option longopts[] = { + { "add", no_argument, NULL, 'a' }, + { "clean", no_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { "remove-obsoletes", no_argument, NULL, 'r' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + struct xbps_handle xh; + int rv, c; + bool clean_mode = false, add_mode = false, rm_mode = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'a': + add_mode = true; + break; + case 'c': + clean_mode = true; + break; + case 'h': + usage(false); + /* NOTREACHED */ + case 'R': + rm_mode = true; + break; + case 'V': + printf("%s\n", XBPS_RELVER); + exit(EXIT_SUCCESS); + } + } + if ((argc == optind) || (!add_mode && !clean_mode && !rm_mode)) { + usage(true); + } else if ((add_mode && (clean_mode || rm_mode)) || + (clean_mode && (add_mode || rm_mode)) || + (rm_mode && (add_mode || clean_mode))) { + fprintf(stderr, "Only one mode can be specified: add, clean " + "or remove-obsoletes.\n"); + exit(EXIT_FAILURE); + } + + /* initialize libxbps */ + memset(&xh, 0, sizeof(xh)); + if ((rv = xbps_init(&xh)) != 0) { + fprintf(stderr, "failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + if (add_mode) { + if (index_add(&xh, argc - optind, argv + optind) == 0) + rv = index_files_add(&xh, argc - optind, argv + optind); + } else if (clean_mode) { + if (index_clean(&xh, argv[optind]) == 0) + rv = index_files_clean(&xh, argv[optind]); + } else if (rm_mode) { + rv = remove_obsoletes(&xh, argv[optind]); + } + + xbps_end(&xh); + exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/bin/xbps-rindex/remove-obsoletes.c b/bin/xbps-rindex/remove-obsoletes.c new file mode 100644 index 00000000..9973fac0 --- /dev/null +++ b/bin/xbps-rindex/remove-obsoletes.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 2012 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 +#include "defs.h" + +int +remove_obsoletes(struct xbps_handle *xhp, const char *repodir) +{ + prop_dictionary_t pkgd; + prop_array_t idx; + DIR *dirp; + struct dirent *dp; + const char *pkgver, *arch; + char *plist, *ext; + int rv = 0; + + if ((plist = xbps_pkg_index_plist(xhp, repodir)) == NULL) + return -1; + + idx = prop_array_internalize_from_zfile(plist); + if (idx == NULL) { + if (errno != ENOENT) { + xbps_error_printf("xbps-repo: cannot read `%s': %s\n", + plist, strerror(errno)); + free(plist); + return -1; + } else { + free(plist); + return 0; + } + } + if (chdir(repodir) == -1) { + fprintf(stderr, "cannot chdir to %s: %s\n", + repodir, strerror(errno)); + prop_object_release(idx); + return errno; + } + if ((dirp = opendir(repodir)) == NULL) { + fprintf(stderr, "failed to open %s: %s\n", + repodir, strerror(errno)); + prop_object_release(idx); + return errno; + } + while ((dp = readdir(dirp))) { + if (strcmp(dp->d_name, "..") == 0) + continue; + if ((ext = strrchr(dp->d_name, '.')) == NULL) + continue; + if (strcmp(ext, ".xbps")) + continue; + + pkgd = xbps_dictionary_metadata_plist_by_url(dp->d_name, + "./props.plist"); + if (pkgd == NULL) { + rv = remove_pkg(repodir, arch, dp->d_name); + if (rv != 0) { + fprintf(stderr, "index: failed to remove " + "package `%s': %s\n", dp->d_name, + strerror(rv)); + prop_object_release(pkgd); + break; + } + printf("Removed broken package `%s'.\n", dp->d_name); + } + prop_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + prop_dictionary_get_cstring_nocopy(pkgd, "architecture", &arch); + /* + * If binpkg is not registered in index, remove binpkg. + */ + if (!xbps_find_pkg_in_array_by_pkgver(xhp, idx, pkgver, arch)) { + rv = remove_pkg(repodir, arch, dp->d_name); + if (rv != 0) { + fprintf(stderr, "index: failed to remove " + "package `%s': %s\n", dp->d_name, + strerror(rv)); + prop_object_release(pkgd); + break; + } + printf("Removed obsolete package `%s'.\n", dp->d_name); + } + prop_object_release(pkgd); + } + (void)closedir(dirp); + prop_object_release(idx); + return rv; +}