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);