firstboot.c revision 64845bdc829d6a6179d0762b7e97ef23828562a3
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <shadow.h>
#include "strv.h"
#include "fileio.h"
#include "copy.h"
#include "build.h"
#include "mkdir.h"
#include "time-util.h"
#include "path-util.h"
#include "locale-util.h"
#include "ask-password-api.h"
static char *arg_timezone = NULL;
static char *arg_hostname = NULL;
static sd_id128_t arg_machine_id = {};
static char *arg_root_password = NULL;
static bool arg_prompt_locale = false;
static bool arg_prompt_timezone = false;
static bool arg_prompt_hostname = false;
static bool arg_prompt_root_password = false;
static bool arg_copy_locale = false;
static bool arg_copy_timezone = false;
static bool arg_copy_root_password = false;
static void clear_string(char *x) {
if (!x)
return;
/* A delicious drop of snake-oil! */
}
static bool press_any_key(void) {
char k = 0;
bool need_nl = true;
printf("-- Press any key to proceed --");
if (need_nl)
putchar('\n');
return k != 'q';
}
static void print_welcome(void) {
const char *os_release = NULL;
static bool done = false;
int r;
if (done)
return;
"PRETTY_NAME", &pretty_name,
NULL);
if (r == -ENOENT) {
"PRETTY_NAME", &pretty_name,
NULL);
}
if (r < 0 && r != -ENOENT)
printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n",
done = true;
}
unsigned n, per_column, i, j;
unsigned break_lines, break_modulo;
n = strv_length(x);
break_lines = lines();
if (break_lines > 2)
break_lines--;
/* The first page gets two extra lines, since we want to show
* a title */
if (break_modulo > 3)
break_modulo -= 3;
for (i = 0; i < per_column; i++) {
for (j = 0; j < n_columns; j ++) {
_cleanup_free_ char *e = NULL;
if (j * per_column + i >= n)
break;
if (!e)
return log_oom();
}
putchar('\n');
/* on the first screen we reserve 2 extra lines for the title */
if (i % break_lines == break_modulo) {
if (!press_any_key())
return 0;
}
}
return 0;
}
static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) {
int r;
for (;;) {
_cleanup_free_ char *p = NULL;
unsigned u;
if (r < 0) {
return r;
}
if (isempty(p)) {
log_warning("No data entered, skipping.");
return 0;
}
r = safe_atou(p, &u);
if (r >= 0) {
char *c;
if (u <= 0 || u > strv_length(l)) {
log_error("Specified entry number out of range.");
continue;
}
c = strdup(l[u-1]);
if (!c)
return log_oom();
*ret = c;
return 0;
}
if (!is_valid(p)) {
log_error("Entered data invalid.");
continue;
}
*ret = p;
p = 0;
return 0;
}
}
static int prompt_locale(void) {
int r;
if (arg_locale || arg_locale_messages)
return 0;
if (!arg_prompt_locale)
return 0;
r = get_locales(&locales);
if (r < 0) {
return r;
}
printf("\nAvailable Locales:\n\n");
if (r < 0)
return r;
putchar('\n');
r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
if (r < 0)
return r;
if (isempty(arg_locale))
return 0;
r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
if (r < 0)
return r;
return 0;
}
static int process_locale(void) {
const char *etc_localeconf;
char* locales[3];
unsigned i = 0;
int r;
return 0;
if (arg_copy_locale && arg_root) {
if (r != -ENOENT) {
if (r < 0) {
return r;
}
return 0;
}
}
r = prompt_locale();
if (r < 0)
return r;
if (!isempty(arg_locale))
if (i == 0)
return 0;
if (r < 0) {
return r;
}
return 0;
}
static int prompt_timezone(void) {
int r;
if (arg_timezone)
return 0;
if (!arg_prompt_timezone)
return 0;
r = get_timezones(&zones);
if (r < 0) {
return r;
}
printf("\nAvailable Time Zones:\n\n");
if (r < 0)
return r;
putchar('\n');
if (r < 0)
return r;
return 0;
}
static int process_timezone(void) {
const char *etc_localtime, *e;
int r;
return 0;
if (arg_copy_timezone && arg_root) {
_cleanup_free_ char *p = NULL;
r = readlink_malloc("/etc/localtime", &p);
if (r != -ENOENT) {
if (r < 0) {
return r;
}
if (symlink(p, etc_localtime) < 0) {
return -errno;
}
return 0;
}
}
r = prompt_timezone();
if (r < 0)
return r;
if (isempty(arg_timezone))
return 0;
if (symlink(e, etc_localtime) < 0) {
return -errno;
}
return 0;
}
static int prompt_hostname(void) {
int r;
if (arg_hostname)
return 0;
if (!arg_prompt_hostname)
return 0;
putchar('\n');
for (;;) {
_cleanup_free_ char *h = NULL;
r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET));
if (r < 0) {
return r;
}
if (isempty(h)) {
log_warning("No hostname entered, skipping.");
break;
}
if (!hostname_is_valid(h)) {
log_error("Specified hostname invalid.");
continue;
}
arg_hostname = h;
h = NULL;
break;
}
return 0;
}
static int process_hostname(void) {
const char *etc_hostname;
int r;
return 0;
r = prompt_hostname();
if (r < 0)
return r;
if (isempty(arg_hostname))
return 0;
if (r < 0) {
return r;
}
return 0;
}
static int process_machine_id(void) {
const char *etc_machine_id;
char id[SD_ID128_STRING_MAX];
int r;
return 0;
if (!arg_root)
return 0;
return 0;
if (r < 0) {
return r;
}
return 0;
}
static int prompt_root_password(void) {
int r;
if (arg_root_password)
return 0;
if (!arg_prompt_root_password)
return 0;
return 0;
putchar('\n');
msg1 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
msg2 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");
for (;;) {
if (r < 0) {
return r;
}
if (isempty(a)) {
log_warning("No password entered, skipping.");
break;
}
if (r < 0) {
clear_string(a);
return r;
}
if (!streq(a, b)) {
log_error("Entered passwords did not match, please try again.");
clear_string(a);
clear_string(b);
continue;
}
clear_string(b);
arg_root_password = a;
a = NULL;
break;
}
return 0;
}
assert(p);
RUN_WITH_UMASK(0777)
if (!f)
return -errno;
errno = 0;
if (putspent(p, f) != 0)
return fflush_and_check(f);
}
static int process_root_password(void) {
static const char table[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"./";
.sp_namp = (char*) "root",
.sp_min = 0,
.sp_max = 99999,
.sp_warn = 7,
.sp_inact = -1,
.sp_expire = -1,
};
unsigned i;
char *j;
const char *etc_shadow;
int r;
return 0;
if (lock < 0)
return lock;
if (arg_copy_root_password && arg_root) {
struct spwd *p;
errno = 0;
p = getspnam("root");
if (!p) {
if (!errno)
log_error("Failed to find shadow entry for root: %m");
return -errno;
}
r = write_root_shadow(etc_shadow, p);
if (r < 0) {
return r;
}
return 0;
}
}
r = prompt_root_password();
if (r < 0)
return r;
if (!arg_root_password)
return 0;
if (r < 0) {
return r;
}
/* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
for (i = 0; i < 16; i++)
j[i++] = '$';
j[i] = 0;
errno = 0;
if (!errno)
log_error("Failed to encrypt password: %m");
return -errno;
}
if (r < 0) {
return r;
}
return 0;
}
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Configures basic settings of the system.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --locale=LOCALE Set primary locale (LANG=)\n"
" --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n"
" --timezone=TIMEZONE Set timezone\n"
" --hostname=NAME Set host name\n"
" --machine-ID=ID Set machine ID\n"
" --root-password=PASSWORD Set root password\n"
" --root-password-file=FILE Set root password from file\n"
" --prompt-locale Prompt the user for locale settings\n"
" --prompt-timezone Prompt the user for timezone\n"
" --prompt-hostname Prompt the user for hostname\n"
" --prompt-root-password Prompt the user for root password\n"
" --prompt Prompt for locale, timezone, hostname, root password\n"
" --copy-locale Copy locale from host\n"
" --copy-timezone Copy timezone from host\n"
" --copy-root-password Copy root password from host\n"
" --copy Copy locale, timezone, root password\n"
" --setup-machine-id Generate a new random machine ID\n"
}
enum {
ARG_VERSION = 0x100,
};
{}
};
int r, c;
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return 0;
case ARG_ROOT:
if (!arg_root)
return log_oom();
}
break;
case ARG_LOCALE:
if (!locale_is_valid(optarg)) {
return -EINVAL;
}
if (!arg_locale)
return log_oom();
break;
case ARG_LOCALE_MESSAGES:
if (!locale_is_valid(optarg)) {
return -EINVAL;
}
if (!arg_locale_messages)
return log_oom();
break;
case ARG_TIMEZONE:
if (!timezone_is_valid(optarg)) {
return -EINVAL;
}
if (!arg_timezone)
return log_oom();
break;
case ARG_ROOT_PASSWORD:
if (!arg_root_password)
return log_oom();
break;
case ARG_ROOT_PASSWORD_FILE:
if (r < 0) {
return r;
}
break;
case ARG_HOSTNAME:
if (!hostname_is_valid(optarg)) {
return -EINVAL;
}
if (!arg_hostname)
return log_oom();
break;
case ARG_MACHINE_ID:
return -EINVAL;
}
break;
case ARG_PROMPT:
break;
case ARG_PROMPT_LOCALE:
arg_prompt_locale = true;
break;
case ARG_PROMPT_TIMEZONE:
arg_prompt_timezone = true;
break;
case ARG_PROMPT_HOSTNAME:
arg_prompt_hostname = true;
break;
case ARG_PROMPT_ROOT_PASSWORD:
arg_prompt_root_password = true;
break;
case ARG_COPY:
break;
case ARG_COPY_LOCALE:
arg_copy_locale = true;
break;
case ARG_COPY_TIMEZONE:
arg_copy_timezone = true;
break;
case ARG_COPY_ROOT_PASSWORD:
arg_copy_root_password = true;
break;
case ARG_SETUP_MACHINE_ID:
r = sd_id128_randomize(&arg_machine_id);
if (r < 0) {
return r;
}
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
return 1;
}
int r;
if (r <= 0)
goto finish;
log_open();
umask(0022);
r = process_locale();
if (r < 0)
goto finish;
r = process_timezone();
if (r < 0)
goto finish;
r = process_hostname();
if (r < 0)
goto finish;
r = process_machine_id();
if (r < 0)
goto finish;
r = process_root_password();
if (r < 0)
goto finish;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}