Add automatic installer and upgrader.

This commit is contained in:
Jonas 'Sortie' Termansen 2017-08-20 12:13:04 +02:00
parent 29a4d79946
commit 884a80de75
12 changed files with 434 additions and 14 deletions

View File

@ -88,7 +88,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
@ -519,6 +520,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)
@ -73,13 +74,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"
@ -160,7 +161,6 @@ void promptx(char* buffer,
const char* answer,
bool catch_if_shell)
{
(void) autoconf_name;
while ( true )
{
printf("\e[1m");
@ -171,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

@ -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"
@ -385,6 +387,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();
@ -422,11 +430,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") |
@ -478,6 +521,13 @@ int main(void)
};
size_t num_readies = sizeof(readies) / sizeof(readies[0]);
const char* ready = readies[arc4random_uniform(num_readies)];
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");
@ -686,7 +736,12 @@ int main(void)
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];
@ -755,7 +810,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?
@ -1030,6 +1086,21 @@ 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"
"include /etc/default/passwd.d/*\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"
"include /etc/default/group.d/*\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];
@ -1139,8 +1210,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++;
@ -1222,7 +1294,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.

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") |
@ -410,6 +453,10 @@ int main(void)
};
size_t num_readies = sizeof(readies) / sizeof(readies[0]);
const char* ready = readies[arc4random_uniform(num_readies)];
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");

View File

@ -18,6 +18,8 @@
set -e
autoinstall=
autoupgrade=
daemons=
directory=
hostname=
@ -51,6 +53,10 @@ for argument do
case $dashdash$argument in
--) dashdash=yes ;;
--autoinstall=*) autoinstall=$parameter ;;
--autoinstall) previous_option=autoinstall ;;
--autoupgrade=*) autoupgrade=$parameter ;;
--autoupgrade) previous_option=autoupgrade ;;
--daemons=*) daemons=$parameter ;;
--daemons) previous_option=daemons ;;
--hostname=*) hostname=$parameter ;;
@ -101,6 +107,15 @@ 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 "$daemons" ]; then
mkdir -p -- "$directory/etc/init"

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 \-daemons Ns = Ns Ar daemons
.Op Fl \-hostname Ns = Ns Ar hostname
.Op Fl \-kblayout Ns = Ns Ar kblayout
@ -53,6 +55,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 \-daemons Ns = Ns Ar daemons
Configures the
.Sy local
@ -286,9 +302,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 ,