int path_is_os_tree(const char *path) { int r; assert(path); /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from * the case where just the os-release file is missing. */ if (laccess(path, F_OK) < 0) return -errno; /* We use /usr/lib/os-release as flag file if something is an OS */ r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); if (r == -ENOENT) { /* Also check for the old location in /etc, just in case. */ r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); if (r == -ENOENT) return 0; /* We got nothing */ } if (r < 0) return r; return 1; }
static int determine_image(const char *image, bool permit_non_existing, char **ret) { int r; /* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the * usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side * (among other things, to make the path independent of the client's working directory) before passing it * over. */ if (image_name_is_valid(image)) { char *c; if (!arg_quiet && laccess(image, F_OK) >= 0) log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. " "Prefix argument with './' to force reference to file in current working directory.", image); c = strdup(image); if (!c) return log_oom(); *ret = c; return 0; } if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Operations on images by path not supported when connecting to remote systems."); r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret); if (r < 0) return log_error_errno(r, "Cannot normalize specified image path '%s': %m", image); return 0; }
static int dkr_pull_pull_layer(DkrPull *i) { _cleanup_free_ char *path = NULL; const char *url, *layer = NULL; int r; assert(i); assert(!i->layer_job); assert(!i->temp_path); assert(!i->final_path); for (;;) { layer = dkr_pull_current_layer(i); if (!layer) return 0; /* no more layers */ path = strjoin(i->image_root, "/.dkr-", layer, NULL); if (!path) return log_oom(); if (laccess(path, F_OK) < 0) { if (errno == ENOENT) break; return log_error_errno(errno, "Failed to check for container: %m"); } log_info("Layer %s already exists, skipping.", layer); i->current_ancestry++; free(path); path = NULL; } log_info("Pulling layer %s...", layer); i->final_path = path; path = NULL; url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer"); r = pull_job_new(&i->layer_job, url, i->glue, i); if (r < 0) return log_error_errno(r, "Failed to allocate layer job: %m"); r = dkr_pull_add_token(i, i->layer_job); if (r < 0) return log_oom(); i->layer_job->on_finished = dkr_pull_job_on_finished; i->layer_job->on_open_disk = dkr_pull_job_on_open_disk; i->layer_job->on_progress = dkr_pull_job_on_progress; i->layer_job->grow_machine_directory = i->grow_machine_directory; r = pull_job_begin(i->layer_job); if (r < 0) return log_error_errno(r, "Failed to start layer job: %m"); return 0; }
static int prompt_root_password(void) { const char *msg1, *msg2, *etc_shadow; int r; if (arg_root_password) return 0; if (!arg_prompt_root_password) return 0; etc_shadow = prefix_roota(arg_root, "/etc/shadow"); if (laccess(etc_shadow, F_OK) >= 0) return 0; print_welcome(); putchar('\n'); msg1 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); msg2 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter new root password again: "); for (;;) { _cleanup_strv_free_erase_ char **a = NULL, **b = NULL; r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(a) != 1) { log_warning("Received multiple passwords, where we expected one."); return -EINVAL; } if (isempty(*a)) { log_warning("No password entered, skipping."); break; } r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (!streq(*a, *b)) { log_error("Entered passwords did not match, please try again."); continue; } arg_root_password = TAKE_PTR(*a); break; } return 0; }
static int prompt_root_password(void) { const char *msg1, *msg2, *etc_shadow; int r; if (arg_root_password) return 0; if (!arg_prompt_root_password) return 0; etc_shadow = prefix_roota(arg_root, "/etc/shadow"); if (laccess(etc_shadow, F_OK) >= 0) return 0; print_welcome(); putchar('\n'); msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); for (;;) { _cleanup_string_free_erase_ char *a = NULL, *b = NULL; r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (isempty(a)) { log_warning("No password entered, skipping."); break; } r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (!streq(a, b)) { log_error("Entered passwords did not match, please try again."); continue; } arg_root_password = a; a = NULL; break; } return 0; }
static int generate_symlink(void) { const char *p = NULL; if (laccess("/system-update", F_OK) < 0) { if (errno == ENOENT) return 0; log_error_errno(errno, "Failed to check for system update: %m"); return -EINVAL; } p = strjoina(arg_dest, "/" SPECIAL_DEFAULT_TARGET); if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", p); return 1; }
static int process_locale(void) { const char *etc_localeconf; char* locales[3]; unsigned i = 0; int r; etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); if (laccess(etc_localeconf, F_OK) >= 0) return 0; if (arg_copy_locale && arg_root) { mkdir_parents(etc_localeconf, 0755); r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, COPY_REFLINK); if (r != -ENOENT) { if (r < 0) return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); log_info("%s copied.", etc_localeconf); return 0; } } r = prompt_locale(); if (r < 0) return r; if (!isempty(arg_locale)) locales[i++] = strjoina("LANG=", arg_locale); if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale)) locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages); if (i == 0) return 0; locales[i] = NULL; mkdir_parents(etc_localeconf, 0755); r = write_env_file(etc_localeconf, locales); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_localeconf); log_info("%s written.", etc_localeconf); return 0; }
static int process_keymap(void) { const char *etc_vconsoleconf; char **keymap; int r; etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf"); if (laccess(etc_vconsoleconf, F_OK) >= 0) return 0; if (arg_copy_keymap && arg_root) { mkdir_parents(etc_vconsoleconf, 0755); r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, 0, COPY_REFLINK); if (r != -ENOENT) { if (r < 0) return log_error_errno(r, "Failed to copy %s: %m", etc_vconsoleconf); log_info("%s copied.", etc_vconsoleconf); return 0; } } r = prompt_keymap(); if (r == -ENOENT) return 0; /* don't fail if no keymaps are installed */ if (r < 0) return r; if (isempty(arg_keymap)) return 0; keymap = STRV_MAKE(strjoina("KEYMAP=", arg_keymap)); r = mkdir_parents(etc_vconsoleconf, 0755); if (r < 0) return log_error_errno(r, "Failed to create the parent directory of %s: %m", etc_vconsoleconf); r = write_env_file(etc_vconsoleconf, keymap); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_vconsoleconf); log_info("%s written.", etc_vconsoleconf); return 0; }
static int process_timezone(void) { const char *etc_localtime, *e; int r; etc_localtime = prefix_roota(arg_root, "/etc/localtime"); if (laccess(etc_localtime, F_OK) >= 0) 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 log_error_errno(r, "Failed to read host timezone: %m"); mkdir_parents(etc_localtime, 0755); if (symlink(p, etc_localtime) < 0) return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); log_info("%s copied.", etc_localtime); return 0; } } r = prompt_timezone(); if (r < 0) return r; if (isempty(arg_timezone)) return 0; e = strjoina("../usr/share/zoneinfo/", arg_timezone); mkdir_parents(etc_localtime, 0755); if (symlink(e, etc_localtime) < 0) return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); log_info("%s written", etc_localtime); return 0; }
static int process_machine_id(void) { const char *etc_machine_id; char id[SD_ID128_STRING_MAX]; int r; etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); if (laccess(etc_machine_id, F_OK) >= 0) return 0; if (sd_id128_is_null(arg_machine_id)) return 0; mkdir_parents(etc_machine_id, 0755); r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC); if (r < 0) return log_error_errno(r, "Failed to write machine id: %m"); log_info("%s written.", etc_machine_id); return 0; }
static int process_hostname(void) { const char *etc_hostname; int r; etc_hostname = prefix_roota(arg_root, "/etc/hostname"); if (laccess(etc_hostname, F_OK) >= 0) return 0; r = prompt_hostname(); if (r < 0) return r; if (isempty(arg_hostname)) return 0; mkdir_parents(etc_hostname, 0755); r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_hostname); log_info("%s written.", etc_hostname); return 0; }
static int process_root_password(void) { static const char table[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "./"; struct spwd item = { .sp_namp = (char*) "root", .sp_min = -1, .sp_max = -1, .sp_warn = -1, .sp_inact = -1, .sp_expire = -1, .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ }; _cleanup_close_ int lock = -1; char salt[3+16+1+1]; uint8_t raw[16]; unsigned i; char *j; const char *etc_shadow; int r; etc_shadow = prefix_roota(arg_root, "/etc/shadow"); if (laccess(etc_shadow, F_OK) >= 0) return 0; mkdir_parents(etc_shadow, 0755); lock = take_etc_passwd_lock(arg_root); if (lock < 0) return log_error_errno(lock, "Failed to take a lock: %m"); if (arg_copy_root_password && arg_root) { struct spwd *p; errno = 0; p = getspnam("root"); if (p || errno != ENOENT) { if (!p) { if (!errno) errno = EIO; return log_error_errno(errno, "Failed to find shadow entry for root: %m"); } r = write_root_shadow(etc_shadow, p); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_shadow); log_info("%s copied.", etc_shadow); return 0; } } r = prompt_root_password(); if (r < 0) return r; if (!arg_root_password) return 0; /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */ r = genuine_random_bytes(raw, 16, RANDOM_BLOCK); if (r < 0) return log_error_errno(r, "Failed to get salt: %m"); /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ assert_cc(sizeof(table) == 64 + 1); j = stpcpy(salt, "$6$"); for (i = 0; i < 16; i++) j[i] = table[raw[i] & 63]; j[i++] = '$'; j[i] = 0; errno = 0; item.sp_pwdp = crypt(arg_root_password, salt); if (!item.sp_pwdp) { if (!errno) errno = EINVAL; return log_error_errno(errno, "Failed to encrypt password: %m"); } item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); r = write_root_shadow(etc_shadow, &item); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_shadow); log_info("%s written.", etc_shadow); return 0; } static int help(void) { _cleanup_free_ char *link = NULL; int r; r = terminal_urlify_man("systemd-firstboot", "1", &link); if (r < 0) return log_oom(); 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" " --keymap=KEYMAP Set keymap\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-keymap Prompt the user for keymap 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 all of the above\n" " --copy-locale Copy locale from host\n" " --copy-keymap Copy keymap from host\n" " --copy-timezone Copy timezone from host\n" " --copy-root-password Copy root password from host\n" " --copy Copy locale, keymap, timezone, root password\n" " --setup-machine-id Generate a new random machine ID\n" "\nSee the %s for details.\n" , program_invocation_short_name , link ); return 0; }
static int pull_dck(int argc, char *argv[], void *userdata) { _cleanup_(dck_import_unrefp) DckImport *import = NULL; _cleanup_event_unref_ sd_event *event = NULL; const char *name, *tag, *local; int r; tag = strchr(argv[1], ':'); if (tag) { name = strndupa(argv[1], tag - argv[1]); tag++; } else { name = argv[1]; tag = "latest"; } if (argc >= 3) local = argv[2]; else { local = strchr(name, '/'); if (local) local++; else local = name; } if (streq(local, "-") || isempty(local)) local = NULL; if (!dck_name_is_valid(name)) { log_error("Remote name '%s' is not valid.", name); return -EINVAL; } if (!dck_tag_is_valid(tag)) { log_error("Tag name '%s' is not valid.", tag); return -EINVAL; } if (local) { const char *p; if (!machine_name_is_valid(tag)) { log_error("Local image name '%s' is not valid.", local); return -EINVAL; } p = strappenda("/var/lib/container/", local); if (laccess(p, F_OK) >= 0) { if (!arg_force) { log_info("Image '%s' already exists.", local); return 0; } } else if (errno != ENOENT) return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local); log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local); } else log_info("Pulling '%s' with tag '%s'.", name, tag); r = sd_event_default(&event); if (r < 0) return log_error_errno(r, "Failed to allocate event loop: %m"); assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0); sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); r = dck_import_new(&import, event, on_finished, event); if (r < 0) return log_error_errno(r, "Failed to allocate importer: %m"); r = dck_import_pull(import, name, tag, local, arg_force); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); r = sd_event_loop(event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); log_info("Exiting."); return 0; }