Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
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;
}
Пример #4
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;
}
Пример #5
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;
}
Пример #7
0
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;
}
Пример #8
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;
}
Пример #9
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;
}
Пример #10
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;
}
Пример #11
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;
}
Пример #12
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;
}
Пример #13
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;
}