mirror of
https://github.com/systemd/systemd
synced 2025-11-21 17:54:46 +01:00
Compare commits
10 Commits
0c0a99599c
...
0c3639d0f5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c3639d0f5 | ||
|
|
054e542c54 | ||
|
|
372f3159fd | ||
|
|
aa27bec194 | ||
|
|
bedcce1a1f | ||
|
|
f233132a67 | ||
|
|
0cfaea3619 | ||
|
|
b128c31f46 | ||
|
|
6d40d0773c | ||
|
|
6f496236bc |
@ -205,6 +205,56 @@
|
|||||||
<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>
|
||||||
@ -1424,9 +1474,10 @@ $ 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. Alternatively, if one or more service credentials
|
console for user name and password and creates one (only if <option>--prompt-new-user</option> is
|
||||||
whose name starts with <literal>home.create.</literal> are passed to the command (containing a user
|
specified). Alternatively, if one or more service credentials whose name starts with
|
||||||
record in JSON format) these users are automatically created at boot.</para>
|
<literal>home.create.</literal> are passed to the command (containing a user record in JSON format)
|
||||||
|
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>
|
||||||
|
|||||||
@ -271,6 +271,17 @@
|
|||||||
</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>
|
||||||
|
|||||||
@ -71,6 +71,7 @@ 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;
|
||||||
@ -138,18 +139,16 @@ 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 your new installation of " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT "!" ANSI_NORMAL "\n", ac, pn);
|
printf(ANSI_HIGHLIGHT "Welcome to " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT "!" ANSI_NORMAL "\n", ac, pn);
|
||||||
else
|
else
|
||||||
printf("Welcome to your new installation of %s!\n", pn);
|
printf("Welcome to %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 your new system!\n");
|
printf("Please configure the system!\n\n");
|
||||||
|
|
||||||
any_key_to_proceed();
|
|
||||||
|
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
@ -417,7 +416,21 @@ static int prompt_keymap(int rfd, sd_varlink **mute_console_link) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arg_prompt_keymap) {
|
bool b;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@ -1236,6 +1249,8 @@ 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"
|
||||||
@ -1286,6 +1301,7 @@ 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,
|
||||||
@ -1325,6 +1341,7 @@ 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 },
|
||||||
@ -1482,6 +1499,7 @@ 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:
|
||||||
@ -1490,6 +1508,11 @@ 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:
|
||||||
@ -1670,7 +1693,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_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#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"
|
||||||
@ -15,6 +16,7 @@
|
|||||||
#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"
|
||||||
@ -49,6 +51,7 @@
|
|||||||
#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"
|
||||||
@ -101,13 +104,17 @@ 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);
|
||||||
@ -2680,77 +2687,21 @@ static int group_completion_callback(const char *key, char ***ret_list, void *us
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_interactively(void) {
|
static int prompt_groups(const char *username, char ***ret_groups) {
|
||||||
_cleanup_free_ char *username = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!arg_prompt_new_user) {
|
assert(username);
|
||||||
log_debug("Prompting for user creation was not requested.");
|
assert(ret_groups);
|
||||||
|
|
||||||
|
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)) {
|
||||||
@ -2761,10 +2712,11 @@ static int create_interactively(void) {
|
|||||||
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_TRIANGULAR_BULLET), username);
|
glyph(GLYPH_LABEL), 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");
|
||||||
|
|
||||||
@ -2834,6 +2786,148 @@ static int create_interactively(void) {
|
|||||||
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);
|
||||||
|
|
||||||
@ -2843,35 +2937,9 @@ 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 log_error_errno(r, "Failed to query user for username: %m");
|
return r;
|
||||||
|
|
||||||
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);
|
||||||
@ -2881,7 +2949,14 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
return create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ false);
|
putchar('\n');
|
||||||
|
|
||||||
|
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);
|
||||||
@ -3021,11 +3096,18 @@ 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"
|
||||||
@ -3262,6 +3344,10 @@ 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[] = {
|
||||||
@ -3368,6 +3454,10 @@ 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 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4927,6 +5017,34 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@ -2020,10 +2020,14 @@ 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:
|
||||||
|
|||||||
@ -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 "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
|
echo -ne "foo\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,22 +219,26 @@ 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 "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
|
echo -ne "foo\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 "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
|
echo -ne "Europe/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 "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
|
echo -ne "foobar\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 "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
|
echo -ne "/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 "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
|
echo -ne "/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
|
||||||
|
|||||||
@ -32,7 +32,7 @@ Before=shutdown.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
ExecStart=systemd-firstboot --prompt-locale --prompt-keymap --prompt-timezone --prompt-root-password --mute-console=yes
|
ExecStart=systemd-firstboot --prompt-locale --prompt-keymap-auto --prompt-timezone --prompt-root-password --mute-console=yes
|
||||||
StandardOutput=tty
|
StandardOutput=tty
|
||||||
StandardInput=tty
|
StandardInput=tty
|
||||||
StandardError=tty
|
StandardError=tty
|
||||||
|
|||||||
@ -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
|
ExecStart=homectl firstboot --prompt-new-user --prompt-shell=no --prompt-groups=no --mute-console=yes
|
||||||
StandardOutput=tty
|
StandardOutput=tty
|
||||||
StandardInput=tty
|
StandardInput=tty
|
||||||
StandardError=tty
|
StandardError=tty
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user