Refactor sysmerge(8) and sysupgrade(8) compatibility hooks.

This commit is contained in:
Jonas 'Sortie' Termansen 2016-08-23 00:28:49 +02:00
parent 4d40dd35dd
commit 4ab5765a95
8 changed files with 271 additions and 59 deletions

View File

@ -20,6 +20,7 @@ conf.o \
devices.o \
execute.o \
fileops.o \
hooks.o \
interactive.o \
manifest.o \
release.o \
@ -27,8 +28,8 @@ release.o \
OBJS=$(MAIN_OBJS) $(UTIL_OBJS)
SYSINSTALL_DEPS=devices execute fileops interactive manifest
SYSMERGE_DEPS=conf fileops execute manifest release
SYSUPGRADE_DEPS=conf devices execute fileops interactive manifest release
SYSMERGE_DEPS=conf fileops execute hooks manifest release
SYSUPGRADE_DEPS=conf devices execute fileops hooks interactive manifest release
SYSINSTALL_OBJS:=sysinstall.o $(SYSINSTALL_DEPS:=.o)
SYSMERGE_OBJS:=sysmerge.o $(SYSMERGE_DEPS:=.o)
@ -67,6 +68,7 @@ conf.o: conf.h
devices.o: devices.h
execute.o: execute.h
fileops.o: fileops.h
hooks.o: release.h
interactive.o: interactive.h execute.h
manifest.o: manifest.h execute.h fileops.h
release.o: release.h

View File

@ -23,12 +23,25 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fileops.h"
char* join_paths(const char* a, const char* b)
{
size_t a_len = strlen(a);
bool has_slash = (a_len && a[a_len-1] == '/') || b[0] == '/';
char* result;
if ( (has_slash && asprintf(&result, "%s%s", a, b) < 0) ||
(!has_slash && asprintf(&result, "%s/%s", a, b) < 0) )
return NULL;
return result;
}
int mkdir_p(const char* path, mode_t mode)
{
int saved_errno = errno;

View File

@ -20,6 +20,7 @@
#ifndef FILEOPS_H
#define FILEOPS_H
char* join_paths(const char* a, const char* b);
int mkdir_p(const char* path, mode_t mode);
int access_or_die(const char* path, int mode);
void mkdir_or_chmod_or_die(const char* path, mode_t mode);

45
sysinstall/hooks.c Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016 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
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* hooks.c
* Upgrade compatibility hooks.
*/
#include <stdbool.h>
#include "hooks.h"
#include "release.h"
void upgrade_prepare(const struct release* old_release,
const struct release* new_release,
const char* source_prefix,
const char* target_prefix)
{
(void) old_release;
(void) new_release;
(void) source_prefix;
(void) target_prefix;
}
void upgrade_finalize(const struct release* old_release,
const struct release* new_release,
const char* source_prefix,
const char* target_prefix)
{
(void) old_release;
(void) new_release;
(void) source_prefix;
(void) target_prefix;
}

34
sysinstall/hooks.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2016 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
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* hooks.h
* Upgrade compatibility hooks.
*/
#ifndef HOOKS_H
#define HOOKS_H
#include "release.h"
void upgrade_prepare(const struct release* old_release,
const struct release* new_release,
const char* source_prefix,
const char* target_prefix);
void upgrade_finalize(const struct release* old_release,
const struct release* new_release,
const char* source_prefix,
const char* target_prefix);
#endif

View File

@ -6,10 +6,10 @@
.Nd upgrade current operating system from a sysroot
.Sh SYNOPSIS
.Nm sysmerge
.Op Fl w
.Op Fl cw
.Op Fl \-booting
.Op Fl \-cancel
.Op Fl \-wait
.Op Fl \-hook-finalize
.Op Fl \-hook-prepare
.Ar source
.Sh DESCRIPTION
.Nm
@ -35,17 +35,10 @@ then the bootloader is reinstalled and reconfigured as needed.
.Pp
.Nm
is an automatic and non-interactive upgrade. It is meant to be used as part of
the development process to upgrade to locally built versions. It does not
contain compatibility to help upgrade across larger changes, or to easily handle
changes to configuration file formats, or other caveats. It places a burden on
the system administrator/developer to know the system changes in detail and to
do any necessary tasks manually.
.Pp
The
the development process to upgrade to locally built versions. The
.Xr sysupgrade 8
program is by contrast an interactive program, meant to help upgrading across
much larger development distances. It contains compatibility to automatically
handle changes in configuration files and other larger changes.
much larger development distances.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
@ -60,13 +53,27 @@ boot target. This installs the
directory onto the root filesystem and removes the
.Pa /sysmerge
directory.
.It Fl \-cancel
.It Fl c , Fl \-cancel
Cancel a pending upgrade that would trigger on the next boot. Remove the
.Pa /sysmerge
directory and restore the old
.Xr kernel 7
and
.Xr initrd 7 .
.It Fl \-hook-finalize
Run the pre-installation compatibility hooks. This is meant to be used by the
old
.Nm
when it invokes the new
.Nm
during a non-waiting upgrade.
.It Fl \-hook-prepare
Run the post-prepare compatibility hooks. This is meant to be used by the
old
.Nm
when it invokes the new
.Nm
during a non-waiting upgrade.
.It Fl w , Fl \-wait
Wait until the next boot to complete the upgrade, rather than finishing it now.
This installs into the

View File

@ -31,6 +31,7 @@
#include "conf.h"
#include "execute.h"
#include "hooks.h"
#include "fileops.h"
#include "manifest.h"
#include "release.h"
@ -70,8 +71,10 @@ int main(int argc, char* argv[])
{
setvbuf(stdout, NULL, _IOLBF, 0); // Pipes.
bool cancel = false;
bool booting = false;
bool cancel = false;
bool hook_finalize = false;
bool hook_prepare = false;
bool wait = false;
const char* argv0 = argv[0];
@ -88,6 +91,7 @@ int main(int argc, char* argv[])
char c;
while ( (c = *++arg) ) switch ( c )
{
case 'c': cancel = false; break;
case 'w': wait = true; break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
@ -99,10 +103,14 @@ int main(int argc, char* argv[])
help(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--version") )
version(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--cancel") )
cancel = true;
else if ( !strcmp(arg, "--booting") )
booting = true;
else if ( !strcmp(arg, "--cancel") )
cancel = true;
else if ( !strcmp(arg, "--hook-finalize") )
hook_finalize = true;
else if ( !strcmp(arg, "--hook-prepare") )
hook_prepare = true;
else if ( !strcmp(arg, "--wait") )
wait = true;
else
@ -115,10 +123,17 @@ int main(int argc, char* argv[])
compact_arguments(&argc, &argv);
if ( 1 < booting + cancel + hook_finalize + hook_prepare + wait )
errx(2, "Mutually incompatible options were passed");
bool hook_only = hook_prepare || hook_finalize;
bool no_source = cancel;
bool no_cancel = booting || hook_only;
const char* source;
if ( cancel )
if ( no_source )
{
source = NULL;
source = "";
if ( 1 < argc )
errx(2, "Unexpected extra operand `%s'", argv[1]);
}
@ -137,22 +152,23 @@ int main(int argc, char* argv[])
errx(2, "Unexpected extra operand `%s'", argv[2]);
}
if ( booting )
{
}
else if ( has_pending_upgrade() )
bool did_cancel = false;
if ( !no_cancel && has_pending_upgrade() )
{
rename("/boot/sortix.bin.sysmerge.orig", "/boot/sortix.bin");
rename("/boot/sortix.initrd.sysmerge.orig", "/boot/sortix.initrd");
execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
execute((const char*[]) { "update-initrd", NULL }, "_e");
execute((const char*[]) { "update-initrd", NULL }, "e");
printf("Cancelled pending system upgrade.\n");
did_cancel = true;
}
else if ( cancel )
printf("No system upgrade was pending.\n");
if ( cancel )
{
if ( !did_cancel )
printf("No system upgrade was pending.\n");
return 0;
}
const char* old_release_path = "/etc/sortix-release";
struct release old_release;
@ -181,47 +197,115 @@ int main(int argc, char* argv[])
struct conf conf;
load_upgrade_conf(&conf, "/etc/upgrade.conf");
bool can_run_old_abi = old_release.abi_major == new_release.abi_major &&
old_release.abi_minor <= new_release.abi_minor;
if ( !can_run_old_abi && !wait )
{
printf("Incompatible %lu.%lu -> %lu.%lu ABI transition, "
"delaying upgrade to next boot.\n",
old_release.abi_major, old_release.abi_major,
new_release.abi_major, new_release.abi_major);
wait = true;
}
bool can_run_new_abi = new_release.abi_major == old_release.abi_major &&
new_release.abi_minor <= old_release.abi_minor;
const char* target;
if ( wait )
bool header;
bool copy_files;
bool run_prepare;
bool run_finalize;
bool my_prepare;
bool my_finalize;
if ( booting )
{
printf("Scheduling upgrade to %s on next boot using %s:\n",
new_release.pretty_name, source);
target = "/sysmerge";
if ( mkdir(target, 0755) < 0 )
err(2, "%s", target);
execute((const char*[]) { "tix-collection", "/sysmerge", "create",
NULL }, "_e");
header = true;
copy_files = true;
run_prepare = true;
my_prepare = true;
run_finalize = true;
my_finalize = true;
}
else if ( hook_prepare )
{
header = false;
copy_files = false;
run_prepare = true;
my_prepare = true;
run_finalize = false;
my_finalize = false;
}
else if ( hook_finalize )
{
header = false;
copy_files = false;
run_prepare = false;
my_prepare = false;
run_finalize = true;
my_finalize = true;
}
else
{
printf("Upgrading to %s using %s:\n", new_release.pretty_name, source);
target = "";
if ( !wait && !can_run_new_abi )
{
printf("%lu.%lu -> %lu.%lu ABI transition, "
"delaying upgrade to next boot.\n",
old_release.abi_major, old_release.abi_major,
new_release.abi_major, new_release.abi_major);
wait = true;
}
header = true;
copy_files = true;
run_prepare = !wait;
my_prepare = false;
run_finalize = !wait;
my_finalize = false;
}
install_manifest("system", source, target);
install_ports(source, target);
if ( header )
{
if ( wait )
printf("Scheduling upgrade to %s on next boot using %s:\n",
new_release.pretty_name, source);
else
printf("Upgrading to %s using %s:\n",
new_release.pretty_name, source);
}
// Compatibility hooks that runs before the old system is replaced.
if ( run_prepare )
{
if ( my_prepare )
{
upgrade_prepare(&old_release, &new_release, source, "/");
}
else
{
char* new_sysmerge = join_paths(source, "sbin/sysmerge");
if ( !new_sysmerge )
err(2, "asprintf");
execute((const char*[]) { new_sysmerge, "--hook-prepare", source,
NULL }, "e");
free(new_sysmerge);
}
if ( hook_prepare )
return 0;
}
if ( copy_files )
{
const char* target = "";
if ( wait )
{
target = "/sysmerge";
if ( mkdir(target, 0755) < 0 )
err(2, "%s", target);
execute((const char*[]) { "tix-collection", "/sysmerge", "create",
NULL }, "e");
}
install_manifest("system", source, target);
install_ports(source, target);
}
if ( wait )
{
printf(" - Scheduling upgrade on next boot...\n");
execute((const char*[]) { "cp", "/boot/sortix.bin",
"/boot/sortix.bin.sysmerge.orig", NULL }, "_e");
"/boot/sortix.bin.sysmerge.orig", NULL }, "e");
execute((const char*[]) { "cp", "/boot/sortix.initrd",
"/boot/sortix.initrd.sysmerge.orig", NULL }, "_e");
"/boot/sortix.initrd.sysmerge.orig", NULL }, "e");
execute((const char*[]) { "cp", "/sysmerge/boot/sortix.bin",
"/boot/sortix.bin", NULL }, "_e");
execute((const char*[]) { "/sysmerge/sbin/update-initrd", NULL }, "_e");
"/boot/sortix.bin", NULL }, "e");
execute((const char*[]) { "/sysmerge/sbin/update-initrd", NULL }, "e");
printf("The system will be upgraded to %s on the next boot.\n",
new_release.pretty_name);
@ -230,6 +314,26 @@ int main(int argc, char* argv[])
return 0;
}
// Compatibility hooks that run after the new system is installed.
if ( run_finalize )
{
if ( my_finalize )
{
upgrade_finalize(&old_release, &new_release, source, "/");
}
else
{
char* new_sysmerge = join_paths(source, "sbin/sysmerge");
if ( !new_sysmerge )
err(2, "asprintf");
execute((const char*[]) { new_sysmerge, "--hook-finalize", source,
NULL }, "e");
free(new_sysmerge);
}
if ( hook_finalize )
return 0;
}
if ( booting )
{
unlink("/boot/sortix.bin.sysmerge.orig");
@ -237,23 +341,23 @@ int main(int argc, char* argv[])
execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
}
if ( !wait && access_or_die("/etc/fstab", F_OK) == 0 )
if ( access_or_die("/etc/fstab", F_OK) == 0 )
{
printf(" - Creating initrd...\n");
execute((const char*[]) { "update-initrd", NULL }, "_e");
execute((const char*[]) { "update-initrd", NULL }, "e");
if ( conf.grub )
{
// TODO: Figure out the root device.
//printf(" - Installing bootloader...\n");
//execute((const char*[]) { "grub-install", "/", NULL }, "_eqQ");
//execute((const char*[]) { "grub-install", "/", NULL }, "eqQ");
printf(" - Configuring bootloader...\n");
execute((const char*[]) { "update-grub", NULL }, "_eqQ");
execute((const char*[]) { "update-grub", NULL }, "eqQ");
}
else if ( access_or_die("/etc/grub.d/10_sortix", F_OK) == 0 )
{
printf(" - Creating bootloader fragment...\n");
execute((const char*[]) { "/etc/grub.d/10_sortix", NULL }, "_eq");
execute((const char*[]) { "/etc/grub.d/10_sortix", NULL }, "eq");
}
}

View File

@ -48,6 +48,7 @@
#include "devices.h"
#include "execute.h"
#include "fileops.h"
#include "hooks.h"
#include "interactive.h"
#include "manifest.h"
#include "release.h"
@ -716,7 +717,10 @@ int main(void)
// TODO: Use an upgrade manifest system that notices files that are now
// untracked or moved from one manifest to another.
if ( conf.system )
{
upgrade_prepare(target_release, &new_release, "/", "");
install_manifest("system", "", ".");
}
if ( has_manifest("src") )
{
if ( conf.newsrc )
@ -754,6 +758,8 @@ int main(void)
}
if ( conf.ports )
install_ports("", ".");
if ( conf.system )
upgrade_finalize(target_release, &new_release, "/", "");
if ( conf.system )
{
printf(" - Creating initrd...\n");