commit 3f3b6d00dd89a374795959394374cd80e2bf76dd Author: juan Date: Mon Aug 17 19:07:20 2009 +0200 Initial import of xbps with code as August '09. --HG-- extra : convert_revision : juan%40xbps-20090817170720-amxxac4a2e8bza1j diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..e6d1dd50 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +include vars.mk + +SUBDIRS = lib bin etc + +.PHONY: all +all: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir; \ + done + +.PHONY: install +install: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir install; \ + done + @echo + @echo "Binaries have been installed into $(SBINDIR)." + @echo "Librares have been installed into $(LIBDIR)." + @echo + @echo "WARNING: Don't forget to rerun ldconfig(1)." + @echo + +uninstall: + -rm -f $(SBINDIR)/xbps-* + -rm -f $(LIBDIR)/libxbps.so* + +.PHONY: clean +clean: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir clean; \ + done diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 00000000..1166c826 --- /dev/null +++ b/bin/Makefile @@ -0,0 +1,26 @@ +include ../vars.mk + +SUBDIRS = xbps-bin +SUBDIRS += xbps-cmpver +SUBDIRS += xbps-digest +SUBDIRS += xbps-pkgdb +SUBDIRS += xbps-repo +SUBDIRS += xbps-src + +.PHONY: all +all: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir; \ + done + +.PHONY: install +install: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir install; \ + done + +.PHONY: clean +clean: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir clean; \ + done diff --git a/bin/xbps-bin/Makefile b/bin/xbps-bin/Makefile new file mode 100644 index 00000000..546853bb --- /dev/null +++ b/bin/xbps-bin/Makefile @@ -0,0 +1,5 @@ +BIN = xbps-bin +OBJS = check.o install.o main.o remove.o ../xbps-repo/util.o + +TOPDIR = ../.. +include $(TOPDIR)/prog.mk diff --git a/bin/xbps-bin/check.c b/bin/xbps-bin/check.c new file mode 100644 index 00000000..2d9297d7 --- /dev/null +++ b/bin/xbps-bin/check.c @@ -0,0 +1,275 @@ +/*- + * Copyright (c) 2009 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 "defs.h" + +/* + * Checks package integrity of an installed package. This + * consists in four tasks: + * + * o Check for metadata files (files.plist and props.plist), + * we only check if the file exists and its dictionary can + * be externalized and is not empty. + * o Check for missing installed files. + * o Check the hash for all installed files, except + * configuration files (which is expected if they are modified). + * o Check for missing run time dependencies. + * + * If any of these checks fail, the package will change its + * state to 'broken'. + */ + +int +xbps_check_pkg_integrity(const char *pkgname) +{ + prop_dictionary_t pkgd, propsd, filesd; + prop_array_t array; + prop_object_t obj; + prop_object_iterator_t iter; + const char *rootdir, *file, *sha256, *reqpkg; + char *path; + int rv = 0; + bool files_broken = false, broken = false; + + assert(pkgname != NULL); + + rootdir = xbps_get_rootdir(); + pkgd = xbps_find_pkg_installed_from_plist(pkgname); + if (pkgd == NULL) { + printf("Package %s is not installed.\n", pkgname); + return 0; + } + + /* + * Check for props.plist metadata file. + */ + path = xbps_xasprintf("%s/%s/metadata/%s/%s", rootdir, + XBPS_META_PATH, pkgname, XBPS_PKGPROPS); + if (path == NULL) { + rv = errno; + goto out; + } + + propsd = prop_dictionary_internalize_from_file(path); + free(path); + if (propsd == NULL) { + printf("%s: unexistent %s metadata file.\n", pkgname, + XBPS_PKGPROPS); + rv = errno; + broken = true; + goto out; + } else if (prop_object_type(propsd) != PROP_TYPE_DICTIONARY) { + printf("%s: invalid %s metadata file.\n", pkgname, + XBPS_PKGPROPS); + rv = EINVAL; + broken = true; + goto out1; + } else if (prop_dictionary_count(propsd) == 0) { + printf("%s: incomplete %s metadata file.\n", pkgname, + XBPS_PKGPROPS); + rv = EINVAL; + broken = true; + goto out1; + } + + /* + * Check for files.plist metadata file. + */ + path = xbps_xasprintf("%s/%s/metadata/%s/%s", rootdir, + XBPS_META_PATH, pkgname, XBPS_PKGFILES); + if (path == NULL) { + rv = errno; + goto out1; + } + + filesd = prop_dictionary_internalize_from_file(path); + free(path); + if (filesd == NULL) { + printf("%s: unexistent %s metadata file.\n", pkgname, + XBPS_PKGPROPS); + rv = ENOENT; + broken = true; + goto out1; + } else if (prop_object_type(filesd) != PROP_TYPE_DICTIONARY) { + printf("%s: invalid %s metadata file.\n", pkgname, + XBPS_PKGFILES); + rv = EINVAL; + broken = true; + goto out2; + } else if (prop_dictionary_count(filesd) == 0) { + printf("%s: incomplete %s metadata file.\n", pkgname, + XBPS_PKGFILES); + rv = EINVAL; + broken = true; + goto out2; + } else if (((array = prop_dictionary_get(filesd, "files")) == NULL) || + ((array = prop_dictionary_get(filesd, "links")) == NULL) || + ((array = prop_dictionary_get(filesd, "dirs")) == NULL)) { + printf("%s: incomplete %s metadata file.\n", pkgname, + XBPS_PKGFILES); + rv = EINVAL; + broken = true; + goto out2; + } + + /* + * Check for missing files and its hash. + */ + array = prop_dictionary_get(filesd, "files"); + if ((prop_object_type(array) == PROP_TYPE_ARRAY) && + prop_array_count(array) > 0) { + iter = xbps_get_array_iter_from_dict(filesd, "files"); + if (iter == NULL) { + rv = ENOMEM; + goto out2; + } + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + path = xbps_xasprintf("%s/%s", rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + rv = errno; + goto out2; + } + prop_dictionary_get_cstring_nocopy(obj, + "sha256", &sha256); + rv = xbps_check_file_hash(path, sha256); + switch (rv) { + case 0: + break; + case ENOENT: + printf("%s: unexistent file %s.\n", + pkgname, file); + files_broken = true; + broken = true; + break; + case ERANGE: + printf("%s: hash mismatch for %s.\n", + pkgname, file); + files_broken = true; + broken = true; + break; + default: + printf("%s: unexpected error for %s (%s)\n", + pkgname, file, strerror(rv)); + break; + } + free(path); + } + prop_object_iterator_release(iter); + if (files_broken) + printf("%s: files check FAILED.\n", pkgname); + } + + /* + * Check for missing configuration files. + */ + array = prop_dictionary_get(filesd, "conf_files"); + if (array && prop_object_type(array) == PROP_TYPE_ARRAY && + prop_array_count(array) > 0) { + iter = xbps_get_array_iter_from_dict(filesd, "conf_files"); + if (iter == NULL) { + rv = ENOMEM; + goto out2; + } + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + path = xbps_xasprintf("%s/%s", rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + rv = ENOMEM; + goto out2; + } + if ((rv = access(path, R_OK)) == -1) { + if (errno == ENOENT) { + printf("%s: unexistent file %s\n", + pkgname, file); + broken = true; + } else + printf("%s: unexpected error for " + "%s (%s)\n", pkgname, file, + strerror(errno)); + } + free(path); + } + prop_object_iterator_release(iter); + if (rv != 0) + printf("%s: configuration files check FAILED.\n", + pkgname); + } + + /* + * Check for missing run time dependencies. + */ + if (xbps_pkg_has_rundeps(propsd)) { + iter = xbps_get_array_iter_from_dict(propsd, "run_depends"); + if (iter == NULL) { + rv = ENOMEM; + goto out2; + } + while ((obj = prop_object_iterator_next(iter))) { + reqpkg = prop_string_cstring_nocopy(obj); + if (xbps_check_is_installed_pkg(reqpkg) < 0) { + rv = ENOENT; + printf("%s: dependency not satisfied: %s\n", + pkgname, reqpkg); + broken = true; + } + } + prop_object_iterator_release(iter); + if (rv == ENOENT) + printf("%s: run-time dependency check FAILED.\n", + pkgname); + } + +out2: + prop_object_release(filesd); +out1: + prop_object_release(propsd); +out: + prop_object_release(pkgd); + + if (broken) { + rv = xbps_set_pkg_state_installed(pkgname, + XBPS_PKG_STATE_BROKEN); + if (rv == 0) + printf("%s: changed package state to broken.\n", + pkgname); + else + printf("%s: can't change package state (%s).\n", + pkgname, strerror(rv)); + } + + xbps_release_regpkgdb_dict(); + + return rv; +} diff --git a/bin/xbps-bin/defs.h b/bin/xbps-bin/defs.h new file mode 100644 index 00000000..e5664073 --- /dev/null +++ b/bin/xbps-bin/defs.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2009 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_BIN_DEFS_H_ +#define _XBPS_BIN_DEFS_H_ + +void xbps_install_pkg(const char *, bool, bool); +void xbps_autoremove_pkgs(void); +void xbps_remove_installed_pkg(const char *, bool); +void xbps_autoupdate_pkgs(bool); +int xbps_check_pkg_integrity(const char *); + +#endif /* !_XBPS_BIN_DEFS_H_ */ diff --git a/bin/xbps-bin/install.c b/bin/xbps-bin/install.c new file mode 100644 index 00000000..0505d6de --- /dev/null +++ b/bin/xbps-bin/install.c @@ -0,0 +1,501 @@ +/*- + * Copyright (c) 2009 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 "defs.h" + +struct transaction { + prop_dictionary_t dict; + prop_object_iterator_t iter; + const char *originpkgname; + bool force; +}; + +static void cleanup(int); +static int exec_transaction(struct transaction *); +static void show_missing_deps(prop_dictionary_t, const char *); +static int show_missing_dep_cb(prop_object_t, void *, bool *); +static void show_package_list(prop_object_iterator_t, const char *); + +static void +show_missing_deps(prop_dictionary_t d, const char *pkgname) +{ + printf("Unable to locate some required packages for %s:\n", + pkgname); + (void)xbps_callback_array_iter_in_dict(d, "missing_deps", + show_missing_dep_cb, NULL); +} + +static int +show_missing_dep_cb(prop_object_t obj, void *arg, bool *loop_done) +{ + const char *pkgname, *version; + + (void)arg; + (void)loop_done; + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + if (pkgname && version) { + printf(" * Missing binary package for: %s >= %s\n", + pkgname, version); + return 0; + } + + return EINVAL; +} + +static int +check_pkg_hashes(prop_object_iterator_t iter) +{ + prop_object_t obj; + const char *pkgname, *repoloc, *filename; + int rv = 0; + pkg_state_t state = 0; + + printf("Checking binary package file(s) integrity...\n"); + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + state = 0; + if (xbps_get_pkg_state_dictionary(obj, &state) != 0) + return EINVAL; + + if (state == XBPS_PKG_STATE_UNPACKED) + continue; + + prop_dictionary_get_cstring_nocopy(obj, "repository", &repoloc); + prop_dictionary_get_cstring_nocopy(obj, "filename", &filename); + rv = xbps_check_pkg_file_hash(obj, repoloc); + if (rv != 0 && rv != ERANGE) { + printf("Unexpected error while checking hash for " + "%s (%s)\n", filename, strerror(rv)); + return -1; + } else if (rv != 0 && rv == ERANGE) { + printf("Hash mismatch for %s, exiting.\n", + filename); + return -1; + } + } + prop_object_iterator_reset(iter); + + return 0; +} + +static void +show_package_list(prop_object_iterator_t iter, const char *match) +{ + prop_object_t obj; + size_t cols = 0; + const char *pkgname, *version, *tract; + bool first = false; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + prop_dictionary_get_cstring_nocopy(obj, "trans-action", &tract); + if (strcmp(match, tract)) + continue; + + cols += strlen(pkgname) + strlen(version) + 4; + if (cols <= 80) { + if (first == false) { + printf(" "); + first = true; + } + } else { + printf("\n "); + cols = strlen(pkgname) + strlen(version) + 4; + } + printf("%s-%s ", pkgname, version); + } + prop_object_iterator_reset(iter); +} + +static int +show_transaction_sizes(prop_object_iterator_t iter) +{ + prop_object_t obj; + uint64_t tsize = 0, dlsize = 0, instsize = 0; + const char *tract; + char size[64]; + bool trans_inst = false, trans_up = false; + + /* + * Iterate over the list of packages that are going to be + * installed and check the file hash. + */ + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_uint64(obj, "filename-size", &tsize); + dlsize += tsize; + tsize = 0; + prop_dictionary_get_uint64(obj, "installed_size", &tsize); + instsize += tsize; + tsize = 0; + } + prop_object_iterator_reset(iter); + + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "trans-action", &tract); + if (strcmp(tract, "install") == 0) + trans_inst = true; + else if (strcmp(tract, "update") == 0) + trans_up = true; + } + prop_object_iterator_reset(iter); + + /* + * Show the list of packages that will be installed. + */ + if (trans_inst) { + printf("The following packages will be installed:\n\n"); + show_package_list(iter, "install"); + printf("\n\n"); + } + if (trans_up) { + printf("The following packages will be updated:\n\n"); + show_package_list(iter, "update"); + printf("\n\n"); + } + + /* + * Show total download/installed size for all required packages. + */ + if (xbps_humanize_number(size, 5, (int64_t)dlsize, + "", HN_AUTOSCALE, HN_NOSPACE) == -1) { + printf("error: humanize_number returns %s\n", + strerror(errno)); + return -1; + } + printf("Total download size: %s\n", size); + if (xbps_humanize_number(size, 5, (int64_t)instsize, + "", HN_AUTOSCALE, HN_NOSPACE) == -1) { + printf("error: humanize_number2 returns %s\n", + strerror(errno)); + return -1; + } + printf("Total installed size: %s\n\n", size); + + return 0; +} + +void +xbps_install_pkg(const char *pkg, bool force, bool update) +{ + struct transaction *trans; + prop_dictionary_t pkgd; + prop_array_t array; + int rv = 0; + + /* + * Find all required pkgs and sort the package transaction. + */ + pkgd = xbps_find_pkg_installed_from_plist(pkg); + if (update) { + if (pkgd) { + if ((rv = xbps_find_new_pkg(pkg, pkgd)) == 0) { + printf("Package '%s' is up to date.\n", pkg); + prop_object_release(pkgd); + cleanup(rv); + } + prop_object_release(pkgd); + } else { + printf("Package '%s' not installed.\n", pkg); + cleanup(rv); + } + } else { + if (pkgd) { + printf("Package '%s' is already installed.\n", pkg); + prop_object_release(pkgd); + cleanup(rv); + } + rv = xbps_prepare_pkg(pkg); + if (rv != 0 && rv == EAGAIN) { + printf("unable to locate %s in repository pool.", pkg); + cleanup(rv); + } else if (rv != 0 && rv != ENOENT) { + printf("unexpected error: %s", strerror(rv)); + cleanup(rv); + } + } + + trans = calloc(1, sizeof(struct transaction)); + if (trans == NULL) + goto out; + + trans->dict = xbps_get_pkg_props(); + if (trans->dict == NULL) { + printf("error: unexistent props dictionary!\n"); + goto out1; + } + + /* + * Bail out if there are unresolved deps. + */ + array = prop_dictionary_get(trans->dict, "missing_deps"); + if (prop_array_count(array) > 0) { + show_missing_deps(trans->dict, pkg); + goto out2; + } + + prop_dictionary_get_cstring_nocopy(trans->dict, + "origin", &trans->originpkgname); + + /* + * It's time to run the transaction! + */ + trans->iter = xbps_get_array_iter_from_dict(trans->dict, "packages"); + if (trans->iter == NULL) { + printf("error: allocating array mem! (%s)", + strerror(errno)); + goto out2; + } + + trans->force = force; + rv = exec_transaction(trans); + + prop_object_iterator_release(trans->iter); +out2: + prop_object_release(trans->dict); +out1: + free(trans); +out: + cleanup(rv); +} + +static int +exec_transaction(struct transaction *trans) +{ + prop_dictionary_t instpkgd; + prop_object_t obj; + const char *pkgname, *version, *instver, *filename, *tract; + int rv = 0; + bool essential, isdep, autoinst; + pkg_state_t state = 0; + + assert(trans != NULL); + assert(trans->dict != NULL); + assert(trans->iter != NULL); + + essential = isdep = autoinst = false; + /* + * Show download/installed size for the transaction. + */ + rv = show_transaction_sizes(trans->iter); + if (rv != 0) + return rv; + + /* + * Ask interactively (if -f not set). + */ + if (trans->force == false) { + if (xbps_noyes("Do you want to continue?") == false) { + printf("Aborting!\n"); + return 0; + } + } + + /* + * Check the SHA256 hash for all required packages. + */ + if ((rv = check_pkg_hashes(trans->iter)) != 0) + return rv; + + /* + * Iterate over the transaction dictionary. + */ + while ((obj = prop_object_iterator_next(trans->iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + prop_dictionary_get_bool(obj, "essential", &essential); + prop_dictionary_get_cstring_nocopy(obj, "filename", &filename); + prop_dictionary_get_cstring_nocopy(obj, "trans-action", &tract); + + if (trans->originpkgname && + strcmp(trans->originpkgname, pkgname)) + isdep = true; + + /* + * If dependency is already unpacked skip this phase. + */ + state = 0; + if (xbps_get_pkg_state_dictionary(obj, &state) != 0) + return EINVAL; + + if (state == XBPS_PKG_STATE_UNPACKED) + continue; + + if (strcmp(tract, "update") == 0) { + instpkgd = xbps_find_pkg_installed_from_plist(pkgname); + if (instpkgd == NULL) { + printf("error: unable to find %s installed " + "dict!\n", pkgname); + return EINVAL; + } + + prop_dictionary_get_cstring_nocopy(instpkgd, + "version", &instver); + autoinst = false; + prop_dictionary_get_bool(instpkgd, "automatic-install", + &autoinst); + isdep = autoinst; + prop_object_release(instpkgd); + + /* + * If this package is not 'essential', just remove + * the old package and install the new one. Otherwise + * we just overwrite the files. + */ + if (essential == false) { + rv = xbps_remove_pkg(pkgname, version, true); + if (rv != 0) { + printf("error: removing %s-%s (%s)\n", + pkgname, instver, strerror(rv)); + return rv; + } + } + } + /* + * Unpack binary package. + */ + printf("Unpacking %s-%s (from .../%s) ...\n", pkgname, version, + filename); + if ((rv = xbps_unpack_binary_pkg(obj, essential)) != 0) { + printf("error: unpacking %s-%s (%s)\n", pkgname, + version, strerror(rv)); + return rv; + } + /* + * Register binary package. + */ + if ((rv = xbps_register_pkg(obj, isdep)) != 0) { + printf("error: registering %s-%s! (%s)\n", + pkgname, version, strerror(rv)); + return rv; + } + isdep = false; + /* + * Set package state to unpacked in the transaction + * dictionary. + */ + if ((rv = xbps_set_pkg_state_dictionary(obj, + XBPS_PKG_STATE_UNPACKED)) != 0) + return rv; + } + prop_object_iterator_reset(trans->iter); + /* + * Configure all unpacked packages. + */ + while ((obj = prop_object_iterator_next(trans->iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + if ((rv = xbps_configure_pkg(pkgname)) != 0) { + printf("Error configuring package %s (%s)\n", + pkgname, strerror(rv)); + return rv; + } + } + + return 0; +} + +void +xbps_autoupdate_pkgs(bool force) +{ + struct transaction *trans; + int rv = 0; + + /* + * Find new package versions. + */ + if ((rv = xbps_find_new_packages()) != 0) { + if (rv == ENOENT) { + printf("No packages currently registered.\n"); + cleanup(0); + } + goto out; + } + + /* + * Prepare transaction data. + */ + trans = calloc(1, sizeof(struct transaction)); + if (trans == NULL) + goto out; + + /* + * Get package transaction dictionary. + */ + trans->dict = xbps_get_pkg_props(); + if (trans->dict == NULL) { + if (errno == 0) { + printf("All packages are up-to-date.\n"); + goto out; + } + printf("Error while checking for new pkgs: %s\n", + strerror(errno)); + goto out1; + } + /* + * Sort the package transaction dictionary. + */ + if ((rv = xbps_sort_pkg_deps(trans->dict)) != 0) { + printf("Error while sorting packages: %s\n", + strerror(rv)); + goto out2; + } + + /* + * It's time to run the transaction! + */ + trans->iter = xbps_get_array_iter_from_dict(trans->dict, "packages"); + if (trans->iter == NULL) { + printf("error: allocating array mem! (%s)\n", strerror(errno)); + goto out2; + } + + trans->force = force; + rv = exec_transaction(trans); + + prop_object_iterator_release(trans->iter); +out2: + prop_object_release(trans->dict); +out1: + free(trans); +out: + cleanup(rv); +} + +static void +cleanup(int rv) +{ + xbps_release_repolist_data(); + xbps_release_regpkgdb_dict(); + exit(rv == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/bin/xbps-bin/main.c b/bin/xbps-bin/main.c new file mode 100644 index 00000000..1aa9f36c --- /dev/null +++ b/bin/xbps-bin/main.c @@ -0,0 +1,276 @@ +/*- + * Copyright (c) 2008-2009 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 "defs.h" +#include "../xbps-repo/util.h" + +static void usage(void); +static int list_pkgs_in_dict(prop_object_t, void *, bool *); + +static void +usage(void) +{ + printf("Usage: xbps-bin [options] [target] [arguments]\n\n" + " Available targets:\n" + " autoremove, autoupdate, check, files, install, list\n" + " purge, remove, show, update\n" + " Targets with arguments:\n" + " check\t\n" + " files\t\n" + " install\t\n" + " purge\t[|]\n" + " reconfigure\t[|]\n" + " remove\t\n" + " show\t\n" + " update\t\n" + " Options shared by all targets:\n" + " -r\t\t\n" + " -v\t\t\n" + " Options used by the (auto)remove and install target:\n" + " -f\t\tForce installation or removal of packages.\n" + " \t\tBeware with this option if you use autoremove!\n" + "\n"); + exit(EXIT_FAILURE); +} + +static int +list_pkgs_in_dict(prop_object_t obj, void *arg, bool *loop_done) +{ + const char *pkgname, *version, *short_desc; + + (void)arg; + (void)loop_done; + + assert(prop_object_type(obj) == PROP_TYPE_DICTIONARY); + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + prop_dictionary_get_cstring_nocopy(obj, "short_desc", &short_desc); + if (pkgname && version && short_desc) { + printf("%s-%s\t%s\n", pkgname, version, short_desc); + return 0; + } + + return EINVAL; +} + +int +main(int argc, char **argv) +{ + prop_dictionary_t dict; + prop_object_t obj; + prop_object_iterator_t iter; + const char *curpkgname; + int c, flags = 0, rv = 0; + bool force = false, verbose = false; + + while ((c = getopt(argc, argv, "Cfr:v")) != -1) { + switch (c) { + case 'f': + flags |= XBPS_FLAG_FORCE; + force = true; + break; + case 'r': + /* To specify the root directory */ + xbps_set_rootdir(optarg); + break; + case 'v': + verbose = true; + flags |= XBPS_FLAG_VERBOSE; + break; + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (flags != 0) + xbps_set_flags(flags); + + if ((dict = xbps_prepare_regpkgdb_dict()) == NULL) { + if (errno != ENOENT) { + rv = errno; + printf("Couldn't initialized regpkgdb dict: %s\n", + strerror(errno)); + goto out; + } + } + + if (strcasecmp(argv[0], "list") == 0) { + /* Lists packages currently registered in database. */ + if (argc != 1) + usage(); + + if (dict == NULL) { + printf("No packages currently installed.\n"); + goto out; + } + + if (!xbps_callback_array_iter_in_dict(dict, "packages", + list_pkgs_in_dict, NULL)) { + rv = errno; + goto out; + } + + } else if (strcasecmp(argv[0], "install") == 0) { + /* Installs a binary package and required deps. */ + if (argc != 2) + usage(); + + xbps_install_pkg(argv[1], force, false); + + } else if (strcasecmp(argv[0], "update") == 0) { + /* Update an installed package. */ + if (argc != 2) + usage(); + + xbps_install_pkg(argv[1], force, true); + + } else if (strcasecmp(argv[0], "remove") == 0) { + /* Removes a binary package. */ + if (argc != 2) + usage(); + + xbps_remove_installed_pkg(argv[1], force); + + } else if (strcasecmp(argv[0], "show") == 0) { + /* Shows info about an installed binary package. */ + if (argc != 2) + usage(); + + rv = show_pkg_info_from_metadir(argv[1]); + if (rv != 0) { + printf("Package %s not installed.\n", argv[1]); + exit(EXIT_FAILURE); + } + + } else if (strcasecmp(argv[0], "files") == 0) { + /* Shows files installed by a binary package. */ + if (argc != 2) + usage(); + + rv = show_pkg_files_from_metadir(argv[1]); + if (rv != 0) { + printf("Package %s not installed.\n", argv[1]); + exit(EXIT_FAILURE); + } + + } else if (strcasecmp(argv[0], "check") == 0) { + /* Checks the integrity of an installed package. */ + if (argc != 2) + usage(); + + rv = xbps_check_pkg_integrity(argv[1]); + + } else if (strcasecmp(argv[0], "autoupdate") == 0) { + /* + * To update all packages currently installed. + */ + if (argc != 1) + usage(); + + xbps_autoupdate_pkgs(force); + + } else if (strcasecmp(argv[0], "autoremove") == 0) { + /* + * Removes orphan pkgs. These packages were installed + * as dependency and any installed package does not depend + * on it currently. + */ + if (argc != 1) + usage(); + + xbps_autoremove_pkgs(); + + } else if (strcasecmp(argv[0], "purge") == 0) { + /* + * Purge a package completely. + */ + if (argc != 2) + usage(); + + if (strcasecmp(argv[1], "all") == 0) { + iter = xbps_get_array_iter_from_dict(dict, "packages"); + if (iter == NULL) + goto out; + + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, + "pkgname", &curpkgname); + if ((rv = xbps_purge_pkg(curpkgname)) != 0) + break; + } + prop_object_iterator_release(iter); + } else { + rv = xbps_purge_pkg(argv[1]); + } + + } else if (strcasecmp(argv[0], "reconfigure") == 0) { + /* + * Reconfigure a package. + */ + if (argc != 2) + usage(); + + if (strcasecmp(argv[1], "all") == 0) { + iter = xbps_get_array_iter_from_dict(dict, "packages"); + if (iter == NULL) + goto out; + + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, + "pkgname", &curpkgname); + if ((rv = xbps_configure_pkg(curpkgname)) != 0) + break; + } + prop_object_iterator_release(iter); + } else { + rv = xbps_configure_pkg(argv[1]); + } + + } else { + usage(); + } + +out: + xbps_release_regpkgdb_dict(); + if (rv != 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} diff --git a/bin/xbps-bin/remove.c b/bin/xbps-bin/remove.c new file mode 100644 index 00000000..6ea2e529 --- /dev/null +++ b/bin/xbps-bin/remove.c @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2008-2009 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 "defs.h" +#include "../xbps-repo/util.h" + +void +xbps_autoremove_pkgs(void) +{ + prop_array_t orphans; + prop_object_t obj; + prop_object_iterator_t iter; + const char *pkgname, *version; + size_t cols = 0; + int rv = 0; + bool first = false; + + /* + * Removes orphan pkgs. These packages were installed + * as dependency and any installed package does not depend + * on it currently. + */ + + orphans = xbps_find_orphan_packages(); + if (orphans == NULL) + exit(EXIT_FAILURE); + if (orphans != NULL && prop_array_count(orphans) == 0) { + printf("There are not orphaned packages currently.\n"); + exit(EXIT_SUCCESS); + } + + iter = prop_array_iterator(orphans); + if (iter == NULL) + goto out; + + printf("The following packages were installed automatically\n" + "(as dependencies) and aren't needed anymore:\n\n"); + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + cols += strlen(pkgname) + strlen(version) + 4; + if (cols <= 80) { + if (first == false) { + printf(" "); + first = true; + } + } else { + printf("\n "); + cols = strlen(pkgname) + strlen(version) + 4; + } + printf("%s-%s ", pkgname, version); + } + prop_object_iterator_reset(iter); + printf("\n\n"); + + if (xbps_noyes("Do you want to remove them?") == false) { + printf("Cancelled!\n"); + goto out2; + } + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + + printf("Removing package %s-%s ...\n", pkgname, version); + if ((rv = xbps_remove_pkg(pkgname, version, false)) != 0) + goto out2; + } +out2: + prop_object_iterator_release(iter); +out: + prop_object_release(orphans); + if (rv != 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} + +void +xbps_remove_installed_pkg(const char *pkgname, bool force) +{ + prop_array_t reqby; + prop_dictionary_t dict; + const char *version; + int rv = 0; + + /* + * First check if package is required by other packages. + */ + dict = xbps_find_pkg_installed_from_plist(pkgname); + if (dict == NULL) { + printf("Package %s is not installed.\n", pkgname); + goto out; + } + prop_dictionary_get_cstring_nocopy(dict, "version", &version); + + reqby = prop_dictionary_get(dict, "requiredby"); + if (reqby != NULL && prop_array_count(reqby) > 0) { + printf("WARNING! %s-%s is required by the following " + "packages:\n\n", pkgname, version); + (void)xbps_callback_array_iter_in_dict(dict, + "requiredby", list_strings_in_array, NULL); + printf("\n\n"); + if (!force) { + if (!xbps_noyes("Do you want to remove %s?", pkgname)) { + printf("Cancelling!\n"); + goto out; + } + } + printf("Forcing %s-%s for deletion!\n", pkgname, version); + } else { + if (!force) { + if (!xbps_noyes("Do you want to remove %s?", pkgname)) { + printf("Cancelling!\n"); + goto out; + } + } + } + + printf("Removing package %s-%s ...\n", pkgname, version); + if ((rv = xbps_remove_pkg(pkgname, version, false)) != 0) { + printf("Unable to remove %s-%s (%s).\n", + pkgname, version, strerror(errno)); + goto out; + } + +out: + xbps_release_regpkgdb_dict(); + if (rv != 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} diff --git a/bin/xbps-cmpver/Makefile b/bin/xbps-cmpver/Makefile new file mode 100644 index 00000000..a8e80c5d --- /dev/null +++ b/bin/xbps-cmpver/Makefile @@ -0,0 +1,4 @@ +BIN = xbps-cmpver + +TOPDIR = ../.. +include $(TOPDIR)/prog.mk diff --git a/bin/xbps-cmpver/main.c b/bin/xbps-cmpver/main.c new file mode 100644 index 00000000..abf99c36 --- /dev/null +++ b/bin/xbps-cmpver/main.c @@ -0,0 +1,19 @@ +/* + * Compare package and version strings + * @ 2008 + * Author: pancake + */ +#include + +int main(int argc, char **argv) +{ + if (argc < 3) { + printf("Usage: xbps-cmpver [installed] [required]\n"); + printf(" xbps-cmpver foo-1.2 foo-2.2 # $? = 1\n"); + printf(" xbps-cmpver foo-1.2 foo-1.1.0 # $? = 0\n"); + printf(" xbps-cmpver foo-1.2 foo-1.2 # $? = 0\n"); + return 1; + } + + return xbps_cmpver(argv[1], argv[2]); +} diff --git a/bin/xbps-digest/Makefile b/bin/xbps-digest/Makefile new file mode 100644 index 00000000..849850f9 --- /dev/null +++ b/bin/xbps-digest/Makefile @@ -0,0 +1,4 @@ +BIN = xbps-digest + +TOPDIR = ../.. +include $(TOPDIR)/prog.mk diff --git a/bin/xbps-digest/main.c b/bin/xbps-digest/main.c new file mode 100644 index 00000000..8fb1161c --- /dev/null +++ b/bin/xbps-digest/main.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2008 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 + +static void +usage(void) +{ + fprintf(stderr, "usage: xbps-digest ...\n"); + exit(EXIT_FAILURE); +} + +int +main(int argc, char **argv) +{ + SHA256_CTX ctx; + uint8_t buffer[BUFSIZ * 20], *digest; + ssize_t bytes; + int i, fd; + + if (argc < 2) + usage(); + + for (i = 1; i < argc; i++) { + if ((fd = open(argv[i], O_RDONLY)) == -1) { + printf("xbps-digest: cannot open %s (%s)\n", argv[i], + strerror(errno)); + exit(EXIT_FAILURE); + } + + digest = malloc(SHA256_DIGEST_STRING_LENGTH); + if (digest == NULL) { + printf("xbps-digest: malloc failed (%s)\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + SHA256_Init(&ctx); + while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) + SHA256_Update(&ctx, buffer, (size_t)bytes); + + printf("%s\n", SHA256_End(&ctx, digest)); + free(digest); + close(fd); + } + + exit(EXIT_SUCCESS); +} diff --git a/bin/xbps-pkgdb/Makefile b/bin/xbps-pkgdb/Makefile new file mode 100644 index 00000000..654a86d3 --- /dev/null +++ b/bin/xbps-pkgdb/Makefile @@ -0,0 +1,4 @@ +BIN = xbps-pkgdb + +TOPDIR = ../.. +include $(TOPDIR)/prog.mk diff --git a/bin/xbps-pkgdb/main.c b/bin/xbps-pkgdb/main.c new file mode 100644 index 00000000..60256620 --- /dev/null +++ b/bin/xbps-pkgdb/main.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2008-2009 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 + +static void +write_plist_file(prop_dictionary_t dict, const char *file) +{ + assert(dict != NULL || file != NULL); + + if (!prop_dictionary_externalize_to_file(dict, file)) { + prop_object_release(dict); + printf("=> ERROR: couldn't write to %s (%s)", + file, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void +usage(void) +{ + printf("usage: xbps-pkgdb [options] [action] [args]\n" + "\n" + " Available actions:\n" + " getpkgname, getpkgrevision, getpkgversion, register\n" + " sanitize-plist, unregister, version\n" + "\n" + " Action arguments:\n" + " getpkgname\t\t\n" + " getpkgrevision\t\n" + " getpkgversion\t\n" + " register\t\t \n" + " sanitize-plist\t\n" + " unregister\t\t \n" + " version\t\t\n" + "\n" + " Options shared by all actions:\n" + " -r\t\t\t\n" + "\n" + " Options used by the register action:\n" + " -a\t\t\tSet automatic installation flag.\n" + "\n" + " Examples:\n" + " $ xbps-pkgdb getpkgname foo-2.0\n" + " $ xbps-pkgdb getpkgrevision foo-2.0_1\n" + " $ xbps-pkgdb getpkgversion foo-2.0\n" + " $ xbps-pkgdb register pkgname 2.0 \"A short description\"\n" + " $ xbps-pkgdb sanitize-plist /blah/foo.plist\n" + " $ xbps-pkgdb unregister pkgname 2.0\n" + " $ xbps-pkgdb version pkgname\n"); + + exit(EXIT_FAILURE); +} + +int +main(int argc, char **argv) +{ + prop_dictionary_t dict; + const char *version; + char *plist, *pkgname, *in_chroot_env, *root = NULL; + bool automatic = false, in_chroot = false; + int c, rv = 0; + + while ((c = getopt(argc, argv, "ar:")) != -1) { + switch (c) { + case 'a': + /* Set automatic install flag */ + automatic = true; + break; + case 'r': + /* To specify the root directory */ + root = strdup(optarg); + if (root == NULL) + exit(EXIT_FAILURE); + xbps_set_rootdir(root); + break; + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + plist = xbps_xasprintf("%s/%s/%s", root, XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) { + printf("=> ERROR: couldn't find regpkdb file (%s)\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + in_chroot_env = getenv("in_chroot"); + if (in_chroot_env != NULL) + in_chroot = true; + + if (strcasecmp(argv[0], "register") == 0) { + /* Registers a package into the database */ + if (argc != 4) + usage(); + + dict = prop_dictionary_create(); + if (dict == NULL) + exit(EXIT_FAILURE); + prop_dictionary_set_cstring_nocopy(dict, "pkgname", argv[1]); + prop_dictionary_set_cstring_nocopy(dict, "version", argv[2]); + prop_dictionary_set_cstring_nocopy(dict, "short_desc", argv[3]); + + rv = xbps_set_pkg_state_installed(argv[1], + XBPS_PKG_STATE_INSTALLED); + if (rv != 0) + exit(EXIT_FAILURE); + + rv = xbps_register_pkg(dict, automatic); + if (rv == EEXIST) { + printf("%s=> %s-%s already registered.\n", + in_chroot ? "[chroot] " : "", argv[1], argv[2]); + } else if (rv != 0) { + printf("%s=> couldn't register %s-%s (%s).\n", + in_chroot ? "[chroot] " : "" , argv[1], argv[2], + strerror(rv)); + } else { + printf("%s=> %s-%s registered successfully.\n", + in_chroot ? "[chroot] " : "", argv[1], argv[2]); + } + + } else if (strcasecmp(argv[0], "unregister") == 0) { + /* Unregisters a package from the database */ + if (argc != 3) + usage(); + + rv = xbps_remove_pkg_dict_from_file(argv[1], plist); + if (rv == ENOENT) { + printf("=> ERROR: %s not registered in database.\n", + argv[1]); + } else if (rv != 0) { + printf("=> ERROR: couldn't unregister %s " + "from database (%s)\n", argv[1], strerror(rv)); + exit(EXIT_FAILURE); + } + + printf("%s=> %s-%s unregistered successfully.\n", + in_chroot ? "[chroot] " : "", argv[1], argv[2]); + + } else if (strcasecmp(argv[0], "version") == 0) { + /* Prints version of an installed package */ + if (argc != 2) + usage(); + + dict = xbps_find_pkg_from_plist(plist, argv[1]); + if (dict == NULL) + exit(EXIT_FAILURE); + + if (!prop_dictionary_get_cstring_nocopy(dict, "version", + &version)) + exit(EXIT_FAILURE); + + printf("%s\n", version); + prop_object_release(dict); + + } else if (strcasecmp(argv[0], "sanitize-plist") == 0) { + /* Sanitize a plist file (properly indent the file) */ + if (argc != 2) + usage(); + + dict = prop_dictionary_internalize_from_file(argv[1]); + if (dict == NULL) { + printf("=> ERROR: couldn't sanitize %s plist file " + "(%s)\n", argv[1], strerror(errno)); + exit(EXIT_FAILURE); + } + write_plist_file(dict, argv[1]); + + } else if (strcasecmp(argv[0], "getpkgversion") == 0) { + /* Returns the version of a pkg string */ + if (argc != 2) + usage(); + + version = xbps_get_pkg_version(argv[1]); + if (version == NULL) { + printf("Invalid string, expected -\n"); + exit(EXIT_FAILURE); + } + printf("%s\n", version); + + } else if (strcasecmp(argv[0], "getpkgname") == 0) { + /* Returns the name of a pkg string */ + if (argc != 2) + usage(); + + pkgname = xbps_get_pkg_name(argv[1]); + if (pkgname == NULL) { + printf("Invalid string, expected -\n"); + exit(EXIT_FAILURE); + } + printf("%s\n", pkgname); + free(pkgname); + + } else if (strcasecmp(argv[0], "getpkgrevision") == 0) { + /* Returns the revision of a pkg string */ + if (argc != 2) + usage(); + + version = xbps_get_pkg_revision(argv[1]); + if (version == NULL) + exit(EXIT_SUCCESS); + + printf("%s\n", version); + + } else { + usage(); + } + + exit(EXIT_SUCCESS); +} diff --git a/bin/xbps-repo/Makefile b/bin/xbps-repo/Makefile new file mode 100644 index 00000000..12e63462 --- /dev/null +++ b/bin/xbps-repo/Makefile @@ -0,0 +1,5 @@ +BIN = xbps-repo +OBJS = main.o util.o index.o + +TOPDIR = ../.. +include $(TOPDIR)/prog.mk diff --git a/bin/xbps-repo/index.c b/bin/xbps-repo/index.c new file mode 100644 index 00000000..3afa3b99 --- /dev/null +++ b/bin/xbps-repo/index.c @@ -0,0 +1,289 @@ +/*- + * Copyright (c) 2009 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 "index.h" + +/* Array of valid architectures */ +static const char *archdirs[] = { "i686", "x86_64", "noarch", NULL }; + +static prop_dictionary_t +repoidx_getdict(const char *pkgdir) +{ + prop_dictionary_t dict; + prop_array_t array; + char *plist; + + plist = xbps_get_pkg_index_plist(pkgdir); + if (plist == NULL) + return NULL; + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + dict = prop_dictionary_create(); + if (dict == NULL) + goto out; + + array = prop_array_create(); + if (array == NULL) { + prop_object_release(dict); + goto out; + } + + prop_dictionary_set(dict, "packages", array); + prop_object_release(array); + prop_dictionary_set_cstring_nocopy(dict, + "location-local", pkgdir); + prop_dictionary_set_cstring_nocopy(dict, + "pkgindex-version", XBPS_PKGINDEX_VERSION); + } +out: + free(plist); + + return dict; +} + +static int +repoidx_addpkg(const char *file, const char *filename, const char *pkgdir) +{ + prop_dictionary_t newpkgd, idxdict, curpkgd; + prop_array_t pkgar; + struct archive *ar; + struct archive_entry *entry; + struct stat st; + const char *pkgname, *version, *regver; + char *sha256, *plist; + int rv = 0; + + ar = archive_read_new(); + if (ar == NULL) { + rv = errno; + goto out; + } + /* Enable support for tar format and all compression methods */ + archive_read_support_compression_all(ar); + archive_read_support_format_tar(ar); + + if ((rv = archive_read_open_filename(ar, file, + ARCHIVE_READ_BLOCKSIZE)) == -1) { + rv = errno; + goto out1; + } + + /* Get existing or create repo index dictionary */ + idxdict = repoidx_getdict(pkgdir); + if (idxdict == NULL) { + rv = errno; + goto out1; + } + plist = xbps_get_pkg_index_plist(pkgdir); + if (plist == NULL) { + prop_dictionary_remove(idxdict, "packages"); + rv = ENOMEM; + goto out2; + } + + /* + * Open the binary package and read the props.plist + * into a buffer. + */ + while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) { + if (strstr(archive_entry_pathname(entry), XBPS_PKGPROPS) == 0) { + archive_read_data_skip(ar); + continue; + } + newpkgd = xbps_read_dict_from_archive_entry(ar, entry); + if (newpkgd == NULL) { + printf("%s: can't read %s metadata file, skipping!\n", + file, XBPS_PKGPROPS); + break; + } + + prop_dictionary_get_cstring_nocopy(newpkgd, "pkgname", + &pkgname); + prop_dictionary_get_cstring_nocopy(newpkgd, "version", + &version); + /* + * 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_dict(idxdict, "packages", pkgname); + if (curpkgd) { + prop_dictionary_get_cstring_nocopy(curpkgd, + "version", ®ver); + if (xbps_cmpver(version, regver) <= 0) { + printf("Skipping %s. Version %s already " + "registered.\n", filename, regver); + prop_object_release(newpkgd); + archive_read_data_skip(ar); + break; + } + /* + * Current package is newer than the one that is + * registered actually, remove old package from + * the index. + */ + rv = xbps_remove_pkg_from_dict(idxdict, + "packages", pkgname); + if (rv != 0) { + prop_object_release(newpkgd); + break; + } + } + + /* + * We have the dictionary now, add the required + * objects for the index. + */ + prop_dictionary_set_cstring_nocopy(newpkgd, "filename", + filename); + sha256 = xbps_get_file_hash(file); + if (sha256 == NULL) { + prop_object_release(newpkgd); + rv = errno; + break; + } + prop_dictionary_set_cstring(newpkgd, "filename-sha256", + sha256); + free(sha256); + + if (stat(file, &st) == -1) { + prop_object_release(newpkgd); + rv = errno; + break; + } + prop_dictionary_set_uint64(newpkgd, "filename-size", + (uint64_t)st.st_size); + /* + * Add dictionary into the index and update package count. + */ + pkgar = prop_dictionary_get(idxdict, "packages"); + if (pkgar == NULL) { + prop_object_release(newpkgd); + rv = errno; + break; + } + if (!xbps_add_obj_to_array(pkgar, newpkgd)) { + prop_object_release(newpkgd); + rv = EINVAL; + break; + } + + prop_dictionary_set_uint64(idxdict, "total-pkgs", + prop_array_count(pkgar)); + if (!prop_dictionary_externalize_to_file(idxdict, plist)) { + rv = errno; + break; + } + printf("Registered %s-%s in package index.\n", + pkgname, version); + break; + } + + free(plist); +out2: + prop_object_release(idxdict); +out1: + archive_read_finish(ar); +out: + return rv; +} + +int +xbps_repo_genindex(const char *pkgdir) +{ + struct dirent *dp; + DIR *dirp; + struct utsname un; + char *binfile, *path; + size_t i; + int rv = 0; + bool foundpkg = false; + + if (uname(&un) == -1) + return errno; + /* + * Iterate over the known architecture directories to find + * binary packages. + */ + for (i = 0; archdirs[i] != NULL; i++) { + if ((strcmp(archdirs[i], un.machine)) && + (strcmp(archdirs[i], "noarch"))) + continue; + + path = xbps_xasprintf("%s/%s", pkgdir, archdirs[i]); + if (path == NULL) + return errno; + + dirp = opendir(path); + if (dirp == NULL) { + free(path); + continue; + } + + while ((dp = readdir(dirp)) != NULL) { + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0)) + continue; + + /* Ignore unknown files */ + if (strstr(dp->d_name, ".xbps") == NULL) + continue; + + foundpkg = true; + binfile = xbps_xasprintf("%s/%s", path, dp->d_name); + if (binfile == NULL) { + (void)closedir(dirp); + free(path); + return errno; + } + rv = repoidx_addpkg(binfile, dp->d_name, pkgdir); + free(binfile); + if (rv != 0) { + (void)closedir(dirp); + free(path); + return rv; + } + } + (void)closedir(dirp); + free(path); + } + + if (foundpkg == false) + rv = ENOENT; + + return rv; +} diff --git a/bin/xbps-repo/index.h b/bin/xbps-repo/index.h new file mode 100644 index 00000000..2de15618 --- /dev/null +++ b/bin/xbps-repo/index.h @@ -0,0 +1,31 @@ +/*- + * Copyright (c) 2009 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_REPO_INDEX_H_ +#define _XBPS_REPO_INDEX_H_ + +int xbps_repo_genindex(const char *); + +#endif /* !_XBPS_REPO_INDEX_H_ */ diff --git a/bin/xbps-repo/main.c b/bin/xbps-repo/main.c new file mode 100644 index 00000000..cc281eb2 --- /dev/null +++ b/bin/xbps-repo/main.c @@ -0,0 +1,288 @@ +/*- + * Copyright (c) 2008-2009 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 "index.h" +#include "util.h" + +typedef struct repository_info { + const char *index_version; + const char *location_local; + const char *location_remote; + uint64_t total_pkgs; +} repo_info_t; + +static bool sanitize_localpath(char *, const char *); +static bool pkgindex_getinfo(prop_dictionary_t, repo_info_t *); +static void usage(void); + +static void +usage(void) +{ + printf("Usage: xbps-repo [options] [action] [arguments]\n\n" + " Available actions:\n" + " add, genindex, list, remove, search, show\n" + " Actions with arguments:\n" + " add\t\t\n" + " genindex\t\n" + " remove\t\n" + " search\t\n" + " show\t\n" + " Options shared by all actions:\n" + " -r\t\t\n" + "\n" + " Examples:\n" + " $ xbps-repo add /path/to/directory\n" + " $ xbps-repo add http://www.location.org/xbps-repo\n" + " $ xbps-repo list\n" + " $ xbps-repo remove /path/to/directory\n" + " $ xbps-repo search klibc\n" + " $ xbps-repo show klibc\n" + " $ xbps-repo genindex /path/to/packages/dir\n"); + exit(EXIT_FAILURE); +} + +static bool +pkgindex_getinfo(prop_dictionary_t dict, repo_info_t *ri) +{ + assert(dict != NULL || ri != NULL); + + if (!prop_dictionary_get_cstring_nocopy(dict, + "pkgindex-version", &ri->index_version)) + return false; + + if (!prop_dictionary_get_cstring_nocopy(dict, + "location-local", &ri->location_local)) + return false; + + /* This one is optional, thus don't panic */ + prop_dictionary_get_cstring_nocopy(dict, "location-remote", + &ri->location_remote); + + if (!prop_dictionary_get_uint64(dict, "total-pkgs", + &ri->total_pkgs)) + return false; + + /* Reject empty repositories, how could this happen? :-) */ + if (ri->total_pkgs <= 0) + return false; + + return true; +} + +static bool +sanitize_localpath(char *buf, const char *path) +{ + char *dirnp, *basenp, *dir, *base, *tmp; + bool rv = false; + + dir = strdup(path); + if (dir == NULL) + return false; + + base = strdup(path); + if (base == NULL) { + free(dir); + return false; + } + + dirnp = dirname(dir); + if (strcmp(dirnp, ".") == 0) + goto out; + + basenp = basename(base); + if (strcmp(basenp, base) == 0) + goto out; + + tmp = strncpy(buf, dirnp, PATH_MAX - 1); + if (sizeof(*tmp) >= PATH_MAX) + goto out; + + buf[strlen(buf) + 1] = '\0'; + if (strcmp(dirnp, "/")) + strncat(buf, "/", 1); + strncat(buf, basenp, PATH_MAX - strlen(buf) - 1); + rv = true; + +out: + free(dir); + free(base); + + return rv; +} + +int +main(int argc, char **argv) +{ + prop_dictionary_t dict; + repo_info_t *rinfo = NULL; + char dpkgidx[PATH_MAX], *plist, *root = NULL; + int c, rv = 0; + + while ((c = getopt(argc, argv, "r:")) != -1) { + switch (c) { + case 'r': + /* To specify the root directory */ + root = optarg; + xbps_set_rootdir(root); + break; + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (strcasecmp(argv[0], "add") == 0) { + /* Adds a new repository to the pool. */ + if (argc != 2) + usage(); + + if (!sanitize_localpath(dpkgidx, argv[1])) + exit(EXIT_FAILURE); + + /* Temp buffer to verify pkgindex file. */ + plist = xbps_get_pkg_index_plist(dpkgidx); + if (plist == NULL) + exit(EXIT_FAILURE); + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + printf("Directory %s does not contain any " + "xbps pkgindex file.\n", dpkgidx); + free(plist); + exit(EXIT_FAILURE); + } + + rinfo = malloc(sizeof(*rinfo)); + if (rinfo == NULL) { + prop_object_release(dict); + free(plist); + exit(EXIT_FAILURE); + } + + if (!pkgindex_getinfo(dict, rinfo)) { + printf("'%s' is incomplete.\n", plist); + prop_object_release(dict); + free(rinfo); + free(plist); + exit(EXIT_FAILURE); + } + + if ((rv = xbps_register_repository(dpkgidx)) != 0) { + printf("ERROR: couldn't register repository (%s)\n", + strerror(rv)); + prop_object_release(dict); + free(rinfo); + free(plist); + exit(EXIT_FAILURE); + } + + printf("Added repository at %s (%s) with %ju packages.\n", + rinfo->location_local, rinfo->index_version, + rinfo->total_pkgs); + + prop_object_release(dict); + free(rinfo); + free(plist); + + } else if (strcasecmp(argv[0], "list") == 0) { + /* Lists all repositories registered in pool. */ + if (argc != 1) + usage(); + + (void)xbps_callback_array_iter_in_repolist( + list_strings_sep_in_array, NULL); + + } else if ((strcasecmp(argv[0], "rm") == 0) || + (strcasecmp(argv[0], "remove") == 0)) { + /* Remove a repository from the pool. */ + if (argc != 2) + usage(); + + if (!sanitize_localpath(dpkgidx, argv[1])) + exit(EXIT_FAILURE); + + if ((rv = xbps_unregister_repository(dpkgidx)) != 0) { + if (rv == ENOENT) + printf("Repository '%s' not actually " + "registered.\n", dpkgidx); + else + printf("ERROR: couldn't unregister " + "repository (%s)\n", strerror(rv)); + exit(EXIT_FAILURE); + } + + } else if (strcasecmp(argv[0], "search") == 0) { + /* + * Search for a package by looking at pkgname/short_desc + * by using shell style match patterns (fnmatch(3)). + */ + if (argc != 2) + usage(); + + (void)xbps_callback_array_iter_in_repolist( + search_string_in_pkgs, argv[1]); + + } else if (strcasecmp(argv[0], "show") == 0) { + /* Shows info about a binary package. */ + if (argc != 2) + usage(); + + rv = xbps_callback_array_iter_in_repolist( + show_pkg_info_from_repolist, argv[1]); + if (rv == 0 && errno == ENOENT) { + printf("Unable to locate package '%s' from " + "repository pool.\n", argv[1]); + exit(EXIT_FAILURE); + } + + } else if (strcasecmp(argv[0], "genindex") == 0) { + /* Generates a package repository index plist file. */ + if (argc != 2) + usage(); + + rv = xbps_repo_genindex(argv[1]); + exit(rv); + + } else { + usage(); + } + + exit(EXIT_SUCCESS); +} diff --git a/bin/xbps-repo/util.c b/bin/xbps-repo/util.c new file mode 100644 index 00000000..ca4189ee --- /dev/null +++ b/bin/xbps-repo/util.c @@ -0,0 +1,377 @@ +/*- + * Copyright (c) 2008-2009 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 "util.h" + +static void show_pkg_info(prop_dictionary_t); +static int show_pkg_namedesc(prop_object_t, void *, bool *); + +static void +show_pkg_info(prop_dictionary_t dict) +{ + prop_object_t obj; + const char *sep; + char size[64]; + int rv = 0; + + assert(dict != NULL); + assert(prop_dictionary_count(dict) != 0); + + obj = prop_dictionary_get(dict, "pkgname"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf("Package: %s\n", prop_string_cstring_nocopy(obj)); + + obj = prop_dictionary_get(dict, "installed_size"); + if (obj && prop_object_type(obj) == PROP_TYPE_NUMBER) { + printf("Installed size: "); + rv = xbps_humanize_number(size, 5, + (int64_t)prop_number_unsigned_integer_value(obj), + "", HN_AUTOSCALE, HN_B|HN_DECIMAL|HN_NOSPACE); + if (rv == -1) + printf("%ju\n", + prop_number_unsigned_integer_value(obj)); + else + printf("%s\n", size); + } + + obj = prop_dictionary_get(dict, "maintainer"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf("Maintainer: %s\n", prop_string_cstring_nocopy(obj)); + + obj = prop_dictionary_get(dict, "architecture"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf("Architecture: %s\n", prop_string_cstring_nocopy(obj)); + + obj = prop_dictionary_get(dict, "version"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf("Version: %s\n", prop_string_cstring_nocopy(obj)); + + obj = prop_dictionary_get(dict, "filename"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) { + printf("Filename: %s", prop_string_cstring_nocopy(obj)); + obj = prop_dictionary_get(dict, "filename-size"); + if (obj && prop_object_type(obj) == PROP_TYPE_NUMBER) { + rv = xbps_humanize_number(size, 5, + (int64_t)prop_number_unsigned_integer_value(obj), + "", HN_AUTOSCALE, HN_B|HN_DECIMAL|HN_NOSPACE); + if (rv == -1) + printf(" (size: %ju)\n", + prop_number_unsigned_integer_value(obj)); + else + printf(" (size: %s)\n", size); + } else + printf("\n"); + } + + obj = prop_dictionary_get(dict, "filename-sha256"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf("SHA256: %s\n", prop_string_cstring_nocopy(obj)); + + obj = prop_dictionary_get(dict, "run_depends"); + if (obj && prop_object_type(obj) == PROP_TYPE_ARRAY) { + printf("Dependencies:\n"); + (void)xbps_callback_array_iter_in_dict(dict, "run_depends", + list_strings_in_array, NULL); + printf("\n\n"); + } + + obj = prop_dictionary_get(dict, "conf_files"); + if (obj && prop_object_type(obj) == PROP_TYPE_ARRAY) { + printf("Configuration files:\n"); + sep = " "; + (void)xbps_callback_array_iter_in_dict(dict, "conf_files", + list_strings_sep_in_array, __UNCONST(sep)); + printf("\n"); + } + + obj = prop_dictionary_get(dict, "keep_dirs"); + if (obj && prop_object_type(obj) == PROP_TYPE_ARRAY) { + printf("Permanent directories:\n"); + sep = " "; + (void)xbps_callback_array_iter_in_dict(dict, "keep_dirs", + list_strings_sep_in_array, __UNCONST(sep)); + printf("\n"); + } + + obj = prop_dictionary_get(dict, "short_desc"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf("Description: %s", prop_string_cstring_nocopy(obj)); + + obj = prop_dictionary_get(dict, "long_desc"); + if (obj && prop_object_type(obj) == PROP_TYPE_STRING) + printf(" %s\n", prop_string_cstring_nocopy(obj)); +} + +int +search_string_in_pkgs(prop_object_t obj, void *arg, bool *loop_done) +{ + prop_dictionary_t dict; + const char *repofile; + char *plist; + + (void)loop_done; + + assert(prop_object_type(obj) == PROP_TYPE_STRING); + + /* Get the location of pkgindex file. */ + repofile = prop_string_cstring_nocopy(obj); + assert(repofile != NULL); + + plist = xbps_get_pkg_index_plist(repofile); + if (plist == NULL) { + errno = ENOENT; + return 0; + } + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + free(plist); + return 0; + } + + printf("From %s repository ...\n", repofile); + xbps_callback_array_iter_in_dict(dict, "packages", + show_pkg_namedesc, arg); + prop_object_release(dict); + free(plist); + + return 0; +} + +int +show_pkg_info_from_metadir(const char *pkgname) +{ + prop_dictionary_t pkgd; + const char *rootdir; + char *plist; + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/metadata/%s/%s", rootdir, + XBPS_META_PATH, pkgname, XBPS_PKGPROPS); + if (plist == NULL) + return EINVAL; + + pkgd = prop_dictionary_internalize_from_file(plist); + if (pkgd == NULL) { + free(plist); + return errno; + } + + show_pkg_info(pkgd); + prop_object_release(pkgd); + free(plist); + + return 0; +} + +int +show_pkg_files_from_metadir(const char *pkgname) +{ + prop_dictionary_t pkgd; + prop_array_t array; + prop_object_iterator_t iter = NULL; + prop_object_t obj; + const char *destdir, *file; + char *plist, *array_str = "files"; + int i, rv = 0; + + destdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/metadata/%s/%s", destdir, + XBPS_META_PATH, pkgname, XBPS_PKGFILES); + if (plist == NULL) + return EINVAL; + + pkgd = prop_dictionary_internalize_from_file(plist); + if (pkgd == NULL) { + free(plist); + return errno; + } + free(plist); + + /* Links. */ + array = prop_dictionary_get(pkgd, "links"); + if (array && prop_array_count(array) > 0) { + iter = xbps_get_array_iter_from_dict(pkgd, "links"); + if (iter == NULL) { + rv = EINVAL; + goto out; + } + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + printf("%s\n", file); + } + prop_object_iterator_release(iter); + } + + /* Files and configuration files. */ + for (i = 0; i < 2; i++) { + if (i == 0) + array_str = "conf_files"; + else + array_str = "files"; + + array = prop_dictionary_get(pkgd, array_str); + if (array == NULL || prop_array_count(array) == 0) + continue; + + iter = xbps_get_array_iter_from_dict(pkgd, array_str); + if (iter == NULL) { + rv = EINVAL; + goto out; + } + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &file); + printf("%s\n", file); + } + prop_object_iterator_release(iter); + } +out: + prop_object_release(pkgd); + + return rv; +} + +int +show_pkg_info_from_repolist(prop_object_t obj, void *arg, bool *loop_done) +{ + prop_dictionary_t dict, pkgdict; + prop_string_t oloc; + const char *repofile, *repoloc; + char *plist; + + assert(prop_object_type(obj) == PROP_TYPE_STRING); + + /* Get the location */ + repofile = prop_string_cstring_nocopy(obj); + + plist = xbps_get_pkg_index_plist(repofile); + if (plist == NULL) + return EINVAL; + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL || prop_dictionary_count(dict) == 0) { + free(plist); + errno = ENOENT; + return 0; + } + + pkgdict = xbps_find_pkg_in_dict(dict, "packages", arg); + if (pkgdict == NULL) { + prop_object_release(dict); + free(plist); + errno = ENOENT; + return 0; + } + + oloc = prop_dictionary_get(dict, "location-remote"); + if (oloc == NULL) + oloc = prop_dictionary_get(dict, "location-local"); + + if (oloc && prop_object_type(oloc) == PROP_TYPE_STRING) + repoloc = prop_string_cstring_nocopy(oloc); + else { + prop_object_release(dict); + free(plist); + return EINVAL; + } + + printf("Repository: %s\n", repoloc); + show_pkg_info(pkgdict); + *loop_done = true; + prop_object_release(dict); + free(plist); + + return 0; +} + +static int +show_pkg_namedesc(prop_object_t obj, void *arg, bool *loop_done) +{ + const char *pkgname, *desc, *ver, *pattern = arg; + + (void)loop_done; + + assert(prop_object_type(obj) == PROP_TYPE_DICTIONARY); + assert(pattern != NULL); + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "short_desc", &desc); + prop_dictionary_get_cstring_nocopy(obj, "version", &ver); + assert(ver != NULL); + + if ((fnmatch(pattern, pkgname, 0) == 0) || + (fnmatch(pattern, desc, 0) == 0)) + printf(" %s-%s - %s\n", pkgname, ver, desc); + + return 0; +} + +int +list_strings_in_array(prop_object_t obj, void *arg, bool *loop_done) +{ + static size_t cols; + static bool first; + + (void)arg; + (void)loop_done; + + assert(prop_object_type(obj) == PROP_TYPE_STRING); + + cols += strlen(prop_string_cstring_nocopy(obj)) + 4; + if (cols <= 80) { + if (first == false) { + printf(" "); + first = true; + } + } else { + printf("\n "); + cols = strlen(prop_string_cstring_nocopy(obj)) + 4; + } + printf("%s ", prop_string_cstring_nocopy(obj)); + + return 0; +} + +int +list_strings_sep_in_array(prop_object_t obj, void *arg, bool *loop_done) +{ + const char *sep = arg; + + (void)loop_done; + + assert(prop_object_type(obj) == PROP_TYPE_STRING); + + printf("%s%s\n", sep ? sep : "", prop_string_cstring_nocopy(obj)); + + return 0; +} diff --git a/bin/xbps-repo/util.h b/bin/xbps-repo/util.h new file mode 100644 index 00000000..92f5db2c --- /dev/null +++ b/bin/xbps-repo/util.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2008-2009 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_REPO_UTIL_H_ +#define _XBPS_REPO_UTIL_H_ + +int search_string_in_pkgs(prop_object_t, void *, bool *); +int show_pkg_info_from_metadir(const char *); +int show_pkg_files_from_metadir(const char *); +int show_pkg_info_from_repolist(prop_object_t, void *, bool *); +int list_strings_in_array(prop_object_t, void *, bool *); +int list_strings_sep_in_array(prop_object_t, void *, bool *); + +#endif /* !_XBPS_REPO_UTIL_H_ */ diff --git a/bin/xbps-src/Makefile b/bin/xbps-src/Makefile new file mode 100644 index 00000000..5efa064c --- /dev/null +++ b/bin/xbps-src/Makefile @@ -0,0 +1,18 @@ +TOPDIR = ../.. +include $(TOPDIR)/vars.mk + +BIN = xbps-src + +.PHONY: all +all: + sed -e "s|@@XBPS_INSTALL_PREFIX@@|$(PREFIX)|g" \ + -e "s|@@XBPS_INSTALL_ETCDIR@@|$(ETCDIR)|g" \ + main.sh > xbps-src + +.PHONY: clean +clean: + -rm -f $(BIN) + +install: all + install -d $(SBINDIR) + install -m 755 $(BIN) $(SBINDIR) diff --git a/bin/xbps-src/main.sh b/bin/xbps-src/main.sh new file mode 100755 index 00000000..d45420a4 --- /dev/null +++ b/bin/xbps-src/main.sh @@ -0,0 +1,291 @@ +#!/bin/sh +# +# xbps - A simple, minimal, fast and uncomplete build package system. +#- +# Copyright (c) 2008 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. +#- +trap "echo && exit 1" INT QUIT + +: ${XBPS_CONFIG_FILE:=@@XBPS_INSTALL_ETCDIR@@/xbps.conf} + +: ${progname:=$(basename $0)} +: ${fakeroot_cmd:=fakeroot} +: ${fetch_cmd:=wget} +: ${xbps_machine:=$(uname -m)} + +usage() +{ + cat << _EOF +$progname: [-C] [-c ] [-u] + +Targets: + build Build a package (fetch + extract + configure + build). + build-pkg [|all] Build a binary package from . + Package must be installed into destdir. If the + keyword is used instead of , all packages + currently installed will be used. + chroot Enter to the chroot in masterdir. + configure Configure a package (fetch + extract + configure). + extract Extract distribution file(s) into build directory. + fetch Download distribution file(s). + info Show information about . + install-destdir build + install into destdir. + install install-destdir + stow. + list List installed packages in masterdir. + listfiles List installed files from . + remove Remove package completely (destdir + masterdir). + stow Copy files from destdir into masterdir and + register package in database. + unstow Remove files from masterdir and unregister + package from database. + +Options: + -C Do not remove build directory after successful installation. + -c Path to global configuration file: + if not specified @@XBPS_INSTALL_ETCDIR@@/xbps.conf is used. + -u Update the checksum in template file if used in 'fetch' target. +_EOF + exit 1 +} + +check_path() +{ + eval local orig="$1" + + case "$orig" in + /) ;; + /*) orig="${orig%/}" ;; + *) orig="$(pwd)/${orig%/}" ;; + esac + + path_fixed="$orig" +} + +run_file() +{ + local file="$1" + + check_path "$file" + . $path_fixed +} + +set_defvars() +{ + local DDIRS i + + : ${XBPS_TEMPLATESDIR:=$XBPS_DISTRIBUTIONDIR/templates} + : ${XBPS_TRIGGERSDIR:=$XBPS_DISTRIBUTIONDIR/triggers} + : ${XBPS_HELPERSDIR:=$XBPS_TEMPLATESDIR/helpers} + : ${XBPS_DBDIR:=$XBPS_MASTERDIR/var/db/xbps} + : ${XBPS_META_PATH:=$XBPS_DBDIR/} + : ${XBPS_PKGMETADIR:=$XBPS_DBDIR/metadata} + : ${XBPS_SHUTILSDIR:=$XBPS_DISTRIBUTIONDIR/shutils} + + DDIRS="XBPS_TEMPLATESDIR XBPS_TRIGGERSDIR" + DDIRS="$DDIRS XBPS_HELPERSDIR XBPS_SHUTILSDIR" + for i in ${DDIRS}; do + eval val="\$$i" + [ ! -d "$val" ] && msg_error "cannot find $i, aborting." + done + + XBPS_REGPKGDB_CMD="xbps-pkgdb -r $XBPS_MASTERDIR" + XBPS_BIN_CMD="xbps-bin -r $XBPS_MASTERDIR" +} + +# +# Checks that all required variables specified in the configuration +# file are properly working. +# +check_config_vars() +{ + local cffound= + local f= + + if [ -z "$config_file_specified" ]; then + config_file_paths="$XBPS_CONFIG_FILE ./etc/xbps.conf" + for f in $config_file_paths; do + [ -f $f ] && XBPS_CONFIG_FILE=$f && \ + cffound=yes && break + done + [ -z "$cffound" ] && msg_error "cannot find a config file" + fi + + run_file ${XBPS_CONFIG_FILE} + XBPS_CONFIG_FILE=$path_fixed + + if [ ! -f "$XBPS_CONFIG_FILE" ]; then + msg_error "cannot find configuration file: $XBPS_CONFIG_FILE" + fi + + local XBPS_VARS="XBPS_MASTERDIR XBPS_DESTDIR XBPS_BUILDDIR \ + XBPS_SRCDISTDIR" + + for f in ${XBPS_VARS}; do + eval val="\$$f" + [ -z "$val" ] && msg_error "'$f' not set in configuration file" + + if [ ! -d "$val" ]; then + mkdir "$val" + [ $? -ne 0 ] && msg_error "couldn't create '$f' directory" + fi + done + + export PATH="$PATH:@@XBPS_INSTALL_PREFIX@@/sbin" +} + +# +# main() +# +while getopts "Cc:u" opt; do + case $opt in + C) dontrm_builddir=yes;; + c) config_file_specified=yes; XBPS_CONFIG_FILE="$OPTARG";; + u) update_checksum=yes;; + --) shift; break;; + esac +done +shift $(($OPTIND - 1)) + +[ $# -eq 0 -o $# -gt 2 ] && usage + +target="$1" +if [ -z "$target" ]; then + echo "=> ERROR: missing target." + usage +fi + +# +# Check configuration vars before anyting else, and set defaults vars. +# +check_config_vars +set_defvars +. $XBPS_SHUTILSDIR/common_funcs.sh + +# Main switch +case "$target" in +build|configure) + . $XBPS_SHUTILSDIR/tmpl_funcs.sh + setup_tmpl $2 + + if [ -z "$base_chroot" -a -z "$in_chroot" ]; then + . $XBPS_SHUTILSDIR/chroot.sh + if [ "$target" = "build" ]; then + xbps_chroot_handler build $2 + else + xbps_chroot_handler configure $2 + fi + else + . $XBPS_SHUTILSDIR/fetch_funcs.sh + fetch_distfiles $2 + if [ ! -f "$XBPS_EXTRACT_DONE" ]; then + . $XBPS_SHUTILSDIR/extract_funcs.sh + extract_distfiles $2 + fi + if [ "$target" = "configure" ]; then + . $XBPS_SHUTILSDIR/configure_funcs.sh + configure_src_phase $2 + else + if [ ! -f "$XBPS_CONFIGURE_DONE" ]; then + . $XBPS_SHUTILSDIR/configure_funcs.sh + configure_src_phase $2 + fi + . $XBPS_SHUTILSDIR/build_funcs.sh + build_src_phase $2 + fi + fi + ;; +build-pkg) + . $XBPS_SHUTILSDIR/make-binpkg.sh + . $XBPS_SHUTILSDIR/tmpl_funcs.sh + if [ "$2" = "all" ]; then + for f in $($XBPS_BIN_CMD list|awk '{print $1}'); do + pkg=$(xbps-pkgdb getpkgname $f) + setup_tmpl ${pkg} + xbps_make_binpkg ${pkg} + reset_tmpl_vars + done + else + setup_tmpl $2 + xbps_make_binpkg $2 + fi + ;; +chroot) + . $XBPS_SHUTILSDIR/chroot.sh + xbps_chroot_handler chroot dummy + ;; +extract|fetch|info) + . $XBPS_SHUTILSDIR/tmpl_funcs.sh + setup_tmpl $2 + if [ "$target" = "info" ]; then + . $XBPS_SHUTILSDIR/tmpl_funcs.sh + info_tmpl $2 + exit $? + fi + if [ "$target" = "fetch" ]; then + . $XBPS_SHUTILSDIR/fetch_funcs.sh + fetch_distfiles $2 $update_checksum + exit $? + fi + . $XBPS_SHUTILSDIR/extract_funcs.sh + extract_distfiles $2 + ;; +install|install-destdir) + [ -z "$2" ] && msg_error "missing package name after target." + [ "$target" = "install-destdir" ] && install_destdir_target=yes + . $XBPS_SHUTILSDIR/pkgtarget_funcs.sh + install_pkg $2 + ;; +list|listfiles) + if [ "$target" = "list" ]; then + $XBPS_BIN_CMD list + exit $? + fi + . $XBPS_SHUTILSDIR/pkgtarget_funcs.sh + list_pkg_files $2 + ;; +remove) + [ -z "$2" ] && msg_error "missing package name after target." + . $XBPS_SHUTILSDIR/pkgtarget_funcs.sh + remove_pkg $2 + ;; +stow) + stow_flag=yes + . $XBPS_SHUTILSDIR/tmpl_funcs.sh + setup_tmpl $2 + . $XBPS_SHUTILSDIR/stow_funcs.sh + stow_pkg $2 + ;; +unstow) + . $XBPS_SHUTILSDIR/tmpl_funcs.sh + setup_tmpl $2 + . $XBPS_SHUTILSDIR/stow_funcs.sh + unstow_pkg $2 + ;; +*) + echo "=> ERROR: invalid target: $target." + usage +esac + +# Agur +exit $? diff --git a/doc/BINPKG_INFO b/doc/BINPKG_INFO new file mode 100644 index 00000000..0c23d55f --- /dev/null +++ b/doc/BINPKG_INFO @@ -0,0 +1,105 @@ +------------------------------------------------------------------------------ + BRIEF INTRODUCTION +------------------------------------------------------------------------------ + +A binary package built with xbps is a normal tar(1) archive, compressed +with bzip2 and has the following structure: + + Package metadata + ----------------- + /INSTALL + /REMOVE + /files.plist + /props.plist + + Package data + ----------------- + /usr + /var + /etc + ... + +Metadata info is stored in the "/var/db/xbps/metadata/$pkgname" +directory and two files will be always be present: files.plist +and props.plist. + +The files.plist file contains the list of files/links/dirs that package +will install, as well as SHA256 hashes for files. + +The props.plist file contains package metadata properties and has the +following structure: + + + pkgname + foo + + version + 3.40 + + maintainer + The Master BOFH + + short_desc + Foo is a virtual package + + long_desc + + Foo is a virtual package to show how the metadata props.plist file works + with xbps handling binary packages. + + architecture + x86_64 + + installed_size + 500000 + + configuration_files + + /etc/foo.conf + ... + + run_depends + + bofh-2.0 + blab-1.1 + ... + + ... + + +The INSTALL/REMOVE executables allows you to trigger any action +at pre/post installation/removal of the binary package. + +The package's dictionary will also be written into the repository's package +index file, that describes information about a binary package on it. +See the BINPKG_REPOSITORY file for more info about repositories. + +------------------------------------------------------------------------------ + HOW TO USE BINARY PACKAGES +------------------------------------------------------------------------------ + +To install binary packages, firstly a repository must be created as well as +some binary packages for it. The flow for this task is: + + 1- xbps-src install + 2- xbps-src build-pkg all [requires sudo access] + 3- xbps-repo genindex $XBPS_PACKAGES + 4- xbps-repo add $XBPS_PACKAGES + 5- xbps-bin install -r /rootdir + +So the tasks are: install the package into destdir (and all its dependencies), +build the binary package from the required package, generate the repository +index, add the repository into the pool and install the binary package. + +Please note that by default, the xbps-* utils accept the -r flag, to specify +the root directory for all operations, in that case the package will be +installed into and metadata files into /var/db/xbps. + +Don't forget to set this flag if you aren't using xbps as the primary +package manager in your system, otherwise it could overwrite some files! + +See the BINPKG_REPOSITORY file for more info about repositories for +binary packages or SRCPKG_INFO for source packages. + +------------------------------------------------------------------------------ + Juan Romero Pardines diff --git a/doc/BINPKG_REPOSITORY b/doc/BINPKG_REPOSITORY new file mode 100644 index 00000000..a5a2e378 --- /dev/null +++ b/doc/BINPKG_REPOSITORY @@ -0,0 +1,116 @@ +------------------------------------------------------------------------------ + BRIEF INTRODUCTION +------------------------------------------------------------------------------ + +A repository for binary packages contains the packages itself, and +an index file describing the information about available packages. + +The structure for this file is just the same than the plist file used +to register installed packages, aka "an array of dictionaries" and +a "dictionary per package". Additional objects are added into the +main dictionary to specify more info, like: + + - pkgindex-version: version used to build the index. + - location-local: local path to the repository. + - location-remote: remote URI repository. + - total-pkgs: total of number of available packages. + +"location-local" will always be created, and it might be exported via +a remote location specified with "location-remote". + +The package dictionary will be the same than the one available in +package's metadata directory "/var/db/xbps/metadata/$pkgname/props.plist", +but some additional objects are added to provide enough info for +the repository itself: + + - filename: name (and path relative to current dir) for the binary + package. + - filename-sha256: SHA256 hash of the binary package. + +Here's how the package index plist file shall look like in a repository: + + + pkgindex-version + 1.0 + location-local + /xbps/repo/local + location-remote + http://www.xbps-remote.org/repo/public + total-pkgs + 666 + available-packages + + + pkgname + klibc + version + 1.5.17 + filename + klibc-1.5.17.x86_64.xbps + filename-sha256 + 7b0de0521983037107cc33f2b1514126432f86ac2be1ef9b9dc51a1e959ea777 + architecture + x86_64 + installed_size + 9471141 + maintainer + Juan RP xtraeme@gmail.com + short_desc + Minimal libc subset for use with initramfs + long_desc + + klibc is intended to be a minimalistic libc subset for use with initramfs. + It is deliberately written for small size, minimal entanglement, and + portability, not speed. It is definitely a work in progress and a lot of + things are still missing. + ... + + ... + + + +------------------------------------------------------------------------------ + HOW TO USE BINPKGS WITH REPOSITORIES +------------------------------------------------------------------------------ + +To build binary packages from all currently installed packages in +XBPS_MASTERDIR: + + $ xbps-src build-pkg all + +To generate the repository package index for your $XBPS_PACKAGESDIR +setting in the configuration file: + + $ xbps-repo genindex /path/to/dir + +After this you can add your own local repository with binary packages: + + $ xbps-repo add /path/to/dir + Added repository at /path/to/dir (1.0) with 6 packages. + $ + +Once it's registered, you can start searching/installing/removing +binary packages. You can add multiple repositories, the order for searching +is the same than they were added; check it with: + + $ xbps-repo list + /storage/xbps/binpkgs + /path/to/dir + $ + +The first repository that has the metadata for a package wins, if not found +it will search in all them until it's found. A repository can also be +unregistered from the pool: + + $ xbps-repo remove /path/to/dir + +To show information about available packages in the repository pool: + + $ xbps-repo show package + +To search for binary packages by specifying a string: + + $ xbps-repo search mypkg + +------------------------------------------------------------------------------ + Juan Romero Pardines diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..134ad9f8 --- /dev/null +++ b/doc/README @@ -0,0 +1,68 @@ +----------------------------------------------------------------------------- + WHAT IS IT? +----------------------------------------------------------------------------- + +xbps - xtraeme's build package system. + +It is a simple build package system that installs packages inside of +a chroot in a destination directory. Once the package has been installed +into this directory, you can make it appear/unappear at the master directory +at any time. It's in spirit the same than GNU stow, but the files are just +copied (there are no soft/hard links). + +xbps has been designed for Linux, and for the moment I'm not interested to +make it work on any other random OS. I've been a NetBSD developer for some +years and I do not want to come back... also the experience has helped to +me to start xbps and not to use pkgsrc, which is very portable but also +not so fast. + +----------------------------------------------------------------------------- + REQUIREMENTS +----------------------------------------------------------------------------- + +xbps uses proplib, a property container object library and it's almost the +same one available for NetBSD. Be sure to have it installed before using +xbps. You can get it at: + +http://code.google.com/p/portableproplib/ + +I'm also the human maintaining the portable proplib package. I'd suggest you +to install it into /usr/local to avoid issues with your distribution packages. + +Additionally the following software is required to be able to build and install +xbps binary/source packages: + + * GNU GCC C++ + * GNU Make + * fakeroot + * wget + * libarchive (development package) + * perl + * sudo + +Super-user privileges are required as well, because all packages are built +in a chroot (except the ones that are included in a virtual package to be +able to build a minimal system for the chroot). + +PLEASE NOTE THAT fakechroot or fakeroot-ng DO NOT WORK. + +------------------------------------------------------------------------------ + HOW TO USE IT +------------------------------------------------------------------------------ + +Before using xbps, some required utilities need to be built and installed +into $(PREFIX); by default they are installed into /usr/local. +You can do this by issuing "make" and "make install" as root in the top +level directory. See the REQUIREMENTS section above for required packages. + +Once the xbps distfiles are installed into prefix, you can start building +packages from source, add local repositories with binary packages, install or +remove them, etc. + +If you are only interested in building/using packages from source, see the +SRCPKG_INFO file. + +For information about binary packages, see the BINPKG_INFO file. + +------------------------------------------------------------------------------ + Juan Romero Pardines diff --git a/doc/SRCPKG_INFO b/doc/SRCPKG_INFO new file mode 100644 index 00000000..5b7899f9 --- /dev/null +++ b/doc/SRCPKG_INFO @@ -0,0 +1,110 @@ +----------------------------------------------------------------------------- + REQUIREMENTS +----------------------------------------------------------------------------- + +To be able to build packages from source the following software is required +in the host system: + + * GNU GCC C++ + * GNU Make + * fakeroot + * wget + * libarchive (development package) + * perl + * sudo + +Super-user privileges are required as well, because all packages are built +in a chroot (except the ones that are included in a virtual package to be +able to build a minimal system for the chroot). + +------------------------------------------------------------------------------ + HOW TO BUILD/HANDLE PACKAGES FROM SOURCE +------------------------------------------------------------------------------ + +Before using xbps-src, some required utilities need to be built and installed +into $(PREFIX); by default they are installed into /usr/local. +You can do this by issuing "make" and "make install" as root in the top +level directory. + +If configuration file is not specified from the command line with the +-c flag, it will first try to use the default location at +/usr/local/etc/xbps.conf (or the installation prefix that was specified +to the make(1) command), and as last resort in the etc directory of the +current directory. + +To avoid problems with libtool and configure scripts finding stuff that is +available in the host system, almost all packages must be built inside of a +chroot. So the first thing would be to create the binary packages with: + + $ xbps-src install xbps-base-chroot + +This will build all required packages via fakeroot in masterdir, therefore you +can run it as normal user. Next commands will require super-user privileges +and all package handling will be done within the chroot. I believe it's the +most easier and faster way to handle clean dependencies; another reason would +be that xbps packages are meant to be used in a system and not just for +ordinary users. So once all packages are built, you can create and enter +to the chroot with: + + $ sudo xbps-src chroot + +Press Control + D to exit from the chroot. The following targets will require +to be done in the chroot: + + build, configure, install, install-destdir, remove, stow and unstow. + +Now let's explain some more about the targets that you can use. To start +installing packages you should use the install target: + + $ sudo xbps-src install glib + +If the package is properly installed, it will be "stowned" automatically. +``stowned´´ means that this package is available in the master directory, +on which xpbs has copied all files from DESTDIR/. + +To remove a currently installed (and stowned) package, you can use: + + $ sudo xbps-src remove glib + +Please note that when you remove it, the package will also be removed +from XBPS_DESTDIR and previously "unstowned". + +To stow an already installed package (from XBPS_DESTDIR/): + + $ sudo xbps-src stow glib + +and to unstow an already installed (stowned) package: + + $ sudo xbps-src unstow glib + +You can also print some stuff about any template build file, e.g: + + $ xbps-src info glib + +To list installed (stowned) packages, use this: + + $ xbps-src list + +To only extract the distfiles, without configuring/building/installing: + + $ xbps-src extract foo + +To not remove the build directory after successful installation: + + $ sudo xbps-src -C install blah + +To only fetch the distfile: + + $ xbps-src fetch blah + +To only install the package, _without_ stowning it into the master directory: + + $ sudo xbps-src install-destdir blob + +To list files installed by a package, note that package must be installed +into destination directory first: + + $ xbps-src listfiles blob + +------------------------------------------------------------------------------ + Juan Romero Pardines diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 00000000..72c4d4ff --- /dev/null +++ b/doc/TODO @@ -0,0 +1,46 @@ +xbps-src: + * Personalized scripts per template to unpack distfiles. + * Multiple URLs to download source distribution files, aliases, etc. + Something like ${sourceforge} to pick up any specified mirror on the list. + * Fix -C flag while building packages via chroot. + +Packages: + * Create /etc/issue. + * Add a trigger for fc-cache. + * Add a trigger to (un)register users/groups. Currently packages that + require user/groups use custom INSTALL/REMOVE scripts, which adds + a lot of duplicated work. + * Add support to stop and restart package's OpenRC service while + removing and upgrading a package. + [PARTIAL: at pre-remove the services are stopped but not yet restarted] + * How to handle kernel package upgrades? dpkg seems to keep previous + package and its initramfs, and a dummy package depends on the latest one. + I think this is the best option. + * Fix initramfs-tools trigger for $pkgname != kernel. + * Fix initramfs-tools 'update-initramfs -a'. + * Fix HAL/PolicyKit for nonroot to be able to admin tasks. + * Fix loadkeys(1) incorrectly loading the olpc keymap vs qwerty. + +xbps-bin: + * Add support to install binary packages without any repository. + * Add a flag to reinstall a package version that is already installed, + overwritting files on disk and updating required_by if required. + Perhaps change the automatic-install object to false, like pkg_install + from NetBSD do. + * Implement shell style match patterns with fnmatch() for install, update + and remove. + * Make -f flag to overwrite files when installing, and to ignore + files with wrong checksum or unexistent when removing. + [PARTIAL: only unexistent files are ignored when removing] + +libxbps: + * Add support to upgrade packages but overwritting current files; + this will fix libc, sh and others. An "essential" boolean obj + seems to be a good way to find such packages, like dpkg. + [PARTIAL: files are overwritten but obsolete files are not + taken into account yet] + +xbps-repo: + * Add support to add only specified packages, rather than looking + at all files in a directory. + * Add support for remote repositories, requires libfetch. diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 00000000..c19e8fab --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,18 @@ +include ../vars.mk + +CONF_FILE = xbps.conf + +.PHONY: all +all: + +.PHONY: clean +clean: + +install: + if [ ! -d $(ETCDIR) ]; then \ + install -d $(ETCDIR); \ + fi + + if [ ! -f $(ETCDIR)/$(CONF_FILE) ]; then \ + install -m 644 $(CONF_FILE) $(ETCDIR); \ + fi diff --git a/etc/xbps.conf b/etc/xbps.conf new file mode 100644 index 00000000..4180d343 --- /dev/null +++ b/etc/xbps.conf @@ -0,0 +1,54 @@ +# +# Configuration file for xbps-src. +# + +# +# Global directory where the xbps distribution files are stored. +# Templates, patches and helper files should all be in that directory. +# +XBPS_DISTRIBUTIONDIR=$HOME/xbps + +# +# Master directory: this is where all symlinks will be +# created pointing at packages installed in XBPS_DESTDIR. +# +XBPS_MASTERDIR=$XBPS_DISTRIBUTIONDIR/depot + +# +# Destination directory: this is where all package files will be +# installed. +# +XBPS_DESTDIR=$XBPS_DISTRIBUTIONDIR/destdir + +# +# Binary packages directory: this is where the binary packages will +# be created to. +# +XBPS_PACKAGESDIR=$XBPS_DISTRIBUTIONDIR/packages + +# +# Directory where source files will be extracted to. +# +XBPS_BUILDDIR=$XBPS_DISTRIBUTIONDIR/builddir + +# +# Directory where source distribution files are stored. +# +XBPS_SRCDISTDIR=$XBPS_DISTRIBUTIONDIR/srcdistdir + +# +# Compilation flags for cc and c++. +# +XBPS_CFLAGS="-O2 -pipe" +XBPS_CXXFLAGS="$XBPS_CFLAGS" + +# +# Number of jobs when running GNU or BSD make style packages. +# +#XBPS_MAKEJOBS=4 + +# +# Cross compilation stuff. +# +#XBPS_CROSS_TARGET=i686-pc-linux-gnu +#XBPS_CROSS_DIR=/storage/mktoolchain/$XBPS_CROSS_TARGET diff --git a/include/sha256.h b/include/sha256.h new file mode 100644 index 00000000..d83a2ae6 --- /dev/null +++ b/include/sha256.h @@ -0,0 +1,51 @@ +/* + * Written by Aaron D. Gifford + * + * Copyright 2000 Aaron D. Gifford. 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. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``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(S) OR CONTRIBUTOR(S) 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 _SHA2_DIGEST_H_ +#define _SHA2_DIGEST_H_ + +/*** SHA-256 Various Length Definitions ***********************/ +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) + +/*** SHA-256 Context Structures *******************************/ +typedef struct _SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; + +int SHA256_Init(SHA256_CTX *); +int SHA256_Update(SHA256_CTX *, const uint8_t *, size_t); +char *SHA256_End(SHA256_CTX *, uint8_t *); + +#endif /* !_SHA2_DIGEST_H_ */ diff --git a/include/xbps_api.h b/include/xbps_api.h new file mode 100644 index 00000000..62e362c6 --- /dev/null +++ b/include/xbps_api.h @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2008-2009 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_API_H_ +#define _XBPS_API_H_ + +#include +#include +#include +#define NDEBUG +#include + +#include +#include +#include + +#include "sha256.h" + +/* Default root PATH for xbps to store metadata info. */ +#define XBPS_META_PATH "/var/db/xbps" + +/* Filename for the repositories plist file. */ +#define XBPS_REPOLIST "repositories.plist" + +/* Filename of the package index plist for a repository. */ +#define XBPS_PKGINDEX "pkg-index.plist" + +/* Filename of the packages register. */ +#define XBPS_REGPKGDB "regpkgdb.plist" + +/* Package metadata files. */ +#define XBPS_PKGPROPS "props.plist" +#define XBPS_PKGFILES "files.plist" + +/* Current version of the package index format. */ +#define XBPS_PKGINDEX_VERSION "1.0" + +/* Verbose messages */ +#define XBPS_FLAG_VERBOSE 0x00000001 +#define XBPS_FLAG_FORCE 0x00000002 + +#define ARCHIVE_READ_BLOCKSIZE 10240 + +#ifndef __UNCONST +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif + +/* From lib/configure.c */ +int xbps_configure_pkg(const char *); + +/* from lib/cmpver.c */ +int xbps_cmpver(const char *, const char *); + +/* From lib/fexec.c */ +int xbps_file_exec(const char *, ...); +int xbps_file_exec_skipempty(const char *, ...); +int xbps_file_chdir_exec(const char *, const char *, ...); + +/* From lib/humanize_number.c */ +#define HN_DECIMAL 0x01 +#define HN_NOSPACE 0x02 +#define HN_B 0x04 +#define HN_DIVISOR_1000 0x08 +#define HN_GETSCALE 0x10 +#define HN_AUTOSCALE 0x20 + +int xbps_humanize_number(char *, size_t, int64_t, const char *, + int, int); + +/* From lib/findpkg.c */ +struct repository_data { + SIMPLEQ_ENTRY(repository_data) chain; + prop_dictionary_t rd_repod; +}; +SIMPLEQ_HEAD(, repository_data) repodata_queue; + +int xbps_prepare_pkg(const char *); +int xbps_find_new_pkg(const char *, prop_dictionary_t); +int xbps_find_new_packages(void); +int xbps_prepare_repolist_data(void); +void xbps_release_repolist_data(void); +prop_dictionary_t xbps_get_pkg_props(void); + +/* From lib/depends.c */ +int xbps_find_deps_in_pkg(prop_dictionary_t, prop_dictionary_t); + +/* From lib/orphans.c */ +prop_array_t xbps_find_orphan_packages(void); + +/* From lib/plist.c */ +bool xbps_add_obj_to_dict(prop_dictionary_t, prop_object_t, + const char *); +bool xbps_add_obj_to_array(prop_array_t, prop_object_t); + +int xbps_callback_array_iter_in_dict(prop_dictionary_t, + const char *, + int (*fn)(prop_object_t, void *, bool *), + void *); +int xbps_callback_array_iter_reverse_in_dict(prop_dictionary_t, + const char *, + int (*fn)(prop_object_t, void *, bool *), + void *); +int xbps_callback_array_iter_in_repolist(int (*fn)(prop_object_t, + void *, bool *), void *); + +prop_dictionary_t xbps_find_pkg_in_dict(prop_dictionary_t, + const char *, const char *); +prop_dictionary_t xbps_find_pkg_from_plist(const char *, const char *); +prop_dictionary_t xbps_find_pkg_installed_from_plist(const char *); +bool xbps_find_string_in_array(prop_array_t, const char *); + +prop_dictionary_t xbps_prepare_regpkgdb_dict(void); +void xbps_release_regpkgdb_dict(void); +prop_object_iterator_t xbps_get_array_iter_from_dict(prop_dictionary_t, + const char *); + +prop_dictionary_t xbps_read_dict_from_archive_entry(struct archive *, + struct archive_entry *); + +int xbps_remove_pkg_dict_from_file(const char *, const char *); +int xbps_remove_pkg_from_dict(prop_dictionary_t, const char *, + const char *); +int xbps_remove_string_from_array(prop_array_t, const char *); + +/* From lib/purge.c */ +int xbps_purge_pkg(const char *); + +/* From lib/register.c */ +int xbps_register_pkg(prop_dictionary_t, bool); +int xbps_unregister_pkg(const char *); + +/* From lib/remove.c */ +int xbps_remove_pkg(const char *, const char *, bool); + +/* From lib/repository.c */ +int xbps_register_repository(const char *); +int xbps_unregister_repository(const char *); + +/* From lib/requiredby.c */ +int xbps_requiredby_pkg_add(prop_array_t, prop_dictionary_t); +int xbps_requiredby_pkg_remove(const char *); + +/* From lib/sortdeps.c */ +int xbps_sort_pkg_deps(prop_dictionary_t); + +/* From lib/state.c */ +typedef enum pkg_state { + XBPS_PKG_STATE_UNPACKED = 1, + XBPS_PKG_STATE_INSTALLED, + XBPS_PKG_STATE_BROKEN, + XBPS_PKG_STATE_CONFIG_FILES, + XBPS_PKG_STATE_NOT_INSTALLED +} pkg_state_t; +int xbps_get_pkg_state_installed(const char *, pkg_state_t *); +int xbps_get_pkg_state_dictionary(prop_dictionary_t, pkg_state_t *); +int xbps_set_pkg_state_installed(const char *, pkg_state_t); +int xbps_set_pkg_state_dictionary(prop_dictionary_t, pkg_state_t); + +/* From lib/unpack.c */ +int xbps_unpack_binary_pkg(prop_dictionary_t, bool); + +/* From lib/util.c */ +char * xbps_xasprintf(const char *, ...); +char * xbps_get_file_hash(const char *); +int xbps_check_file_hash(const char *, const char *); +int xbps_check_pkg_file_hash(prop_dictionary_t, const char *); +int xbps_check_is_installed_pkg(const char *); +bool xbps_check_is_installed_pkgname(const char *); +char * xbps_get_pkg_index_plist(const char *); +char * xbps_get_pkg_name(const char *); +const char * xbps_get_pkg_version(const char *); +const char * xbps_get_pkg_revision(const char *); +bool xbps_pkg_has_rundeps(prop_dictionary_t); +void xbps_set_rootdir(const char *); +const char * xbps_get_rootdir(void); +void xbps_set_flags(int); +int xbps_get_flags(void); +bool xbps_yesno(const char *, ...); +bool xbps_noyes(const char *, ...); + +#endif /* !_XBPS_API_H_ */ diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..e4cfda19 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,44 @@ +include ../vars.mk + +# The shared library. +MAJOR = 0 +MINOR = 0 +MICRO = 0 +LIBXBPS_SO = $(LIBXBPS).$(MAJOR).$(MINOR).$(MICRO) +LIBXBPS = libxbps.so +LIBXBPS_STATIC = libxbps.a +LIBXBPS_LDFLAGS = -larchive -lprop -shared -Wl,-soname,$(LIBXBPS).$(MAJOR) + +OBJECTS = configure.o cmpver.o depends.o fexec.o findpkg.o +OBJECTS += humanize_number.o orphans.o plist.o purge.o register.o remove.o +OBJECTS += repository.o requiredby.o sha256.o sortdeps.o state.o unpack.o +OBJECTS += util.o + +all: $(LIBXBPS) $(LIBXBPS_STATIC) +.PHONY: all + +$(LIBXBPS): $(OBJECTS) + $(CC) $(LIBXBPS_LDFLAGS) $^ -o $(LIBXBPS_SO) + -ln -sf $(LIBXBPS_SO) $(LIBXBPS).$(MAJOR) + -ln -sf $(LIBXBPS_SO) $(LIBXBPS) + +$(LIBXBPS_STATIC): $(OBJECTS) + $(AR) rcs $@ $^ + ranlib $@ + +install: $(LIBXBPS) $(LIBXBPS_STATIC) + install -d $(LIBDIR) + install -m 644 $(LIBXBPS_STATIC) $(LIBDIR) + install -m 644 $(LIBXBPS_SO) $(LIBDIR) + cp -a $(LIBXBPS) $(LIBDIR) + cp -a $(LIBXBPS).$(MAJOR) $(LIBDIR) + +.PHONY: clean +clean: clean-lib clean-objs + +clean-lib: + -rm -f $(LIBXBPS)* + -rm -f $(LIBXBPS_STATIC) + +clean-objs: + -rm -f *.o diff --git a/lib/cmpver.c b/lib/cmpver.c new file mode 100644 index 00000000..5fd9ede9 --- /dev/null +++ b/lib/cmpver.c @@ -0,0 +1,282 @@ +/* + * FreeBSD install - a package for the installation and maintenance + * of non-core utilities. + * + * 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. + * + * Maxim Sobolev + * 31 July 2001 + * + * Written by Oliver Eikemeier + * Based on work of Jeremy D. Lea. + * + */ + +#include +#include +#include +#include +#include + +/* + * split_version(pkgname, endname, epoch, revision) returns a pointer to + * the version portion of a package name and the two special components. + * + * Syntax is: ${PKGNAME}-${VERSION}[_${PKGREVISION}][-${EPOCH}] + * + */ +static const char * +split_version(const char *pkgname, const char **endname, unsigned long *epoch, + unsigned long *revision) +{ + char *ch; + const char *versionstr; + const char *endversionstr; + + assert(pkgname != NULL); + + /* Look for the last '-' the the pkgname */ + ch = strrchr(pkgname, '-'); + /* Cheat if we are just passed a version, not a valid package name */ + versionstr = ch ? ch + 1 : pkgname; + + /* Look for the last '_' in the version string, advancing the end pointer */ + ch = strrchr(versionstr, '_'); + if (revision != NULL) { + *revision = ch ? strtoul(ch + 1, NULL, 10) : 0; + } + endversionstr = ch; + + /* Look for the last '-' in the remaining version string */ + ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, '-'); + if (epoch != NULL) { + *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0; + } + if (ch && !endversionstr) + endversionstr = ch; + + /* + * set the pointer behind the last character of the version without + * revision or epoch + */ + if (endname) + *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0'); + + return versionstr; +} + +/* + * VERSIONs are composed of components separated by dots. A component + * consists of a version number, a letter and a patchlevel number. + */ + +typedef struct { + long n; + long pl; + int a; +} version_component; + +/* + * get_component(position, component) gets the value of the next component + * (number - letter - number triple) and returns a pointer to the next character + * after any leading separators + * + * - components are separated by dots + * - characters !~ [a-zA-Z0-9.+*] are treated as separators + * (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect: + * 1.0.1:2003.09.16 < 1.0:2003.09.16 + * - consecutive separators are collapsed (10..1 = 10.1) + * - missing separators are inserted, essentially + * letter number letter => letter number . letter (10a1b2 = 10a1.b2) + * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0) + * - the letter sort order is: [none], a, b, ..., z; numbers without letters + * sort first (10 < 10a < 10b) + * - missing version numbers (in components starting with a letter) sort as -1 + * (a < 0, 10.a < 10) + * - a separator is inserted before the special strings "pl", "alpha", "beta", + * "pre" and "rc". + * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc" + * sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3 + * < 0.1beta2 = 0.1.b2 < 0.1) + * - other strings use only the first letter for sorting, case is ignored + * (1.d2 = 1.dev2 = 1.Development2) + * - The special component `*' is guaranteed to be the smallest possible + * component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*) + * - components separated by `+' are handled by version_cmp below + */ + +static const struct { + const char *name; + size_t namelen; + int value; +} stage[] = { + { "pl", 2, 0 }, + { "alpha", 5, 'a'-'a'+1 }, + { "beta", 4, 'b'-'a'+1 }, + { "pre", 3, 'p'-'a'+1 }, + { "rc", 2, 'r'-'a'+1 }, + { NULL, 0, -1 } +}; + +static const char * +get_component(const char *position, version_component *component) +{ + const char *pos = position; + int hasstage = 0, haspatchlevel = 0; + + assert(pos != NULL); + + /* handle version number */ + if (isdigit((unsigned char)*pos)) { + char *endptr; + component->n = strtol(pos, &endptr, 10); + /* should we test for errno == ERANGE? */ + pos = endptr; + } else if (*pos == '*') { + component->n = -2; + do { + pos++; + } while(*pos && *pos != '+'); + } else { + component->n = -1; + hasstage = 1; + } + + /* handle letter */ + if (isalpha((unsigned char)*pos)) { + int c = tolower((unsigned char)*pos); + haspatchlevel = 1; + /* handle special suffixes */ + if (isalpha((unsigned char)pos[1])) { + int i; + for (i = 0; stage[i].name; i++) { + if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0 + && !isalpha((unsigned char)pos[stage[i].namelen])) { + if (hasstage) { + /* stage to value */ + component->a = stage[i].value; + pos += stage[i].namelen; + } else { + /* insert dot */ + component->a = 0; + haspatchlevel = 0; + } + c = 0; + break; + } + } + } + /* unhandled above */ + if (c) { + /* use the first letter and skip following */ + component->a = c - 'a' + 1; + do { + ++pos; + } while (isalpha((unsigned char)*pos)); + } + } else { + component->a = 0; + haspatchlevel = 0; + } + + if (haspatchlevel) { + /* handle patch number */ + if (isdigit((unsigned char)*pos)) { + char *endptr; + component->pl = strtol(pos, &endptr, 10); + /* should we test for errno == ERANGE? */ + pos = endptr; + } else { + component->pl = -1; + } + } else { + component->pl = 0; + } + + /* skip trailing separators */ + while (*pos && !isdigit((unsigned char)*pos) && + !isalpha((unsigned char)*pos) && + *pos != '+' && *pos != '*') { + pos++; + } + + return pos; +} + +/* + * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version + * components of pkg1 is less than, equal to or greater than pkg2. No + * comparison of the basenames is done. + * + * The port version is defined by: + * ${VERSION}[_${PKGREVISION}][-${EPOCH}] + * ${EPOCH} supersedes ${VERSION} supersedes ${PKGREVISION}. + * + * The epoch and revision are defined to be a single number, while the rest + * of the version should conform to the porting guidelines. It can contain + * multiple components, separated by a period, including letters. + */ +int +xbps_cmpver(const char *pkg1, const char *pkg2) +{ + const char *v1, *v2, *ve1, *ve2; + unsigned long e1, e2, r1, r2; + int result = 0; + + v1 = split_version(pkg1, &ve1, &e1, &r1); + v2 = split_version(pkg2, &ve2, &e2, &r2); + + /* Check epoch, version, and pkgrevision, in that order. */ + if (e1 != e2) { + result = (e1 < e2 ? -1 : 1); + } + + /* Shortcut check for equality before invoking the parsing routines. */ + if (result == 0 && (ve1 - v1 != ve2 - v2 || + strncasecmp(v1, v2, (size_t)ve1 - (size_t)v1) != 0)) { + /* Loop over different components (the parts separated by dots). + * If any component differs, we have the basis for an inequality. */ + while(result == 0 && (v1 < ve1 || v2 < ve2)) { + int block_v1 = 0; + int block_v2 = 0; + version_component vc1 = {0, 0, 0}; + version_component vc2 = {0, 0, 0}; + if (v1 < ve1 && *v1 != '+') { + v1 = get_component(v1, &vc1); + } else { + block_v1 = 1; + } + if (v2 < ve2 && *v2 != '+') { + v2 = get_component(v2, &vc2); + } else { + block_v2 = 1; + } + if (block_v1 && block_v2) { + if (v1 < ve1) + v1++; + if (v2 < ve2) + v2++; + } else if (vc1.n != vc2.n) { + result = (vc1.n < vc2.n ? -1 : 1); + } else if (vc1.a != vc2.a) { + result = (vc1.a < vc2.a ? -1 : 1); + } else if (vc1.pl != vc2.pl) { + result = (vc1.pl < vc2.pl ? -1 : 1); + } + } + } + + /* Compare revision numbers. */ + if (result == 0 && r1 != r2) { + result = (r1 < r2 ? -1 : 1); + } + + return result; +} diff --git a/lib/configure.c b/lib/configure.c new file mode 100644 index 00000000..908184c2 --- /dev/null +++ b/lib/configure.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2009 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 + +/* + * Configure a package that is currently unpacked. This + * runs the post INSTALL action if required and updates the + * package state to installed. + */ +int +xbps_configure_pkg(const char *pkgname) +{ + prop_dictionary_t pkgd; + const char *rootdir, *version; + char *buf; + int rv = 0, flags = 0; + pkg_state_t state = 0; + bool reconfigure = false; + + assert(pkgname != NULL); + + rootdir = xbps_get_rootdir(); + flags = xbps_get_flags(); + + if ((rv = xbps_get_pkg_state_installed(pkgname, &state)) != 0) + return rv; + + if (state == XBPS_PKG_STATE_INSTALLED) { + if ((flags & XBPS_FLAG_FORCE) == 0) + return 0; + + reconfigure = true; + } else if (state != XBPS_PKG_STATE_UNPACKED) + return EINVAL; + + pkgd = xbps_find_pkg_installed_from_plist(pkgname); + if (pkgd == NULL) + return ENOENT; + + prop_dictionary_get_cstring_nocopy(pkgd, "version", &version); + prop_object_release(pkgd); + + printf("%sonfiguring package %s-%s...\n", + reconfigure ? "Rec" : "C", pkgname, version); + + buf = xbps_xasprintf(".%s/metadata/%s/INSTALL", + XBPS_META_PATH, pkgname); + if (buf == NULL) + return errno; + + if (access(buf, R_OK) == 0) { + if (strcmp(rootdir, "") == 0) + rootdir = "/"; + + if (chdir(rootdir) == -1) + return errno; + + if ((rv = xbps_file_chdir_exec(rootdir, buf, "post", + pkgname, version, NULL)) != 0) { + free(buf); + printf("%s: post INSTALL action returned: %s\n", + pkgname, strerror(errno)); + return rv; + } + } else { + if (errno != ENOENT) { + free(buf); + return errno; + } + } + free(buf); + + return xbps_set_pkg_state_installed(pkgname, XBPS_PKG_STATE_INSTALLED); +} diff --git a/lib/depends.c b/lib/depends.c new file mode 100644 index 00000000..bbd13794 --- /dev/null +++ b/lib/depends.c @@ -0,0 +1,349 @@ +/*- + * Copyright (c) 2008-2009 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 + +static int add_missing_reqdep(prop_dictionary_t, const char *, const char *); +static int find_repo_deps(prop_dictionary_t, prop_dictionary_t, prop_array_t); +static int find_repo_missing_deps(prop_dictionary_t, prop_dictionary_t); + +static int +store_dependency(prop_dictionary_t master, prop_dictionary_t depd, + prop_dictionary_t repod) +{ + prop_dictionary_t dict; + prop_array_t array; + const char *repoloc, *pkgname; + int rv = 0; + pkg_state_t state = 0; + + assert(master != NULL); + assert(depd != NULL); + assert(repod != NULL); + /* + * Get some info about dependencies and current repository. + */ + prop_dictionary_get_cstring_nocopy(depd, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(repod, "location-local", &repoloc); + + dict = prop_dictionary_copy(depd); + if (dict == NULL) + return errno; + + array = prop_dictionary_get(master, "unsorted_deps"); + if (array == NULL) { + prop_object_release(dict); + return errno; + } + /* + * Always set "not-installed" package state. Will be overwritten + * to its correct state later. + */ + rv = xbps_set_pkg_state_dictionary(dict, XBPS_PKG_STATE_NOT_INSTALLED); + if (rv != 0) { + prop_object_release(dict); + return rv; + } + /* + * Overwrite package state in dictionary if it was unpacked + * previously. + */ + rv = xbps_get_pkg_state_installed(pkgname, &state); + if (rv == 0) { + if ((rv = xbps_set_pkg_state_dictionary(dict, state)) != 0) { + prop_object_release(dict); + return rv; + } + } + /* + * Add required objects into package dep's dictionary. + */ + prop_dictionary_set_cstring(dict, "repository", repoloc); + /* + * Remove some unneeded objects. + */ + prop_dictionary_remove(dict, "conf_files"); + prop_dictionary_remove(dict, "keep_dirs"); + prop_dictionary_remove(dict, "maintainer"); + prop_dictionary_remove(dict, "long_desc"); + + /* + * Add the dictionary into the array. + */ + if (!xbps_add_obj_to_array(array, dict)) { + prop_object_release(dict); + return EINVAL; + } + + return 0; +} + +static int +add_missing_reqdep(prop_dictionary_t master, const char *pkgname, + const char *version) +{ + prop_array_t array; + prop_dictionary_t depd; + + assert(master != NULL); + assert(pkgname != NULL); + assert(version != NULL); + + /* + * Adds a package into the missing deps array. + */ + if (xbps_find_pkg_in_dict(master, "missing_deps", pkgname)) + return EEXIST; + + array = prop_dictionary_get(master, "missing_deps"); + depd = prop_dictionary_create(); + if (depd == NULL) + return ENOMEM; + + prop_dictionary_set_cstring(depd, "pkgname", pkgname); + prop_dictionary_set_cstring(depd, "version", version); + if (!xbps_add_obj_to_array(array, depd)) { + prop_object_release(depd); + return EINVAL; + } + + return 0; +} + +int +xbps_find_deps_in_pkg(prop_dictionary_t master, prop_dictionary_t pkg) +{ + prop_array_t pkg_rdeps, missing_rdeps; + struct repository_data *rdata; + int rv = 0; + + assert(pkg != NULL); + assert(iter != NULL); + + pkg_rdeps = prop_dictionary_get(pkg, "run_depends"); + if (pkg_rdeps == NULL) + return 0; + + /* + * Iterate over the repository pool and find out if we have + * all available binary packages. + */ + SIMPLEQ_FOREACH(rdata, &repodata_queue, chain) { + /* + * This will find direct and indirect deps, + * if any of them is not there it will be added + * into the missing_deps array. + */ + rv = find_repo_deps(master, rdata->rd_repod, pkg_rdeps); + if (rv != 0) { + if (rv == ENOENT) { + rv = 0; + continue; + } + break; + } + } + + missing_rdeps = prop_dictionary_get(master, "missing_deps"); + if (prop_array_count(missing_rdeps) == 0) + return 0; + + /* + * If there are missing deps, iterate one more time + * just in case that indirect deps weren't found. + */ + SIMPLEQ_FOREACH(rdata, &repodata_queue, chain) { + rv = find_repo_missing_deps(master, rdata->rd_repod); + if (rv != 0 && rv != ENOENT) + return rv; + } + + return 0; +} + +static int +find_repo_missing_deps(prop_dictionary_t master, prop_dictionary_t repo) +{ + prop_array_t array; + prop_dictionary_t curpkgd; + prop_object_t obj; + prop_object_iterator_t iter; + const char *pkgname, *version; + int rv = 0; + + assert(repo != NULL); + assert(pkg != NULL); + + array = prop_dictionary_get(master, "missing_deps"); + if (prop_array_count(array) == 0) + return 0; + + iter = prop_array_iterator(array); + if (iter == NULL) + return ENOMEM; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(obj, "version", &version); + /* + * If required package is not in repo, add it into the + * missing deps array and pass to the next one. + */ + curpkgd = xbps_find_pkg_in_dict(repo, "packages", pkgname); + if (curpkgd == NULL) { + rv = add_missing_reqdep(master, pkgname, version); + if (rv != 0 && rv != EEXIST) + break; + else { + rv = ENOENT; + continue; + } + } + /* + * Package is on repo, add it into the dictionary. + */ + if ((rv = store_dependency(master, curpkgd, repo)) != 0) + break; + /* + * Remove package from missing_deps array now. + */ + rv = xbps_remove_pkg_from_dict(master, + "missing_deps", pkgname); + if (rv != 0 && rv != ENOENT) + break; + + prop_object_iterator_reset(iter); + } + prop_object_iterator_release(iter); + + return rv; +} + +static int +find_repo_deps(prop_dictionary_t master, prop_dictionary_t repo, + prop_array_t pkg_rdeps) +{ + prop_dictionary_t curpkgd, tmpd = NULL; + prop_array_t curpkg_rdeps; + prop_object_t obj; + prop_object_iterator_t iter; + const char *reqpkg, *reqvers; + char *pkgname; + int rv = 0; + + iter = prop_array_iterator(pkg_rdeps); + if (iter == NULL) + return ENOMEM; + + /* + * Iterate over the list of required run dependencies for + * current package. + */ + while ((obj = prop_object_iterator_next(iter))) { + reqpkg = prop_string_cstring_nocopy(obj); + pkgname = xbps_get_pkg_name(reqpkg); + reqvers = xbps_get_pkg_version(reqpkg); + /* + * Check if required dep is satisfied and installed. + */ + if (xbps_check_is_installed_pkg(reqpkg) >= 0) { + free(pkgname); + continue; + } + /* + * Check if package is already added in the + * array of unsorted deps. + */ + if (xbps_find_pkg_in_dict(master, "unsorted_deps", pkgname)) { + free(pkgname); + continue; + } + /* + * If required package is not in repo, add it into the + * missing deps array and pass to the next one. + */ + curpkgd = xbps_find_pkg_in_dict(repo, "packages", pkgname); + if (curpkgd == NULL) { + rv = add_missing_reqdep(master, pkgname, reqvers); + free(pkgname); + if (rv != 0 && rv != EEXIST) + break; + else { + rv = ENOENT; + continue; + } + } + /* + * If package is installed but version doesn't satisfy + * the dependency mark it as an update, otherwise as + * an install. + */ + tmpd = xbps_find_pkg_installed_from_plist(pkgname); + if (tmpd != NULL) { + prop_dictionary_set_cstring_nocopy(curpkgd, + "trans-action", "update"); + prop_object_release(tmpd); + } else { + prop_dictionary_set_cstring_nocopy(curpkgd, + "trans-action", "install"); + } + /* + * Package is on repo, add it into the dictionary. + */ + if ((rv = store_dependency(master, curpkgd, repo)) != 0) { + free(pkgname); + break; + } + /* + * Remove package from missing_deps now it's been found. + */ + rv = xbps_remove_pkg_from_dict(master, + "missing_deps", pkgname); + if (rv != 0 && rv != ENOENT) { + free(pkgname); + break; + } + free(pkgname); + /* + * If package doesn't have rundeps, pass to the next one. + */ + curpkg_rdeps = prop_dictionary_get(curpkgd, "run_depends"); + if (curpkg_rdeps == NULL) + continue; + /* + * Iterate on required pkg to find more deps. + */ + if (!find_repo_deps(master, repo, curpkg_rdeps)) + continue; + } + prop_object_iterator_release(iter); + + return rv; +} diff --git a/lib/fexec.c b/lib/fexec.c new file mode 100644 index 00000000..a6f8469e --- /dev/null +++ b/lib/fexec.c @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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 + +static int vfcexec(const char *, int, const char *, va_list); +static int pfcexec(const char *, const char *, const char **); + +/* + * Fork, then if /bin/sh exists change root directory to + * path; otherwise just change current working directory. + * Execute the command and arguments in the argv array. + * wait for the command to finish, then return the exit status. + */ +static int +pfcexec(const char *path, const char *file, const char **argv) +{ + pid_t child; + int status; + + child = vfork(); + switch (child) { + case 0: + if (path != NULL) { + /* + * If root and /bin/sh exists chroot to + * destdir and exec the command. Otherwise + * just change CWD to destdir. + */ + if (getuid() == 0 && access("./bin/sh", R_OK) == 0) { + if (chroot(path) == -1) + _exit(127); + if (chdir("/") == -1) + _exit(127); + } else { + if (chdir(path) == -1) + _exit(127); + } + } + (void)execvp(file, (char ** const)__UNCONST(argv)); + _exit(127); + /* NOTREACHED */ + case -1: + return -1; + } + + while (waitpid(child, &status, 0) < 0) { + if (errno != EINTR) + return -1; + } + + if (!WIFEXITED(status)) + return -1; + + return WEXITSTATUS(status); +} + +static int +vfcexec(const char *path, int skipempty, const char *arg, va_list ap) +{ + const char **argv; + size_t argv_size, argc; + int retval; + + argv_size = 16; + if ((argv = malloc(argv_size * sizeof(*argv))) == NULL) { + errno = ENOMEM; + return -1; + } + + argv[0] = arg; + argc = 1; + + do { + if (argc == argv_size) { + argv_size *= 2; + argv = realloc(argv, argv_size * sizeof(*argv)); + if (argv == NULL) { + errno = ENOMEM; + return -1; + } + } + + arg = va_arg(ap, const char *); + if (skipempty && arg && strlen(arg) == 0) + continue; + + argv[argc++] = arg; + + } while (arg != NULL); + + retval = pfcexec(path, argv[0], argv); + free(argv); + + return retval; +} + +int +xbps_file_exec(const char *arg, ...) +{ + va_list ap; + int result; + + va_start(ap, arg); + result = vfcexec(NULL, 0, arg, ap); + va_end(ap); + + return result; +} + +int +xbps_file_exec_skipempty(const char *arg, ...) +{ + va_list ap; + int result; + + va_start(ap, arg); + result = vfcexec(NULL, 1, arg, ap); + va_end(ap); + + return result; +} + +int +xbps_file_chdir_exec(const char *path, const char *arg, ...) +{ + va_list ap; + int result; + + va_start(ap, arg); + result = vfcexec(path, 0, arg, ap); + va_end(ap); + + return result; +} diff --git a/lib/findpkg.c b/lib/findpkg.c new file mode 100644 index 00000000..f1f82404 --- /dev/null +++ b/lib/findpkg.c @@ -0,0 +1,458 @@ +/*- + * Copyright (c) 2009 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 + +static prop_dictionary_t pkg_props; +static bool pkg_props_initialized; + +static int set_pkg_state(prop_dictionary_t, const char *); + +static int +create_pkg_props_dictionary(void) +{ + prop_array_t unsorted, missing; + int rv = 0; + + if (pkg_props_initialized) + return 0; + + pkg_props = prop_dictionary_create(); + if (pkg_props == NULL) + return ENOMEM; + + missing = prop_array_create(); + if (missing == NULL) { + rv = ENOMEM; + goto fail; + } + + unsorted = prop_array_create(); + if (unsorted == NULL) { + rv = ENOMEM; + goto fail2; + } + + if (!xbps_add_obj_to_dict(pkg_props, missing, "missing_deps")) { + rv = EINVAL; + goto fail3; + } + if (!xbps_add_obj_to_dict(pkg_props, unsorted, "unsorted_deps")) { + rv = EINVAL; + goto fail3; + } + + pkg_props_initialized = true; + + return rv; + +fail3: + prop_object_release(unsorted); +fail2: + prop_object_release(missing); +fail: + prop_object_release(pkg_props); + + return rv; +} + +prop_dictionary_t +xbps_get_pkg_props(void) +{ + if (pkg_props_initialized == false) + return NULL; + + return pkg_props; +} + +int +xbps_prepare_repolist_data(void) +{ + prop_dictionary_t dict = NULL; + prop_array_t array; + prop_object_t obj; + prop_object_iterator_t iter; + struct repository_data *rdata; + const char *rootdir; + char *plist; + int rv = 0; + static bool repodata_initialized; + + if (repodata_initialized) + return 0; + + SIMPLEQ_INIT(&repodata_queue); + + rootdir = xbps_get_rootdir(); + if (rootdir == NULL) + rootdir = ""; + + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REPOLIST); + if (plist == NULL) { + rv = EINVAL; + goto out; + } + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + free(plist); + rv = errno; + goto out; + } + free(plist); + + array = prop_dictionary_get(dict, "repository-list"); + if (array == NULL) { + rv = EINVAL; + goto out1; + } + + iter = prop_array_iterator(array); + if (iter == NULL) { + rv = ENOMEM; + goto out1; + } + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + /* + * Iterate over the repository pool and add the dictionary + * for current repository into the queue. + */ + plist = + xbps_get_pkg_index_plist(prop_string_cstring_nocopy(obj)); + if (plist == NULL) { + rv = EINVAL; + goto out2; + } + + rdata = malloc(sizeof(struct repository_data)); + if (rdata == NULL) { + rv = errno; + goto out2; + } + + rdata->rd_repod = prop_dictionary_internalize_from_file(plist); + if (rdata->rd_repod == NULL) { + free(plist); + rv = errno; + goto out2; + } + free(plist); + SIMPLEQ_INSERT_TAIL(&repodata_queue, rdata, chain); + } + + repodata_initialized = true; + +out2: + prop_object_iterator_release(iter); +out1: + prop_object_release(dict); +out: + if (rv != 0) + xbps_release_repolist_data(); + + return rv; + +} + +void +xbps_release_repolist_data(void) +{ + struct repository_data *rdata; + + while ((rdata = SIMPLEQ_FIRST(&repodata_queue)) != NULL) { + SIMPLEQ_REMOVE(&repodata_queue, rdata, repository_data, chain); + prop_object_release(rdata->rd_repod); + free(rdata); + } +} + +int +xbps_find_new_packages(void) +{ + prop_dictionary_t dict; + prop_object_t obj; + prop_object_iterator_t iter; + const char *pkgname; + int rv = 0; + + /* + * Prepare dictionary with all registered packages. + */ + dict = xbps_prepare_regpkgdb_dict(); + if (dict == NULL) + return ENOENT; + + /* + * Prepare dictionary with all registered repositories. + */ + if ((rv = xbps_prepare_repolist_data()) != 0) + return rv; + + iter = xbps_get_array_iter_from_dict(dict, "packages"); + if (iter == NULL) + return EINVAL; + + /* + * Find out if there is a newer version for all currently + * installed packages. + */ + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + if ((rv = xbps_find_new_pkg(pkgname, obj)) != 0) { + prop_object_iterator_release(iter); + return rv; + } + } + prop_object_iterator_release(iter); + + return rv; +} + +int +xbps_find_new_pkg(const char *pkgname, prop_dictionary_t instpkg) +{ + prop_dictionary_t pkgrd = NULL; + prop_array_t unsorted; + struct repository_data *rdata; + const char *repoloc, *repover, *instver; + int rv = 0; + + assert(pkgname != NULL); + assert(instpkg != NULL); + + SIMPLEQ_FOREACH(rdata, &repodata_queue, chain) { + /* + * Get the package dictionary from current repository. + * If it's not there, pass to the next repository. + */ + pkgrd = xbps_find_pkg_in_dict(rdata->rd_repod, + "packages", pkgname); + if (pkgrd != NULL) { + /* + * Check if installed version is >= than the + * one available in current repository. + */ + prop_dictionary_get_cstring_nocopy(instpkg, + "version", &instver); + prop_dictionary_get_cstring_nocopy(pkgrd, + "version", &repover); + if (xbps_cmpver(instver, repover) >= 0) + goto out; + + break; + } + } + if (pkgrd == NULL) + return 0; + + /* + * Create master pkg dictionary. + */ + if ((rv = create_pkg_props_dictionary()) != 0) + goto out; + + /* + * Set repository in pkg dictionary. + */ + if (!prop_dictionary_get_cstring_nocopy(rdata->rd_repod, + "location-local", &repoloc)) { + rv = EINVAL; + goto out; + } + prop_dictionary_set_cstring(pkgrd, "repository", repoloc); + + /* + * Construct the dependency chain for this package. + */ + if ((rv = xbps_find_deps_in_pkg(pkg_props, pkgrd)) != 0) + goto out; + + /* + * Add required package dictionary into the packages + * dictionary. + */ + unsorted = prop_dictionary_get(pkg_props, "unsorted_deps"); + if (unsorted == NULL) { + rv = EINVAL; + goto out; + } + + /* + * Always set "not-installed" package state. Will be overwritten + * to its correct state later. + */ + if ((rv = set_pkg_state(pkgrd, pkgname)) != 0) + goto out; + + prop_dictionary_set_cstring_nocopy(pkgrd, "trans-action", "update"); + + if (!prop_array_add(unsorted, pkgrd)) + rv = errno; + +out: + if (rv != 0) + xbps_release_repolist_data(); + + return rv; +} + +static int +set_pkg_state(prop_dictionary_t pkgd, const char *pkgname) +{ + pkg_state_t state = 0; + int rv = 0; + + rv = xbps_set_pkg_state_dictionary(pkgd, XBPS_PKG_STATE_NOT_INSTALLED); + if (rv != 0) + return rv; + + /* + * Overwrite package state in dictionary if it was unpacked + * previously. + */ + rv = xbps_get_pkg_state_installed(pkgname, &state); + if (rv == 0) { + if ((rv = xbps_set_pkg_state_dictionary(pkgd, state)) != 0) + return rv; + } else if (rv == ENOENT) + rv = 0; + + return rv; +} + +int +xbps_prepare_pkg(const char *pkgname) +{ + prop_dictionary_t pkgrd = NULL; + prop_array_t pkgs_array; + struct repository_data *rdata; + const char *repoloc; + int rv = 0; + + assert(pkgname != NULL); + + if ((rv = xbps_prepare_repolist_data()) != 0) + return rv; + + SIMPLEQ_FOREACH(rdata, &repodata_queue, chain) { + /* + * Get the package dictionary from current repository. + * If it's not there, pass to the next repository. + */ + pkgrd = xbps_find_pkg_in_dict(rdata->rd_repod, + "packages", pkgname); + if (pkgrd != NULL) + break; + } + if (pkgrd == NULL) { + rv = EAGAIN; + goto out; + } + + /* + * Create master pkg dictionary. + */ + if ((rv = create_pkg_props_dictionary()) != 0) + goto out; + + /* + * Set repository in pkg dictionary. + */ + if (!prop_dictionary_get_cstring_nocopy(rdata->rd_repod, + "location-local", &repoloc)) { + rv = EINVAL; + goto out; + } + prop_dictionary_set_cstring(pkgrd, "repository", repoloc); + prop_dictionary_set_cstring(pkg_props, "origin", pkgname); + + /* + * Check if this package needs dependencies. + */ + if (xbps_pkg_has_rundeps(pkgrd)) { + /* + * Construct the dependency chain for this package. + */ + if ((rv = xbps_find_deps_in_pkg(pkg_props, pkgrd)) != 0) + goto out; + + /* + * Sort the dependency chain for this package. + */ + if ((rv = xbps_sort_pkg_deps(pkg_props)) != 0) + goto out; + } else { + /* + * Package has no deps, so we have to create the + * "packages" array. + */ + pkgs_array = prop_array_create(); + if (pkgs_array == NULL) { + rv = errno; + goto out; + } + if (!prop_dictionary_set(pkg_props, "packages", + pkgs_array)) { + rv = errno; + goto out; + } + } + + /* + * Add required package dictionary into the packages + * dictionary. + */ + pkgs_array = prop_dictionary_get(pkg_props, "packages"); + if (pkgs_array == NULL || + prop_object_type(pkgs_array) != PROP_TYPE_ARRAY) { + rv = EINVAL; + goto out; + } + + /* + * Always set "not-installed" package state. Will be overwritten + * to its correct state later. + */ + if ((rv = set_pkg_state(pkgrd, pkgname)) != 0) + goto out; + + prop_dictionary_set_cstring_nocopy(pkgrd, "trans-action", "install"); + + if (!prop_array_add(pkgs_array, pkgrd)) + rv = errno; + +out: + xbps_release_repolist_data(); + + return rv; +} diff --git a/lib/humanize_number.c b/lib/humanize_number.c new file mode 100644 index 00000000..5e3dc19a --- /dev/null +++ b/lib/humanize_number.c @@ -0,0 +1,143 @@ +/* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */ + +/* + * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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 + +int +xbps_humanize_number(char *buf, size_t len, int64_t bytes, + const char *suffix, int scale, int flags) +{ + const char *prefixes, *sep; + int b, i, r, maxscale, s1, s2, sign; + int64_t divisor, max; + size_t baselen; + + assert(buf != NULL); + assert(suffix != NULL); + assert(scale >= 0); + + if (flags & HN_DIVISOR_1000) { + /* SI for decimal multiplies */ + divisor = 1000; + if (flags & HN_B) + prefixes = "B\0k\0M\0G\0T\0P\0E"; + else + prefixes = "\0\0k\0M\0G\0T\0P\0E"; + } else { + /* + * binary multiplies + * XXX IEC 60027-2 recommends Ki, Mi, Gi... + */ + divisor = 1024; + if (flags & HN_B) + prefixes = "B\0K\0M\0G\0T\0P\0E"; + else + prefixes = "\0\0K\0M\0G\0T\0P\0E"; + } + +#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1]) + maxscale = 7; + + if (scale >= maxscale && + (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0) + return (-1); + + if (buf == NULL || suffix == NULL) + return (-1); + + if (len > 0) + buf[0] = '\0'; + if (bytes < 0) { + sign = -1; + bytes *= -100; + baselen = 3; /* sign, digit, prefix */ + } else { + sign = 1; + bytes *= 100; + baselen = 2; /* digit, prefix */ + } + if (flags & HN_NOSPACE) + sep = ""; + else { + sep = " "; + baselen++; + } + baselen += strlen(suffix); + + /* Check if enough room for `x y' + suffix + `\0' */ + if (len < baselen + 1) + return (-1); + + if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { + /* See if there is additional columns can be used. */ + for (max = 100, i = (int)(len - baselen); i-- > 0;) + max *= 10; + + /* + * Divide the number until it fits the given column. + * If there will be an overflow by the rounding below, + * divide once more. + */ + for (i = 0; bytes >= max - 50 && i < maxscale; i++) + bytes /= divisor; + + if (scale & HN_GETSCALE) + return (i); + } else + for (i = 0; i < scale && i < maxscale; i++) + bytes /= divisor; + + /* If a value <= 9.9 after rounding and ... */ + if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + /* baselen + \0 + .N */ + if (len < baselen + 1 + 2) + return (-1); + b = ((int)bytes + 5) / 10; + s1 = b / 10; + s2 = b % 10; + r = snprintf(buf, len, "%d%s%d%s%s%s", + sign * s1, localeconv()->decimal_point, s2, + sep, SCALE2PREFIX(i), suffix); + } else + r = snprintf(buf, len, "%" PRId64 "%s%s%s", + sign * ((bytes + 50) / 100), + sep, SCALE2PREFIX(i), suffix); + + return (r); +} diff --git a/lib/orphans.c b/lib/orphans.c new file mode 100644 index 00000000..ba473409 --- /dev/null +++ b/lib/orphans.c @@ -0,0 +1,152 @@ +/*- + * Copyright (c) 2009 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 + +struct orphan_pkg { + SIMPLEQ_ENTRY(orphan_pkg) chain; + prop_dictionary_t dict; + const char *pkgname; +}; + +static SIMPLEQ_HEAD(orphan_head, orphan_pkg) orphan_list = + SIMPLEQ_HEAD_INITIALIZER(orphan_list); + +static int +find_orphan_pkg(prop_object_t obj, void *arg, bool *loop_done) +{ + prop_array_t reqby; + prop_object_t obj2; + prop_object_iterator_t iter; + struct orphan_pkg *orphan; + char *pkgname; + unsigned int ndep = 0, cnt = 0; + bool automatic = false; + + (void)arg; + (void)loop_done; + + if (!prop_dictionary_get_bool(obj, "automatic-install", &automatic)) + return EINVAL; + + if (!automatic) + return 0; + + reqby = prop_dictionary_get(obj, "requiredby"); + if (reqby == NULL) + return 0; + else if (prop_object_type(reqby) != PROP_TYPE_ARRAY) + return EINVAL; + + if ((cnt = prop_array_count(reqby)) == 0) + goto add_orphan; + + iter = prop_array_iterator(reqby); + if (iter == NULL) + return errno; + + while ((obj2 = prop_object_iterator_next(iter)) != NULL) { + pkgname = xbps_get_pkg_name(prop_string_cstring_nocopy(obj2)); + SIMPLEQ_FOREACH(orphan, &orphan_list, chain) { + if (strcmp(orphan->pkgname, pkgname) == 0) { + ndep++; + break; + } + } + free(pkgname); + } + prop_object_iterator_release(iter); + if (ndep != cnt) + return 0; + +add_orphan: + orphan = NULL; + orphan = malloc(sizeof(struct orphan_pkg)); + if (orphan == NULL) + return errno; + + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &orphan->pkgname); + orphan->dict = prop_dictionary_copy(obj); + SIMPLEQ_INSERT_TAIL(&orphan_list, orphan, chain); + + return 0; +} + +static void +cleanup(void) +{ + struct orphan_pkg *orphan; + + while ((orphan = SIMPLEQ_FIRST(&orphan_list)) != NULL) { + SIMPLEQ_REMOVE(&orphan_list, orphan, orphan_pkg, chain); + prop_object_release(orphan->dict); + free(orphan); + } +} + +prop_array_t +xbps_find_orphan_packages(void) +{ + prop_array_t array; + prop_dictionary_t dict; + struct orphan_pkg *orphan; + int rv = 0; + + if ((dict = xbps_prepare_regpkgdb_dict()) == NULL) + return NULL; + /* + * Find out all orphans by looking at the + * regpkgdb dictionary and iterate in reverse order + * in which packages were installed. + */ + rv = xbps_callback_array_iter_reverse_in_dict(dict, "packages", + find_orphan_pkg, NULL); + if (rv != 0) { + cleanup(); + return NULL; + } + /* + * Prepare an array with all packages previously found. + */ + array = prop_array_create(); + if (array == NULL) { + cleanup(); + return NULL; + } + while ((orphan = SIMPLEQ_FIRST(&orphan_list)) != NULL) { + prop_array_add(array, orphan->dict); + SIMPLEQ_REMOVE(&orphan_list, orphan, orphan_pkg, chain); + prop_object_release(orphan->dict); + free(orphan); + } + + return array; +} diff --git a/lib/plist.c b/lib/plist.c new file mode 100644 index 00000000..ff5b4b3c --- /dev/null +++ b/lib/plist.c @@ -0,0 +1,458 @@ +/*- + * Copyright (c) 2008-2009 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 + +static prop_dictionary_t regpkgdb_dict; +static bool regpkgdb_initialized; + +bool +xbps_add_obj_to_dict(prop_dictionary_t dict, prop_object_t obj, + const char *key) +{ + assert(dict != NULL); + assert(obj != NULL); + assert(key != NULL); + + if (!prop_dictionary_set(dict, key, obj)) { + prop_object_release(dict); + return false; + } + + prop_object_release(obj); + return true; +} + +bool +xbps_add_obj_to_array(prop_array_t array, prop_object_t obj) +{ + assert(array != NULL); + assert(obj != NULL); + + if (!prop_array_add(array, obj)) { + prop_object_release(array); + return false; + } + + prop_object_release(obj); + return true; +} + +int +xbps_callback_array_iter_in_repolist(int (*fn)(prop_object_t, void *, bool *), + void *arg) +{ + prop_dictionary_t repolistd; + const char *rootdir; + char *plist; + int rv = 0; + + assert(fn != NULL); + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REPOLIST); + if (plist == NULL) + return EINVAL; + + /* + * Get the dictionary with the list of registered repositories. + */ + repolistd = prop_dictionary_internalize_from_file(plist); + if (repolistd == NULL) + return EINVAL; + + /* + * Iterate over the repository pool and run the associated + * callback function. The loop is stopped when the bool + * argument is true or the cb returns non 0. + */ + rv = xbps_callback_array_iter_in_dict(repolistd, "repository-list", + fn, arg); + prop_object_release(repolistd); + free(plist); + + return rv; +} + +int +xbps_callback_array_iter_in_dict(prop_dictionary_t dict, const char *key, + int (*fn)(prop_object_t, void *, bool *), + void *arg) +{ + prop_object_iterator_t iter; + prop_object_t obj; + int rv = 0; + bool cbloop_done = false; + + assert(dict != NULL); + assert(key != NULL); + assert(fn != NULL); + + iter = xbps_get_array_iter_from_dict(dict, key); + if (iter == NULL) + return EINVAL; + + while ((obj = prop_object_iterator_next(iter))) { + rv = (*fn)(obj, arg, &cbloop_done); + if (rv != 0 || cbloop_done) + break; + } + + prop_object_iterator_release(iter); + return rv; +} + +int +xbps_callback_array_iter_reverse_in_dict(prop_dictionary_t dict, + const char *key, int (*fn)(prop_object_t, void *, bool *), void *arg) +{ + prop_array_t array; + prop_object_t obj; + int rv = 0; + bool cbloop_done = false; + unsigned int cnt = 0; + + assert(dict != NULL); + assert(key != NULL); + assert(fn != NULL); + + array = prop_dictionary_get(dict, key); + if (array == NULL || prop_object_type(array) != PROP_TYPE_ARRAY) + return EINVAL; + + if ((cnt = prop_array_count(array)) == 0) + return 0; + + while (cnt--) { + obj = prop_array_get(array, cnt); + rv = (*fn)(obj, arg, &cbloop_done); + if (rv != 0 || cbloop_done) + break; + } + + return rv; +} + +prop_dictionary_t +xbps_find_pkg_from_plist(const char *plist, const char *pkgname) +{ + prop_dictionary_t dict, obj, res; + + assert(plist != NULL); + assert(pkgname != NULL); + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + errno = ENOENT; + return NULL; + } + + obj = xbps_find_pkg_in_dict(dict, "packages", pkgname); + if (obj == NULL) { + prop_object_release(dict); + errno = ENOENT; + return NULL; + } + + res = prop_dictionary_copy(obj); + prop_object_release(dict); + + return res; +} + +prop_dictionary_t +xbps_find_pkg_installed_from_plist(const char *pkgname) +{ + prop_dictionary_t d, pkgd; + pkg_state_t state = 0; + + d = xbps_prepare_regpkgdb_dict(); + if (d == NULL) + return NULL; + + pkgd = xbps_find_pkg_in_dict(d, "packages", pkgname); + if (pkgd == NULL) + return NULL; + + if (xbps_get_pkg_state_installed(pkgname, &state) != 0) + return NULL; + + switch (state) { + case XBPS_PKG_STATE_INSTALLED: + case XBPS_PKG_STATE_UNPACKED: + return prop_dictionary_copy(pkgd); + default: + return NULL; + } +} + +prop_dictionary_t +xbps_find_pkg_in_dict(prop_dictionary_t dict, const char *key, + const char *pkgname) +{ + prop_object_iterator_t iter; + prop_object_t obj; + const char *dpkgn; + + assert(dict != NULL); + assert(pkgname != NULL); + assert(key != NULL); + + iter = xbps_get_array_iter_from_dict(dict, key); + if (iter == NULL) + return NULL; + + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &dpkgn); + if (strcmp(dpkgn, pkgname) == 0) + break; + } + prop_object_iterator_release(iter); + + return obj; +} + +prop_dictionary_t +xbps_prepare_regpkgdb_dict(void) +{ + const char *rootdir; + char *plist; + + if (regpkgdb_initialized == false) { + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) + return NULL; + + regpkgdb_dict = prop_dictionary_internalize_from_file(plist); + if (regpkgdb_dict == NULL) { + free(plist); + return NULL; + } + free(plist); + regpkgdb_initialized = true; + } + + return regpkgdb_dict; +} + +void +xbps_release_regpkgdb_dict(void) +{ + if (regpkgdb_initialized == false) + return; + + prop_object_release(regpkgdb_dict); + regpkgdb_dict = NULL; + regpkgdb_initialized = false; +} + +bool +xbps_find_string_in_array(prop_array_t array, const char *val) +{ + prop_object_iterator_t iter; + prop_object_t obj; + + assert(array != NULL); + assert(val != NULL); + + iter = prop_array_iterator(array); + if (iter == NULL) + return false; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + if (prop_object_type(obj) != PROP_TYPE_STRING) + continue; + if (prop_string_equals_cstring(obj, val)) { + prop_object_iterator_release(iter); + return true; + } + } + + prop_object_iterator_release(iter); + return false; +} + +prop_object_iterator_t +xbps_get_array_iter_from_dict(prop_dictionary_t dict, const char *key) +{ + prop_array_t array; + + assert(dict != NULL); + assert(key != NULL); + + array = prop_dictionary_get(dict, key); + if (array == NULL || prop_object_type(array) != PROP_TYPE_ARRAY) + return NULL; + + return prop_array_iterator(array); +} + +int +xbps_remove_string_from_array(prop_array_t array, const char *str) +{ + prop_object_t obj; + prop_object_iterator_t iter; + size_t idx = 0; + bool found = false; + + assert(array != NULL); + assert(str != NULL); + + iter = prop_array_iterator(array); + if (iter == NULL) + return ENOMEM; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + if (prop_object_type(obj) != PROP_TYPE_STRING) + continue; + if (prop_string_equals_cstring(obj, str)) { + found = true; + break; + } + idx++; + } + prop_object_iterator_release(iter); + if (found == false) + return ENOENT; + + prop_array_remove(array, idx); + + return 0; +} + +int +xbps_remove_pkg_from_dict(prop_dictionary_t dict, const char *key, + const char *pkgname) +{ + prop_array_t array; + prop_object_t obj; + prop_object_iterator_t iter; + const char *curpkgname; + size_t i = 0; + bool found = false; + + assert(dict != NULL); + assert(key != NULL); + assert(pkgname != NULL); + + array = prop_dictionary_get(dict, key); + if (array == NULL || prop_object_type(array) != PROP_TYPE_ARRAY) + return EINVAL; + + iter = prop_array_iterator(array); + if (iter == NULL) + return errno; + + /* Iterate over the array of dictionaries to find its index. */ + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", + &curpkgname); + if ((curpkgname && (strcmp(curpkgname, pkgname) == 0))) { + found = true; + break; + } + i++; + } + prop_object_iterator_release(iter); + if (found == true) + prop_array_remove(array, i); + else + return ENOENT; + + return 0; +} + +int +xbps_remove_pkg_dict_from_file(const char *pkg, const char *plist) +{ + prop_dictionary_t pdict; + int rv = 0; + + assert(pkg != NULL); + assert(plist != NULL); + + pdict = prop_dictionary_internalize_from_file(plist); + if (pdict == NULL) + return errno; + + rv = xbps_remove_pkg_from_dict(pdict, "packages", pkg); + if (rv != 0) { + prop_object_release(pdict); + return rv; + } + + if (!prop_dictionary_externalize_to_file(pdict, plist)) { + prop_object_release(pdict); + return errno; + } + + prop_object_release(pdict); + + return 0; +} + +prop_dictionary_t +xbps_read_dict_from_archive_entry(struct archive *ar, + struct archive_entry *entry) +{ + prop_dictionary_t d; + size_t buflen = 0; + ssize_t nbytes = -1; + char *buf; + + assert(ar != NULL); + assert(entry != NULL); + + buflen = (size_t)archive_entry_size(entry); + buf = malloc(buflen); + if (buf == NULL) + return NULL; + + nbytes = archive_read_data(ar, buf, buflen); + if ((size_t)nbytes != buflen) { + free(buf); + return NULL; + } + + d = prop_dictionary_internalize(buf); + free(buf); + if (d == NULL) + return NULL; + else if (prop_object_type(d) != PROP_TYPE_DICTIONARY) { + prop_object_release(d); + return NULL; + } + + return d; +} diff --git a/lib/purge.c b/lib/purge.c new file mode 100644 index 00000000..8b307551 --- /dev/null +++ b/lib/purge.c @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2009 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 + +static int remove_pkg_metadata(const char *); + +/* + * Purge a package that is currently in "config-files" state. + * This removes configuration files if they weren't modified, + * removes metadata files and fully unregisters the package. + */ +int +xbps_purge_pkg(const char *pkgname) +{ + prop_dictionary_t dict; + prop_array_t array; + prop_object_t obj; + prop_object_iterator_t iter; + const char *rootdir, *file, *sha256; + char *path; + int rv = 0, flags; + pkg_state_t state = 0; + + assert(pkgname != NULL); + rootdir = xbps_get_rootdir(); + flags = xbps_get_flags(); + + /* + * Skip packages that aren't in "config-files" state. + */ + if ((rv = xbps_get_pkg_state_installed(pkgname, &state)) != 0) + return rv; + + if (state != XBPS_PKG_STATE_CONFIG_FILES) + return 0; + + /* + * Iterate over the pkg file list dictionary and remove all + * unmodified configuration files. + */ + path = xbps_xasprintf("%s/%s/metadata/%s/%s", + rootdir, XBPS_META_PATH, pkgname, XBPS_PKGFILES); + if (path == NULL) + return errno; + + dict = prop_dictionary_internalize_from_file(path); + if (dict == NULL) { + free(path); + return errno; + } + free(path); + + array = prop_dictionary_get(dict, "conf_files"); + if (array == NULL) { + goto out; + } else if (prop_object_type(array) != PROP_TYPE_ARRAY) { + prop_object_release(dict); + return EINVAL; + } else if (prop_array_count(array) == 0) { + goto out; + } + + iter = xbps_get_array_iter_from_dict(dict, "conf_files"); + if (iter == NULL) + return EINVAL; + + while ((obj = prop_object_iterator_next(iter))) { + if (!prop_dictionary_get_cstring_nocopy(obj, "file", &file)) { + prop_object_iterator_release(iter); + prop_object_release(dict); + return EINVAL; + } + path = xbps_xasprintf("%s/%s", rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + prop_object_release(dict); + return EINVAL; + } + prop_dictionary_get_cstring_nocopy(obj, "sha256", &sha256); + rv = xbps_check_file_hash(path, sha256); + if (rv == ENOENT) { + printf("Configuration file %s doesn't exist!\n", file); + free(path); + continue; + } else if (rv == ERANGE) { + if (flags & XBPS_FLAG_VERBOSE) + printf("Configuration file %s has been " + "modified, preserving...\n", file); + + free(path); + continue; + } else if (rv != 0 && rv != ERANGE) { + free(path); + prop_object_iterator_release(iter); + prop_object_release(dict); + return rv; + } + if ((rv = remove(path)) == -1) { + if (flags & XBPS_FLAG_VERBOSE) + printf("WARNING: can't remove %s (%s)\n", + file, strerror(errno)); + + free(path); + continue; + } + if (flags & XBPS_FLAG_VERBOSE) + printf("Removed configuration file %s\n", file); + + free(path); + } + + prop_object_iterator_release(iter); +out: + prop_object_release(dict); + + if ((rv = remove_pkg_metadata(pkgname)) == 0) { + if ((rv = xbps_unregister_pkg(pkgname)) == 0) + printf("Package %s has been purged successfully.\n", + pkgname); + } + + return rv; +} + +static int +remove_pkg_metadata(const char *pkgname) +{ + struct dirent *dp; + DIR *dirp; + const char *rootdir; + char *metadir, *path; + int flags = 0, rv = 0; + + assert(pkgname != NULL); + + rootdir = xbps_get_rootdir(); + flags = xbps_get_flags(); + + metadir = xbps_xasprintf("%s/%s/metadata/%s", rootdir, + XBPS_META_PATH, pkgname); + if (metadir == NULL) + return errno; + + dirp = opendir(metadir); + if (dirp == NULL) { + free(metadir); + return errno; + } + + while ((dp = readdir(dirp)) != NULL) { + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0)) + continue; + + path = xbps_xasprintf("%s/%s", metadir, dp->d_name); + if (path == NULL) { + (void)closedir(dirp); + free(metadir); + return -1; + } + + if ((rv = unlink(path)) == -1) { + if (flags & XBPS_FLAG_VERBOSE) + printf("WARNING: can't remove %s (%s)\n", + pkgname, strerror(errno)); + } + free(path); + } + (void)closedir(dirp); + rv = rmdir(metadir); + free(metadir); + + return rv; +} diff --git a/lib/register.c b/lib/register.c new file mode 100644 index 00000000..43a0be0d --- /dev/null +++ b/lib/register.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2008-2009 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 + +int +xbps_register_pkg(prop_dictionary_t pkgrd, bool automatic) +{ + prop_dictionary_t dict, pkgd; + prop_array_t array; + const char *pkgname, *version, *desc, *rootdir; + char *plist; + int rv = 0; + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) + return EINVAL; + + prop_dictionary_get_cstring_nocopy(pkgrd, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(pkgrd, "version", &version); + prop_dictionary_get_cstring_nocopy(pkgrd, "short_desc", &desc); + + dict = prop_dictionary_internalize_from_file(plist); + if (dict != NULL) { + pkgd = xbps_find_pkg_in_dict(dict, "packages", pkgname); + if (pkgd == NULL) { + rv = ENOENT; + goto out; + } + prop_dictionary_set_cstring_nocopy(pkgd, "version", version); + prop_dictionary_set_cstring_nocopy(pkgd, "short_desc", desc); + prop_dictionary_set_bool(pkgd, "automatic-install", automatic); + + /* + * Add the requiredby objects for dependent packages. + */ + if (pkgrd && xbps_pkg_has_rundeps(pkgrd)) { + array = prop_dictionary_get(dict, "packages"); + if (array == NULL) { + prop_object_release(pkgd); + rv = ENOENT; + goto out; + } + rv = xbps_requiredby_pkg_add(array, pkgrd); + if (rv != 0) { + prop_object_release(pkgd); + goto out; + } + } + /* + * Write plist file to storage. + */ + if (!prop_dictionary_externalize_to_file(dict, plist)) + rv = errno; + } else { + free(plist); + return ENOENT; + } +out: + prop_object_release(dict); + free(plist); + + return rv; +} + +int +xbps_unregister_pkg(const char *pkgname) +{ + const char *rootdir; + char *plist; + int rv = 0; + + assert(pkgname != NULL); + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) + return EINVAL; + + rv = xbps_remove_pkg_dict_from_file(pkgname, plist); + free(plist); + + return rv; +} diff --git a/lib/remove.c b/lib/remove.c new file mode 100644 index 00000000..ad3c8fb5 --- /dev/null +++ b/lib/remove.c @@ -0,0 +1,288 @@ +/*- + * Copyright (c) 2009 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 + +static int remove_pkg_files(prop_dictionary_t); + +static int +remove_pkg_files(prop_dictionary_t dict) +{ + prop_array_t array; + prop_object_iterator_t iter; + prop_object_t obj; + prop_bool_t bobj; + const char *file, *rootdir, *sha256; + char *path = NULL; + int flags = 0, rv = 0; + + rootdir = xbps_get_rootdir(); + flags = xbps_get_flags(); + + /* Links */ + array = prop_dictionary_get(dict, "links"); + if (array == NULL || prop_array_count(array) == 0) + goto files; + + iter = xbps_get_array_iter_from_dict(dict, "links"); + if (iter == NULL) + return EINVAL; + + while ((obj = prop_object_iterator_next(iter))) { + if (!prop_dictionary_get_cstring_nocopy(obj, "file", &file)) { + prop_object_iterator_release(iter); + return EINVAL; + } + path = xbps_xasprintf("%s/%s", rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + return EINVAL; + } + if ((rv = remove(path)) == -1) { + if (flags & XBPS_FLAG_VERBOSE) + printf("WARNING: can't remove link %s (%s)\n", + file, strerror(errno)); + free(path); + continue; + } + if (flags & XBPS_FLAG_VERBOSE) + printf("Removed link: %s\n", file); + + free(path); + } + prop_object_iterator_release(iter); + path = NULL; + +files: + /* Regular files */ + array = prop_dictionary_get(dict, "files"); + if (array == NULL || prop_array_count(array) == 0) + goto dirs; + + iter = xbps_get_array_iter_from_dict(dict, "files"); + if (iter == NULL) + return EINVAL; + + while ((obj = prop_object_iterator_next(iter))) { + if (!prop_dictionary_get_cstring_nocopy(obj, "file", &file)) { + prop_object_iterator_release(iter); + return EINVAL; + } + path = xbps_xasprintf("%s/%s", rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + return EINVAL; + } + prop_dictionary_get_cstring_nocopy(obj, "sha256", &sha256); + rv = xbps_check_file_hash(path, sha256); + if (rv == ENOENT) { + printf("WARNING: '%s' doesn't exist!\n", file); + free(path); + continue; + } else if (rv == ERANGE) { + if (flags & XBPS_FLAG_VERBOSE) + printf("WARNING: SHA256 doesn't match " + "for file %s, ignoring...\n", file); + + free(path); + continue; + } else if (rv != 0 && rv != ERANGE) { + free(path); + prop_object_iterator_release(iter); + return rv; + } + if ((rv = remove(path)) == -1) { + if (flags & XBPS_FLAG_VERBOSE) + printf("WARNING: can't remove file %s (%s)\n", + file, strerror(errno)); + + free(path); + continue; + } + if (flags & XBPS_FLAG_VERBOSE) + printf("Removed file: %s\n", file); + + free(path); + } + prop_object_iterator_release(iter); + path = NULL; + +dirs: + /* Directories */ + array = prop_dictionary_get(dict, "dirs"); + if (array == NULL || prop_array_count(array) == 0) + return 0; + + iter = xbps_get_array_iter_from_dict(dict, "dirs"); + if (iter == NULL) + return EINVAL; + + while ((obj = prop_object_iterator_next(iter))) { + if ((bobj = prop_dictionary_get(obj, "keep")) != NULL) { + /* Skip permanent directory. */ + continue; + } + if (!prop_dictionary_get_cstring_nocopy(obj, "file", &file)) { + prop_object_iterator_release(iter); + return EINVAL; + } + path = xbps_xasprintf("%s/%s", rootdir, file); + if (path == NULL) { + prop_object_iterator_release(iter); + return EINVAL; + } + if ((rv = rmdir(path)) == -1) { + if (errno == ENOTEMPTY) { + free(path); + continue; + } + if (flags & XBPS_FLAG_VERBOSE) { + printf("WARNING: can't remove " + "directory %s (%s)\n", file, + strerror(errno)); + free(path); + continue; + } + } + if (flags & XBPS_FLAG_VERBOSE) + printf("Removed directory: %s\n", file); + + free(path); + } + prop_object_iterator_release(iter); + + return 0; +} + +int +xbps_remove_pkg(const char *pkgname, const char *version, bool update) +{ + prop_dictionary_t dict; + const char *rootdir = xbps_get_rootdir(); + char *path, *buf; + int rv = 0; + bool prepostf = false; + + assert(pkgname != NULL); + assert(version != NULL); + + /* + * Check if pkg is installed before anything else. + */ + if (xbps_check_is_installed_pkgname(pkgname) == false) + return ENOENT; + + if (strcmp(rootdir, "") == 0) + rootdir = "/"; + + if (chdir(rootdir) == -1) + return errno; + + buf = xbps_xasprintf(".%s/metadata/%s/REMOVE", + XBPS_META_PATH, pkgname); + if (buf == NULL) + return errno; + + /* + * Find out if the REMOVE file exists. + */ + if (access(buf, R_OK) == 0) { + /* + * Run the pre remove action. + */ + prepostf = true; + rv = xbps_file_chdir_exec(rootdir, buf, "pre", pkgname, + version, NULL); + if (rv != 0) { + printf("%s: prerm action target error (%s)\n", pkgname, + strerror(errno)); + free(buf); + return rv; + } + } + + /* + * Iterate over the pkg file list dictionary and remove all + * files, configuration files, links and dirs. + */ + path = xbps_xasprintf("%s/%s/metadata/%s/%s", + rootdir, XBPS_META_PATH, pkgname, XBPS_PKGFILES); + if (path == NULL) { + free(buf); + return errno; + } + + dict = prop_dictionary_internalize_from_file(path); + if (dict == NULL) { + free(buf); + free(path); + return errno; + } + free(path); + + rv = remove_pkg_files(dict); + if (rv != 0) { + free(buf); + prop_object_release(dict); + return rv; + } + prop_object_release(dict); + + /* + * Run the post remove action if REMOVE file is there + * and we aren't updating a package. + */ + if (update == false && prepostf) { + if ((rv = xbps_file_chdir_exec(rootdir, buf, "post", + pkgname, version, NULL)) != 0) { + printf("%s: postrm action target error (%s)\n", + pkgname, strerror(errno)); + free(buf); + return rv; + } + } + free(buf); + + /* + * Update the requiredby array of all required dependencies. + */ + if ((rv = xbps_requiredby_pkg_remove(pkgname)) != 0) + return rv; + + /* + * Set package state to "config-files". + */ + rv = xbps_set_pkg_state_installed(pkgname, + XBPS_PKG_STATE_CONFIG_FILES); + + return rv; +} diff --git a/lib/repository.c b/lib/repository.c new file mode 100644 index 00000000..1d6edebf --- /dev/null +++ b/lib/repository.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2008-2009 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 + +int +xbps_register_repository(const char *uri) +{ + prop_dictionary_t dict; + prop_array_t array; + prop_object_t obj = NULL; + const char *rootdir; + char *plist; + int rv = 0; + + assert(uri != NULL); + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REPOLIST); + if (plist == NULL) + return errno; + + /* First check if we have the repository plist file. */ + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + /* Looks like not, create it. */ + dict = prop_dictionary_create(); + if (dict == NULL) { + rv = errno; + goto out2; + } + /* Create the array and add the repository URI on it. */ + array = prop_array_create(); + if (array == NULL) { + rv = errno; + goto out; + } + if (!prop_array_set_cstring_nocopy(array, 0, uri)) { + rv = errno; + goto out; + } + /* Add the array obj into the main dictionary. */ + if (!xbps_add_obj_to_dict(dict, array, "repository-list")) { + rv = errno; + goto out; + } + } else { + /* Append into the array, the plist file exists. */ + array = prop_dictionary_get(dict, "repository-list"); + if (array == NULL) { + rv = errno; + goto out; + } + /* It seems that this object is already there */ + if (xbps_find_string_in_array(array, uri)) { + errno = EEXIST; + goto out; + } + + obj = prop_string_create_cstring(uri); + if (!xbps_add_obj_to_array(array, obj)) { + prop_object_release(obj); + rv = errno; + goto out; + } + } + + /* Write dictionary into plist file. */ + if (!prop_dictionary_externalize_to_file(dict, plist)) { + if (obj) + prop_object_release(obj); + rv = errno; + } +out: + prop_object_release(dict); +out2: + free(plist); + + return rv; +} + +int +xbps_unregister_repository(const char *uri) +{ + prop_dictionary_t dict; + prop_array_t array; + const char *rootdir; + char *plist; + int rv = 0; + + assert(uri != NULL); + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REPOLIST); + if (plist == NULL) + return errno; + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + free(plist); + return errno; + } + + array = prop_dictionary_get(dict, "repository-list"); + if (array == NULL) { + rv = errno; + goto out; + } + + rv = xbps_remove_string_from_array(array, uri); + if (rv == 0) { + /* Update plist file. */ + if (!prop_dictionary_externalize_to_file(dict, plist)) + rv = errno; + } + +out: + prop_object_release(dict); + free(plist); + + return rv; +} diff --git a/lib/requiredby.c b/lib/requiredby.c new file mode 100644 index 00000000..6e9acc3c --- /dev/null +++ b/lib/requiredby.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2009 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 + +static int +add_pkg_into_reqby(prop_dictionary_t pkgd, const char *reqname) +{ + prop_array_t array; + prop_string_t reqstr; + bool alloc = false; + + array = prop_dictionary_get(pkgd, "requiredby"); + if (array == NULL) { + alloc = true; + array = prop_array_create(); + if (array == NULL) + return ENOMEM; + } + + if (xbps_find_string_in_array(array, reqname)) + return EEXIST; + + reqstr = prop_string_create_cstring(reqname); + if (reqstr == NULL) { + if (alloc) + prop_object_release(array); + return errno; + } + + if (!xbps_add_obj_to_array(array, reqstr)) { + if (alloc) + prop_object_release(array); + + prop_object_release(reqstr); + return EINVAL; + } + + if (!alloc) + return 0; + + if (!xbps_add_obj_to_dict(pkgd, array, "requiredby")) { + if (alloc) + prop_object_release(array); + + return EINVAL; + } + + return 0; +} + +static int +remove_pkg_from_reqby(prop_object_t obj, void *arg, bool *loop_done) +{ + prop_array_t array; + prop_object_t obj2; + prop_object_iterator_t iter; + const char *pkgname = arg; + char *curpkgname; + size_t idx = 0; + bool found = false; + + (void)loop_done; + + array = prop_dictionary_get(obj, "requiredby"); + if (array == NULL || prop_array_count(array) == 0) + return 0; + + iter = prop_array_iterator(array); + if (iter == NULL) + return ENOMEM; + + while ((obj2 = prop_object_iterator_next(iter)) != NULL) { + curpkgname = + xbps_get_pkg_name(prop_string_cstring_nocopy(obj2)); + if (strcmp(curpkgname, pkgname) == 0) { + free(curpkgname); + found = true; + break; + } + free(curpkgname); + idx++; + } + prop_object_iterator_release(iter); + if (found) + prop_array_remove(array, idx); + + return 0; +} + +int +xbps_requiredby_pkg_remove(const char *pkgname) +{ + prop_dictionary_t dict; + const char *rootdir; + char *plist; + int rv = 0; + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) + return EINVAL; + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + free(plist); + return errno; + } + + rv = xbps_callback_array_iter_in_dict(dict, "packages", + remove_pkg_from_reqby, __UNCONST(pkgname)); + if (rv == 0) { + if (!prop_dictionary_externalize_to_file(dict, plist)) + rv = errno; + } + + prop_object_release(dict); + free(plist); + + return rv; +} + +int +xbps_requiredby_pkg_add(prop_array_t regar, prop_dictionary_t pkg) +{ + prop_array_t rdeps; + prop_object_t obj, obj2; + prop_object_iterator_t iter, iter2; + const char *reqname, *pkgname, *version; + char *rdepname, *fpkgn; + int rv = 0; + + prop_dictionary_get_cstring_nocopy(pkg, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(pkg, "version", &version); + fpkgn = xbps_xasprintf("%s-%s", pkgname, version); + if (fpkgn == NULL) + return ENOMEM; + + rdeps = prop_dictionary_get(pkg, "run_depends"); + if (rdeps == NULL || prop_array_count(rdeps) == 0) { + free(fpkgn); + return EINVAL; + } + + iter = prop_array_iterator(rdeps); + if (iter == NULL) { + free(fpkgn); + return ENOMEM; + } + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + rdepname = xbps_get_pkg_name(prop_string_cstring_nocopy(obj)); + iter2 = prop_array_iterator(regar); + if (iter2 == NULL) { + free(rdepname); + rv = ENOMEM; + goto out; + } + + /* + * Iterate over the array to find the dictionary for the + * current run dependency. + */ + while ((obj2 = prop_object_iterator_next(iter2)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj2, "pkgname", + &reqname); + if (strcmp(rdepname, reqname) == 0) { + rv = add_pkg_into_reqby(obj2, fpkgn); + if (rv != 0) { + free(rdepname); + prop_object_iterator_release(iter2); + goto out; + } + break; + } + } + prop_object_iterator_release(iter2); + free(rdepname); + } + +out: + free(fpkgn); + prop_object_iterator_release(iter); + + return rv; +} diff --git a/lib/sha256.c b/lib/sha256.c new file mode 100644 index 00000000..06fffec1 --- /dev/null +++ b/lib/sha256.c @@ -0,0 +1,466 @@ +/* $NetBSD: sha2.c,v 1.18 2009/06/25 14:05:18 joerg Exp $ */ +/* $KAME: sha2.c,v 1.9 2003/07/20 00:28:38 itojun Exp $ */ + +/* + * sha2.c + * + * Version 1.0.0beta1 + * + * Written by Aaron D. Gifford + * + * Copyright 2000 Aaron D. Gifford. 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. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``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(S) OR CONTRIBUTOR(S) 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 "xbps_api.h" + +/*** SHA-256 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) + +/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +static void SHA256_Transform(SHA256_CTX *, const uint32_t*); +static int SHA256_Final(uint8_t *, SHA256_CTX *); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-256: */ +static const uint32_t K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +static const uint32_t sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/*** SHA-256: *********************************************************/ +int +SHA256_Init(SHA256_CTX *context) +{ + if (context == NULL) + return 1; + + memcpy(context->state, sha256_initial_hash_value, + (size_t)(SHA256_DIGEST_LENGTH)); + memset(context->buffer, 0, (size_t)(SHA256_BLOCK_LENGTH)); + context->bitcount = 0; + + return 1; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + W256[j] = be32toh(*data); \ + ++data; \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND256(a,b,c,d,e,f,g,h) \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +static void +SHA256_Transform(SHA256_CTX *context, const uint32_t *data) +{ + uint32_t a, b, c, d, e, f, g, h, s0, s1; + uint32_t T1, *W256; + int j; + + W256 = (uint32_t *)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void +SHA256_Transform(SHA256_CTX *context, const uint32_t *data) +{ + uint32_t a, b, c, d, e, f, g, h, s0, s1; + uint32_t T1, T2, *W256; + int j; + + W256 = (uint32_t *)(void *)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + W256[j] = be32toh(*data); + ++data; + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +int +SHA256_Update(SHA256_CTX *context, const uint8_t *data, size_t len) +{ + unsigned int freespace, usedspace; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return 1; + } + + usedspace = (unsigned int)((context->bitcount >> 3) % + SHA256_BLOCK_LENGTH); + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memcpy(&context->buffer[usedspace], data, + (size_t)(freespace)); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; + SHA256_Transform(context, + (uint32_t *)(void *)context->buffer); + } else { + /* The buffer is not yet full */ + memcpy(&context->buffer[usedspace], data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return 1; + } + } + /* + * Process as many complete blocks as possible. + * + * Check alignment of the data pointer. If it is 32bit aligned, + * SHA256_Transform can be called directly on the data stream, + * otherwise enforce the alignment by copy into the buffer. + */ + if ((uintptr_t)data % 4 == 0) { + while (len >= SHA256_BLOCK_LENGTH) { + SHA256_Transform(context, + (const uint32_t *)(const void *)data); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + } else { + while (len >= SHA256_BLOCK_LENGTH) { + memcpy(context->buffer, data, SHA256_BLOCK_LENGTH); + SHA256_Transform(context, + (const uint32_t *)(const void *)context->buffer); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + } + if (len > 0) { + /* There's left-overs, so save 'em */ + memcpy(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; + + return 1; +} + +static int +SHA224_256_Final(uint8_t digest[], SHA256_CTX *context, size_t len) +{ + uint32_t *d = (void *)digest; + unsigned int usedspace; + size_t i; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != NULL) { + usedspace = (unsigned int)((context->bitcount >> 3) % + SHA256_BLOCK_LENGTH); + context->bitcount = htobe64(context->bitcount); + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, + (size_t)(SHA256_SHORT_BLOCK_LENGTH - + usedspace)); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, + (size_t)(SHA256_BLOCK_LENGTH - + usedspace)); + } + /* Do second-to-last transform: */ + SHA256_Transform(context, + (uint32_t *)(void *)context->buffer); + + /* And set-up for the last transform: */ + memset(context->buffer, 0, + (size_t)(SHA256_SHORT_BLOCK_LENGTH)); + } + } else { + /* Set-up for the last transform: */ + memset(context->buffer, 0, + (size_t)(SHA256_SHORT_BLOCK_LENGTH)); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + memcpy(&context->buffer[SHA256_SHORT_BLOCK_LENGTH], + &context->bitcount, sizeof(context->bitcount)); + + /* Final transform: */ + SHA256_Transform(context, (uint32_t *)(void *)context->buffer); + + for (i = 0; i < len / 4; i++) + d[i] = htobe32(context->state[i]); + } + + /* Clean up state data: */ + memset(context, 0, sizeof(*context)); + usedspace = 0; + + return 1; +} + +static int +SHA256_Final(uint8_t digest[], SHA256_CTX *context) +{ + return SHA224_256_Final(digest, context, SHA256_DIGEST_LENGTH); +} + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char sha2_hex_digits[] = "0123456789abcdef"; + +char * +SHA256_End(SHA256_CTX *ctx, uint8_t *buffer) +{ + uint8_t digest[SHA256_DIGEST_LENGTH], *d = digest; + uint8_t *ret; + int i; + + /* Sanity check: */ + assert(ctx != NULL); + + if ((ret = buffer) != NULL) { + SHA256_Final(digest, ctx); + + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { + *buffer++ = (uint8_t)sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = (uint8_t)sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char) 0; + } else { + (void)memset(ctx, 0, sizeof(SHA256_CTX)); + } + (void)memset(digest, 0, SHA256_DIGEST_LENGTH); + return (char *)ret; +} diff --git a/lib/sortdeps.c b/lib/sortdeps.c new file mode 100644 index 00000000..abc917c4 --- /dev/null +++ b/lib/sortdeps.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2009 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 + +struct sorted_dependency { + SIMPLEQ_ENTRY(sorted_dependency) chain; + prop_dictionary_t dict; +}; + +static SIMPLEQ_HEAD(sdep_head, sorted_dependency) sdep_list = + SIMPLEQ_HEAD_INITIALIZER(sdep_list); + +static struct sorted_dependency * +find_sorteddep_by_name(const char *pkgname) +{ + struct sorted_dependency *sdep = NULL; + const char *curname; + + SIMPLEQ_FOREACH(sdep, &sdep_list, chain) { + prop_dictionary_get_cstring_nocopy(sdep->dict, + "pkgname", &curname); + if (strcmp(pkgname, curname) == 0) + break; + } + + return sdep; +} + +int +xbps_sort_pkg_deps(prop_dictionary_t chaindeps) +{ + prop_array_t sorted, unsorted, rundeps; + prop_object_t obj, obj2; + prop_object_iterator_t iter, iter2; + struct sorted_dependency *sdep; + size_t ndeps = 0, rundepscnt = 0, cnt = 0; + const char *pkgname; + char *curpkgnamedep; + int rv = 0; + + assert(chaindeps != NULL); + + sorted = prop_array_create(); + if (sorted == NULL) + return ENOMEM; + + /* + * All required deps are satisfied (already installed). + */ + unsorted = prop_dictionary_get(chaindeps, "unsorted_deps"); + if (prop_array_count(unsorted) == 0) { + prop_dictionary_set(chaindeps, "packages", sorted); + return 0; + } + + ndeps = prop_array_count(unsorted); + unsorted = prop_dictionary_get(chaindeps, "unsorted_deps"); + + iter = prop_array_iterator(unsorted); + if (iter == NULL) { + prop_object_release(sorted); + return ENOMEM; + } +again: + /* + * Order all deps by looking at its run_depends array. + */ + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); + if (find_sorteddep_by_name(pkgname) != NULL) + continue; + + sdep = malloc(sizeof(*sdep)); + if (sdep == NULL) { + rv = ENOMEM; + goto out; + } + /* + * Packages that don't have deps go unsorted, because + * it doesn't matter. + */ + rundeps = prop_dictionary_get(obj, "run_depends"); + if (rundeps == NULL || prop_array_count(rundeps) == 0) { + sdep->dict = prop_dictionary_copy(obj); + SIMPLEQ_INSERT_TAIL(&sdep_list, sdep, chain); + cnt++; + continue; + } + iter2 = prop_array_iterator(rundeps); + if (iter2 == NULL) { + free(sdep); + rv = ENOMEM; + goto out; + } + /* + * Iterate over the run_depends array, and find out if they + * were already added in the sorted list. + */ + while ((obj2 = prop_object_iterator_next(iter2)) != NULL) { + curpkgnamedep = + xbps_get_pkg_name(prop_string_cstring_nocopy(obj2)); + /* + * If dependency is already installed or queued, + * pass to the next one. + */ + if (xbps_check_is_installed_pkgname(curpkgnamedep)) + rundepscnt++; + else if (find_sorteddep_by_name(curpkgnamedep) != NULL) + rundepscnt++; + + free(curpkgnamedep); + } + prop_object_iterator_release(iter2); + + /* Add dependency if all its required deps are already added */ + if (prop_array_count(rundeps) == rundepscnt) { + sdep->dict = prop_dictionary_copy(obj); + SIMPLEQ_INSERT_TAIL(&sdep_list, sdep, chain); + rundepscnt = 0; + cnt++; + continue; + } + free(sdep); + rundepscnt = 0; + } + + /* Iterate until all deps are processed. */ + if (cnt < ndeps) { + prop_object_iterator_reset(iter); + goto again; + } + prop_object_iterator_release(iter); + + /* + * Add all sorted dependencies into the sorted deps array. + */ + while ((sdep = SIMPLEQ_FIRST(&sdep_list)) != NULL) { + prop_array_add(sorted, sdep->dict); + SIMPLEQ_REMOVE(&sdep_list, sdep, sorted_dependency, chain); + prop_object_release(sdep->dict); + free(sdep); + } + + /* + * Sanity check that the array contains the same number of + * objects than the total number of required dependencies. + */ + if (ndeps != prop_array_count(sorted)) { + rv = EINVAL; + goto out; + } + + if (!prop_dictionary_set(chaindeps, "packages", sorted)) + rv = EINVAL; + + prop_dictionary_remove(chaindeps, "unsorted_deps"); + +out: + /* + * Release resources used by temporary sorting. + */ + prop_object_release(sorted); + while ((sdep = SIMPLEQ_FIRST(&sdep_list)) != NULL) { + SIMPLEQ_REMOVE(&sdep_list, sdep, sorted_dependency, chain); + prop_object_release(sdep->dict); + free(sdep); + } + + return rv; +} diff --git a/lib/state.c b/lib/state.c new file mode 100644 index 00000000..7953f5d1 --- /dev/null +++ b/lib/state.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 2009 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 + +static int +set_new_state(prop_dictionary_t dict, pkg_state_t state) +{ + const char *state_str; + + assert(dict != NULL); + + switch (state) { + case XBPS_PKG_STATE_UNPACKED: + state_str = "unpacked"; + break; + case XBPS_PKG_STATE_INSTALLED: + state_str = "installed"; + break; + case XBPS_PKG_STATE_BROKEN: + state_str = "broken"; + break; + case XBPS_PKG_STATE_CONFIG_FILES: + state_str = "config-files"; + break; + case XBPS_PKG_STATE_NOT_INSTALLED: + state_str = "not-installed"; + break; + default: + return -1; + } + + if (!prop_dictionary_set_cstring_nocopy(dict, "state", state_str)) + return -1; + + return 0; +} + +static pkg_state_t +get_state(prop_dictionary_t dict) +{ + const char *state_str; + pkg_state_t state = 0; + + assert(dict != NULL); + + prop_dictionary_get_cstring_nocopy(dict, "state", &state_str); + if (state_str == NULL) + return 0; + + if (strcmp(state_str, "unpacked") == 0) + state = XBPS_PKG_STATE_UNPACKED; + else if (strcmp(state_str, "installed") == 0) + state = XBPS_PKG_STATE_INSTALLED; + else if (strcmp(state_str, "broken") == 0) + state = XBPS_PKG_STATE_BROKEN; + else if (strcmp(state_str, "config-files") == 0) + state = XBPS_PKG_STATE_CONFIG_FILES; + else if (strcmp(state_str, "not-installed") == 0) + state = XBPS_PKG_STATE_NOT_INSTALLED; + else + return 0; + + return state; +} + +int +xbps_get_pkg_state_installed(const char *pkgname, pkg_state_t *state) +{ + prop_dictionary_t dict, pkgd; + const char *rootdir; + char *plist; + + assert(pkgname != NULL); + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) + return errno; + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + free(plist); + return errno; + } + free(plist); + + pkgd = xbps_find_pkg_in_dict(dict, "packages", pkgname); + if (pkgd == NULL) { + prop_object_release(dict); + return ENOENT; + } + *state = get_state(pkgd); + if (*state == 0) { + prop_object_release(dict); + return EINVAL; + } + prop_object_release(dict); + + return 0; +} + +int +xbps_get_pkg_state_dictionary(prop_dictionary_t dict, pkg_state_t *state) +{ + assert(dict != NULL); + + if ((*state = get_state(dict)) == 0) + return EINVAL; + + return 0; +} + +int +xbps_set_pkg_state_dictionary(prop_dictionary_t dict, pkg_state_t state) +{ + assert(dict != NULL); + + return set_new_state(dict, state); +} + +int +xbps_set_pkg_state_installed(const char *pkgname, pkg_state_t state) +{ + prop_dictionary_t dict, pkgd; + prop_array_t array; + const char *rootdir; + char *plist; + int rv = 0; + bool newpkg = false; + + rootdir = xbps_get_rootdir(); + plist = xbps_xasprintf("%s/%s/%s", rootdir, + XBPS_META_PATH, XBPS_REGPKGDB); + if (plist == NULL) + return EINVAL; + + dict = prop_dictionary_internalize_from_file(plist); + if (dict == NULL) { + dict = prop_dictionary_create(); + if (dict == NULL) { + free(plist); + return ENOMEM; + } + array = prop_array_create(); + if (array == NULL) { + rv = ENOMEM; + goto out; + } + pkgd = prop_dictionary_create(); + if (pkgd == NULL) { + prop_object_release(array); + rv = errno; + goto out; + } + prop_dictionary_set_cstring_nocopy(pkgd, "pkgname", pkgname); + if ((rv = set_new_state(pkgd, state)) != 0) { + prop_object_release(array); + goto out; + } + if (!xbps_add_obj_to_array(array, pkgd)) { + prop_object_release(array); + rv = EINVAL; + goto out; + } + if (!xbps_add_obj_to_dict(dict, array, "packages")) { + prop_object_release(array); + rv = EINVAL; + goto out; + } + + } else { + pkgd = xbps_find_pkg_in_dict(dict, "packages", pkgname); + if (pkgd == NULL) { + newpkg = true; + pkgd = prop_dictionary_create(); + prop_dictionary_set_cstring_nocopy(pkgd, "pkgname", + pkgname); + } + array = prop_dictionary_get(dict, "packages"); + if (array == NULL) { + rv = ENOENT; + goto out; + } + if ((rv = set_new_state(pkgd, state)) != 0) { + prop_object_release(pkgd); + goto out; + } + if (newpkg && !xbps_add_obj_to_array(array, pkgd)) { + prop_object_release(pkgd); + rv = EINVAL; + goto out; + } + } + + if (!prop_dictionary_externalize_to_file(dict, plist)) + rv = errno; + +out: + prop_object_release(dict); + free(plist); + + return rv; +} diff --git a/lib/unpack.c b/lib/unpack.c new file mode 100644 index 00000000..3cc10e09 --- /dev/null +++ b/lib/unpack.c @@ -0,0 +1,454 @@ +/*- + * Copyright (c) 2008-2009 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 + +static int unpack_archive_fini(struct archive *, prop_dictionary_t, bool); +static void set_extract_flags(int); + +int +xbps_unpack_binary_pkg(prop_dictionary_t pkg, bool essential) +{ + prop_string_t filename, repoloc, arch; + struct archive *ar; + const char *pkgname; + char *binfile; + int pkg_fd, rv = 0; + + assert(pkg != NULL); + + /* + * Append filename to the full path for binary pkg. + */ + prop_dictionary_get_cstring_nocopy(pkg, "pkgname", &pkgname); + filename = prop_dictionary_get(pkg, "filename"); + arch = prop_dictionary_get(pkg, "architecture"); + repoloc = prop_dictionary_get(pkg, "repository"); + if (filename == NULL || arch == NULL || repoloc == NULL) + return ENOTSUP; + + binfile = xbps_xasprintf("%s/%s/%s", + prop_string_cstring_nocopy(repoloc), + prop_string_cstring_nocopy(arch), + prop_string_cstring_nocopy(filename)); + if (binfile == NULL) + return EINVAL; + + if ((pkg_fd = open(binfile, O_RDONLY)) == -1) { + rv = errno; + goto out; + } + + ar = archive_read_new(); + if (ar == NULL) { + rv = ENOMEM; + goto out2; + } + + /* + * Enable support for tar format and all compression methods. + */ + archive_read_support_compression_all(ar); + archive_read_support_format_tar(ar); + + if ((rv = archive_read_open_fd(ar, pkg_fd, + ARCHIVE_READ_BLOCKSIZE)) != 0) + goto out3; + + rv = unpack_archive_fini(ar, pkg, essential); + /* + * If installation of package was successful, make sure the package + * is really on storage (if possible). + */ + if (rv == 0) + if (fdatasync(pkg_fd) == -1) + rv = errno; +out3: + archive_read_finish(ar); +out2: + (void)close(pkg_fd); +out: + free(binfile); + + if (rv == 0) { + /* + * Set package state to unpacked. + */ + rv = xbps_set_pkg_state_installed(pkgname, + XBPS_PKG_STATE_UNPACKED); + } + + return rv; +} + +/* + * Flags for extracting files in binary packages. If a package + * is marked as "essential", its files will be overwritten and then + * the old and new dictionaries are compared to find out if there + * are some files that were in the old package that should be removed. + */ +#define EXTRACT_FLAGS ARCHIVE_EXTRACT_SECURE_NODOTDOT | \ + ARCHIVE_EXTRACT_SECURE_SYMLINKS +#define FEXTRACT_FLAGS ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | \ + ARCHIVE_EXTRACT_TIME | EXTRACT_FLAGS + +static void +set_extract_flags(int flags) +{ + if (getuid() == 0) + flags = FEXTRACT_FLAGS; + else + flags = EXTRACT_FLAGS; +} + +static int +install_config_file(prop_dictionary_t d, struct archive_entry *entry, + const char *pkgname, int flags, bool skip) +{ + prop_dictionary_t forigd; + prop_object_t obj, obj2; + prop_object_iterator_t iter, iter2; + const char *cffile, *sha256_new; + char *buf, *sha256_cur = NULL, *sha256_orig = NULL; + int rv = 0; + bool install_new = false; + + if (d == NULL) + return 0; + + iter = xbps_get_array_iter_from_dict(d, "conf_files"); + if (iter == NULL) + return 0; + + /* + * Get original hash for the file from current + * installed package. + */ + buf = xbps_xasprintf(".%s/metadata/%s/%s", XBPS_META_PATH, + pkgname, XBPS_PKGFILES); + if (buf == NULL) + return errno; + + forigd = prop_dictionary_internalize_from_file(buf); + free(buf); + if (forigd != NULL) { + iter2 = xbps_get_array_iter_from_dict(forigd, "conf_files"); + if (iter2 != NULL) { + while ((obj2 = prop_object_iterator_next(iter2))) { + prop_dictionary_get_cstring_nocopy(obj2, + "file", &cffile); + if (strstr(archive_entry_pathname(entry), + cffile)) { + prop_dictionary_get_cstring(obj2, + "sha256", &sha256_orig); + break; + } + } + prop_object_iterator_release(iter2); + } + prop_object_release(forigd); + } + + /* + * Compare original, installed and new hash for current file. + */ + while ((obj = prop_object_iterator_next(iter))) { + prop_dictionary_get_cstring_nocopy(obj, "file", &cffile); + if (strstr(archive_entry_pathname(entry), cffile) == 0) + continue; + buf = xbps_xasprintf(".%s", cffile); + if (buf == NULL) { + prop_object_iterator_release(iter); + return errno; + } + prop_dictionary_get_cstring_nocopy(obj, "sha256", &sha256_new); + sha256_cur = xbps_get_file_hash(buf); + free(buf); + if (sha256_cur == NULL) { + if (errno == ENOENT) { + /* + * File not installed, install new one. + */ + install_new = true; + break; + } else { + rv = errno; + break; + } + } + + /* + * Orig = X, Curr = X, New = X + * + * Install new file. + */ + if ((strcmp(sha256_orig, sha256_cur) == 0) && + (strcmp(sha256_cur, sha256_new) == 0) && + (strcmp(sha256_orig, sha256_new) == 0)) { + install_new = true; + break; + /* + * Orig = X, Curr = X, New = Y + * + * Install new file. + */ + } else if ((strcmp(sha256_orig, sha256_cur) == 0) && + (strcmp(sha256_cur, sha256_new)) && + (strcmp(sha256_orig, sha256_new))) { + install_new = true; + break; + /* + * Orig = X, Curr = Y, New = X + * + * Keep current file as is. + */ + } else if ((strcmp(sha256_orig, sha256_cur)) && + (strcmp(sha256_orig, sha256_new) == 0) && + (strcmp(sha256_cur, sha256_new))) { + skip = true; + break; + /* + * Orig = X, Curr = Y, New = Y + * + * Install new file. + */ + } else if ((strcmp(sha256_orig, sha256_cur)) && + (strcmp(sha256_cur, sha256_new) == 0) && + (strcmp(sha256_orig, sha256_new))) { + install_new = true; + break; + /* + * Orig = X, Curr = Y, New = Z + * + * Install new file as file.new. + */ + } else if ((strcmp(sha256_orig, sha256_cur)) && + (strcmp(sha256_cur, sha256_new)) && + (strcmp(sha256_orig, sha256_new))) { + buf = xbps_xasprintf(".%s.new", cffile); + if (buf == NULL) { + rv = errno; + break; + } + printf("Installing new configuration " + "file %s.new\n", cffile); + install_new = true; + archive_entry_set_pathname(entry, buf); + free(buf); + break; + } + } + + if (install_new) + set_extract_flags(flags); + if (sha256_orig) + free(sha256_orig); + if (sha256_cur) + free(sha256_cur); + + prop_object_iterator_release(iter); + + return rv; +} + +/* + * TODO: remove printfs and return appropiate errors to be interpreted by + * the consumer. + */ +static int +unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg, + bool essential) +{ + prop_dictionary_t filesd; + struct archive_entry *entry; + const char *pkgname, *version, *rootdir; + char *buf, *buf2; + int rv = 0, flags, lflags, eflags; + bool actgt = false, skip_entry = false; + + assert(ar != NULL); + assert(pkg != NULL); + rootdir = xbps_get_rootdir(); + flags = xbps_get_flags(); + + if (strcmp(rootdir, "") == 0) + rootdir = "/"; + + if (chdir(rootdir) == -1) + return errno; + + prop_dictionary_get_cstring_nocopy(pkg, "pkgname", &pkgname); + prop_dictionary_get_cstring_nocopy(pkg, "version", &version); + + set_extract_flags(lflags); + + while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) { + /* + * Always overwrite pkg metadata files. Other files + * in the archive aren't overwritten unless a package + * defines the "essential" boolean obj, or the + * XBPS_FLAG_FORCE is specified. + */ + eflags = 0; + eflags = lflags; + if (strcmp("./INSTALL", archive_entry_pathname(entry)) && + strcmp("./REMOVE", archive_entry_pathname(entry)) && + strcmp("./files.plist", archive_entry_pathname(entry)) && + strcmp("./props.plist", archive_entry_pathname(entry))) { + if (((flags & XBPS_FLAG_FORCE) == 0) || + essential == false) { + eflags |= ARCHIVE_EXTRACT_NO_OVERWRITE; + eflags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; + } + } + + /* + * Run the pre INSTALL action if the file is there. + */ + if (strcmp("./INSTALL", archive_entry_pathname(entry)) == 0) { + buf = xbps_xasprintf(".%s/metadata/%s/INSTALL", + XBPS_META_PATH, pkgname); + if (buf == NULL) + return errno; + + actgt = true; + archive_entry_set_pathname(entry, buf); + + if (archive_read_extract(ar, entry, eflags) != 0) { + if ((rv = archive_errno(ar)) != EEXIST) { + free(buf); + return rv; + } + } + + if ((rv = xbps_file_chdir_exec(rootdir, buf, "pre", + pkgname, version, NULL)) != 0) { + free(buf); + printf("%s: preinst action target error %s\n", + pkgname, strerror(errno)); + return rv; + } + /* pass to the next entry if successful */ + free(buf); + continue; + + /* + * Unpack metadata files in final directory. + */ + } else if (strcmp("./REMOVE", + archive_entry_pathname(entry)) == 0) { + buf2 = xbps_xasprintf(".%s/metadata/%s/REMOVE", + XBPS_META_PATH, pkgname); + if (buf2 == NULL) + return errno; + archive_entry_set_pathname(entry, buf2); + free(buf2); + buf2 = NULL; + + } else if (strcmp("./files.plist", + archive_entry_pathname(entry)) == 0) { + /* + * Now we have a dictionary from the entry + * in memory. Will be written to disk later, when + * all files are extracted. + */ + filesd = xbps_read_dict_from_archive_entry(ar, entry); + if (filesd == NULL) + return errno; + + /* Pass to next entry */ + continue; + + } else if (strcmp("./props.plist", + archive_entry_pathname(entry)) == 0) { + buf2 = xbps_xasprintf(".%s/metadata/%s/props.plist", + XBPS_META_PATH, pkgname); + if (buf2 == NULL) + return errno; + archive_entry_set_pathname(entry, buf2); + free(buf2); + } + /* + * Handle configuration files. + */ + if ((rv = install_config_file(filesd, entry, pkgname, + eflags, skip_entry)) != 0) { + prop_object_release(filesd); + return rv; + } + if (skip_entry) { + archive_read_data_skip(ar); + skip_entry = false; + continue; + } + /* + * Extract entry from archive. + */ + if (archive_read_extract(ar, entry, eflags) != 0) { + rv = archive_errno(ar); + if (rv != EEXIST) { + printf("ERROR: %s...exiting!\n", + archive_error_string(ar)); + return rv;; + } else if (rv == EEXIST) { + if (flags & XBPS_FLAG_VERBOSE) { + printf("WARNING: ignoring existent " + "path: %s\n", + archive_entry_pathname(entry)); + } + rv = 0; + continue; + } + } + if (flags & XBPS_FLAG_VERBOSE) + printf(" %s\n", archive_entry_pathname(entry)); + } + + if ((rv = archive_errno(ar)) == 0) { + /* + * Now that all files were successfully unpacked, we + * can safely externalize files.plist because the path + * is reachable. + */ + buf2 = xbps_xasprintf(".%s/metadata/%s/files.plist", + XBPS_META_PATH, pkgname); + if (!prop_dictionary_externalize_to_file(filesd, buf2)) { + prop_object_release(filesd); + free(buf2); + return errno; + } + free(buf2); + } + prop_object_release(filesd); + + return rv; +} diff --git a/lib/util.c b/lib/util.c new file mode 100644 index 00000000..2e5c81be --- /dev/null +++ b/lib/util.c @@ -0,0 +1,372 @@ +/*- + * Copyright (c) 2008-2009 Juan Romero Pardines. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static bool question(bool, const char *, va_list); + +static const char *rootdir; +static int flags; + +char * +xbps_get_file_hash(const char *file) +{ + SHA256_CTX ctx; + char *hash; + uint8_t buf[BUFSIZ * 20], digest[SHA256_DIGEST_STRING_LENGTH]; + ssize_t bytes; + int fd; + + if ((fd = open(file, O_RDONLY)) == -1) + return NULL; + + SHA256_Init(&ctx); + while ((bytes = read(fd, buf, sizeof(buf))) > 0) + SHA256_Update(&ctx, buf, (size_t)bytes); + hash = strdup(SHA256_End(&ctx, digest)); + (void)close(fd); + + return hash; +} + +int +xbps_check_file_hash(const char *path, const char *sha256) +{ + char *res; + + res = xbps_get_file_hash(path); + if (res == NULL) + return errno; + + if (strcmp(sha256, res)) { + free(res); + return ERANGE; + } + free(res); + + return 0; +} + +int +xbps_check_pkg_file_hash(prop_dictionary_t pkgd, const char *repoloc) +{ + const char *sha256, *arch, *filename; + char *binfile; + int rv = 0; + + assert(repoloc != NULL); + + if (!prop_dictionary_get_cstring_nocopy(pkgd, "filename", &filename)) + return EINVAL; + + if (!prop_dictionary_get_cstring_nocopy(pkgd, "filename-sha256", + &sha256)) + return EINVAL; + + if (!prop_dictionary_get_cstring_nocopy(pkgd, "architecture", &arch)) + return EINVAL; + + binfile = xbps_xasprintf("%s/%s/%s", repoloc, arch, filename); + if (binfile == NULL) + return EINVAL; + + rv = xbps_check_file_hash(binfile, sha256); + free(binfile); + + return rv; +} + +int +xbps_check_is_installed_pkg(const char *pkg) +{ + prop_dictionary_t dict; + const char *reqver, *instver; + char *pkgname; + int rv = 0; + pkg_state_t state = 0; + + assert(pkg != NULL); + + pkgname = xbps_get_pkg_name(pkg); + reqver = xbps_get_pkg_version(pkg); + + dict = xbps_find_pkg_installed_from_plist(pkgname); + if (dict == NULL) { + free(pkgname); + return -1; /* not installed */ + } + + /* + * Check that package state is fully installed, not + * unpacked or something else. + */ + if (xbps_get_pkg_state_installed(pkgname, &state) != 0) { + free(pkgname); + return -1; + } + free(pkgname); + + if (state != XBPS_PKG_STATE_INSTALLED) + return -1; + + /* Get version from installed package */ + prop_dictionary_get_cstring_nocopy(dict, "version", &instver); + + /* Compare installed and required version. */ + rv = xbps_cmpver(instver, reqver); + + prop_object_release(dict); + + return rv; +} + +bool +xbps_check_is_installed_pkgname(const char *pkgname) +{ + prop_dictionary_t pkgd; + + assert(pkgname != NULL); + + pkgd = xbps_find_pkg_installed_from_plist(pkgname); + if (pkgd) { + prop_object_release(pkgd); + return true; + } + + return false; +} + +const char * +xbps_get_pkg_version(const char *pkg) +{ + const char *tmp; + + assert(pkg != NULL); + + /* Get the required version */ + tmp = strrchr(pkg, '-'); + if (tmp == NULL) + return NULL; + + return tmp + 1; /* skip first '-' */ +} + +const char * +xbps_get_pkg_revision(const char *pkg) +{ + const char *tmp; + + assert(pkg != NULL); + + /* Get the required revision */ + tmp = strrchr(pkg, '_'); + if (tmp == NULL) + return NULL; + + return tmp + 1; /* skip first '_' */ +} + +char * +xbps_get_pkg_name(const char *pkg) +{ + const char *tmp; + char *pkgname; + size_t len = 0; + + assert(pkg != NULL); + + /* Get package name */ + tmp = strrchr(pkg, '-'); + if (tmp == NULL) + return NULL; + + len = strlen(pkg) - strlen(tmp) + 1; + + pkgname = malloc(len); + memcpy(pkgname, pkg, len - 1); + pkgname[len - 1] = '\0'; + + return pkgname; +} + +char * +xbps_get_pkg_index_plist(const char *path) +{ + struct utsname un; + + assert(path != NULL); + + if (uname(&un) == -1) + return NULL; + + return xbps_xasprintf("%s/%s/%s", path, un.machine, XBPS_PKGINDEX); +} + +bool +xbps_pkg_has_rundeps(prop_dictionary_t pkg) +{ + prop_array_t array; + + assert(pkg != NULL); + array = prop_dictionary_get(pkg, "run_depends"); + if (array && prop_array_count(array) > 0) + return true; + + return false; +} + +void +xbps_set_rootdir(const char *dir) +{ + assert(dir != NULL); + rootdir = dir; +} + +const char * +xbps_get_rootdir(void) +{ + if (rootdir == NULL) + rootdir = ""; + + return rootdir; +} + +void +xbps_set_flags(int lflags) +{ + flags = lflags; +} + +int +xbps_get_flags(void) +{ + return flags; +} + +char * +xbps_xasprintf(const char *fmt, ...) +{ + va_list ap; + char *buf; + + va_start(ap, fmt); + if (vasprintf(&buf, fmt, ap) == -1) + return NULL; + va_end(ap); + + return buf; +} + +/* + * The following functions were taken from pacman (src/pacman/util.c) + * Copyright (c) 2002-2007 by Judd Vinet + */ +bool +xbps_yesno(const char *fmt, ...) +{ + va_list ap; + bool res; + + va_start(ap, fmt); + res = question(1, fmt, ap); + va_end(ap); + + return res; +} + +bool +xbps_noyes(const char *fmt, ...) +{ + va_list ap; + bool res; + + va_start(ap, fmt); + res = question(0, fmt, ap); + va_end(ap); + + return res; +} + +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 ", "[Y/n]"); + else + fprintf(stderr, " %s ", "[y/N]"); + + if (fgets(response, 32, stdin)) { + (void)strtrim(response); + if (strlen(response) == 0) + return preset; + + if ((strcasecmp(response, "y") == 0) || + (strcasecmp(response, "yes") == 0)) + return true; + else if ((strcasecmp(response, "n") == 0) || + (strcasecmp(response, "no") == 0)) + return false; + } + return false; +} diff --git a/prog.mk b/prog.mk new file mode 100644 index 00000000..02d2709c --- /dev/null +++ b/prog.mk @@ -0,0 +1,22 @@ +include $(TOPDIR)/vars.mk + +OBJS ?= main.o + +all: $(BIN) +.PHONY: all + +$(BIN): $(OBJS) + $(CC) $(LDFLAGS) $^ -o $@ + +.PHONY: clean +clean: clean-bins clean-objs + +clean-bins: + -rm -f $(BIN) + +clean-objs: + -rm -f $(OBJS) + +install: $(BIN) + install -d $(SBINDIR) + install $(INSTALL_STRIPPED) -m 755 $(BIN) $(SBINDIR) diff --git a/vars.mk b/vars.mk new file mode 100644 index 00000000..dabebe1a --- /dev/null +++ b/vars.mk @@ -0,0 +1,16 @@ +# Common variables. + +PREFIX ?= /usr/local +SBINDIR ?= $(PREFIX)/sbin +LIBDIR ?= $(PREFIX)/lib +ETCDIR ?= $(PREFIX)/etc +TOPDIR ?= .. +INSTALL_STRIPPED ?= -s + +LDFLAGS += -L$(TOPDIR)/lib -L$(PREFIX)/lib -lxbps +CPPFLAGS += -I$(TOPDIR)/include -D_BSD_SOURCE -D_XOPEN_SOURCE=600 +CPPFLAGS += -D_GNU_SOURCE +WARNFLAGS ?= -pedantic -std=c99 -Wall -Wextra -Werror -Wshadow -Wformat=2 +WARNFLAGS += -Wmissing-declarations -Wcomment -Wunused-macros -Wendif-labels +WARNFLAGS += -Wcast-qual -Wcast-align -Wconversion +CFLAGS += $(WARNFLAGS) -O2 -fPIC -DPIC