From d18918390082ba2a5c7495bd2fe3bed160ecf37b Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 15 Jul 2023 16:43:27 +0200 Subject: [PATCH] Third generation Tix. The .tix.tar.xz binary package format now stores the contents in the root rather than the data/ subdirectory and the tix metadata now has the same layout as the loose files in /tix, such that a .tix.tar.xz package can simply be directly extracted into the filesystem. The /tix/manifest/ is now included in the binary package rather than being generated on installation. The /tix/collection.conf and /tix/tixinfo metadata files are now in the tix-vars(1) format in the style of port(5). The /tix/installed.list file has been removed since it isn't loose file compatible and one can list the /tix/tixinfo directory instead. The /tix/repository.list file has been removed since the feature is unused and doesn't match the future direction of tix. The kernel support for tix binary packages has been removed since it will simply install by extracting the tar archive into the root filesystem. Add the post-install sha256sum to the port version stamp. --- Makefile | 2 +- build-aux/build-ports.sh | 4 +- build-aux/iso-grub-cfg.sh | 2 +- kernel/initrd.cpp | 185 +------------------- share/man/man7/following-development.7 | 20 +++ share/man/man7/user-guide.7 | 4 +- share/sysinstall/hooks/sortix-1.1-tix-3g | 0 sysinstall/hooks.c | 138 ++++++++++++++- sysinstall/manifest.c | 127 ++++---------- tix/tix-build.c | 201 +++++++++++++++++----- tix/tix-collection.c | 73 +++++--- tix/tix-install.c | 210 +++++++++++++---------- tix/tix-port | 25 ++- tix/tix-vars.c | 1 + tix/tix.c | 56 ++++-- tix/util.h | 134 +++++++++------ 16 files changed, 670 insertions(+), 512 deletions(-) create mode 100644 share/sysinstall/hooks/sortix-1.1-tix-3g diff --git a/Makefile b/Makefile index f50c24a9..1c0b5098 100644 --- a/Makefile +++ b/Makefile @@ -464,7 +464,7 @@ $(LIVE_INITRD): sysroot echo "You can view the installation instructions by typing:" && \ echo && \ echo " man installation") > $(LIVE_INITRD).d/root/welcome - tix-collection $(LIVE_INITRD).d create --platform=$(HOST) --prefix= --generation=2 + tix-collection $(LIVE_INITRD).d create --platform=$(HOST) --prefix= --generation=3 LC_ALL=C ls -A $(LIVE_INITRD).d | \ tar -cf $(LIVE_INITRD) -C $(LIVE_INITRD).d --numeric-owner --owner=0 --group=0 -T - rm -rf $(LIVE_INITRD).d diff --git a/build-aux/build-ports.sh b/build-aux/build-ports.sh index 61aa9fde..b91141d9 100755 --- a/build-aux/build-ports.sh +++ b/build-aux/build-ports.sh @@ -82,7 +82,7 @@ export CXXFLAGS # Initialize Tix package management in the system root if absent. if [ "$OPERATION" = build ]; then if [ ! -e "$SYSROOT/tix/collection.conf" ]; then - tix-collection "$SYSROOT" create --platform=$HOST --prefix= --generation=2 + tix-collection "$SYSROOT" create --platform=$HOST --prefix= --generation=3 fi fi @@ -138,7 +138,7 @@ for PACKAGE in $PACKAGES; do --collection="$SYSROOT" \ --destination="$SORTIX_REPOSITORY_DIR" \ ${END:+--end="$END"} \ - --generation=2 \ + --generation=3 \ --host=$HOST \ ${SORTIX_PORTS_MIRROR:+--mirror="$SORTIX_PORTS_MIRROR"} \ --mirror-directory="$SORTIX_MIRROR_DIR" \ diff --git a/build-aux/iso-grub-cfg.sh b/build-aux/iso-grub-cfg.sh index 03c0b59c..420c6ed3 100755 --- a/build-aux/iso-grub-cfg.sh +++ b/build-aux/iso-grub-cfg.sh @@ -371,7 +371,7 @@ for port in $ports; do fi if \$port_$(portvar "$port"); then echo -n "Loading /$tix ($(human_size $tix)) ... " - module /$tix --tix + module /$tix echo done fi EOF diff --git a/kernel/initrd.cpp b/kernel/initrd.cpp index f4e8d5a4..50bb3a36 100644 --- a/kernel/initrd.cpp +++ b/kernel/initrd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -447,18 +447,6 @@ static bool ReadTar(TAR* TAR) } } -static bool SearchTar(struct initrd_context* ctx, TAR* TAR, const char* path) -{ - OpenTar(TAR, ctx->initrd, ctx->initrd_size); - while ( ReadTar(TAR) ) - { - if ( !strcmp(TAR->name, path) ) - return true; - } - CloseTar(TAR); - return false; -} - static void ExtractTarObject(Ref desc, struct initrd_context* ctx, TAR* TAR) @@ -517,166 +505,6 @@ static void ExtractTar(Ref desc, struct initrd_context* ctx) CloseTar(&TAR); } -static bool TarIsTix(struct initrd_context* ctx) -{ - TAR TAR; - bool result = SearchTar(ctx, &TAR, "tix/tixinfo"); - CloseTar(&TAR); - return result; -} - -static char* tixinfo_lookup(const char* info, - size_t info_size, - const char* what) -{ - size_t what_length = strlen(what); - while ( info_size ) - { - size_t line_length = 0; - while ( line_length < info_size && info[line_length] != '\n' ) - line_length++; - if ( what_length <= line_length && - !strncmp(info, what, what_length) && - info[what_length] == '=' ) - { - char* result = strndup(info + what_length + 1, - line_length - (what_length + 1)); - if ( !result ) - Panic("initrd tar malloc failure"); - return result; - } - info += line_length; - info_size -= line_length; - if ( info_size && info[0] == '\n' ) - { - info++; - info_size--; - } - } - return NULL; -} - -static void DescriptorWriteLine(Ref desc, - ioctx_t* ioctx, - const char* str) -{ - size_t len = strlen(str); - while ( str[0] ) - { - ssize_t done = desc->write(ioctx, (unsigned char*) str, len); - if ( done <= 0 ) - PanicF("initrd tix metadata write: %m"); - str += done; - len -= done; - } - if ( desc->write(ioctx, (unsigned char*) "\n", 1) != 1 ) - PanicF("initrd tix metadata write: %m"); -} - -static int manifest_sort(const void* a_ptr, const void* b_ptr) -{ - const char* a = *(const char* const*) a_ptr; - const char* b = *(const char* const*) b_ptr; - return strcmp(a, b); -} - -static void ExtractTix(Ref desc, struct initrd_context* ctx) -{ - TAR TAR; - if ( !SearchTar(ctx, &TAR, "tix/tixinfo") ) - Panic("initrd was not tix"); - char* pkg_name = - tixinfo_lookup((const char*) TAR.data, TAR.size, "pkg.name"); - if ( !pkg_name ) - Panic("initrd tixinfo lacked pkg.name"); - if ( desc->mkdir(&ctx->ioctx, "/tix", 0755) < 0 && errno != EEXIST ) - PanicF("/tix: %m"); - if ( desc->mkdir(&ctx->ioctx, "/tix/tixinfo", 0755) < 0 && errno != EEXIST ) - PanicF("/tix/tixinfo: %m"); - if ( desc->mkdir(&ctx->ioctx, "/tix/manifest", 0755) < 0 && errno != EEXIST ) - PanicF("/tix/manifest: %m"); - char* tixinfo_path; - if ( asprintf(&tixinfo_path, "/tix/tixinfo/%s", pkg_name) < 0 ) - Panic("initrd tar malloc failure"); - char* TAR_oldname = TAR.name; - TAR.name = tixinfo_path; - ExtractTarObject(desc, ctx, &TAR); - TAR.name = TAR_oldname; - free(tixinfo_path); - CloseTar(&TAR); - Ref installed_list = - desc->open(&ctx->ioctx, "/tix/installed.list", - O_CREATE | O_WRITE | O_APPEND, 0644); - if ( !installed_list ) - PanicF("/tix/installed.list: %m"); - DescriptorWriteLine(installed_list, &ctx->ioctx, pkg_name); - installed_list.Reset(); - size_t manifest_list_size = 0; - OpenTar(&TAR, ctx->initrd, ctx->initrd_size); - while ( ReadTar(&TAR) ) - { - if ( !strncmp(TAR.name, "data", 4) && TAR.name[4] == '/' ) - manifest_list_size++; - } - CloseTar(&TAR); - char** manifest_list = new char*[manifest_list_size]; - if ( !manifest_list ) - Panic("initrd tar malloc failure"); - OpenTar(&TAR, ctx->initrd, ctx->initrd_size); - size_t manifest_list_count = 0; - while ( ReadTar(&TAR) ) - { - if ( strncmp(TAR.name, "data", 4) != 0 || TAR.name[4] != '/' ) - continue; - char* path = strdup(TAR.name + 4); - if ( !path ) - Panic("initrd tar malloc failure"); - size_t length = strlen(path); - // Trim the trailing slash from directories. - if ( 2 <= length && path[length-1] == '/' ) - path[length-1] = '\0'; - manifest_list[manifest_list_count++] = path; - } - CloseTar(&TAR); - qsort(manifest_list, manifest_list_count, sizeof(char*), manifest_sort); - char* manifest_path; - if ( asprintf(&manifest_path, "/tix/manifest/%s", pkg_name) < 0 ) - Panic("initrd tar malloc failure"); - Ref manifest = - desc->open(&ctx->ioctx, manifest_path, O_WRITE | O_CREATE | O_TRUNC, 0644); - if ( !manifest ) - PanicF("%s: %m", manifest_path); - free(manifest_path); - for ( size_t i = 0; i < manifest_list_count; i++ ) - DescriptorWriteLine(manifest, &ctx->ioctx, manifest_list[i]); - manifest.Reset(); - for ( size_t i = 0; i < manifest_list_count; i++ ) - free(manifest_list[i]); - delete[] manifest_list; - OpenTar(&TAR, ctx->initrd, ctx->initrd_size); - const char* subdir = "data/"; - size_t subdir_length = strlen(subdir); - while ( ReadTar(&TAR) ) - { - bool name_data = !strncmp(TAR.name, subdir, subdir_length) && - TAR.name[subdir_length]; - bool linkname_data = !strncmp(TAR.linkname, subdir, subdir_length) && - TAR.linkname[subdir_length]; - if ( name_data ) - { - TAR.name += subdir_length; - if ( linkname_data ) - TAR.linkname += subdir_length; - ExtractTarObject(desc, ctx, &TAR); - TAR.name -= subdir_length; - if ( linkname_data ) - TAR.linkname -= subdir_length; - } - } - CloseTar(&TAR); - free(pkg_name); -} - static int ExtractTo_mkdir(Ref desc, ioctx_t* ctx, const char* path, mode_t mode) { @@ -791,16 +619,7 @@ static void ExtractModule(struct multiboot_mod_list* module, ExtractInitrd(desc, ctx); else if ( sizeof(struct tar) <= ctx->initrd_size && !memcmp(ctx->initrd + offsetof(struct tar, magic), "ustar", 5) ) - { - if ( !strcmp(cmdline, "--tar") ) - ExtractTar(desc, ctx); - else if ( !strcmp(cmdline, "--tix") ) - ExtractTix(desc, ctx); - else if ( TarIsTix(ctx) ) - ExtractTix(desc, ctx); - else - ExtractTar(desc, ctx); - } + ExtractTar(desc, ctx); else if ( sizeof(xz_magic) <= ctx->initrd_size && !memcmp(ctx->initrd, xz_magic, sizeof(xz_magic)) ) Panic("Bootloader failed to decompress an xz initrd, " diff --git a/share/man/man7/following-development.7 b/share/man/man7/following-development.7 index 8cf1c000..c3364cc5 100644 --- a/share/man/man7/following-development.7 +++ b/share/man/man7/following-development.7 @@ -69,6 +69,26 @@ releasing Sortix x.y, foo." to allow the maintainer to easily .Xr grep 1 for it after a release. .Sh CHANGES +.Ss Third generation Tix +The tix binary package format has upgraded from generation 2 to 3 and has a new +internal layout that can be directly extracted into the filesystem. +The metatdata in +.Pa /tix +is now using the +.Xr tix-vars 1 +format in the style of +.Xr port 5 . +.Pp +Tix must be upgraded to build the new ports: +.Bd -literal + cd /src/tix && + make clean && + make install +.Ed +.Pp +An upgrade hook will migrate the +.Pa /tix +metadata to tix generation 3. .Ss Add include and comment support to passwd(5) and group(5) The .Xr passwd 5 diff --git a/share/man/man7/user-guide.7 b/share/man/man7/user-guide.7 index d7f26a12..ffa01ae7 100644 --- a/share/man/man7/user-guide.7 +++ b/share/man/man7/user-guide.7 @@ -95,8 +95,8 @@ command shows the current memory usage. .Ss Third Party Software Releases come with useful third party software installed. The -.Pa /tix/installed.list -file lists all currently installed ports. +.Pa /tix/tixinfo +directory lists all currently installed ports. .Ss Source Code Releases come full with the system source code in .Pa /src diff --git a/share/sysinstall/hooks/sortix-1.1-tix-3g b/share/sysinstall/hooks/sortix-1.1-tix-3g new file mode 100644 index 00000000..e69de29b diff --git a/sysinstall/hooks.c b/sysinstall/hooks.c index d7e12564..dcab27c4 100644 --- a/sysinstall/hooks.c +++ b/sysinstall/hooks.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, 2018, 2020, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2016, 2017, 2018, 2020, 2021, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,9 +19,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -111,6 +113,74 @@ static bool hook_needs_to_be_run(const char* source_prefix, return result; } +// If a hook needs to run a finalization step after the upgrade, it can leave +// behind a .running file in the hooks directory, which is deleted once the +// hook has run. +__attribute__((used)) +static char* hook_finalization_file(const char* target_prefix, const char* hook) +{ + char* target_path; + if ( asprintf(&target_path, "%s/share/sysinstall/hooks/%s.running", + target_prefix, hook) < 0 ) + { + warn("malloc"); + _exit(2); + } + return target_path; +} + +__attribute__((used)) +static void hook_want_finalization(const char* target_prefix, const char* hook) +{ + // TODO: After releasing Sortix 1.1, remove compatibility for Sortix 1.0 + // not having the /share/sysinstall/hooks directory. + char* path; + if ( asprintf(&path, "%s/share/sysinstall", target_prefix) < 0 ) + { + warn("malloc"); + _exit(2); + } + mkdir(path, 0755); + free(path); + if ( asprintf(&path, "%s/share/sysinstall/hooks", target_prefix) < 0 ) + { + warn("malloc"); + _exit(2); + } + mkdir(path, 0755); + free(path); + char* target_path = hook_finalization_file(target_prefix, hook); + int fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if ( fd < 0 ) + { + warn("%s", target_path); + _exit(2); + } + close(fd); + free(target_path); +} + +__attribute__((used)) +static bool hook_needs_finalization(const char* target_prefix, const char* hook) +{ + char* target_path = hook_finalization_file(target_prefix, hook); + bool result = !access_or_die(target_path, F_OK); + free(target_path); + return result; +} + +__attribute__((used)) +static void hook_did_finalization(const char* target_prefix, const char* hook) +{ + char* target_path = hook_finalization_file(target_prefix, hook); + if ( unlink(target_path) && errno != ENOENT ) + { + warn("%s", target_path); + _exit(2); + } + free(target_path); +} + void upgrade_prepare(const struct release* old_release, const struct release* new_release, const char* source_prefix, @@ -354,6 +424,42 @@ void upgrade_prepare(const struct release* old_release, } free(group_path); } + + // TODO: After releasing Sortix 1.1, remove this compatibility. + if ( hook_needs_to_be_run(source_prefix, target_prefix, + "sortix-1.1-tix-3g") ) + { + char* path = join_paths(target_prefix, "/tix/collection.conf"); + if ( !path ) + { + warn("malloc"); + _exit(2); + } + FILE* fp = fopen(path, "w"); + if ( fp ) + { + printf(" - Migrating to tix version 3...\n"); + struct utsname uts; + uname(&uts); + if ( fprintf(fp, "TIX_COLLECTION_VERSION=3\n") < 0 || + fprintf(fp, "PREFIX=\n") < 0 || + fprintf(fp, "PLATFORM=%s-%s\n", + uts.machine, uts.sysname) < 0 || + fclose(fp) == EOF ) + { + warn("%s", path); + _exit(2); + } + } + else + { + warn("%s", path); + _exit(2); + } + free(path); + // Delay deleting installed.list since it's needed for the upgrade. + hook_want_finalization(target_prefix, "sortix-1.1-tix-3g"); + } } void upgrade_finalize(const struct release* old_release, @@ -365,6 +471,36 @@ void upgrade_finalize(const struct release* old_release, (void) new_release; (void) source_prefix; (void) target_prefix; + + if ( hook_needs_finalization(target_prefix, "sortix-1.1-tix-3g") ) + { + printf(" - Finishing migration to tix version 3...\n"); + char* path = join_paths(target_prefix, "/tix/installed.list"); + if ( !path ) + { + warn("malloc"); + _exit(2); + } + if ( unlink(path) < 0 && errno != ENOENT ) + { + warn("%s", path); + _exit(2); + } + free(path); + path = join_paths(target_prefix, "/tix/repository.list"); + if ( !path ) + { + warn("malloc"); + _exit(2); + } + if ( unlink(path) < 0 && errno != ENOENT ) + { + warn("%s", path); + _exit(2); + } + free(path); + hook_did_finalization(target_prefix, "sortix-1.1-tix-3g"); + } } // TODO: After releasing Sortix 1.1, remove this compatibility. These manifests diff --git a/sysinstall/manifest.c b/sysinstall/manifest.c index ff3c23f1..8da171b8 100644 --- a/sysinstall/manifest.c +++ b/sysinstall/manifest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 2020, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2015, 2018, 2020, 2021, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -479,8 +480,8 @@ void install_manifest(const char* manifest, _exit(2); } } - // Write out the new manifests atomically afterwards to ensure no paths are - // leaked if the operation is aborted part way. + // Write out the new tixinfo afterwards to ensure no paths are leaked if the + // operation is aborted part way. char* in_tixinfo; char* out_tixinfo; if ( asprintf(&in_tixinfo, "%s/tix/tixinfo/%s", from_prefix, @@ -537,81 +538,6 @@ void install_manifest(const char* manifest, } free(in_tixinfo); free(out_tixinfo); - // Likewise write out the new installation list atomically afterwards to - // ensure no manifests are leaked if the operation is aborted part way. - char* installed_path; - char* installed_path_new; - if ( asprintf(&installed_path, "%s/tix/installed.list", to_prefix) < 0 || - asprintf(&installed_path_new, "%s/tix/installed.list.new", - to_prefix) < 0 ) - { - warn("malloc"); - _exit(2); - } - size_t installed_count; - char** installed = read_lines_file(installed_path, &installed_count); - if ( !installed ) - { - warn("%s", installed_path); - _exit(2); - } - size_t installed_length = installed_count; - if ( is_tix ) - { - bool found = false; - for ( size_t i = 0; !found && i < installed_count; i++ ) - found = !strcmp(installed[i], manifest); - if ( !found && !string_array_append(&installed, &installed_count, - &installed_length, manifest) ) - { - warn("malloc"); - _exit(2); - } - } - else - { - size_t o = 0; - for ( size_t i = 0; i < installed_count; i++ ) - { - if ( !strcmp(installed[i], manifest) ) - free(installed[i]); - else - installed[o++] = installed[i]; - } - installed_count = o; - } - string_array_sort_strcmp(installed, installed_count); - mode_t temp_umask = umask(0022); - FILE* installed_fp = fopen(installed_path_new, "w"); - if ( !installed_fp ) - { - warn("%s", installed_path_new); - _exit(2); - } - umask(temp_umask); - for ( size_t i = 0; i < installed_count; i++ ) - { - const char* name = installed[i]; - if ( fputs(name, installed_fp) == EOF || - fputc('\n', installed_fp) == EOF ) - { - warn("%s", installed_path_new); - _exit(2); - } - } - if ( fclose(installed_fp) == EOF ) - { - warn("%s", installed_path_new); - _exit(2); - } - if ( rename(installed_path_new, installed_path) < 0 ) - { - warn("rename: %s -> %s", installed_path_new, installed_path); - _exit(2); - } - string_array_free(&installed, &installed_count, &installed_length); - free(installed_path); - free(installed_path_new); if ( in_files != empty ) { for ( size_t i = 0; i < in_files_count; i++ ) @@ -701,36 +627,47 @@ void install_manifests(const char* const* manifests, char** read_installed_list(const char* prefix, size_t* out_count) { - char* path; - if ( asprintf(&path, "%s/tix/installed.list", prefix) < 0 ) + char* tixinfo; + if ( asprintf(&tixinfo, "%s/tix/tixinfo", prefix) < 0 ) { warn("malloc"); _exit(2); } + size_t count; + size_t length; char** installed; - size_t installed_count; - if ( !access_or_die(path, F_OK) ) + if ( !string_array_init(&installed, &count, &length) ) { - if ( !(installed = read_lines_file(path, &installed_count)) ) - { - warn("%s", path); - _exit(2); - } - string_array_sort_strcmp(installed, installed_count); + warn("malloc"); + _exit(2); } - else + DIR* dir = opendir(tixinfo); + if ( !dir ) { - installed = malloc(1); - if ( !installed ) + if ( errno == ENOENT ) + return *out_count = count, installed; + warn("opendir: %s", tixinfo); + _exit(2); + } + struct dirent* entry; + while ( (errno = 0, entry = readdir(dir)) ) + { + if ( entry->d_name[0] == '.' ) + continue; + if ( !string_array_append(&installed, &count, &length, entry->d_name) ) { warn("malloc"); _exit(2); } - installed_count = 0; } - free(path); - *out_count = installed_count; - return installed; + if ( errno ) + { + warn("readdir: %s", tixinfo); + _exit(2); + } + free(tixinfo); + string_array_sort_strcmp(installed, count); + return *out_count = count, installed; } void install_manifests_detect(const char* from_prefix, diff --git a/tix/tix-build.c b/tix/tix-build.c index d56a9139..efd99299 100644 --- a/tix/tix-build.c +++ b/tix/tix-build.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 2015, 2016, 2020, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2013-2016, 2020, 2022-2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -667,22 +668,32 @@ static void Build(struct metainfo* minfo) Make(minfo, build_target, NULL, true, subdir); } -static void CreateDestination(void) +static void CreateDestination(struct metainfo* minfo) { char* tardir_rel = join_paths(tmp_root, "tix"); - char* destdir_rel = join_paths(tardir_rel, "data"); - char* tixdir_rel = join_paths(tardir_rel, "tix"); - - if ( mkdir(tardir_rel, 0777) < 0 ) - err(1, "mkdir: %s", tardir_rel); - if ( mkdir(destdir_rel, 0755) != 0 ) - err(1, "mkdir: `%s'", destdir_rel); - if ( mkdir(tixdir_rel, 0755) != 0 ) - err(1, "mkdir: `%s'", tixdir_rel); - + if ( !tardir_rel ) + err(1, "malloc"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + const char* prefix = 3 <= minfo->generation ? minfo->prefix : ""; + char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix); + if ( !prefixdir_rel ) + err(1, "malloc"); + if ( mkdir_p(prefixdir_rel, 0755) < 0 ) + err(1, "mkdir: %s", prefixdir_rel); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( minfo->generation == 2 ) + { + char* destdir_rel = join_paths(prefixdir_rel, "data"); + char* tixdir_rel = join_paths(prefixdir_rel, "tix"); + if ( mkdir(destdir_rel, 0755) != 0 ) + err(1, "mkdir: `%s'", destdir_rel); + if ( mkdir(tixdir_rel, 0755) != 0 ) + err(1, "mkdir: `%s'", tixdir_rel); + free(tixdir_rel); + free(destdir_rel); + } + free(prefixdir_rel); free(tardir_rel); - free(destdir_rel); - free(tixdir_rel); } static void Install(struct metainfo* minfo) @@ -692,10 +703,12 @@ static void Install(struct metainfo* minfo) metainfo_get_def(minfo, "MAKE_INSTALL_TARGET", "pkg.make.install-target", "install"); char* tardir_rel = join_paths(tmp_root, "tix"); - char* destdir_rel = join_paths(tardir_rel, "data"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + char* destdir_rel = minfo->generation == 3 ? strdup(tardir_rel) : + join_paths(tardir_rel, "data"); char* destdir = realpath(destdir_rel, NULL); if ( !destdir ) - err(1, "realpath: %s", destdir_rel); + err(1, "realpath: %s", tardir_rel); Make(minfo, install_target, destdir, true, subdir); @@ -716,7 +729,9 @@ static void PostInstall(struct metainfo* minfo) const char* subdir = metainfo_get(minfo, "SUBDIR", "pkg.subdir"); char* tardir_rel = join_paths(tmp_root, "tix"); - char* destdir_rel = join_paths(tardir_rel, "data"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + char* destdir_rel = minfo->generation == 3 ? strdup(tardir_rel) : + join_paths(tardir_rel, "data"); char* destdir = realpath(destdir_rel, NULL); if ( !destdir ) err(1, "realpath: %s", destdir_rel); @@ -758,9 +773,28 @@ static void TixInfo(struct metainfo* minfo) char* tardir_rel = join_paths(tmp_root, "tix"); if ( !tardir_rel ) err(1, "malloc"); - char* tixinfo_rel = join_paths(tardir_rel, "tix/tixinfo"); - if ( !tixinfo_rel ) + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + const char* prefix = 3 <= minfo->generation ? minfo->prefix : ""; + char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix); + if ( !prefixdir_rel ) err(1, "malloc"); + char* tixdir_rel = join_paths(prefixdir_rel, "tix"); + if ( !tixdir_rel ) + err(1, "malloc"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( 3 <= minfo->generation && mkdir(tixdir_rel, 0755) && errno != EEXIST ) + err(1, "%s", tixdir_rel); + char* tixinfodir_rel = join_paths(tixdir_rel, "tixinfo"); + if ( !tixinfodir_rel ) + err(1, "malloc"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( 3 <= minfo->generation && + mkdir(tixinfodir_rel, 0755) && errno != EEXIST ) + err(1, "%s", tixdir_rel); + char* tixinfo_rel = 3 <= minfo->generation ? + join_paths(tixinfodir_rel, minfo->package_name) : + strdup(tixinfodir_rel); + const char* alias = metainfo_get(minfo, "ALIAS_OF", "pkg.alias-of"); const char* runtime_deps = metainfo_get(minfo, "RUNTIME_DEPS", "pkg.runtime-deps"); @@ -772,20 +806,48 @@ static void TixInfo(struct metainfo* minfo) if ( !tixinfo_fp ) err(1, "`%s'", tixinfo_rel); - fprintf(tixinfo_fp, "tix.version=1\n"); - fprintf(tixinfo_fp, "tix.class=tix\n"); - fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host); - fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name); - if ( alias ) - fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias); + if ( 3 <= minfo->generation ) + { + // TODO: Shell escape the values if needed. + fwrite_variable(tixinfo_fp, "TIX_VERSION", "3"); + fwrite_variable(tixinfo_fp, "NAME", minfo->package_name); + const char* version = metainfo_get(minfo, "VERSION", "VERSION"); + if ( version ) + fwrite_variable(tixinfo_fp, "VERSION", version); + const char* version_2 = metainfo_get(minfo, "VERSION_2", "VERSION_2"); + if ( version_2 ) + fwrite_variable(tixinfo_fp, "VERSION_2", version_2); + fwrite_variable(tixinfo_fp, "PLATFORM", minfo->host); + if ( alias ) + fwrite_variable(tixinfo_fp, "ALIAS_OF", alias); + else + { + if ( runtime_deps ) + fwrite_variable(tixinfo_fp, "RUNTIME_DEPS", runtime_deps); + if ( location_independent ) + fwrite_variable(tixinfo_fp, "LOCATION_INDEPENDENT", "true"); + else + fwrite_variable(tixinfo_fp, "PREFIX", minfo->prefix); + } + } + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. else { - if ( runtime_deps ) - fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps); - if ( location_independent ) - fprintf(tixinfo_fp, "pkg.location-independent=true\n"); + fprintf(tixinfo_fp, "tix.version=1\n"); + fprintf(tixinfo_fp, "tix.class=tix\n"); + fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host); + fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name); + if ( alias ) + fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias); else - fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix); + { + if ( runtime_deps ) + fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps); + if ( location_independent ) + fprintf(tixinfo_fp, "pkg.location-independent=true\n"); + else + fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix); + } } if ( ferror(tixinfo_fp) || fflush(tixinfo_fp) == EOF ) @@ -793,27 +855,64 @@ static void TixInfo(struct metainfo* minfo) fclose(tixinfo_fp); free(tardir_rel); + free(prefixdir_rel); + free(tixdir_rel); + free(tixinfodir_rel); free(tixinfo_rel); } +static void TixManifest(struct metainfo* minfo) +{ + if ( !fork_and_wait_or_recovery() ) + return; + char* tardir_rel = join_paths(tmp_root, "tix"); + if ( !tardir_rel ) + err(1, "malloc"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + const char* prefix = 3 <= minfo->generation ? minfo->prefix : ""; + char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix); + if ( !prefixdir_rel ) + err(1, "malloc"); + if ( chdir(prefixdir_rel) < 0 ) + err(1, "%s", prefixdir_rel); + if ( mkdir("tix", 0755) && errno != EEXIST ) + err(1, "%s", "tix"); + if ( mkdir("tix/manifest", 0755) && errno != EEXIST ) + err(1, "%s", "tix/manifest"); + char* command; + if ( asprintf(&command, + "find . -name tix -prune -o -print | " + "sed -E -e 's,^\\.$,/,' -e 's,^\\./,/,' | " + "LC_ALL=C sort > tix/manifest/%s", + minfo->package_name) < 0 ) + err(1, "malloc"); + const char* cmd_argv[] = { "sh", "-c", command, NULL }; + recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); + err(127, "%s", cmd_argv[0]); +} + static void Package(struct metainfo* minfo) { if ( !fork_and_wait_or_recovery() ) return; - char* tardir_rel = join_paths(tmp_root, "tix"); if ( !tardir_rel ) err(1, "malloc"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + const char* prefix = 3 <= minfo->generation ? minfo->prefix : ""; + char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix); + if ( !prefixdir_rel ) + err(1, "malloc"); char* package_tix = print_string("%s/%s.tix.tar.xz", minfo->destination, minfo->package_name); if ( !package_tix ) err(1, "malloc"); printf("Creating `%s'...\n", package_tix); - + fflush(stdout); const char* cmd_argv[] = { minfo->tar, - "-C", tardir_rel, + "-C", prefixdir_rel, "--remove-files", "--create", "--xz", @@ -821,12 +920,30 @@ static void Package(struct metainfo* minfo) "--owner=0", "--group=0", "--file", package_tix, + "--", "tix", - "data", NULL }; - recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); - err(127, "%s", cmd_argv[0]); + string_array_t cmd = string_array_make(); + for ( size_t i = 0; cmd_argv[i]; i++ ) + if ( !string_array_append(&cmd, cmd_argv[i]) ) + err(1, "malloc"); + struct dirent** entries; + int count = scandir(prefixdir_rel, &entries, NULL, alphasort); + if ( count < 0 ) + err(1, "scandir: %s", prefixdir_rel); + for ( int i = 0; i < count; i++ ) + { + const char* name = entries[i]->d_name; + if ( !strcmp(name, ".") || !strcmp(name, "..") || !strcmp(name, "tix") ) + continue; + if ( !string_array_append(&cmd, name) ) + err(1, "malloc"); + } + if ( !string_array_append(&cmd, NULL) ) + err(1, "malloc"); + recovery_execvp(cmd.strings[0], (char* const*) cmd.strings); + err(127, "%s", cmd.strings[0]); } static void Compile(struct metainfo* minfo) @@ -970,7 +1087,7 @@ static void BuildPackage(struct metainfo* minfo) "specify the intended destination prefix using --prefix", minfo->package_name); - CreateDestination(); + CreateDestination(minfo); // Possibly build a native version of the package to aid cross-compilation. // This is an anti-feature needed for broken packages that don't properly @@ -991,6 +1108,9 @@ static void BuildPackage(struct metainfo* minfo) if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PACKAGE, minfo) ) { TixInfo(minfo); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( 3 <= minfo->generation ) + TixManifest(minfo); Package(minfo); } } @@ -1144,6 +1264,9 @@ int main(int argc, char* argv[]) minfo.generation = atoi(generation_string); free(generation_string); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( minfo.generation != 2 && minfo.generation != 3 ) + errx(1, "Unsupported generation: %i", minfo.generation); if ( !(minfo.start_step = step_of_step_name(start_step_string)) ) { @@ -1220,8 +1343,8 @@ int main(int argc, char* argv[]) minfo.tixbuildinfo = true; minfo.package_info = string_array_make(); string_array_t* package_info = &minfo.package_info; - if ( !dictionary_append_file_path(package_info, - minfo.package_info_path) ) + if ( variables_append_file_path(package_info, + minfo.package_info_path) < 0 ) err(1, "`%s'", minfo.package_info_path); } else diff --git a/tix/tix-collection.c b/tix/tix-collection.c index a0789931..f58cdc23 100644 --- a/tix/tix-collection.c +++ b/tix/tix-collection.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -107,13 +108,6 @@ int main(int argc, char* argv[]) compact_arguments(&argc, &argv); ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv); - VerifyCommandLineCollection(&collection); - - int generation = atoi(generation_string); - free(generation_string); - - if ( !prefix ) - prefix = strdup(collection); if ( argc == 1 ) { @@ -121,12 +115,30 @@ int main(int argc, char* argv[]) exit(1); } + // The collection directory might not exist yet. + if ( strcmp(argv[1], "create") != 0 ) + VerifyCommandLineCollection(&collection); + + int generation = atoi(generation_string); + free(generation_string); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( generation != 2 && generation != 3 ) + errx(1, "Unsupported generation: %i", generation); + + if ( !prefix ) + prefix = strdup(collection); + const char* cmd = argv[1]; if ( !strcmp(cmd, "create") ) { if ( !platform && !(platform = GetBuildTriplet()) ) err(1, "unable to determine platform, use --platform"); + if ( mkdir_p(collection, 0755) != 0 ) + err(1, "mkdir: `%s'", collection); + + VerifyCommandLineCollection(&collection); + char* tix_path = join_paths(collection, "tix"); if ( mkdir_p(tix_path, 0755) != 0 ) err(1, "mkdir: `%s'", tix_path); @@ -149,26 +161,41 @@ int main(int argc, char* argv[]) errx(1, "error: `%s' already exists, a tix collection is " "already installed at `%s'.", collection_conf_path, collection); - fprintf(conf_fp, "tix.version=1\n"); - fprintf(conf_fp, "tix.class=collection\n"); - fprintf(conf_fp, "collection.generation=%i\n", generation); - fprintf(conf_fp, "collection.prefix=%s\n", !strcmp(prefix, "/") ? "" : - prefix); - fprintf(conf_fp, "collection.platform=%s\n", platform); + if ( 3 <= generation ) + { + fwrite_variable(conf_fp, "TIX_COLLECTION_VERSION", "3"); + fwrite_variable(conf_fp, "PREFIX", + !strcmp(prefix, "/") ? "" : prefix); + fwrite_variable(conf_fp, "PLATFORM", platform); + } + // TODO: After releasing Sortix 1.1, delete generation 2 compatibility. + else + { + fprintf(conf_fp, "tix.version=1\n"); + fprintf(conf_fp, "tix.class=collection\n"); + fprintf(conf_fp, "collection.generation=%i\n", generation); + fprintf(conf_fp, "collection.prefix=%s\n", + !strcmp(prefix, "/") ? "" : prefix); + fprintf(conf_fp, "collection.platform=%s\n", platform); + } fclose(conf_fp); free(collection_conf_path); - const char* repo_list_path = join_paths(tixdb_path, "repository.list"); - FILE* repo_list_fp = fopen(repo_list_path, "w"); - if ( !repo_list_fp ) - err(1, "`%s'", repo_list_path); - fclose(repo_list_fp); + // TODO: After releasing Sortix 1.1, delete generation 2 compatibility. + if ( generation < 3 ) + { + const char* repo_list_path = join_paths(tixdb_path, "repository.list"); + FILE* repo_list_fp = fopen(repo_list_path, "w"); + if ( !repo_list_fp ) + err(1, "`%s'", repo_list_path); + fclose(repo_list_fp); - const char* inst_list_path = join_paths(tixdb_path, "installed.list"); - FILE* inst_list_fp = fopen(inst_list_path, "w"); - if ( !inst_list_fp ) - err(1, "`%s'", inst_list_path); - fclose(inst_list_fp); + const char* inst_list_path = join_paths(tixdb_path, "installed.list"); + FILE* inst_list_fp = fopen(inst_list_path, "w"); + if ( !inst_list_fp ) + err(1, "`%s'", inst_list_path); + fclose(inst_list_fp); + } return 0; } diff --git a/tix/tix-install.c b/tix/tix-install.c index 05ddfd90..d01c074d 100644 --- a/tix/tix-install.c +++ b/tix/tix-install.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, 2016, 2017, 2020, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2015-2017, 2020, 2022-2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -78,50 +79,24 @@ void VerifyTixDatabase(const char* prefix, err(1, "error: tix collection information unavailable: `%s'", info_path); } - char* installed_list_path = join_paths(tixdb_path, "installed.list"); - FILE* installed_list_fp = fopen(installed_list_path, "a"); - if ( !installed_list_fp ) - { - warn("error: unable to open `%s' for writing", - installed_list_path); - errx(1, "error: `%s': do you have sufficient permissions to " - "administer this tix collection?", prefix); - } - fclose(installed_list_fp); - free(installed_list_path); free(info_path); } bool IsPackageInstalled(const char* tixdb_path, const char* package) { - char* installed_list_path = join_paths(tixdb_path, "installed.list"); - FILE* installed_list_fp = fopen(installed_list_path, "r"); - if ( !installed_list_fp ) - err(1, "`%s'", installed_list_path); - - bool ret = false; - char* line = NULL; - size_t line_size = 0; - ssize_t line_len; - while ( 0 < (line_len = getline(&line, &line_size, installed_list_fp)) ) - { - if ( line[line_len-1] == '\n' ) - line[--line_len] = '\0'; - if ( !strcmp(line, package) ) - { - ret = true; - break; - } - } - free(line); - if ( ferror(installed_list_fp) ) - err(1, "`%s'", installed_list_path); - - fclose(installed_list_fp); - free(installed_list_path); - return ret; + char* tixinfo_dir = join_paths(tixdb_path, "tixinfo"); + if ( !tixinfo_dir ) + err(1, "malloc"); + char* tixinfo = join_paths(tixinfo_dir, package); + if ( !tixinfo ) + err(1, "malloc"); + bool installed = !access(tixinfo, F_OK); + free(tixinfo); + free(tixinfo_dir); + return installed; } +// TODO: After releasing Sortix 1.1, delete generation 2 compatibility. void MarkPackageAsInstalled(const char* tixdb_path, const char* package) { char* installed_list_path = join_paths(tixdb_path, "installed.list"); @@ -228,18 +203,40 @@ int main(int argc, char* argv[]) char* coll_conf_path = join_paths(tix_directory_path, "collection.conf"); string_array_t coll_conf = string_array_make(); - if ( !dictionary_append_file_path(&coll_conf, coll_conf_path) ) - err(1, "`%s'", coll_conf_path); - VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path); - free(coll_conf_path); + switch ( variables_append_file_path(&coll_conf, coll_conf_path) ) + { + case -1: err(1, "%s", coll_conf_path); + case -2: errx(2, "%s: Syntax error", coll_conf_path); + } - const char* coll_generation = dictionary_get(&coll_conf, "collection.generation"); - assert(coll_generation); + VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path); + + const char* coll_generation = + dictionary_get(&coll_conf, "TIX_COLLECTION_VERSION"); + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + if ( !coll_generation ) + coll_generation = dictionary_get(&coll_conf, "collection.generation"); + if ( !coll_generation ) + err(1, "%s: No TIX_COLLECTION_VERSION was set", coll_conf_path); generation = atoi(coll_generation); - coll_prefix = dictionary_get(&coll_conf, "collection.prefix"); - assert(coll_prefix); - coll_platform = dictionary_get(&coll_conf, "collection.platform"); - assert(coll_platform); + if ( generation == 3 ) + { + coll_prefix = dictionary_get(&coll_conf, "PREFIX"); + coll_platform = dictionary_get(&coll_conf, "PLATFORM"); + } + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. + else if ( generation == 2 ) + { + coll_prefix = dictionary_get(&coll_conf, "collection.prefix"); + coll_platform = dictionary_get(&coll_conf, "collection.platform"); + } + else + errx(1, "%s: Unsupported TIX_COLLECTION_VERSION: %i", + coll_conf_path, generation); + if ( !coll_prefix ) + err(1, "%s: No PREFIX was set", coll_conf_path); + if ( !coll_platform ) + err(1, "%s: No PLATFORM was set", coll_conf_path); for ( int i = 1; i < argc; i++ ) InstallPackage(argv[i]); @@ -247,6 +244,7 @@ int main(int argc, char* argv[]) string_array_reset(&coll_conf); free(tix_directory_path); + free(coll_conf_path); return 0; } @@ -263,25 +261,47 @@ void InstallPackage(const char* tix_path) if ( !IsFile(tix_path) ) err(1, "`%s'", tix_path); - const char* tixinfo_path = "tix/tixinfo"; + // TODO: After releasing Sortix 1.1, delete generation 2 compatibility. + bool modern = true; + const char* tixinfo_path = "tix/tixinfo/"; if ( !TarContainsFile(tix_path, tixinfo_path) ) - errx(1, "`%s' doesn't contain a `%s' file", tix_path, tixinfo_path); + { + const char* tixinfo_path_old = "tix/tixinfo"; + if ( !TarContainsFile(tix_path, tixinfo_path_old) ) + errx(1, "`%s' doesn't contain a `%s' directory", tix_path, + tixinfo_path); + tixinfo_path = tixinfo_path_old; + modern = false; + } string_array_t tixinfo = string_array_make(); FILE* tixinfo_fp = TarOpenFile(tix_path, tixinfo_path); - dictionary_append_file(&tixinfo, tixinfo_fp); + switch ( variables_append_file(&tixinfo, tixinfo_fp) ) + { + case -1: err(1, "%s: %s", tix_path, tixinfo_path); + case -2: errx(1, "%s: %s: Syntax error", tix_path, tixinfo_path); + } + fclose(tixinfo_fp); - const char* package_name = dictionary_get(&tixinfo, "pkg.name"); + const char* version = dictionary_get(&tixinfo, "TIX_VERSION"); + if ( modern && (!version || strcmp(version, "3") != 0) ) + errx(1, "%s: unsupported TIX_VERSION: %s", tix_path, version); + + const char* package_name = + dictionary_get(&tixinfo, modern ? "NAME" : "pkg.name"); assert(package_name); - const char* package_prefix = dictionary_get(&tixinfo, "pkg.prefix"); + const char* package_prefix = + dictionary_get(&tixinfo, modern ? "PREFIX" : "pkg.prefix"); assert(package_prefix || !package_prefix); - const char* package_platform = dictionary_get(&tixinfo, "tix.platform"); + const char* package_platform = + dictionary_get(&tixinfo, modern ? "PLATFORM" : "tix.platform"); assert(package_platform || !package_platform); - bool already_installed = IsPackageInstalled(tix_directory_path, package_name); + bool already_installed = + IsPackageInstalled(tix_directory_path, package_name); if ( already_installed && !reinstall ) errx(1, "error: package `%s' is already installed. Use --reinstall " "to force reinstallation.", package_name); @@ -313,44 +333,51 @@ void InstallPackage(const char* tix_path) fflush(stdout); } + const char* data = modern ? "" : "data"; char* data_and_prefix = package_prefix && package_prefix[0] ? - print_string("data%s", package_prefix) : - strdup("data"); + print_string("%s%s", data, package_prefix) : + strdup(data); - char* tixinfo_out_path = print_string("%s/tixinfo/%s", tix_directory_path, package_name); - int tixinfo_fd = open(tixinfo_out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if ( tixinfo_fd < 0 ) - err(1, "%s", tixinfo_out_path); - TarExtractFileToFD(tix_path, "tix/tixinfo", tixinfo_fd); - close(tixinfo_fd); - - FILE* index_fp = TarOpenIndex(tix_path); - string_array_t files = string_array_make(); - string_array_append_file(&files, index_fp); - qsort(files.strings, files.length, sizeof(char*), strcmp_indirect); - char* manifest_path = print_string("%s/manifest/%s", tix_directory_path, package_name); - FILE* manifest_fp = fopen(manifest_path, "w"); - if ( !manifest_fp ) - err(1, "%s", manifest_path); - for ( size_t i = 0; i < files.length; i++ ) + if ( !modern ) { - char* str = files.strings[i]; - if ( strncmp(str, "data", strlen("data")) != 0 ) - continue; - str += strlen("data"); - if ( str[0] && str[0] != '/' ) - continue; - size_t len = strlen(str); - while ( 2 <= len && str[len-1] == '/' ) - str[--len] = '\0'; - if ( fprintf(manifest_fp, "%s\n", str) < 0 ) + char* tixinfo_out_path = + print_string("%s/tixinfo/%s", tix_directory_path, package_name); + int tixinfo_fd = + open(tixinfo_out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if ( tixinfo_fd < 0 ) + err(1, "%s", tixinfo_out_path); + TarExtractFileToFD(tix_path, "tix/tixinfo", tixinfo_fd); + close(tixinfo_fd); + + FILE* index_fp = TarOpenIndex(tix_path); + string_array_t files = string_array_make(); + string_array_append_file(&files, index_fp); + qsort(files.strings, files.length, sizeof(char*), strcmp_indirect); + char* manifest_path = + print_string("%s/manifest/%s", tix_directory_path, package_name); + FILE* manifest_fp = fopen(manifest_path, "w"); + if ( !manifest_fp ) err(1, "%s", manifest_path); + for ( size_t i = 0; i < files.length; i++ ) + { + char* str = files.strings[i]; + if ( strncmp(str, "data", strlen("data")) != 0 ) + continue; + str += strlen("data"); + if ( str[0] && str[0] != '/' ) + continue; + size_t len = strlen(str); + while ( 2 <= len && str[len-1] == '/' ) + str[--len] = '\0'; + if ( fprintf(manifest_fp, "%s\n", str) < 0 ) + err(1, "%s", manifest_path); + } + if ( ferror(manifest_fp) || fflush(manifest_fp) == EOF ) + err(1, "%s", manifest_path); + fclose(manifest_fp); + string_array_reset(&files); + fclose(index_fp); } - if ( ferror(manifest_fp) || fflush(manifest_fp) == EOF ) - err(1, "%s", manifest_path); - fclose(manifest_fp); - string_array_reset(&files); - fclose(index_fp); if ( fork_and_wait_or_death() ) { @@ -358,14 +385,14 @@ void InstallPackage(const char* tix_path) const char* cmd_argv[] = { "tar", - print_string("--strip-components=%zu", num_strips), "-C", collection, "--extract", "--file", tix_path, "--keep-directory-symlink", "--same-permissions", "--no-same-owner", - data_and_prefix, + modern ? NULL : print_string("--strip-components=%zu", num_strips), + modern ? NULL : data_and_prefix, NULL }; execvp(cmd_argv[0], (char* const*) cmd_argv); @@ -373,7 +400,8 @@ void InstallPackage(const char* tix_path) } free(data_and_prefix); - if ( !already_installed ) + // TODO: After releasing Sortix 1.1, delete generation 2 compatibility. + if ( generation <= 2 && !already_installed ) MarkPackageAsInstalled(tix_directory_path, package_name); string_array_reset(&tixinfo); diff --git a/tix/tix-port b/tix/tix-port index 256f6040..de94c3da 100755 --- a/tix/tix-port +++ b/tix/tix-port @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2022 Jonas 'Sortie' Termansen. +# Copyright (c) 2022, 2023 Jonas 'Sortie' Termansen. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -304,6 +304,9 @@ desired_version() {( if [ -f "$port.rmpatch" ]; then stamp="$stamp.$(cat "$port.rmpatch" | sha256sum | grep -Eo '^[^ ]*')" fi + if [ -f "$port.post-install" ]; then + stamp="$stamp.$(cat "$port.post-install" | sha256sum | grep -Eo '^[^ ]*')" + fi DEVELOPMENT=$(tix-vars -d false "$port.port" DEVELOPMENT) if [ "$DEVELOPMENT" = true ]; then stamp="$stamp.development" @@ -428,12 +431,20 @@ strip_tix() {( strip=${host+$host-}strip dir=$(mktemp -d) tar -C "$dir" -xf "$1" - $strip -d "$dir/data/bin/"* 2>/dev/null || true - $strip -d "$dir/data/lib/"* 2>/dev/null || true - $strip -d "$dir/data/libexec"* 2>/dev/null || true - $strip -d "$dir/data/libexec/git-core/"* 2>/dev/null || true - $strip -d "$dir/data/sbin/"* 2>/dev/null || true - (cd "$dir" && tar --numeric-owner --owner=0 --group=0 -cJf port.tar.tix.xz tix data) + # TODO: After releasing Sortix 1.1, remove the data directory compatibility. + if [ -e "$dir/data" ]; then + data_dir="$dir/data" + else + data_dir="$dir" + fi + $strip -d "$data_dir/bin/"* 2>/dev/null || true + $strip -d "$data_dir/lib/"* 2>/dev/null || true + $strip -d "$data_dir/libexec"* 2>/dev/null || true + $strip -d "$data_dir/libexec/git-core/"* 2>/dev/null || true + $strip -d "$data_dir/sbin/"* 2>/dev/null || true + (cd "$dir" && + LC_ALL=C ls -A | grep -Ev '^tix$' | + tar --numeric-owner --owner=0 --group=0 -cJf port.tar.tix.xz tix -T -) cp "$dir/port.tar.tix.xz" "$1" rm -rf "$dir" )} diff --git a/tix/tix-vars.c b/tix/tix-vars.c index 22df77d7..ed91167e 100644 --- a/tix/tix-vars.c +++ b/tix/tix-vars.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/tix/tix.c b/tix/tix.c index bed658ad..33067aab 100644 --- a/tix/tix.c +++ b/tix/tix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2015, 2016, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,6 @@ typedef struct char* tixdb_path; string_array_t coll_conf; string_array_t repo_list; - string_array_t inst_list; bool reinstall; } params_t; @@ -92,20 +92,36 @@ string_array_t GetPackageDependencies(params_t* params, const char* pkg_name) if ( !pkg_path ) err(1, "unable to locate package `%s'", pkg_name); - const char* tixinfo_path = "tix/tixinfo"; + // TODO: After releasing Sortix 1.1, delete generation 2 compatibility. + bool modern = false; + const char* tixinfo_path = "tix/tixinfo/"; if ( !TarContainsFile(pkg_path, tixinfo_path) ) - errx(1, "`%s' doesn't contain a `%s' file", pkg_path, tixinfo_path); + { + const char* tixinfo_path_old = "tix/tixinfo"; + if ( !TarContainsFile(pkg_path, tixinfo_path_old) ) + errx(1, "`%s' doesn't contain a `%s' directory", pkg_path, + tixinfo_path); + tixinfo_path = tixinfo_path_old; + modern = false; + } string_array_t tixinfo = string_array_make(); FILE* tixinfo_fp = TarOpenFile(pkg_path, tixinfo_path); - dictionary_append_file(&tixinfo, tixinfo_fp); + switch ( variables_append_file(&tixinfo, tixinfo_fp) ) + { + case -1: err(1, "%s: %s", pkg_path, tixinfo_path); + case -2: errx(1, "%s: %s: Syntax error", pkg_path, tixinfo_path); + } + fclose(tixinfo_fp); VerifyTixInformation(&tixinfo, pkg_path); - const char* deps = dictionary_get_def(&tixinfo, "pkg.runtime-deps", ""); + const char* deps = dictionary_get_def(&tixinfo, modern ? "RUNTIME_DEPS" : + "pkg.runtime-deps", ""); string_array_append_token_string(&ret, deps); - const char* alias = dictionary_get_def(&tixinfo, "pkg.alias-of", ""); + const char* alias = dictionary_get_def(&tixinfo, modern ? "ALIAS_OF" : + "pkg.alias-of", ""); string_array_append_token_string(&ret, alias); string_array_reset(&tixinfo); @@ -224,23 +240,22 @@ int main(int argc, char* argv[]) char* coll_conf_path = join_paths(params.tixdb_path, "collection.conf"); params.coll_conf = string_array_make(); - if ( !dictionary_append_file_path(¶ms.coll_conf, coll_conf_path) ) - err(1, "`%s'", coll_conf_path); + switch ( variables_append_file_path(¶ms.coll_conf, coll_conf_path) ) + { + case -1: err(1, "%s", coll_conf_path); + case -2: errx(2, "%s: Syntax error", coll_conf_path); + } VerifyTixCollectionConfiguration(¶ms.coll_conf, coll_conf_path); free(coll_conf_path); + // TODO: Decide the fate of repository.list. char* repo_list_path = join_paths(params.tixdb_path, "repository.list"); params.repo_list = string_array_make(); - if ( !string_array_append_file_path(¶ms.repo_list, repo_list_path) ) + if ( !string_array_append_file_path(¶ms.repo_list, repo_list_path) && + errno != ENOENT ) err(1, "`%s'", repo_list_path); free(repo_list_path); - char* inst_list_path = join_paths(params.tixdb_path, "installed.list"); - params.inst_list = string_array_make(); - if ( !string_array_append_file_path(¶ms.inst_list, inst_list_path) ) - err(1, "`%s'", inst_list_path); - free(inst_list_path); - if ( argc == 1 ) errx(1, "error: no command specified."); @@ -252,10 +267,16 @@ int main(int argc, char* argv[]) string_array_t work = string_array_make(); + char* tixinfo_dir = join_paths(params.tixdb_path, "tixinfo"); + if ( !tixinfo_dir ) + err(1, "malloc"); for ( int i = 2; i < argc; i++ ) { const char* pkg_name = argv[i]; - if ( string_array_contains(¶ms.inst_list, pkg_name) ) + char* tixinfo = join_paths(tixinfo_dir, pkg_name); + bool installed = !access(tixinfo, F_OK); + free(tixinfo); + if ( installed ) { if ( params.reinstall ) { @@ -268,6 +289,7 @@ int main(int argc, char* argv[]) GetPackageRecursiveDependencies(¶ms, &work, pkg_name); } + free(tixinfo_dir); for ( size_t i = 0; i < work.length; i++ ) InstallPackageOfName(¶ms, work.strings[i]); diff --git a/tix/util.h b/tix/util.h index d91690c4..f21d6d1b 100644 --- a/tix/util.h +++ b/tix/util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, 2016, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,7 +20,7 @@ #ifndef UTIL_H #define UTIL_H -#define DEFAULT_GENERATION "2" +#define DEFAULT_GENERATION "3" extern char** environ; @@ -326,34 +326,6 @@ void dictionary_normalize_entry(char* entry) entry[output_off] = '\0'; } -void dictionary_append_file(string_array_t* sa, FILE* fp) -{ - char* entry = NULL; - size_t entry_size = 0; - ssize_t entry_length; - while ( 0 < (entry_length = getline(&entry, &entry_size, fp)) ) - { - if ( entry[entry_length-1] == '\n' ) - entry[--entry_length] = '\0'; - dictionary_normalize_entry(entry); - if ( entry[0] == '#' ) - continue; - string_array_append(sa, entry); - } - free(entry); - assert(!ferror(fp)); -} - -bool dictionary_append_file_path(string_array_t* sa, const char* path) -{ - FILE* fp = fopen(path, "r"); - if ( !fp ) - return false; - dictionary_append_file(sa, fp); - fclose(fp); - return true; -} - bool is_identifier_char(char c) { return ('a' <= c && c <= 'z') || @@ -595,10 +567,24 @@ const char* getenv_def(const char* var, const char* def) int mkdir_p(const char* path, mode_t mode) { int saved_errno = errno; - if ( mkdir(path, mode) != 0 && errno != EEXIST ) - return -1; - errno = saved_errno; - return 0; + if ( !mkdir(path, mode) ) + return 0; + if ( errno == ENOENT ) + { + char* prev = strdup(path); + if ( !prev ) + return -1; + int status = mkdir_p(dirname(prev), mode | 0500); + free(prev); + if ( status < 0 ) + return -1; + errno = saved_errno; + if ( !mkdir(path, mode) ) + return 0; + } + if ( errno == EEXIST ) + return errno = saved_errno, 0; + return -1; } static void compact_arguments(int* argc, char*** argv) @@ -925,23 +911,39 @@ void VerifyCommandLineCollection(char** collection) void VerifyTixCollectionConfiguration(string_array_t* info, const char* path) { + // TODO: After releasing Sortix 1.1, remove generation 2 compatibility. const char* tix_version = dictionary_get(info, "tix.version"); - if ( !tix_version ) - errx(1, "error: `%s': no `tix.version' variable declared", path); - if ( atoi(tix_version) != 1 ) - errx(1, "error: `%s': tix version `%s' not supported", path, - tix_version); - const char* tix_class = dictionary_get(info, "tix.class"); - if ( !tix_class ) - errx(1, "error: `%s': no `tix.class' variable declared", path); - if ( strcmp(tix_class, "collection") != 0 ) - errx(1, "error: `%s': error: unexpected tix class `%s'.", path, - tix_class); - if ( !(dictionary_get(info, "collection.prefix")) ) - errx(1, "error: `%s': no `collection.prefix' variable declared", path); - if ( !(dictionary_get(info, "collection.platform")) ) - errx(1, "error: `%s': no `collection.platform' variable declared", - path); + if ( tix_version ) + { + if ( !tix_version ) + errx(1, "error: `%s': no `tix.version' variable declared", path); + if ( atoi(tix_version) != 1 ) + errx(1, "error: `%s': tix version `%s' not supported", path, + tix_version); + const char* tix_class = dictionary_get(info, "tix.class"); + if ( !tix_class ) + errx(1, "error: `%s': no `tix.class' variable declared", path); + if ( strcmp(tix_class, "collection") != 0 ) + errx(1, "error: `%s': error: unexpected tix class `%s'.", path, + tix_class); + if ( !(dictionary_get(info, "collection.prefix")) ) + errx(1, "error: `%s': no `collection.prefix' variable declared", + path); + if ( !(dictionary_get(info, "collection.platform")) ) + errx(1, "error: `%s': no `collection.platform' variable declared", + path); + return; + } + + const char* version = dictionary_get(info, "TIX_COLLECTION_VERSION"); + if ( !version ) + errx(1, "%s: Mandatory TIX_COLLECTION_VERSION was not set", path); + if ( atoi(version) != 3 ) + errx(1, "%s: Unsupported: TIX_COLLECTION_VERSION: %s", path, version); + if ( !(dictionary_get(info, "PREFIX")) ) + errx(1, "%s: Mandatory PREFIX was not set", path); + if ( !(dictionary_get(info, "PLATFORM")) ) + errx(1, "%s: Mandatory PLATFORM was not set", path); } static pid_t original_pid; @@ -1032,6 +1034,37 @@ void fprint_shell_variable_assignment(FILE* fp, const char* variable, const char } } +bool needs_single_quote(const char* string) +{ + for ( size_t i = 0; string[i]; i++ ) + { + char c = string[i]; + if ( !(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + c == '/' || c == '_' || c == '.' || c == '+' || c == ':' || + c == '%' || c == '$' || c == '{' || c == '}' || c == '-') ) + return true; + } + return false; +} + +void fwrite_variable(FILE* fp, const char* key, const char* value) +{ + fprintf(fp, "%s=", key); + if ( !needs_single_quote(value) ) + fprintf(fp, "%s\n", value); + else + { + fputc('\'', fp); + for ( size_t i = 0; value[i]; i++ ) + if ( value[i] == '\'' ) + fprintf(fp, "'\\''"); + else + fputc(value[i], fp); + fputs("'\n", fp); + } +} + bool is_success_exit_status(int status) { return WIFEXITED(status) && WEXITSTATUS(status) == 0; @@ -1149,6 +1182,7 @@ int recovery_execvp(const char* path, char* const* argv) } printf("\n"); + fflush(stdout); if ( recovery_configure_state(false) == RECOVERY_STATE_PRINT_COMMAND ) _exit(0);