Add automatic installer and upgrader.

This commit is contained in:
Jonas 'Sortie' Termansen 2017-08-20 12:13:04 +02:00
parent 47dffdb913
commit 96b98dde59
13 changed files with 479 additions and 49 deletions

View File

@ -84,7 +84,8 @@ default bootloader menu option and timeout, the default hostname, the default
keyboard layout, the default graphics resolution, adding files of your choice to
the live environment, control which drivers are loaded by default, control which
live environment daemons are started by default, deploy ssh keys so secure shell
connections are trusted on the first connection, and so on.
connections are trusted on the first connection, configure automatic
installation and upgrading, and and so on.
.Pp
Warning: The live environment does not come with any random entropy and entropy
gathering is not yet implemented.

View File

@ -18,7 +18,8 @@ default bootloader menu option and timeout, the default hostname, the default
keyboard layout, the default graphics resolution, adding files of your choice to
the live environment, control which drivers are loaded by default, control which
live environment daemons are started by default, deploy ssh keys so secure shell
connections are trusted on the first connection, and so on.
connections are trusted on the first connection, configure automatic
installation and upgrading, and and so on.
.Ss Prerequisites
.Bl -bullet -compact
.It
@ -516,6 +517,27 @@ To customize a release so it boots to a console instead of the GUI:
tix-iso-bootconfig --disable-gui bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Ss Automatic Installation
To customize a release so it automatically installs itself according to
.Pa autoinstall.conf
(see
.Xr autoinstall.conf 5 ) :
.Bd -literal
tix-iso-liveconfig --autoinstall=autoinstall.conf liveconfig
tix-iso-bootconfig --liveconfig=liveconfig --default=1 bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Ss Automatic Upgrade
To customize a release so it automatically upgrades a local installation
according to
.Pa autoupgrade.conf
(see
.Xr autoupgrade.conf 5 ) :
.Bd -literal
tix-iso-liveconfig --autoinstall=autoupgrade.conf liveconfig
tix-iso-bootconfig --liveconfig=liveconfig --default=2 bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Sh SEE ALSO
.Xr xorriso 1 ,
.Xr development 7 ,

View File

@ -31,7 +31,8 @@ default bootloader menu option and timeout, the default hostname, the default
keyboard layout, the default graphics resolution, adding files of your choice to
the live environment, control which drivers are loaded by default, control which
live environment daemons are started by default, deploy ssh keys so secure shell
connections are trusted on the first connection, and so on.
connections are trusted on the first connection, configure automatic
installation and upgrading, and and so on.
.Pp
Warning: The live environment does not come with any random entropy and entropy
gathering is not yet implemented.

View File

@ -16,6 +16,7 @@ sysmerge.o \
sysupgrade.o \
UTIL_OBJS=\
autoconf.o \
conf.o \
devices.o \
execute.o \
@ -28,9 +29,9 @@ string_array.o \
OBJS=$(MAIN_OBJS) $(UTIL_OBJS)
SYSINSTALL_DEPS=conf devices execute fileops interactive manifest release string_array
SYSINSTALL_DEPS=autoconf conf devices execute fileops interactive manifest release string_array
SYSMERGE_DEPS=conf fileops execute hooks manifest release string_array
SYSUPGRADE_DEPS=conf devices execute fileops hooks interactive manifest release string_array
SYSUPGRADE_DEPS=autoconf conf devices execute fileops hooks interactive manifest release string_array
SYSINSTALL_OBJS:=sysinstall.o $(SYSINSTALL_DEPS:=.o)
SYSMERGE_OBJS:=sysmerge.o $(SYSMERGE_DEPS:=.o)
@ -71,13 +72,14 @@ sysupgrade: $(SYSUPGRADE_OBJS)
sysinstall.o: $(SYSINSTALL_DEPS:=.h)
sysmerge.o: $(SYSMERGE_DEPS:=.h)
sysupgrade.o: $(SYSUPGRADE_DEPS:=.h)
autoconf.o: autoconf.h
conf.o: conf.h
devices.o: devices.h
execute.o: execute.h
fileops.o: fileops.h string_array.h
string_array.o: string_array.h
hooks.o: fileops.h manifest.h release.h string_array.h
interactive.o: interactive.h execute.h
interactive.o: interactive.h autoconf.h execute.h
manifest.o: manifest.h fileops.h string_array.h
release.o: release.h

132
sysinstall/autoconf.c Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2017 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.
*
* autoconf.c
* Parser for autoinstall.conf(5) and autoupgrade.conf(5).
*/
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "autoconf.h"
// TODO: This file is very unfinished draft stuff.
bool has_autoconf = false;
const char* autoconf_get(const char* name)
{
if ( !name || !has_autoconf )
return NULL;
return getenv(name);
}
void autoconf_load(const char* path)
{
FILE* fp = fopen(path, "r");
if ( !fp )
{
if ( errno != ENOENT )
warn("%s", path);
return;
}
char* line = NULL;
size_t line_size = 0;
ssize_t line_length;
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
{
if ( line[line_length - 1] == '\n' )
line[--line_length] = '\0';
line_length = 0;
while ( line[line_length] && line[line_length] != '#' )
line_length++;
line[line_length] = '\0';
char* name = line;
if ( !*name || *name == '=' )
continue;
size_t name_length = 1;
while ( name[name_length] &&
name[name_length] != '=' &&
name[name_length] != '+' )
name_length++;
if ( name[name_length + 0] == '+' &&
name[name_length + 1] == '+' &&
name[name_length + 2] == '=' )
{
name[name_length + 0] = '\0';
char* value = name + name_length + 3;
const char* existing = getenv(name);
if ( existing )
{
char* full;
if ( asprintf(&full, "%s\n%s", existing, value) < 0 )
err(2, "%s: asprintf", path);
setenv(name, full, 1);
free(full);
}
else
setenv(name, value, 1);
}
else if ( name[name_length + 0] == '+' &&
name[name_length + 1] == '=' )
{
name[name_length + 0] = '\0';
char* value = name + name_length + 2;
const char* existing = getenv(name);
if ( existing )
{
char* full;
if ( asprintf(&full, "%s %s", existing, value) < 0 )
err(2, "%s: asprintf", path);
setenv(name, full, 1);
free(full);
}
else
setenv(name, value, 1);
}
else if ( name[name_length + 0] == '=' )
{
name[name_length + 0] = '\0';
char* value = name + name_length + 1;
setenv(name, value, 1);
}
else
{
// TODO: Graceful.
errx(2, "%s: Bad line: %s", path, line);
}
char* value = name + name_length;
while ( *value && isblank((unsigned char) *value) )
value++;
if ( *value != '=' )
continue;
value++;
while ( *value && isblank((unsigned char) *value) )
value++;
name[name_length] = '\0';
}
// TODO: Graceful error.
if ( ferror(fp) )
err(2, "%s", path);
free(line);
fclose(fp);
has_autoconf = true;
}

28
sysinstall/autoconf.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017 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.
*
* autoconf.h
* Parser for autoinstall.conf(5) and autoupgrade.conf(5).
*/
#ifndef AUTOCONF_H
#define AUTOCONF_H
extern bool has_autoconf;
const char* autoconf_get(const char* name);
void autoconf_load(const char* path);
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2017, 2021 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
@ -24,6 +24,7 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "execute.h"
@ -34,6 +35,7 @@ int execute(const char* const* argv, const char* flags, ...)
bool exit_on_failure = false;
bool foreground = false;
bool gid_set = false;
const char* input = NULL;
bool raw_exit_code = false;
bool uid_set = false;
bool quiet = false;
@ -50,6 +52,7 @@ int execute(const char* const* argv, const char* flags, ...)
case 'e': exit_on_failure = true; break;
case 'f': foreground = true; break;
case 'g': gid_set = true; gid = va_arg(ap, gid_t); break;
case 'i': input = va_arg(ap, const char*); break;
case 'r': raw_exit_code = true; break;
case 'u': uid_set = true; uid = va_arg(ap, uid_t); break;
case 'q': quiet = true; break;
@ -90,15 +93,56 @@ int execute(const char* const* argv, const char* flags, ...)
tcsetpgrp(0, getpgid(0));
sigprocmask(SIG_SETMASK, &oldset, NULL);
}
if ( input )
{
int pipes[2];
if ( pipe(pipes) < 0 )
{
if ( !quiet_stderr )
warn("pipe: %s", argv[0]);
_exit(2);
}
pid_t input_pid = fork();
if ( input_pid < 0 )
{
if ( !quiet_stderr )
warn("fork: %s", argv[0]);
_exit(2);
}
else if ( input_pid == 0 )
{
close(pipes[0]);
size_t left = strlen(input);
while ( *input )
{
ssize_t written = write(pipes[1], input, left);
if ( written <= 0 )
break;
input += written;
left -= written;
}
_exit(0);
}
close(pipes[1]);
close(0);
dup2(pipes[0], 0);
close(pipes[0]);
}
if ( quiet )
{
close(1);
open("/dev/null", O_WRONLY);
if ( open("/dev/null", O_WRONLY) < 0 )
{
if ( !quiet_stderr )
warn("/dev/null");
_exit(2);
}
}
if ( quiet_stderr )
{
close(2);
open("/dev/null", O_WRONLY);
if ( open("/dev/null", O_WRONLY) < 0 )
_exit(2);
}
execvp(argv[0], (char* const*) argv);
warn("%s", argv[0]);

View File

@ -33,6 +33,7 @@
#include <display.h>
#include "autoconf.h"
#include "execute.h"
#include "interactive.h"
@ -146,14 +147,16 @@ void textf(const char* format, ...)
void prompt(char* buffer,
size_t buffer_size,
const char* autoconf_name,
const char* question,
const char* answer)
{
promptx(buffer, buffer_size, question, answer, false);
promptx(buffer, buffer_size, autoconf_name, question, answer, false);
}
void promptx(char* buffer,
size_t buffer_size,
const char* autoconf_name,
const char* question,
const char* answer,
bool catch_if_shell)
@ -168,6 +171,21 @@ void promptx(char* buffer,
else
printf(" ");
fflush(stdout);
const char* autoconf_value = autoconf_get(autoconf_name);
const char* accept_defaults = autoconf_get("accept_defaults");
const char* automatic_answer = NULL;
if ( autoconf_value )
automatic_answer = autoconf_value;
else if ( accept_defaults && !strcasecmp(accept_defaults, "yes") )
automatic_answer = answer;
if ( automatic_answer )
{
printf("%s\n", automatic_answer);
printf("\e[22m");
fflush(stdout);
strlcpy(buffer, automatic_answer, buffer_size);
return;
}
fgets(buffer, buffer_size, stdin);
printf("\e[22m");
fflush(stdout);

View File

@ -29,10 +29,12 @@ __attribute__((format(printf, 1, 2)))
void textf(const char* format, ...);
void prompt(char* buffer,
size_t buffer_size,
const char* autoconf_name,
const char* question,
const char* answer);
void promptx(char* buffer,
size_t buffer_size,
const char* autoconf_name,
const char* question,
const char* answer,
bool catch_if_shell);

View File

@ -34,6 +34,7 @@
#include <fstab.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -53,6 +54,7 @@
#include <mount/partition.h>
#include <mount/uuid.h>
#include "autoconf.h"
#include "conf.h"
#include "devices.h"
#include "execute.h"
@ -378,6 +380,12 @@ void exit_gui(int code)
exit(code);
}
static void cancel_on_sigint(int signum)
{
(void) signum;
errx(2, "fatal: Installation canceled");
}
int main(void)
{
shlvl();
@ -415,11 +423,46 @@ int main(void)
if ( !conf_load(&conf, "/etc/upgrade.conf") && errno != ENOENT )
warn("/etc/upgrade.conf");
autoconf_load("/etc/autoinstall.conf");
static char input[256];
textf("Hello and welcome to the " BRAND_DISTRIBUTION_NAME " " VERSIONSTR ""
" installer for %s.\n\n", uts.machine);
if ( autoconf_get("ready") &&
autoconf_get("confirm_install") )
{
int countdown = 10;
// TODO: Is atoi defined on all inputs? Use a larger integer type!
// Check for bad input!
if ( autoconf_get("countdown") )
countdown = atoi(autoconf_get("countdown"));
sigset_t old_set;
sigset_t new_set;
sigemptyset(&new_set);
sigaddset(&new_set, SIGINT);
sigprocmask(SIG_BLOCK, &new_set, &old_set);
struct sigaction old_sa;
struct sigaction new_sa = { 0 };
new_sa.sa_handler = cancel_on_sigint;
sigaction(SIGINT, &new_sa, &old_sa);
for ( ; 0 < countdown; countdown-- )
{
textf("Automatically installing " BRAND_DISTRIBUTION_NAME " "
VERSIONSTR " in %i %s... (Control-C to cancel)\n", countdown,
countdown != 1 ? "seconds" : "second");
sigprocmask(SIG_SETMASK, &old_set, NULL);
sleep(1);
sigprocmask(SIG_BLOCK, &new_set, &old_set);
}
textf("Automatically installing " BRAND_DISTRIBUTION_NAME " "
VERSIONSTR "...\n");
text("\n");
sigaction(SIGINT, &old_sa, NULL);
sigprocmask(SIG_SETMASK, &old_set, NULL);
}
// '|' rather than '||' is to ensure side effects.
if ( missing_program("cut") |
missing_program("dash") |
@ -433,7 +476,8 @@ int main(void)
"software installed to properly install this operating system.\n");
while ( true )
{
prompt(input, sizeof(input), "Sure you want to proceed?", "no");
prompt(input, sizeof(input), "ignore_missing_programs",
"Sure you want to proceed?", "no");
if ( strcasecmp(input, "no") == 0 )
return 0;
if ( strcasecmp(input, "yes") == 0 )
@ -462,7 +506,14 @@ int main(void)
};
size_t num_readies = sizeof(readies) / sizeof(readies[0]);
const char* ready = readies[arc4random_uniform(num_readies)];
prompt(input, sizeof(input), "Ready?", ready);
if ( autoconf_get("disked") )
text("Warning: This installer will perform automatic harddisk "
"partitioning!\n");
if ( autoconf_get("confirm_install") &&
!strcasecmp(autoconf_get("confirm_install"), "yes") )
text("Warning: This installer will automatically install an operating "
"system!\n");
prompt(input, sizeof(input), "ready", "Ready?", ready);
text("\n");
text("This is not yet a fully fledged operating system. You should adjust "
@ -500,7 +551,7 @@ int main(void)
while ( kblayout_setable )
{
// TODO: Detect the name of the current keyboard layout.
prompt(input, sizeof(input),
prompt(input, sizeof(input), "kblayout",
"Choose your keyboard layout ('?' or 'L' for list)", "default");
if ( !strcmp(input, "?") ||
!strcmp(input, "l") ||
@ -586,7 +637,7 @@ int main(void)
const char* def = good ? "no" : "yes";
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "videomode",
"Select a default display resolution? (yes/no)", def);
if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
continue;
@ -642,7 +693,7 @@ int main(void)
while ( true )
{
const char* def = bootloader_default ? "yes" : "no";
prompt(accept_grub, sizeof(accept_grub),
prompt(accept_grub, sizeof(accept_grub), "grub",
"Install a new GRUB bootloader?", def);
if ( strcasecmp(accept_grub, "no") == 0 ||
strcasecmp(accept_grub, "yes") == 0 )
@ -664,12 +715,18 @@ int main(void)
while ( true )
{
prompt(accept_grub_password, sizeof(accept_grub_password),
"grub_password",
"Password protect interactive bootloader? (yes/no)", "yes");
if ( strcasecmp(accept_grub_password, "no") == 0 ||
strcasecmp(accept_grub_password, "yes") == 0 )
break;
}
while ( !strcasecmp(accept_grub_password, "yes") )
if ( autoconf_get("grub_password_hash") )
{
const char* hash = autoconf_get("grub_password_hash");
install_configurationf("grubpw", "w", "%s\n", hash);
}
else while ( !strcasecmp(accept_grub_password, "yes") )
{
char first[128];
char second[128];
@ -686,8 +743,8 @@ int main(void)
if ( !strcmp(first, "") )
{
char answer[32];
prompt(answer, sizeof(answer),
"Empty password is stupid, are you sure? (yes/no)", "no");
prompt(answer, sizeof(answer), "grub_password_empty",
"Empty password is stupid, are you sure? (yes/no)", "no");
if ( strcasecmp(answer, "yes") != 0 )
continue;
}
@ -738,7 +795,8 @@ int main(void)
text("Type man to display the disked(8) man page.\n");
not_first = true;
const char* argv[] = { "disked", "--fstab=fstab", NULL };
if ( execute(argv, "f") != 0 )
const char* disked_input = autoconf_get("disked");
if ( execute(argv, "fi", disked_input) != 0 )
{
// TODO: We also end up here on SIGINT.
// TODO: Offer a shell here instead of failing?
@ -815,6 +873,7 @@ int main(void)
while ( true )
{
prompt(return_to_disked, sizeof(return_to_disked),
"missing_bios_boot_partition",
"Return to disked to make a BIOS boot partition?", "yes");
if ( strcasecmp(accept_grub, "no") == 0 ||
strcasecmp(accept_grub, "yes") == 0 )
@ -852,7 +911,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "confirm_install",
"Install " BRAND_DISTRIBUTION_NAME "? "
"(yes/no/exit/poweroff/reboot/halt)", "yes");
if ( !strcasecmp(input, "yes") )
@ -988,7 +1047,7 @@ int main(void)
fclose(defhost_fp);
}
char hostname[HOST_NAME_MAX + 1] = "";
prompt(hostname, sizeof(hostname), "System hostname?",
prompt(hostname, sizeof(hostname), "hostname", "System hostname?",
defhost[0] ? defhost : NULL);
if ( !install_configurationf("etc/hostname", "w", "%s\n", hostname) )
continue;
@ -1012,6 +1071,18 @@ int main(void)
{
textf("Root account already exists, skipping creating it.\n");
}
else if ( autoconf_get("password_hash_root") )
{
const char* hash = autoconf_get("password_hash_root");
if ( !install_configurationf("etc/passwd", "a",
"root:%s:0:0:root:/root:sh\n", hash) )
err(2, "etc/passwd");
textf("User '%s' added to /etc/passwd\n", "root");
if ( !install_configurationf("etc/group", "a", "root::0:root\n") )
err(2, "etc/passwd");
install_skel("/root", 0, 0);
textf("Group '%s' added to /etc/group.\n", "root");
}
else while ( true )
{
char first[128];
@ -1027,7 +1098,7 @@ int main(void)
if ( !strcmp(first, "") )
{
char answer[32];
prompt(answer, sizeof(answer),
prompt(answer, sizeof(answer), "empty_password",
"Empty password is stupid, are you sure? (yes/no)", "no");
if ( strcasecmp(answer, "yes") != 0 )
continue;
@ -1053,16 +1124,17 @@ int main(void)
// TODO: This should also be done in sysupgrade(8).
struct ssh_file
{
const char* key;
const char* path;
const char* pub;
};
const struct ssh_file ssh_files[] =
{
{"/root/.ssh/authorized_keys", NULL},
{"/root/.ssh/config", NULL},
{"/root/.ssh/id_dsa", "/root/.ssh/id_dsa.pub"},
{"/root/.ssh/id_rsa", "/root/.ssh/id_rsa.pub"},
{"/root/.ssh/known_hosts", NULL},
{"copy_ssh_authorized_keys_root", "/root/.ssh/authorized_keys", NULL},
{"copy_ssh_config_root", "/root/.ssh/config", NULL},
{"copy_ssh_id_dsa_root", "/root/.ssh/id_dsa", "/root/.ssh/id_dsa.pub"},
{"copy_ssh_id_rsa_root", "/root/.ssh/id_rsa", "/root/.ssh/id_rsa.pub"},
{"copy_ssh_known_hosts_root", "/root/.ssh/known_hosts", NULL},
};
size_t ssh_files_count = sizeof(ssh_files) / sizeof(ssh_files[0]);
for ( size_t i = 0; i < ssh_files_count; i++ )
@ -1079,7 +1151,7 @@ int main(void)
char question[256];
snprintf(question, sizeof(question),
"Copy %s from installer environment?", file->path);
prompt(input, sizeof(input), question, "no");
prompt(input, sizeof(input), file->key, question, "no");
if ( strcasecmp(input, "no") == 0 )
break;
if ( strcasecmp(input, "yes") != 0 )
@ -1116,8 +1188,9 @@ int main(void)
text("Congratulations, the system is now functional! This is a good time "
"to do further customization of the system.\n\n");
// TODO: autoconf users support.
bool made_user = false;
for ( uid_t uid = 1000; true; )
for ( uid_t uid = 1000; !has_autoconf; )
{
while ( passwd_has_uid("etc/passwd", uid) )
uid++;
@ -1126,7 +1199,7 @@ int main(void)
const char* question = "Setup a user? (enter username or 'no')";
if ( made_user )
question = "Setup another user? (enter username or 'no')";
prompt(userstr, sizeof(userstr), question, "no");
prompt(userstr, sizeof(userstr), NULL, question, "no");
if ( !strcmp(userstr, "no") )
break;
if ( !strcmp(userstr, "yes") )
@ -1140,7 +1213,7 @@ int main(void)
continue;
}
static char name[256];
prompt(name, sizeof(name), "Full name of user?", user);
prompt(name, sizeof(name), NULL, "Full name of user?", user);
char first[128];
char second[128];
while ( true )
@ -1156,7 +1229,7 @@ int main(void)
if ( !strcmp(first, "") )
{
char answer[32];
prompt(answer, sizeof(answer),
prompt(answer, sizeof(answer), "empty_password",
"Empty password is stupid, are you sure? (yes/no)", "no");
if ( strcasecmp(answer, "yes") != 0 )
continue;
@ -1199,7 +1272,9 @@ int main(void)
uid++;
made_user = true;
}
text("\n");
// TODO: autoconf support.
if ( !has_autoconf )
text("\n");
// TODO: Ask if networking should be disabled / enabled.
@ -1218,7 +1293,7 @@ int main(void)
"warning applies to outgoing secure connections as well.\n\n");
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "enable_ssh",
"Enable ssh server?", "no");
if ( strcasecmp(input, "no") == 0 )
break;
@ -1264,7 +1339,7 @@ int main(void)
{
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "copy_sshd_private_keys",
"Copy sshd private keys from installer environment?", "no");
if ( strcasecmp(input, "no") == 0 )
break;
@ -1303,7 +1378,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "finally",
"What now? (exit/poweroff/reboot/halt/boot)", "boot");
if ( !strcasecmp(input, "exit") )
exit(0);

View File

@ -28,6 +28,7 @@
#include <err.h>
#include <errno.h>
#include <fstab.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
@ -42,6 +43,7 @@
#include <mount/harddisk.h>
#include <mount/partition.h>
#include "autoconf.h"
#include "conf.h"
#include "devices.h"
#include "execute.h"
@ -338,6 +340,12 @@ void exit_gui(int code)
exit(code);
}
static void cancel_on_sigint(int signum)
{
(void) signum;
errx(2, "fatal: Upgrade canceled");
}
int main(void)
{
shlvl();
@ -358,6 +366,8 @@ int main(void)
if ( atexit(exit_handler) != 0 )
err(2, "atexit");
autoconf_load("/etc/autoupgrade.conf");
struct utsname uts;
uname(&uts);
@ -366,6 +376,39 @@ int main(void)
textf("Hello and welcome to the " BRAND_DISTRIBUTION_NAME " " VERSIONSTR ""
" upgrader for %s.\n\n", uts.machine);
if ( autoconf_get("ready") &&
autoconf_get("confirm_install") )
{
int countdown = 10;
// TODO: Is atoi defined on all inputs? Use a larger integer type!
// Check for bad input!
if ( autoconf_get("countdown") )
countdown = atoi(autoconf_get("countdown"));
sigset_t old_set;
sigset_t new_set;
sigemptyset(&new_set);
sigaddset(&new_set, SIGINT);
sigprocmask(SIG_BLOCK, &new_set, &old_set);
struct sigaction old_sa;
struct sigaction new_sa = { 0 };
new_sa.sa_handler = cancel_on_sigint;
sigaction(SIGINT, &new_sa, &old_sa);
for ( ; 0 < countdown; countdown-- )
{
textf("Automatically upgrading to " BRAND_DISTRIBUTION_NAME " "
VERSIONSTR " in %i %s... (Control-C to cancel)\n", countdown,
countdown != 1 ? "seconds" : "second");
sigprocmask(SIG_SETMASK, &old_set, NULL);
sleep(1);
sigprocmask(SIG_BLOCK, &new_set, &old_set);
}
textf("Automatically upgrading " BRAND_DISTRIBUTION_NAME " "
VERSIONSTR "...\n");
text("\n");
sigaction(SIGINT, &old_sa, NULL);
sigprocmask(SIG_SETMASK, &old_set, NULL);
}
// '|' rather than '||' is to ensure side effects.
if ( missing_program("cut") |
missing_program("dash") |
@ -379,7 +422,8 @@ int main(void)
"software installed to properly upgrade installations.\n");
while ( true )
{
prompt(input, sizeof(input), "Sure you want to proceed?", "no");
prompt(input, sizeof(input), "ignore_missing_programs",
"Sure you want to proceed?", "no");
if ( strcasecmp(input, "no") == 0 )
return 0;
if ( strcasecmp(input, "yes") == 0 )
@ -409,14 +453,18 @@ int main(void)
};
size_t num_readies = sizeof(readies) / sizeof(readies[0]);
const char* ready = readies[arc4random_uniform(num_readies)];
prompt(input, sizeof(input), "Ready?", ready);
if ( autoconf_get("confirm_upgrade") &&
!strcasecmp(autoconf_get("confirm_upgrade"), "yes") )
text("Warning: This upgrader will automatically upgrade an operating "
"system!\n");
prompt(input, sizeof(input), "ready", "Ready?", ready);
text("\n");
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0);
while ( kblayout_setable )
{
// TODO: Detect the name of the current keyboard layout.
prompt(input, sizeof(input),
prompt(input, sizeof(input), "kblayout",
"Choose your keyboard layout ('?' or 'L' for list)", "default");
if ( !strcmp(input, "?") ||
!strcmp(input, "l") ||
@ -490,7 +538,7 @@ int main(void)
const char* def = good ? "no" : "yes";
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "videomode",
"Select display resolution? (yes/no)", def);
if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
continue;
@ -530,7 +578,8 @@ int main(void)
{
while ( true )
{
prompt(input, sizeof(input), "No existing installations found, "
prompt(input, sizeof(input), "run_installer_instead",
"No existing installations found, "
"run installer instead? (yes/no)", "yes");
if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
break;
@ -560,7 +609,8 @@ int main(void)
const char* def = NULL;
if ( installations_count == 1 )
def = path_of_blockdevice(installations[0].bdev);
prompt(input, sizeof(input), "Which installation to upgrade?", def);
prompt(input, sizeof(input), "which_installaton",
"Which installation to upgrade?", def);
target = NULL;
for ( size_t i = 0; i < installations_count; i++ )
{
@ -594,7 +644,7 @@ int main(void)
"promise this will work!\n", target->machine, source_machine);
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "switch_architecture",
"Change the existing installation to another architecture?",
"no");
if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
@ -614,7 +664,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "downgrade_release",
"Downgrade to an earlier release?", "no");
if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
break;
@ -631,7 +681,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "skip_release",
"Skip across releases?", "no");
if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
break;
@ -650,7 +700,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "downgrade_abi",
"Downgrade to an earlier ABI?", "no");
if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
break;
@ -716,7 +766,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "cancel_pending_sysmerge",
"Cancel pending sysmerge upgrade?", "yes");
if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
break;
@ -783,7 +833,7 @@ int main(void)
while ( true )
{
promptx(input, sizeof(input),
promptx(input, sizeof(input), "confirm_upgrade",
"Upgrade? (yes/no/exit/poweroff/reboot/halt)", "yes", true);
if ( !strcasecmp(input, "yes") )
break;
@ -942,7 +992,7 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input),
prompt(input, sizeof(input), "finally",
"What now? (exit/poweroff/reboot/halt)", "reboot");
if ( !strcasecmp(input, "exit") )
exit(0);

View File

@ -18,6 +18,8 @@
set -e
autoinstall=
autoupgrade=
directory=
hostname=
kblayout=
@ -50,6 +52,10 @@ for argument do
case $dashdash$argument in
--) dashdash=yes ;;
--autoinstall=*) autoinstall=$parameter ;;
--autoinstall) previous_option=autoinstall ;;
--autoupgrade=*) autoupgrade=$parameter ;;
--autoupgrade) previous_option=autoupgrade ;;
--hostname=*) hostname=$parameter ;;
--hostname) previous_option=hostname ;;
--kblayout=*) kblayout=$parameter ;;
@ -98,6 +104,16 @@ fi
mkdir -p "$directory"
if [ -n "$autoinstall" ]; then
mkdir -p -- "$directory/etc"
cp -- "$autoinstall" "$directory/etc/autoinstall.conf"
fi
if [ -n "$autoupgrade" ]; then
mkdir -p "-- $directory/etc"
cp -- "$autoupgrade" "$directory/etc/autoupgrade.conf"
fi
if [ -n "$hostname" ]; then
mkdir -p -- "$directory/etc"
printf "%s\n" "$hostname" > "$directory/etc/hostname"

View File

@ -6,6 +6,8 @@
.Nd generate additional live environment configuration for Sortix .iso releases
.Sh SYNOPSIS
.Nm
.Op Fl \-autoinstall Ns = Ns Ar file
.Op Fl \-autoupgrade Ns = Ns Ar file
.Op Fl \-hostname Ns = Ns Ar hostname
.Op Fl \-kblayout Ns = Ns Ar kblayout
.Op Fl \-root-ssh-authorized-keys Ns = Ns Ar file
@ -52,6 +54,20 @@ installations made from inside it.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl \-autoinstall Ns = Ns Ar file
Copy
.Ar file
to
.Pa output-directory/etc/autoinstall.conf .
(See
.Xr autoinstall.conf 5 )
.It Fl \-autoupgrade Ns = Ns Ar file
Copy
.Ar file
to
.Pa output-directory/etc/autoupgrade.conf .
(See
.Xr autoupgrade.conf 5 )
.It Fl \-hostname Ns = Ns Ar hostname
Set the live environment's hostname by writing
.Ar hostname
@ -276,9 +292,32 @@ rm -f bootconfig/boot/liveconfig.xz # When no longer useful.
rm -f sortix.iso # When no longer useful.
# And erase any media made from sortix.iso when no longer useful.
.Ed
.Ss Automatic Installation
To customize a release so it automatically installs itself according to
.Pa autoinstall.conf
(see
.Xr autoinstall.conf 5 ) :
.Bd -literal
tix-iso-liveconfig --autoinstall=autoinstall.conf liveconfig
tix-iso-bootconfig --liveconfig=liveconfig --default=1 bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Ss Automatic Upgrade
To customize a release so it automatically upgrades a local installation
according to
.Pa autoupgrade.conf
(see
.Xr autoupgrade.conf 5 ) :
.Bd -literal
tix-iso-liveconfig --autoinstall=autoupgrade.conf liveconfig
tix-iso-bootconfig --liveconfig=liveconfig --default=2 bootconfig
tix-iso-add sortix.iso bootconfig
.Ed
.Sh SEE ALSO
.Xr ssh-keygen 1 ,
.Xr xorriso 1 ,
.Xr autoinstall.conf 5 ,
.Xr autoupgrade.conf 5 ,
.Xr hostname 5 ,
.Xr kblayout 5 ,
.Xr ssh_config 5 ,