1
0
mirror of https://github.com/systemd/systemd synced 2025-11-22 02:04:45 +01:00

Compare commits

..

No commits in common. "0c3639d0f59901c04a7e65620ae229091c1a5c60" and "0c0a99599c105f5732e744397a1b575614360028" have entirely different histories.

8 changed files with 115 additions and 326 deletions

View File

@ -205,56 +205,6 @@
<xi:include href="version-info.xml" xpointer="v258"/></listitem> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--prompt-new-user</option></term>
<listitem><para>If used in conjunction with <command>firstboot</command> and no regular user account
exists on the system so far the tool will interactively query for user information and create an
account.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--prompt-shell=</option></term>
<listitem><para>Takes a boolean argument. Controls whether to interactively query for a login shell,
if <command>firstboot --prompt-new-user</command> is used. Defaults to true.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--prompt-groups=</option></term>
<listitem><para>Takes a boolean argument. Controls whether to interactively query for auxiliary
groups to add the new user to, if <command>firstboot --prompt-new-user</command> is used. Defaults to
true.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--chrome=</option></term>
<listitem><para>Takes a boolean argument. By default the interactive user creation via
<command>firstboot --prompt-new-user</command> screen will show reverse color "chrome" bars at the
top and and the bottom of the terminal screen, which may be disabled by setting this option to
false.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--mute-console=</option></term>
<listitem><para>Takes a boolean argument. If true kernel log output and service manager status output
to the system console is temporarily disabled while <command>firstboot --prompt-new-user</command> is
running, so that its own output is not interrupted. Defaults to false.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--match=</option></term> <term><option>--match=</option></term>
<term><option>-A</option></term> <term><option>-A</option></term>
@ -1474,10 +1424,9 @@ $ ssh lennart@targetsystem</programlisting>
<listitem><para>This command is supposed to be invoked during the initial boot of the system. It <listitem><para>This command is supposed to be invoked during the initial boot of the system. It
checks whether any regular home area exists so far, and if not queries the user interactively on the checks whether any regular home area exists so far, and if not queries the user interactively on the
console for user name and password and creates one (only if <option>--prompt-new-user</option> is console for user name and password and creates one. Alternatively, if one or more service credentials
specified). Alternatively, if one or more service credentials whose name starts with whose name starts with <literal>home.create.</literal> are passed to the command (containing a user
<literal>home.create.</literal> are passed to the command (containing a user record in JSON format) record in JSON format) these users are automatically created at boot.</para>
these users are automatically created at boot.</para>
<para>This command is invoked by the <filename>systemd-homed-firstboot.service</filename> service <para>This command is invoked by the <filename>systemd-homed-firstboot.service</filename> service
unit.</para> unit.</para>

View File

@ -271,17 +271,6 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--prompt-keymap-auto</option></term>
<listitem><para>If invoked from a virtual terminal TTY equivalent to
<option>--prompt-keymap</option>, otherwise has no effect. Or in other words, only prompts
interactively for a keymap if a local keyboard is used for interactivity that requires it.</para>
<xi:include href="version-info.xml" xpointer="v259"/>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--copy-locale</option></term> <term><option>--copy-locale</option></term>
<term><option>--copy-keymap</option></term> <term><option>--copy-keymap</option></term>

View File

@ -71,7 +71,6 @@ static char *arg_root_shell = NULL;
static char *arg_kernel_cmdline = NULL; static char *arg_kernel_cmdline = NULL;
static bool arg_prompt_locale = false; static bool arg_prompt_locale = false;
static bool arg_prompt_keymap = false; static bool arg_prompt_keymap = false;
static bool arg_prompt_keymap_auto = false;
static bool arg_prompt_timezone = false; static bool arg_prompt_timezone = false;
static bool arg_prompt_hostname = false; static bool arg_prompt_hostname = false;
static bool arg_prompt_root_password = false; static bool arg_prompt_root_password = false;
@ -139,16 +138,18 @@ static void print_welcome(int rfd, sd_varlink **mute_console_link) {
ac = isempty(ansi_color) ? "0" : ansi_color; ac = isempty(ansi_color) ? "0" : ansi_color;
if (colors_enabled()) if (colors_enabled())
printf(ANSI_HIGHLIGHT "Welcome to " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT "!" ANSI_NORMAL "\n", ac, pn); printf(ANSI_HIGHLIGHT "Welcome to your new installation of " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT "!" ANSI_NORMAL "\n", ac, pn);
else else
printf("Welcome to %s!\n", pn); printf("Welcome to your new installation of %s!\n", pn);
putchar('\n'); putchar('\n');
if (emoji_enabled()) { if (emoji_enabled()) {
fputs(glyph(GLYPH_SPARKLES), stdout); fputs(glyph(GLYPH_SPARKLES), stdout);
putchar(' '); putchar(' ');
} }
printf("Please configure the system!\n\n"); printf("Please configure your new system!\n");
any_key_to_proceed();
done = true; done = true;
} }
@ -416,21 +417,7 @@ static int prompt_keymap(int rfd, sd_varlink **mute_console_link) {
return 0; return 0;
} }
bool b; if (!arg_prompt_keymap) {
if (arg_prompt_keymap_auto) {
_cleanup_free_ char *ttyname = NULL;
r = getttyname_harder(STDOUT_FILENO, &ttyname);
if (r < 0) {
log_debug_errno(r, "Cannot determine TTY we are connected, ignoring: %m");
b = false; /* if we can't resolve this, it's probably not a VT */
} else {
b = tty_is_vc_resolve(ttyname);
log_debug("Detected connection to local console: %s", yes_no(b));
}
} else
b = arg_prompt_keymap;
if (!b) {
log_debug("Prompting for keymap was not requested."); log_debug("Prompting for keymap was not requested.");
return 0; return 0;
} }
@ -1249,8 +1236,6 @@ static int help(void) {
" Set kernel command line\n" " Set kernel command line\n"
" --prompt-locale Prompt the user for locale settings\n" " --prompt-locale Prompt the user for locale settings\n"
" --prompt-keymap Prompt the user for keymap settings\n" " --prompt-keymap Prompt the user for keymap settings\n"
" --prompt-keymap-auto Prompt the user for keymap settings if invoked\n"
" on local console\n"
" --prompt-timezone Prompt the user for timezone\n" " --prompt-timezone Prompt the user for timezone\n"
" --prompt-hostname Prompt the user for hostname\n" " --prompt-hostname Prompt the user for hostname\n"
" --prompt-root-password Prompt the user for root password\n" " --prompt-root-password Prompt the user for root password\n"
@ -1301,7 +1286,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PROMPT, ARG_PROMPT,
ARG_PROMPT_LOCALE, ARG_PROMPT_LOCALE,
ARG_PROMPT_KEYMAP, ARG_PROMPT_KEYMAP,
ARG_PROMPT_KEYMAP_AUTO,
ARG_PROMPT_TIMEZONE, ARG_PROMPT_TIMEZONE,
ARG_PROMPT_HOSTNAME, ARG_PROMPT_HOSTNAME,
ARG_PROMPT_ROOT_PASSWORD, ARG_PROMPT_ROOT_PASSWORD,
@ -1341,7 +1325,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "prompt", no_argument, NULL, ARG_PROMPT }, { "prompt", no_argument, NULL, ARG_PROMPT },
{ "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE },
{ "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP },
{ "prompt-keymap-auto", no_argument, NULL, ARG_PROMPT_KEYMAP_AUTO },
{ "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE },
{ "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME },
{ "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD },
@ -1499,7 +1482,6 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PROMPT: case ARG_PROMPT:
arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname =
arg_prompt_root_password = arg_prompt_root_shell = true; arg_prompt_root_password = arg_prompt_root_shell = true;
arg_prompt_keymap_auto = false;
break; break;
case ARG_PROMPT_LOCALE: case ARG_PROMPT_LOCALE:
@ -1508,11 +1490,6 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PROMPT_KEYMAP: case ARG_PROMPT_KEYMAP:
arg_prompt_keymap = true; arg_prompt_keymap = true;
arg_prompt_keymap_auto = false;
break;
case ARG_PROMPT_KEYMAP_AUTO:
arg_prompt_keymap_auto = true;
break; break;
case ARG_PROMPT_TIMEZONE: case ARG_PROMPT_TIMEZONE:
@ -1693,7 +1670,7 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
if (r > 0 && !enabled) { if (r > 0 && !enabled) {
log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts."); log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
arg_prompt_locale = arg_prompt_keymap = arg_prompt_keymap_auto = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false; arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false;
} }
} }

View File

@ -4,7 +4,6 @@
#include <unistd.h> #include <unistd.h>
#include "sd-bus.h" #include "sd-bus.h"
#include "sd-varlink.h"
#include "ask-password-api.h" #include "ask-password-api.h"
#include "bitfield.h" #include "bitfield.h"
@ -16,7 +15,6 @@
#include "capability-list.h" #include "capability-list.h"
#include "capability-util.h" #include "capability-util.h"
#include "cgroup-util.h" #include "cgroup-util.h"
#include "chase.h"
#include "creds-util.h" #include "creds-util.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "dns-domain.h" #include "dns-domain.h"
@ -51,7 +49,6 @@
#include "pretty-print.h" #include "pretty-print.h"
#include "proc-cmdline.h" #include "proc-cmdline.h"
#include "process-util.h" #include "process-util.h"
#include "prompt-util.h"
#include "recurse-dir.h" #include "recurse-dir.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#include "runtime-scope.h" #include "runtime-scope.h"
@ -104,17 +101,13 @@ static enum {
} arg_export_format = EXPORT_FORMAT_FULL; } arg_export_format = EXPORT_FORMAT_FULL;
static uint64_t arg_capability_bounding_set = UINT64_MAX; static uint64_t arg_capability_bounding_set = UINT64_MAX;
static uint64_t arg_capability_ambient_set = UINT64_MAX; static uint64_t arg_capability_ambient_set = UINT64_MAX;
static bool arg_prompt_new_user = false;
static char *arg_blob_dir = NULL; static char *arg_blob_dir = NULL;
static bool arg_blob_clear = false; static bool arg_blob_clear = false;
static Hashmap *arg_blob_files = NULL; static Hashmap *arg_blob_files = NULL;
static char *arg_key_name = NULL; static char *arg_key_name = NULL;
static bool arg_dry_run = false; static bool arg_dry_run = false;
static bool arg_seize = true; static bool arg_seize = true;
static bool arg_prompt_new_user = false;
static bool arg_prompt_shell = true;
static bool arg_prompt_groups = true;
static bool arg_chrome = true;
static bool arg_mute_console = false;
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, sd_json_variant_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, sd_json_variant_unrefp);
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, sd_json_variant_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, sd_json_variant_unrefp);
@ -2687,21 +2680,77 @@ static int group_completion_callback(const char *key, char ***ret_list, void *us
return 0; return 0;
} }
static int prompt_groups(const char *username, char ***ret_groups) { static int create_interactively(void) {
_cleanup_free_ char *username = NULL;
int r; int r;
assert(username); if (!arg_prompt_new_user) {
assert(ret_groups); log_debug("Prompting for user creation was not requested.");
if (!arg_prompt_groups) {
*ret_groups = NULL;
return 0; return 0;
} }
putchar('\n'); putchar('\n');
if (emoji_enabled()) {
fputs(glyph(GLYPH_HOME), stdout);
putchar(' ');
}
printf("Please create your user account!\n");
if (!any_key_to_proceed()) {
log_notice("Skipping.");
return 0;
}
(void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
for (;;) {
username = mfree(username);
r = ask_string(&username,
"%s Please enter user name to create (empty to skip): ",
glyph(GLYPH_TRIANGULAR_BULLET));
if (r < 0)
return log_error_errno(r, "Failed to query user for username: %m");
if (isempty(username)) {
log_info("No data entered, skipping.");
return 0;
}
if (!valid_user_group_name(username, /* flags= */ 0)) {
log_notice("Specified user name is not a valid UNIX user name, try again: %s", username);
continue;
}
r = userdb_by_name(username, /* match= */ NULL, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL);
if (r == -ESRCH)
break;
if (r < 0)
return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username);
log_notice("Specified user '%s' exists already, try again.", username);
}
r = sd_json_variant_set_field_string(&arg_identity_extra, "userName", username);
if (r < 0)
return log_error_errno(r, "Failed to set userName field: %m");
/* Let's not insist on a strong password in the firstboot interactive interface. Insisting on this is
* really annoying, as the user cannot just invoke the tool again with "--enforce-password-policy=no"
* because after all the tool is called from the boot process, and not from an interactive
* shell. Moreover, when setting up an initial system we can assume the user owns it, and hence we
* don't need to hard enforce some policy on password strength some organization or OS vendor
* requires. Note that this just disables the *strict* enforcement of the password policy. Even with
* this disabled we'll still tell the user in the UI that the password is too weak and suggest better
* ones, even if we then accept the weak ones if the user insists, by repeating it. */
r = sd_json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
if (r < 0)
return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
_cleanup_strv_free_ char **available = NULL, **groups = NULL; _cleanup_strv_free_ char **available = NULL, **groups = NULL;
for (;;) { for (;;) {
_cleanup_free_ char *s = NULL;
strv_sort_uniq(groups); strv_sort_uniq(groups);
if (!strv_isempty(groups)) { if (!strv_isempty(groups)) {
@ -2712,11 +2761,10 @@ static int prompt_groups(const char *username, char ***ret_groups) {
log_info("Currently selected groups: %s", j); log_info("Currently selected groups: %s", j);
} }
_cleanup_free_ char *s = NULL;
r = ask_string_full(&s, r = ask_string_full(&s,
group_completion_callback, &available, group_completion_callback, &available,
"%s Please enter an auxiliary group for user %s (empty to continue, \"list\" to list available groups): ", "%s Please enter an auxiliary group for user %s (empty to continue, \"list\" to list available groups): ",
glyph(GLYPH_LABEL), username); glyph(GLYPH_TRIANGULAR_BULLET), username);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query user for auxiliary group: %m"); return log_error_errno(r, "Failed to query user for auxiliary group: %m");
@ -2786,148 +2834,6 @@ static int prompt_groups(const char *username, char ***ret_groups) {
return log_oom(); return log_oom();
} }
*ret_groups = TAKE_PTR(groups);
return 0;
}
static int shell_is_ok(const char *path, void *userdata) {
int r;
assert(path);
if (!valid_shell(path)) {
log_error("String '%s' is not a valid path to a shell, refusing.", path);
return false;
}
r = chase_and_access(path, /* root= */ NULL, CHASE_MUST_BE_REGULAR, X_OK, /* ret_path= */ NULL) >= 0;
if (r == -ENOENT) {
log_error_errno(r, "Shell '%s' does not exist, try again.", path);
return false;
}
if (ERRNO_IS_NEG_PRIVILEGE(r)) {
log_error_errno(r, "File '%s' is not executable, try again.", path);
return false;
}
if (r < 0)
return log_error_errno(r, "Failed to check if shell '%s' exists and is executable: %m", path);
return true;
}
static int prompt_shell(const char *username, char **ret_shell) {
assert(username);
assert(ret_shell);
if (!arg_prompt_shell) {
*ret_shell = NULL;
return 0;
}
putchar('\n');
_cleanup_free_ char *q = strjoin("Please enter the shell to use for user ", username);
if (!q)
return log_oom();
return prompt_loop(
q,
GLYPH_SHELL,
/* menu= */ NULL,
/* accepted= */ NULL,
/* ellipsize_percentage= */ 0,
/* n_columns= */ 3,
/* column_width= */ 20,
shell_is_ok,
/* refresh= */ NULL,
/* userdata= */ NULL,
PROMPT_MAY_SKIP|PROMPT_SILENT_VALIDATE,
ret_shell);
}
static int username_is_ok(const char *name, void *userdata) {
int r;
assert(name);
if (!valid_user_group_name(name, /* flags= */ 0)) {
log_notice("Specified user name is not a valid UNIX user name, try again: %s", name);
return false;
}
r = userdb_by_name(name, /* match= */ NULL, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL);
if (r == -ESRCH)
return true;
if (r < 0)
return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", name);
log_notice("Specified user '%s' exists already, try again.", name);
return false;
}
static int create_interactively(void) {
_cleanup_free_ char *username = NULL;
int r;
if (!arg_prompt_new_user) {
log_debug("Prompting for user creation was not requested.");
return 0;
}
_cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
(void) mute_console(&mute_console_link);
(void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
if (arg_chrome)
chrome_show("Create a User Account", /* bottom= */ NULL);
DEFER_VOID_CALL(chrome_hide);
if (emoji_enabled()) {
fputs(glyph(GLYPH_HOME), stdout);
putchar(' ');
}
printf("Please create your user account!\n\n");
r = prompt_loop("Please enter user name to create",
GLYPH_IDCARD,
/* menu= */ NULL,
/* accepted= */ NULL,
/* ellipsize_percentage= */ 60,
/* n_columns= */ 3,
/* column_width= */ 20,
username_is_ok,
/* refresh= */ NULL,
/* userdata= */ NULL,
PROMPT_MAY_SKIP|PROMPT_SILENT_VALIDATE,
&username);
if (r < 0)
return r;
if (isempty(username))
return 0;
r = sd_json_variant_set_field_string(&arg_identity_extra, "userName", username);
if (r < 0)
return log_error_errno(r, "Failed to set userName field: %m");
/* Let's not insist on a strong password in the firstboot interactive interface. Insisting on this is
* really annoying, as the user cannot just invoke the tool again with "--enforce-password-policy=no"
* because after all the tool is called from the boot process, and not from an interactive
* shell. Moreover, when setting up an initial system we can assume the user owns it, and hence we
* don't need to hard enforce some policy on password strength some organization or OS vendor
* requires. Note that this just disables the *strict* enforcement of the password policy. Even with
* this disabled we'll still tell the user in the UI that the password is too weak and suggest better
* ones, even if we then accept the weak ones if the user insists, by repeating it. */
r = sd_json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
if (r < 0)
return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
_cleanup_strv_free_ char **groups = NULL;
r = prompt_groups(username, &groups);
if (r < 0)
return r;
if (!strv_isempty(groups)) { if (!strv_isempty(groups)) {
strv_sort_uniq(groups); strv_sort_uniq(groups);
@ -2937,9 +2843,35 @@ static int create_interactively(void) {
} }
_cleanup_free_ char *shell = NULL; _cleanup_free_ char *shell = NULL;
r = prompt_shell(username, &shell);
for (;;) {
shell = mfree(shell);
r = ask_string(&shell,
"%s Please enter the shell to use for user %s (empty for default): ",
glyph(GLYPH_TRIANGULAR_BULLET), username);
if (r < 0) if (r < 0)
return r; return log_error_errno(r, "Failed to query user for username: %m");
if (isempty(shell)) {
log_info("No data entered, leaving at default.");
break;
}
if (!valid_shell(shell)) {
log_notice("Specified shell is not a valid UNIX shell path, try again: %s", shell);
continue;
}
r = RET_NERRNO(access(shell, X_OK));
if (r >= 0)
break;
if (r != -ENOENT)
return log_error_errno(r, "Failed to check if shell %s exists: %m", shell);
log_notice("Specified shell '%s' is not installed, try another one.", shell);
}
if (!isempty(shell)) { if (!isempty(shell)) {
log_info("Selected %s as the shell for user %s", shell, username); log_info("Selected %s as the shell for user %s", shell, username);
@ -2949,14 +2881,7 @@ static int create_interactively(void) {
return log_error_errno(r, "Failed to set shell field: %m"); return log_error_errno(r, "Failed to set shell field: %m");
} }
putchar('\n'); return create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ false);
r = create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ false);
if (r < 0)
return r;
log_info("Successfully created account '%s'.", username);
return 0;
} }
static int add_signing_keys_from_credentials(void); static int add_signing_keys_from_credentials(void);
@ -3096,18 +3021,11 @@ static int help(int argc, char *argv[], void *userdata) {
" -E When specified once equals -j --export-format=\n" " -E When specified once equals -j --export-format=\n"
" stripped, when specified twice equals\n" " stripped, when specified twice equals\n"
" -j --export-format=minimal\n" " -j --export-format=minimal\n"
" --prompt-new-user firstboot: Query user interactively for user\n"
" to create\n"
" --key-name=NAME Key name when adding a signing key\n" " --key-name=NAME Key name when adding a signing key\n"
" --seize=no Do not strip existing signatures of user record\n" " --seize=no Do not strip existing signatures of user record\n"
" when creating\n" " when creating\n"
" --prompt-new-user firstboot: Query user interactively for user\n"
" to create\n"
" --prompt-groups=no In first-boot mode, don't prompt for auxiliary\n"
" group memberships\n"
" --prompt-shell=no In first-boot mode, don't prompt for shells\n"
" --chrome=no In first-boot mode, don't show colour bar at top\n"
" and bottom of terminal\n"
" --mute-console=yes In first-boot mode, tell kernel/PID 1 to not\n"
" write to the console while running\n"
"\n%4$sGeneral User Record Properties:%5$s\n" "\n%4$sGeneral User Record Properties:%5$s\n"
" -c --real-name=REALNAME Real name for user\n" " -c --real-name=REALNAME Real name for user\n"
" --realm=REALM Realm to create user in\n" " --realm=REALM Realm to create user in\n"
@ -3344,10 +3262,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_KEY_NAME, ARG_KEY_NAME,
ARG_SEIZE, ARG_SEIZE,
ARG_MATCH, ARG_MATCH,
ARG_PROMPT_SHELL,
ARG_PROMPT_GROUPS,
ARG_CHROME,
ARG_MUTE_CONSOLE,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -3454,10 +3368,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "key-name", required_argument, NULL, ARG_KEY_NAME }, { "key-name", required_argument, NULL, ARG_KEY_NAME },
{ "seize", required_argument, NULL, ARG_SEIZE }, { "seize", required_argument, NULL, ARG_SEIZE },
{ "match", required_argument, NULL, ARG_MATCH }, { "match", required_argument, NULL, ARG_MATCH },
{ "prompt-shell", required_argument, NULL, ARG_PROMPT_SHELL },
{ "prompt-groups", required_argument, NULL, ARG_PROMPT_GROUPS },
{ "chrome", required_argument, NULL, ARG_CHROME },
{ "mute-console", required_argument, NULL, ARG_MUTE_CONSOLE },
{} {}
}; };
@ -5017,34 +4927,6 @@ static int parse_argv(int argc, char *argv[]) {
match_identity = &arg_identity_extra_other_machines; match_identity = &arg_identity_extra_other_machines;
break; break;
case ARG_PROMPT_SHELL:
r = parse_boolean_argument("--prompt-shell=", optarg, &arg_prompt_shell);
if (r < 0)
return r;
break;
case ARG_PROMPT_GROUPS:
r = parse_boolean_argument("--prompt-groups=", optarg, &arg_prompt_groups);
if (r < 0)
return r;
break;
case ARG_CHROME:
r = parse_boolean_argument("--chrome=", optarg, &arg_chrome);
if (r < 0)
return r;
break;
case ARG_MUTE_CONSOLE:
r = parse_boolean_argument("--mute-console=", optarg, &arg_mute_console);
if (r < 0)
return r;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;

View File

@ -2020,14 +2020,10 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
} }
case CONSOLE_GUI: case CONSOLE_GUI:
/* Enable support for the qemu guest agent for clipboard sharing, resolution scaling, etc. */
r = strv_extend_many( r = strv_extend_many(
&cmdline, &cmdline,
"-vga", "-vga",
"virtio", "virtio");
"-device", "virtio-serial",
"-chardev", "spicevmc,id=vdagent,debug=0,name=vdagent",
"-device", "virtserialport,chardev=vdagent,name=org.qemu.guest_agent.0");
break; break;
case CONSOLE_NATIVE: case CONSOLE_NATIVE:

View File

@ -211,7 +211,7 @@ set +o pipefail
# We can do only limited testing here, since it's all an interactive stuff, so # We can do only limited testing here, since it's all an interactive stuff, so
# --prompt is skipped on purpose and only limited --prompt-root-password # --prompt is skipped on purpose and only limited --prompt-root-password
# testing can be done. # testing can be done.
echo -ne "foo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
grep -q "LANG=foo" "$ROOT$LOCALE_PATH" grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so # systemd-firstboot in prompt-keymap mode requires keymaps to be installed so
@ -219,26 +219,22 @@ grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
# compatible keymaps (from the kbd package), skip this test if the keymaps are # compatible keymaps (from the kbd package), skip this test if the keymaps are
# missing. # missing.
if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then
echo -ne "foo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
rm "$ROOT/etc/vconsole.conf"
# this should be a NOP, given that stdout is connected to /dev/null, and hence not a VT
systemd-firstboot --root="$ROOT" --prompt-keymap-auto > /dev/null
fi fi
echo -ne "Europe/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
echo -ne "foobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
grep -q "foobar" "$ROOT/etc/hostname" grep -q "foobar" "$ROOT/etc/hostname"
# With no root password provided, a locked account should be created. # With no root password provided, a locked account should be created.
systemd-firstboot --root="$ROOT" --prompt-root-password </dev/null systemd-firstboot --root="$ROOT" --prompt-root-password </dev/null
grep -q "^root:x:0:0:" "$ROOT/etc/passwd" grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:!\*:" "$ROOT/etc/shadow" grep -q "^root:!\*:" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
echo -ne "/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
# Existing files should not get overwritten # Existing files should not get overwritten
echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
# Now without the welcome screen but with force # Now without the welcome screen but with force
echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no

View File

@ -32,7 +32,7 @@ Before=shutdown.target
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=systemd-firstboot --prompt-locale --prompt-keymap-auto --prompt-timezone --prompt-root-password --mute-console=yes ExecStart=systemd-firstboot --prompt-locale --prompt-keymap --prompt-timezone --prompt-root-password --mute-console=yes
StandardOutput=tty StandardOutput=tty
StandardInput=tty StandardInput=tty
StandardError=tty StandardError=tty

View File

@ -17,7 +17,7 @@ Before=systemd-user-sessions.service first-boot-complete.target
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=homectl firstboot --prompt-new-user --prompt-shell=no --prompt-groups=no --mute-console=yes ExecStart=homectl firstboot --prompt-new-user
StandardOutput=tty StandardOutput=tty
StandardInput=tty StandardInput=tty
StandardError=tty StandardError=tty