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.
This commit is contained in:
Jonas 'Sortie' Termansen 2023-07-15 16:43:27 +02:00
parent b819428bd2
commit d189183900
16 changed files with 670 additions and 512 deletions

View File

@ -464,7 +464,7 @@ $(LIVE_INITRD): sysroot
echo "You can view the installation instructions by typing:" && \ echo "You can view the installation instructions by typing:" && \
echo && \ echo && \
echo " man installation") > $(LIVE_INITRD).d/root/welcome 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 | \ LC_ALL=C ls -A $(LIVE_INITRD).d | \
tar -cf $(LIVE_INITRD) -C $(LIVE_INITRD).d --numeric-owner --owner=0 --group=0 -T - tar -cf $(LIVE_INITRD) -C $(LIVE_INITRD).d --numeric-owner --owner=0 --group=0 -T -
rm -rf $(LIVE_INITRD).d rm -rf $(LIVE_INITRD).d

View File

@ -82,7 +82,7 @@ export CXXFLAGS
# Initialize Tix package management in the system root if absent. # Initialize Tix package management in the system root if absent.
if [ "$OPERATION" = build ]; then if [ "$OPERATION" = build ]; then
if [ ! -e "$SYSROOT/tix/collection.conf" ]; 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
fi fi
@ -138,7 +138,7 @@ for PACKAGE in $PACKAGES; do
--collection="$SYSROOT" \ --collection="$SYSROOT" \
--destination="$SORTIX_REPOSITORY_DIR" \ --destination="$SORTIX_REPOSITORY_DIR" \
${END:+--end="$END"} \ ${END:+--end="$END"} \
--generation=2 \ --generation=3 \
--host=$HOST \ --host=$HOST \
${SORTIX_PORTS_MIRROR:+--mirror="$SORTIX_PORTS_MIRROR"} \ ${SORTIX_PORTS_MIRROR:+--mirror="$SORTIX_PORTS_MIRROR"} \
--mirror-directory="$SORTIX_MIRROR_DIR" \ --mirror-directory="$SORTIX_MIRROR_DIR" \

View File

@ -371,7 +371,7 @@ for port in $ports; do
fi fi
if \$port_$(portvar "$port"); then if \$port_$(portvar "$port"); then
echo -n "Loading /$tix ($(human_size $tix)) ... " echo -n "Loading /$tix ($(human_size $tix)) ... "
module /$tix --tix module /$tix
echo done echo done
fi fi
EOF EOF

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * 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<Descriptor> desc, static void ExtractTarObject(Ref<Descriptor> desc,
struct initrd_context* ctx, struct initrd_context* ctx,
TAR* TAR) TAR* TAR)
@ -517,166 +505,6 @@ static void ExtractTar(Ref<Descriptor> desc, struct initrd_context* ctx)
CloseTar(&TAR); 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> desc, ioctx_t* ctx, static int ExtractTo_mkdir(Ref<Descriptor> desc, ioctx_t* ctx,
const char* path, mode_t mode) const char* path, mode_t mode)
{ {
@ -791,16 +619,7 @@ static void ExtractModule(struct multiboot_mod_list* module,
ExtractInitrd(desc, ctx); ExtractInitrd(desc, ctx);
else if ( sizeof(struct tar) <= ctx->initrd_size && else if ( sizeof(struct tar) <= ctx->initrd_size &&
!memcmp(ctx->initrd + offsetof(struct tar, magic), "ustar", 5) ) !memcmp(ctx->initrd + offsetof(struct tar, magic), "ustar", 5) )
{ ExtractTar(desc, ctx);
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);
}
else if ( sizeof(xz_magic) <= ctx->initrd_size && else if ( sizeof(xz_magic) <= ctx->initrd_size &&
!memcmp(ctx->initrd, xz_magic, sizeof(xz_magic)) ) !memcmp(ctx->initrd, xz_magic, sizeof(xz_magic)) )
Panic("Bootloader failed to decompress an xz initrd, " Panic("Bootloader failed to decompress an xz initrd, "

View File

@ -69,6 +69,26 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
.Xr grep 1 .Xr grep 1
for it after a release. for it after a release.
.Sh CHANGES .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) .Ss Add include and comment support to passwd(5) and group(5)
The The
.Xr passwd 5 .Xr passwd 5

View File

@ -95,8 +95,8 @@ command shows the current memory usage.
.Ss Third Party Software .Ss Third Party Software
Releases come with useful third party software installed. Releases come with useful third party software installed.
The The
.Pa /tix/installed.list .Pa /tix/tixinfo
file lists all currently installed ports. directory lists all currently installed ports.
.Ss Source Code .Ss Source Code
Releases come full with the system source code in Releases come full with the system source code in
.Pa /src .Pa /src

View File

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -19,9 +19,11 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -111,6 +113,74 @@ static bool hook_needs_to_be_run(const char* source_prefix,
return result; 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, void upgrade_prepare(const struct release* old_release,
const struct release* new_release, const struct release* new_release,
const char* source_prefix, const char* source_prefix,
@ -354,6 +424,42 @@ void upgrade_prepare(const struct release* old_release,
} }
free(group_path); 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, void upgrade_finalize(const struct release* old_release,
@ -365,6 +471,36 @@ void upgrade_finalize(const struct release* old_release,
(void) new_release; (void) new_release;
(void) source_prefix; (void) source_prefix;
(void) target_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 // TODO: After releasing Sortix 1.1, remove this compatibility. These manifests

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -20,6 +20,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -479,8 +480,8 @@ void install_manifest(const char* manifest,
_exit(2); _exit(2);
} }
} }
// Write out the new manifests atomically afterwards to ensure no paths are // Write out the new tixinfo afterwards to ensure no paths are leaked if the
// leaked if the operation is aborted part way. // operation is aborted part way.
char* in_tixinfo; char* in_tixinfo;
char* out_tixinfo; char* out_tixinfo;
if ( asprintf(&in_tixinfo, "%s/tix/tixinfo/%s", from_prefix, if ( asprintf(&in_tixinfo, "%s/tix/tixinfo/%s", from_prefix,
@ -537,81 +538,6 @@ void install_manifest(const char* manifest,
} }
free(in_tixinfo); free(in_tixinfo);
free(out_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 ) if ( in_files != empty )
{ {
for ( size_t i = 0; i < in_files_count; i++ ) 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** read_installed_list(const char* prefix, size_t* out_count)
{ {
char* path; char* tixinfo;
if ( asprintf(&path, "%s/tix/installed.list", prefix) < 0 ) if ( asprintf(&tixinfo, "%s/tix/tixinfo", prefix) < 0 )
{ {
warn("malloc"); warn("malloc");
_exit(2); _exit(2);
} }
size_t count;
size_t length;
char** installed; char** installed;
size_t installed_count; if ( !string_array_init(&installed, &count, &length) )
if ( !access_or_die(path, F_OK) )
{ {
if ( !(installed = read_lines_file(path, &installed_count)) ) warn("malloc");
{ _exit(2);
warn("%s", path);
_exit(2);
}
string_array_sort_strcmp(installed, installed_count);
} }
else DIR* dir = opendir(tixinfo);
if ( !dir )
{ {
installed = malloc(1); if ( errno == ENOENT )
if ( !installed ) 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"); warn("malloc");
_exit(2); _exit(2);
} }
installed_count = 0;
} }
free(path); if ( errno )
*out_count = installed_count; {
return installed; 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, void install_manifests_detect(const char* from_prefix,

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -27,6 +27,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <limits.h> #include <limits.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
@ -667,22 +668,32 @@ static void Build(struct metainfo* minfo)
Make(minfo, build_target, NULL, true, subdir); 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* tardir_rel = join_paths(tmp_root, "tix");
char* destdir_rel = join_paths(tardir_rel, "data"); if ( !tardir_rel )
char* tixdir_rel = join_paths(tardir_rel, "tix"); err(1, "malloc");
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( mkdir(tardir_rel, 0777) < 0 ) const char* prefix = 3 <= minfo->generation ? minfo->prefix : "";
err(1, "mkdir: %s", tardir_rel); char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix);
if ( mkdir(destdir_rel, 0755) != 0 ) if ( !prefixdir_rel )
err(1, "mkdir: `%s'", destdir_rel); err(1, "malloc");
if ( mkdir(tixdir_rel, 0755) != 0 ) if ( mkdir_p(prefixdir_rel, 0755) < 0 )
err(1, "mkdir: `%s'", tixdir_rel); 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(tardir_rel);
free(destdir_rel);
free(tixdir_rel);
} }
static void Install(struct metainfo* minfo) static void Install(struct metainfo* minfo)
@ -692,10 +703,12 @@ static void Install(struct metainfo* minfo)
metainfo_get_def(minfo, "MAKE_INSTALL_TARGET", metainfo_get_def(minfo, "MAKE_INSTALL_TARGET",
"pkg.make.install-target", "install"); "pkg.make.install-target", "install");
char* tardir_rel = join_paths(tmp_root, "tix"); 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); char* destdir = realpath(destdir_rel, NULL);
if ( !destdir ) if ( !destdir )
err(1, "realpath: %s", destdir_rel); err(1, "realpath: %s", tardir_rel);
Make(minfo, install_target, destdir, true, subdir); 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"); const char* subdir = metainfo_get(minfo, "SUBDIR", "pkg.subdir");
char* tardir_rel = join_paths(tmp_root, "tix"); 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); char* destdir = realpath(destdir_rel, NULL);
if ( !destdir ) if ( !destdir )
err(1, "realpath: %s", destdir_rel); err(1, "realpath: %s", destdir_rel);
@ -758,9 +773,28 @@ static void TixInfo(struct metainfo* minfo)
char* tardir_rel = join_paths(tmp_root, "tix"); char* tardir_rel = join_paths(tmp_root, "tix");
if ( !tardir_rel ) if ( !tardir_rel )
err(1, "malloc"); err(1, "malloc");
char* tixinfo_rel = join_paths(tardir_rel, "tix/tixinfo"); // TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( !tixinfo_rel ) const char* prefix = 3 <= minfo->generation ? minfo->prefix : "";
char* prefixdir_rel = print_string("%s%s", tardir_rel, prefix);
if ( !prefixdir_rel )
err(1, "malloc"); 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* alias = metainfo_get(minfo, "ALIAS_OF", "pkg.alias-of");
const char* runtime_deps = const char* runtime_deps =
metainfo_get(minfo, "RUNTIME_DEPS", "pkg.runtime-deps"); metainfo_get(minfo, "RUNTIME_DEPS", "pkg.runtime-deps");
@ -772,20 +806,48 @@ static void TixInfo(struct metainfo* minfo)
if ( !tixinfo_fp ) if ( !tixinfo_fp )
err(1, "`%s'", tixinfo_rel); err(1, "`%s'", tixinfo_rel);
fprintf(tixinfo_fp, "tix.version=1\n"); if ( 3 <= minfo->generation )
fprintf(tixinfo_fp, "tix.class=tix\n"); {
fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host); // TODO: Shell escape the values if needed.
fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name); fwrite_variable(tixinfo_fp, "TIX_VERSION", "3");
if ( alias ) fwrite_variable(tixinfo_fp, "NAME", minfo->package_name);
fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias); 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 else
{ {
if ( runtime_deps ) fprintf(tixinfo_fp, "tix.version=1\n");
fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps); fprintf(tixinfo_fp, "tix.class=tix\n");
if ( location_independent ) fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host);
fprintf(tixinfo_fp, "pkg.location-independent=true\n"); fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name);
if ( alias )
fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias);
else 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 ) if ( ferror(tixinfo_fp) || fflush(tixinfo_fp) == EOF )
@ -793,27 +855,64 @@ static void TixInfo(struct metainfo* minfo)
fclose(tixinfo_fp); fclose(tixinfo_fp);
free(tardir_rel); free(tardir_rel);
free(prefixdir_rel);
free(tixdir_rel);
free(tixinfodir_rel);
free(tixinfo_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) static void Package(struct metainfo* minfo)
{ {
if ( !fork_and_wait_or_recovery() ) if ( !fork_and_wait_or_recovery() )
return; return;
char* tardir_rel = join_paths(tmp_root, "tix"); char* tardir_rel = join_paths(tmp_root, "tix");
if ( !tardir_rel ) if ( !tardir_rel )
err(1, "malloc"); 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", char* package_tix = print_string("%s/%s.tix.tar.xz",
minfo->destination, minfo->package_name); minfo->destination, minfo->package_name);
if ( !package_tix ) if ( !package_tix )
err(1, "malloc"); err(1, "malloc");
printf("Creating `%s'...\n", package_tix); printf("Creating `%s'...\n", package_tix);
fflush(stdout);
const char* cmd_argv[] = const char* cmd_argv[] =
{ {
minfo->tar, minfo->tar,
"-C", tardir_rel, "-C", prefixdir_rel,
"--remove-files", "--remove-files",
"--create", "--create",
"--xz", "--xz",
@ -821,12 +920,30 @@ static void Package(struct metainfo* minfo)
"--owner=0", "--owner=0",
"--group=0", "--group=0",
"--file", package_tix, "--file", package_tix,
"--",
"tix", "tix",
"data",
NULL NULL
}; };
recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); string_array_t cmd = string_array_make();
err(127, "%s", cmd_argv[0]); 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) static void Compile(struct metainfo* minfo)
@ -970,7 +1087,7 @@ static void BuildPackage(struct metainfo* minfo)
"specify the intended destination prefix using --prefix", "specify the intended destination prefix using --prefix",
minfo->package_name); minfo->package_name);
CreateDestination(); CreateDestination(minfo);
// Possibly build a native version of the package to aid cross-compilation. // 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 // 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) ) if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PACKAGE, minfo) )
{ {
TixInfo(minfo); TixInfo(minfo);
// TODO: After releasing Sortix 1.1, remove generation 2 compatibility.
if ( 3 <= minfo->generation )
TixManifest(minfo);
Package(minfo); Package(minfo);
} }
} }
@ -1144,6 +1264,9 @@ int main(int argc, char* argv[])
minfo.generation = atoi(generation_string); minfo.generation = atoi(generation_string);
free(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)) ) 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.tixbuildinfo = true;
minfo.package_info = string_array_make(); minfo.package_info = string_array_make();
string_array_t* package_info = &minfo.package_info; string_array_t* package_info = &minfo.package_info;
if ( !dictionary_append_file_path(package_info, if ( variables_append_file_path(package_info,
minfo.package_info_path) ) minfo.package_info_path) < 0 )
err(1, "`%s'", minfo.package_info_path); err(1, "`%s'", minfo.package_info_path);
} }
else else

View File

@ -27,6 +27,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
@ -107,13 +108,6 @@ int main(int argc, char* argv[])
compact_arguments(&argc, &argv); compact_arguments(&argc, &argv);
ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv); ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv);
VerifyCommandLineCollection(&collection);
int generation = atoi(generation_string);
free(generation_string);
if ( !prefix )
prefix = strdup(collection);
if ( argc == 1 ) if ( argc == 1 )
{ {
@ -121,12 +115,30 @@ int main(int argc, char* argv[])
exit(1); 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]; const char* cmd = argv[1];
if ( !strcmp(cmd, "create") ) if ( !strcmp(cmd, "create") )
{ {
if ( !platform && !(platform = GetBuildTriplet()) ) if ( !platform && !(platform = GetBuildTriplet()) )
err(1, "unable to determine platform, use --platform"); 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"); char* tix_path = join_paths(collection, "tix");
if ( mkdir_p(tix_path, 0755) != 0 ) if ( mkdir_p(tix_path, 0755) != 0 )
err(1, "mkdir: `%s'", tix_path); 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 " errx(1, "error: `%s' already exists, a tix collection is "
"already installed at `%s'.", collection_conf_path, "already installed at `%s'.", collection_conf_path,
collection); collection);
fprintf(conf_fp, "tix.version=1\n"); if ( 3 <= generation )
fprintf(conf_fp, "tix.class=collection\n"); {
fprintf(conf_fp, "collection.generation=%i\n", generation); fwrite_variable(conf_fp, "TIX_COLLECTION_VERSION", "3");
fprintf(conf_fp, "collection.prefix=%s\n", !strcmp(prefix, "/") ? "" : fwrite_variable(conf_fp, "PREFIX",
prefix); !strcmp(prefix, "/") ? "" : prefix);
fprintf(conf_fp, "collection.platform=%s\n", platform); 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); fclose(conf_fp);
free(collection_conf_path); free(collection_conf_path);
const char* repo_list_path = join_paths(tixdb_path, "repository.list"); // TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
FILE* repo_list_fp = fopen(repo_list_path, "w"); if ( generation < 3 )
if ( !repo_list_fp ) {
err(1, "`%s'", repo_list_path); const char* repo_list_path = join_paths(tixdb_path, "repository.list");
fclose(repo_list_fp); 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"); const char* inst_list_path = join_paths(tixdb_path, "installed.list");
FILE* inst_list_fp = fopen(inst_list_path, "w"); FILE* inst_list_fp = fopen(inst_list_path, "w");
if ( !inst_list_fp ) if ( !inst_list_fp )
err(1, "`%s'", inst_list_path); err(1, "`%s'", inst_list_path);
fclose(inst_list_fp); fclose(inst_list_fp);
}
return 0; return 0;
} }

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -27,6 +27,7 @@
#include <errno.h> #include <errno.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <limits.h> #include <limits.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
@ -78,50 +79,24 @@ void VerifyTixDatabase(const char* prefix,
err(1, "error: tix collection information unavailable: `%s'", err(1, "error: tix collection information unavailable: `%s'",
info_path); 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); free(info_path);
} }
bool IsPackageInstalled(const char* tixdb_path, const char* package) bool IsPackageInstalled(const char* tixdb_path, const char* package)
{ {
char* installed_list_path = join_paths(tixdb_path, "installed.list"); char* tixinfo_dir = join_paths(tixdb_path, "tixinfo");
FILE* installed_list_fp = fopen(installed_list_path, "r"); if ( !tixinfo_dir )
if ( !installed_list_fp ) err(1, "malloc");
err(1, "`%s'", installed_list_path); char* tixinfo = join_paths(tixinfo_dir, package);
if ( !tixinfo )
bool ret = false; err(1, "malloc");
char* line = NULL; bool installed = !access(tixinfo, F_OK);
size_t line_size = 0; free(tixinfo);
ssize_t line_len; free(tixinfo_dir);
while ( 0 < (line_len = getline(&line, &line_size, installed_list_fp)) ) return installed;
{
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;
} }
// TODO: After releasing Sortix 1.1, delete generation 2 compatibility.
void MarkPackageAsInstalled(const char* tixdb_path, const char* package) void MarkPackageAsInstalled(const char* tixdb_path, const char* package)
{ {
char* installed_list_path = join_paths(tixdb_path, "installed.list"); 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"); char* coll_conf_path = join_paths(tix_directory_path, "collection.conf");
string_array_t coll_conf = string_array_make(); string_array_t coll_conf = string_array_make();
if ( !dictionary_append_file_path(&coll_conf, coll_conf_path) ) switch ( variables_append_file_path(&coll_conf, coll_conf_path) )
err(1, "`%s'", coll_conf_path); {
VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path); case -1: err(1, "%s", coll_conf_path);
free(coll_conf_path); case -2: errx(2, "%s: Syntax error", coll_conf_path);
}
const char* coll_generation = dictionary_get(&coll_conf, "collection.generation"); VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path);
assert(coll_generation);
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); generation = atoi(coll_generation);
coll_prefix = dictionary_get(&coll_conf, "collection.prefix"); if ( generation == 3 )
assert(coll_prefix); {
coll_platform = dictionary_get(&coll_conf, "collection.platform"); coll_prefix = dictionary_get(&coll_conf, "PREFIX");
assert(coll_platform); 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++ ) for ( int i = 1; i < argc; i++ )
InstallPackage(argv[i]); InstallPackage(argv[i]);
@ -247,6 +244,7 @@ int main(int argc, char* argv[])
string_array_reset(&coll_conf); string_array_reset(&coll_conf);
free(tix_directory_path); free(tix_directory_path);
free(coll_conf_path);
return 0; return 0;
} }
@ -263,25 +261,47 @@ void InstallPackage(const char* tix_path)
if ( !IsFile(tix_path) ) if ( !IsFile(tix_path) )
err(1, "`%s'", 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) ) 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(); string_array_t tixinfo = string_array_make();
FILE* tixinfo_fp = TarOpenFile(tix_path, tixinfo_path); 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); 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); 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); 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); 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 ) if ( already_installed && !reinstall )
errx(1, "error: package `%s' is already installed. Use --reinstall " errx(1, "error: package `%s' is already installed. Use --reinstall "
"to force reinstallation.", package_name); "to force reinstallation.", package_name);
@ -313,44 +333,51 @@ void InstallPackage(const char* tix_path)
fflush(stdout); fflush(stdout);
} }
const char* data = modern ? "" : "data";
char* data_and_prefix = package_prefix && package_prefix[0] ? char* data_and_prefix = package_prefix && package_prefix[0] ?
print_string("data%s", package_prefix) : print_string("%s%s", data, package_prefix) :
strdup("data"); strdup(data);
char* tixinfo_out_path = print_string("%s/tixinfo/%s", tix_directory_path, package_name); if ( !modern )
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]; char* tixinfo_out_path =
if ( strncmp(str, "data", strlen("data")) != 0 ) print_string("%s/tixinfo/%s", tix_directory_path, package_name);
continue; int tixinfo_fd =
str += strlen("data"); open(tixinfo_out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if ( str[0] && str[0] != '/' ) if ( tixinfo_fd < 0 )
continue; err(1, "%s", tixinfo_out_path);
size_t len = strlen(str); TarExtractFileToFD(tix_path, "tix/tixinfo", tixinfo_fd);
while ( 2 <= len && str[len-1] == '/' ) close(tixinfo_fd);
str[--len] = '\0';
if ( fprintf(manifest_fp, "%s\n", str) < 0 ) 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); 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() ) if ( fork_and_wait_or_death() )
{ {
@ -358,14 +385,14 @@ void InstallPackage(const char* tix_path)
const char* cmd_argv[] = const char* cmd_argv[] =
{ {
"tar", "tar",
print_string("--strip-components=%zu", num_strips),
"-C", collection, "-C", collection,
"--extract", "--extract",
"--file", tix_path, "--file", tix_path,
"--keep-directory-symlink", "--keep-directory-symlink",
"--same-permissions", "--same-permissions",
"--no-same-owner", "--no-same-owner",
data_and_prefix, modern ? NULL : print_string("--strip-components=%zu", num_strips),
modern ? NULL : data_and_prefix,
NULL NULL
}; };
execvp(cmd_argv[0], (char* const*) cmd_argv); execvp(cmd_argv[0], (char* const*) cmd_argv);
@ -373,7 +400,8 @@ void InstallPackage(const char* tix_path)
} }
free(data_and_prefix); 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); MarkPackageAsInstalled(tix_directory_path, package_name);
string_array_reset(&tixinfo); string_array_reset(&tixinfo);

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/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 # Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above # purpose with or without fee is hereby granted, provided that the above
@ -304,6 +304,9 @@ desired_version() {(
if [ -f "$port.rmpatch" ]; then if [ -f "$port.rmpatch" ]; then
stamp="$stamp.$(cat "$port.rmpatch" | sha256sum | grep -Eo '^[^ ]*')" stamp="$stamp.$(cat "$port.rmpatch" | sha256sum | grep -Eo '^[^ ]*')"
fi 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) DEVELOPMENT=$(tix-vars -d false "$port.port" DEVELOPMENT)
if [ "$DEVELOPMENT" = true ]; then if [ "$DEVELOPMENT" = true ]; then
stamp="$stamp.development" stamp="$stamp.development"
@ -428,12 +431,20 @@ strip_tix() {(
strip=${host+$host-}strip strip=${host+$host-}strip
dir=$(mktemp -d) dir=$(mktemp -d)
tar -C "$dir" -xf "$1" tar -C "$dir" -xf "$1"
$strip -d "$dir/data/bin/"* 2>/dev/null || true # TODO: After releasing Sortix 1.1, remove the data directory compatibility.
$strip -d "$dir/data/lib/"* 2>/dev/null || true if [ -e "$dir/data" ]; then
$strip -d "$dir/data/libexec"* 2>/dev/null || true data_dir="$dir/data"
$strip -d "$dir/data/libexec/git-core/"* 2>/dev/null || true else
$strip -d "$dir/data/sbin/"* 2>/dev/null || true data_dir="$dir"
(cd "$dir" && tar --numeric-owner --owner=0 --group=0 -cJf port.tar.tix.xz tix data) 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" cp "$dir/port.tar.tix.xz" "$1"
rm -rf "$dir" rm -rf "$dir"
)} )}

View File

@ -25,6 +25,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -27,6 +27,7 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <limits.h> #include <limits.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
@ -45,7 +46,6 @@ typedef struct
char* tixdb_path; char* tixdb_path;
string_array_t coll_conf; string_array_t coll_conf;
string_array_t repo_list; string_array_t repo_list;
string_array_t inst_list;
bool reinstall; bool reinstall;
} params_t; } params_t;
@ -92,20 +92,36 @@ string_array_t GetPackageDependencies(params_t* params, const char* pkg_name)
if ( !pkg_path ) if ( !pkg_path )
err(1, "unable to locate package `%s'", pkg_name); 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) ) 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(); string_array_t tixinfo = string_array_make();
FILE* tixinfo_fp = TarOpenFile(pkg_path, tixinfo_path); 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); fclose(tixinfo_fp);
VerifyTixInformation(&tixinfo, pkg_path); 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); 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_append_token_string(&ret, alias);
string_array_reset(&tixinfo); 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"); char* coll_conf_path = join_paths(params.tixdb_path, "collection.conf");
params.coll_conf = string_array_make(); params.coll_conf = string_array_make();
if ( !dictionary_append_file_path(&params.coll_conf, coll_conf_path) ) switch ( variables_append_file_path(&params.coll_conf, coll_conf_path) )
err(1, "`%s'", coll_conf_path); {
case -1: err(1, "%s", coll_conf_path);
case -2: errx(2, "%s: Syntax error", coll_conf_path);
}
VerifyTixCollectionConfiguration(&params.coll_conf, coll_conf_path); VerifyTixCollectionConfiguration(&params.coll_conf, coll_conf_path);
free(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"); char* repo_list_path = join_paths(params.tixdb_path, "repository.list");
params.repo_list = string_array_make(); params.repo_list = string_array_make();
if ( !string_array_append_file_path(&params.repo_list, repo_list_path) ) if ( !string_array_append_file_path(&params.repo_list, repo_list_path) &&
errno != ENOENT )
err(1, "`%s'", repo_list_path); err(1, "`%s'", repo_list_path);
free(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(&params.inst_list, inst_list_path) )
err(1, "`%s'", inst_list_path);
free(inst_list_path);
if ( argc == 1 ) if ( argc == 1 )
errx(1, "error: no command specified."); errx(1, "error: no command specified.");
@ -252,10 +267,16 @@ int main(int argc, char* argv[])
string_array_t work = string_array_make(); 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++ ) for ( int i = 2; i < argc; i++ )
{ {
const char* pkg_name = argv[i]; const char* pkg_name = argv[i];
if ( string_array_contains(&params.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 ) if ( params.reinstall )
{ {
@ -268,6 +289,7 @@ int main(int argc, char* argv[])
GetPackageRecursiveDependencies(&params, &work, pkg_name); GetPackageRecursiveDependencies(&params, &work, pkg_name);
} }
free(tixinfo_dir);
for ( size_t i = 0; i < work.length; i++ ) for ( size_t i = 0; i < work.length; i++ )
InstallPackageOfName(&params, work.strings[i]); InstallPackageOfName(&params, work.strings[i]);

View File

@ -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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -20,7 +20,7 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
#define DEFAULT_GENERATION "2" #define DEFAULT_GENERATION "3"
extern char** environ; extern char** environ;
@ -326,34 +326,6 @@ void dictionary_normalize_entry(char* entry)
entry[output_off] = '\0'; 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) bool is_identifier_char(char c)
{ {
return ('a' <= c && c <= 'z') || 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 mkdir_p(const char* path, mode_t mode)
{ {
int saved_errno = errno; int saved_errno = errno;
if ( mkdir(path, mode) != 0 && errno != EEXIST ) if ( !mkdir(path, mode) )
return -1; return 0;
errno = saved_errno; if ( errno == ENOENT )
return 0; {
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) 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) 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"); const char* tix_version = dictionary_get(info, "tix.version");
if ( !tix_version ) if ( tix_version )
errx(1, "error: `%s': no `tix.version' variable declared", path); {
if ( atoi(tix_version) != 1 ) if ( !tix_version )
errx(1, "error: `%s': tix version `%s' not supported", path, errx(1, "error: `%s': no `tix.version' variable declared", path);
tix_version); if ( atoi(tix_version) != 1 )
const char* tix_class = dictionary_get(info, "tix.class"); errx(1, "error: `%s': tix version `%s' not supported", path,
if ( !tix_class ) tix_version);
errx(1, "error: `%s': no `tix.class' variable declared", path); const char* tix_class = dictionary_get(info, "tix.class");
if ( strcmp(tix_class, "collection") != 0 ) if ( !tix_class )
errx(1, "error: `%s': error: unexpected tix class `%s'.", path, errx(1, "error: `%s': no `tix.class' variable declared", path);
tix_class); if ( strcmp(tix_class, "collection") != 0 )
if ( !(dictionary_get(info, "collection.prefix")) ) errx(1, "error: `%s': error: unexpected tix class `%s'.", path,
errx(1, "error: `%s': no `collection.prefix' variable declared", path); tix_class);
if ( !(dictionary_get(info, "collection.platform")) ) if ( !(dictionary_get(info, "collection.prefix")) )
errx(1, "error: `%s': no `collection.platform' variable declared", errx(1, "error: `%s': no `collection.prefix' variable declared",
path); 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; 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) bool is_success_exit_status(int status)
{ {
return WIFEXITED(status) && WEXITSTATUS(status) == 0; return WIFEXITED(status) && WEXITSTATUS(status) == 0;
@ -1149,6 +1182,7 @@ int recovery_execvp(const char* path, char* const* argv)
} }
printf("\n"); printf("\n");
fflush(stdout);
if ( recovery_configure_state(false) == RECOVERY_STATE_PRINT_COMMAND ) if ( recovery_configure_state(false) == RECOVERY_STATE_PRINT_COMMAND )
_exit(0); _exit(0);