STRV_FOREACH_PAIR(link, p, links) { char *f = strappenda(original_dir, *p); char *l = strappenda(original_dir, *link); assert_se(mkdir_parents(l, 0755) >= 0); assert_se(symlink(f, l) == 0); }
static int link_compatibility(const char *units) { const char *f, *t; f = strappenda(units, "/systemd-bus-proxyd.socket"); t = strappenda(arg_dest, "/" SPECIAL_DBUS_SOCKET); mkdir_parents_label(t, 0755); if (symlink(f, t) < 0) { log_error("Failed to create symlink %s: %m", t); return -errno; } f = strappenda(units, "/systemd-bus-proxyd.socket"); t = strappenda(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket"); mkdir_parents_label(t, 0755); if (symlink(f, t) < 0) { log_error("Failed to create symlink %s: %m", t); return -errno; } t = strappenda(arg_dest, "/" SPECIAL_DBUS_SERVICE); if (symlink("/dev/null", t) < 0) { log_error("Failed to mask %s: %m", t); return -errno; } return 0; }
static void test_conf_files_list(bool use_root) { char tmp_dir[] = "/tmp/test-conf-files-XXXXXX"; _cleanup_strv_free_ char **found_files = NULL; const char *root_dir, *search_1, *search_2, *expect_a, *expect_b; setup_test_dir(tmp_dir, "/dir1/a.conf", "/dir2/a.conf", "/dir2/b.conf", NULL); if (use_root) { root_dir = tmp_dir; search_1 = "/dir1"; search_2 = "/dir2"; } else { root_dir = NULL; search_1 = strappenda(tmp_dir, "/dir1"); search_2 = strappenda(tmp_dir, "/dir2"); } expect_a = strappenda(tmp_dir, "/dir1/a.conf"); expect_b = strappenda(tmp_dir, "/dir2/b.conf"); assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0); strv_print(found_files); assert_se(found_files); assert_se(streq_ptr(found_files[0], expect_a)); assert_se(streq_ptr(found_files[1], expect_b)); assert_se(found_files[2] == NULL); assert_se(rm_rf_dangerous(tmp_dir, false, true, false) == 0); }
STRV_FOREACH_PAIR(link, p, links) { _cleanup_free_ char *target = NULL; char *f = strappenda(original_dir, *p); char *l = strappenda(copy_dir, *link); assert_se(readlink_and_canonicalize(l, &target) == 0); assert_se(path_equal(f, target)); }
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("/etc/shadow"); if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; print_welcome(); 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 (;;) { _cleanup_free_ char *a = NULL, *b = NULL; r = ask_password_tty(msg1, 0, false, NULL, &a); if (r < 0) { log_error("Failed to query root password: %s", strerror(-r)); return r; } if (isempty(a)) { log_warning("No password entered, skipping."); break; } r = ask_password_tty(msg2, 0, false, NULL, &b); if (r < 0) { log_error("Failed to query root password: %s", strerror(-r)); 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; }
static int link_busnames_target(const char *units) { const char *f, *t; f = strappenda(units, "/" SPECIAL_BUSNAMES_TARGET); t = strappenda(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET); mkdir_parents_label(t, 0755); if (symlink(f, t) < 0) { log_error("Failed to create symlink %s: %m", t); return -errno; } return 0; }
static int generate_display_manager_alias(void) { _cleanup_free_ char *default_dm_path = NULL, *enabled_dm_unit = NULL; const char *default_dm = NULL, *in_mem_symlink = NULL, *target_unit_path = NULL; bool dm_service_exists = true; int r; r = read_full_file(default_dm_file, &default_dm_path, NULL); if (r < 0) { log_debug("No %s file, nothing to generate", default_dm_file); return 0; } default_dm = strstrip(basename(default_dm_path)); r = readlink_value(dm_service_unit, &enabled_dm_unit); if (r < 0) { enabled_dm_unit = strdup(""); dm_service_exists = false; } /* all is fine if the info matches */ if (streq(strappenda(default_dm, ".service"), enabled_dm_unit)) return 0; target_unit_path = strappenda(SYSTEM_DATA_UNIT_PATH, "/", default_dm, ".service"); /* we only create the alias symlink for non sysvinit services */ if (access(target_unit_path, F_OK) < 0 && (errno == ENOENT)) { /* if the dm service was already disabled, nothing to be done */ if (!dm_service_exists) { log_debug("No %s file, nothing to mask", dm_service_unit); return 0; } log_warning("%s is not a systemd unit, we disable the systemd enabled display manager", target_unit_path); target_unit_path = "/dev/null"; } else { log_warning("%s points at %s while the default systemd unit is %s. Reconfiguring %s as default.", default_dm_file, default_dm, enabled_dm_unit, default_dm); } in_mem_symlink = strappenda(dest, "/display-manager.service"); mkdir_parents_label(in_mem_symlink, 0755); if (symlink(target_unit_path, in_mem_symlink) < 0) { log_error("Failed to create symlink %s: %m", in_mem_symlink); return -errno; } return 0; }
static int process_locale(void) { const char *etc_localeconf; char* locales[3]; unsigned i = 0; int r; etc_localeconf = prefix_roota("/etc/locale.conf"); if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) return 0; if (arg_copy_locale && arg_root) { mkdir_parents(etc_localeconf, 0755); r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644); if (r != -ENOENT) { if (r < 0) { log_error("Failed to copy %s: %s", etc_localeconf, strerror(-r)); return r; } log_info("%s copied.", etc_localeconf); return 0; } } r = prompt_locale(); if (r < 0) return r; if (!isempty(arg_locale)) locales[i++] = strappenda("LANG=", arg_locale); if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale)) locales[i++] = strappenda("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) { log_error("Failed to write %s: %s", etc_localeconf, strerror(-r)); return r; } log_info("%s written.", etc_localeconf); return 0; }
static bool condition_test_needs_update(Condition *c) { const char *p; struct stat usr, other; assert(c); assert(c->parameter); assert(c->type == CONDITION_NEEDS_UPDATE); /* If the file system is read-only we shouldn't suggest an update */ if (path_is_read_only_fs(c->parameter) > 0) return c->negate; /* Any other failure means we should allow the condition to be true, * so that we rather invoke too many update tools then too * few. */ if (!path_is_absolute(c->parameter)) return !c->negate; p = strappenda(c->parameter, "/.updated"); if (lstat(p, &other) < 0) return !c->negate; if (lstat("/usr/", &usr) < 0) return !c->negate; return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec || (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate; }
static int add_fsck(FILE *f, const char *what, const char *where, const char *type, int passno) { assert(f); if (passno == 0) return 0; if (type && !streq(type, "auto")) { int r; const char *checker; checker = strappenda("/sbin/fsck.", type); r = access(checker, X_OK); if (r < 0) { log_warning("Checking was requested for %s, but %s cannot be used: %m", what, checker); /* treat missing check as essentially OK */ return errno == ENOENT ? 0 : -errno; } } if (streq(where, "/")) { char *lnk; lnk = strappenda(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service"); mkdir_parents_label(lnk, 0755); if (symlink("systemd-fsck-root.service", lnk) < 0) { log_error("Failed to create symlink %s: %m", lnk); return -errno; } } else { _cleanup_free_ char *fsck = NULL; fsck = unit_name_from_path_instance("systemd-fsck", what, ".service"); if (!fsck) return log_oom(); fprintf(f, "RequiresOverridable=%s\n" "After=%s\n", fsck, fsck); } return 0; }
void microhttpd_logger(void *arg, const char *fmt, va_list ap) { char *f; f = strappenda("microhttpd: ", fmt); DISABLE_WARNING_FORMAT_NONLITERAL; log_internalv(LOG_INFO, 0, NULL, 0, NULL, f, ap); REENABLE_WARNING; }
STRV_FOREACH(p, files) { _cleanup_free_ char *buf = NULL; size_t sz = 0; char *f = strappenda(copy_dir, *p); assert_se(access(f, F_OK) == 0); assert_se(read_full_file(f, &buf, &sz) == 0); assert_se(streq(buf, "file\n")); }
static void test_tmpdir(const char *id, const char *A, const char *B) { _cleanup_free_ char *a, *b; struct stat x, y; char *c, *d; assert_se(setup_tmp_dirs(id, &a, &b) == 0); assert_se(startswith(a, A)); assert_se(startswith(b, B)); assert_se(stat(a, &x) >= 0); assert_se(stat(b, &y) >= 0); assert_se(S_ISDIR(x.st_mode)); assert_se(S_ISDIR(y.st_mode)); assert_se((x.st_mode & 01777) == 0700); assert_se((y.st_mode & 01777) == 0700); c = strappenda(a, "/tmp"); d = strappenda(b, "/tmp"); assert_se(stat(c, &x) >= 0); assert_se(stat(d, &y) >= 0); assert_se(S_ISDIR(x.st_mode)); assert_se(S_ISDIR(y.st_mode)); assert_se((x.st_mode & 01777) == 01777); assert_se((y.st_mode & 01777) == 01777); assert_se(rmdir(c) >= 0); assert_se(rmdir(d) >= 0); assert_se(rmdir(a) >= 0); assert_se(rmdir(b) >= 0); }
static int process_timezone(void) { const char *etc_localtime, *e; int r; etc_localtime = prefix_roota("/etc/localtime"); if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 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) { log_error("Failed to read host timezone: %s", strerror(-r)); return r; } mkdir_parents(etc_localtime, 0755); if (symlink(p, etc_localtime) < 0) { log_error("Failed to create %s symlink: %m", etc_localtime); return -errno; } log_info("%s copied.", etc_localtime); return 0; } } r = prompt_timezone(); if (r < 0) return r; if (isempty(arg_timezone)) return 0; e = strappenda("../usr/share/zoneinfo/", arg_timezone); mkdir_parents(etc_localtime, 0755); if (symlink(e, etc_localtime) < 0) { log_error("Failed to create %s symlink: %m", etc_localtime); return -errno; } log_info("%s written", etc_localtime); return 0; }
static int generate_symlink(void) { const char *p = NULL; if (access("/system-update", F_OK) < 0) { if (errno == ENOENT) return 0; log_error_errno(errno, "Failed to check for system update: %m"); return -EINVAL; } p = strappenda(arg_dest, "/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 0; }
static void test_copy_tree(void) { char original_dir[] = "/tmp/test-copy_tree/"; char copy_dir[] = "/tmp/test-copy_tree-copy/"; char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file"); char **links = STRV_MAKE("link", "file", "link2", "dir1/file"); char **p, **link; rm_rf_dangerous(copy_dir, false, true, false); rm_rf_dangerous(original_dir, false, true, false); STRV_FOREACH(p, files) { char *f = strappenda(original_dir, *p); assert_se(mkdir_parents(f, 0755) >= 0); assert_se(write_string_file(f, "file") == 0); }
/* read the 256 bytes PCI configuration space to check the multi-function bit */ static bool is_pci_multifunction(struct udev_device *dev) { _cleanup_fclose_ FILE *f = NULL; const char *filename; uint8_t config[64]; filename = strappenda(udev_device_get_syspath(dev), "/config"); f = fopen(filename, "re"); if (!f) return false; if (fread(&config, sizeof(config), 1, f) != 1) return false; /* bit 0-6 header type, bit 7 multi/single function device */ if ((config[PCI_HEADER_TYPE] & 0x80) != 0) return true; return false; }
static int setup_test(Manager **m) { char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit", "directorynotempty", "makedirectory"); char **test_path; Manager *tmp; int r; assert_se(m); r = manager_new(SYSTEMD_USER, true, &tmp); if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { printf("Skipping test: manager_new: %s", strerror(-r)); return -EXIT_TEST_SKIP; } assert_se(r >= 0); assert_se(manager_startup(tmp, NULL, NULL) >= 0); STRV_FOREACH(test_path, tests_path) { rm_rf_dangerous(strappenda("/tmp/test-path_", *test_path), false, true, false); }
int main(int argc, char** argv) { const char *p = argv[1] ?: "/tmp"; char *pattern = strappenda(p, "/systemd-test-XXXXXX"); _cleanup_close_ int fd, fd2; _cleanup_free_ char *cmd, *cmd2; fd = open_tmpfile(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0); system(cmd); fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(unlink(pattern) == 0); assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0); system(cmd2); return 0; }
static int busname_verify(BusName *n) { char *e; assert(n); if (UNIT(n)->load_state != UNIT_LOADED) return 0; if (!service_name_is_valid(n->name)) { log_error_unit(UNIT(n)->id, "%s's Name= setting is not a valid service name Refusing.", UNIT(n)->id); return -EINVAL; } e = strappenda(n->name, ".busname"); if (!unit_has_name(UNIT(n), e)) { log_error_unit(UNIT(n)->id, "%s's Name= setting doesn't match unit name. Refusing.", UNIT(n)->id); return -EINVAL; } return 0; }
int button_open(Button *b) { char *p, name[256]; int r; assert(b); if (b->fd >= 0) { close(b->fd); b->fd = -1; } p = strappenda("/dev/input/", b->name); b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); if (b->fd < 0) { log_warning("Failed to open %s: %m", b->name); return -errno; } if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { log_error("Failed to get input name: %m"); r = -errno; goto fail; } r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); if (r < 0) { log_error("Failed to add button event: %s", strerror(-r)); goto fail; } log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name); return 0; fail: close(b->fd); b->fd = -1; return r; }
static int add_root_mount(void) { _cleanup_free_ char *what = NULL; const char *opts; if (isempty(arg_root_what)) { log_debug("Could not find a root= entry on the kernel commandline."); return 0; } what = fstab_node_to_udev_node(arg_root_what); if (!path_is_absolute(what)) { log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype)); return 0; } if (!arg_root_options) opts = arg_root_rw > 0 ? "rw" : "ro"; else if (arg_root_rw >= 0 || (!mount_test_option(arg_root_options, "ro") && !mount_test_option(arg_root_options, "rw"))) opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro"); else opts = arg_root_options; log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype)); return add_mount(what, "/sysroot", arg_root_fstype, opts, 1, false, false, false, SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline"); }
static int reply_query_state(DnsQuery *q) { _cleanup_free_ char *ip = NULL; const char *name; int r; if (q->request_hostname) name = q->request_hostname; else { r = in_addr_to_string(q->request_family, &q->request_address, &ip); if (r < 0) return r; name = ip; } switch (q->state) { case DNS_TRANSACTION_NO_SERVERS: return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); case DNS_TRANSACTION_TIMEOUT: return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); case DNS_TRANSACTION_INVALID_REPLY: return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); case DNS_TRANSACTION_RESOURCES: return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources"); case DNS_TRANSACTION_ABORTED: return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted"); case DNS_TRANSACTION_FAILURE: { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; if (q->answer_rcode == DNS_RCODE_NXDOMAIN) sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name); else { const char *rc, *n; char p[3]; /* the rcode is 4 bits long */ rc = dns_rcode_to_string(q->answer_rcode); if (!rc) { sprintf(p, "%i", q->answer_rcode); rc = p; } n = strappenda(_BUS_ERROR_DNS, rc); sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc); } return sd_bus_reply_method_error(q->request, &error); } case DNS_TRANSACTION_NULL: case DNS_TRANSACTION_PENDING: case DNS_TRANSACTION_SUCCESS: default: assert_not_reached("Impossible state"); } }
int mount_cgroup_controllers(char ***join_controllers) { int r; char buf[LINE_MAX]; _cleanup_set_free_free_ Set *controllers = NULL; _cleanup_fclose_ FILE *f; /* Mount all available cgroup controllers that are built into the kernel. */ f = fopen("/proc/cgroups", "re"); if (!f) { log_error("Failed to enumerate cgroup controllers: %m"); return 0; } controllers = set_new(string_hash_func, string_compare_func); if (!controllers) return log_oom(); /* Ignore the header line */ (void) fgets(buf, sizeof(buf), f); for (;;) { char *controller; int enabled = 0; if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { if (feof(f)) break; log_error("Failed to parse /proc/cgroups."); return -EIO; } if (!enabled) { free(controller); continue; } r = set_consume(controllers, controller); if (r < 0) { log_error("Failed to add controller to set."); return r; } } for (;;) { MountPoint p = { .what = "cgroup", .type = "cgroup", .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV, .mode = MNT_IN_CONTAINER, }; char ***k = NULL; _cleanup_free_ char *options = NULL, *controller; controller = set_steal_first(controllers); if (!controller) break; if (join_controllers) for (k = join_controllers; *k; k++) if (strv_find(*k, controller)) break; if (k && *k) { char **i, **j; for (i = *k, j = *k; *i; i++) { if (!streq(*i, controller)) { char _cleanup_free_ *t; t = set_remove(controllers, *i); if (!t) { free(*i); continue; } } *(j++) = *i; } *j = NULL; options = strv_join(*k, ","); if (!options) return log_oom(); } else { options = controller; controller = NULL; } p.where = strappenda("/sys/fs/cgroup/", options); p.options = options; r = mount_one(&p, true); if (r < 0) return r; if (r > 0 && k && *k) { char **i; for (i = *k; *i; i++) { char *t = strappenda("/sys/fs/cgroup/", *i); r = symlink(options, t); if (r < 0 && errno != EEXIST) { log_error("Failed to create symlink %s: %m", t); return -errno; } } } } return 0; } static int nftw_cb( const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) { /* No need to label /dev twice in a row... */ if (_unlikely_(ftwbuf->level == 0)) return FTW_CONTINUE; label_fix(fpath, false, false); /* /run/initramfs is static data and big, no need to * dynamically relabel its contents at boot... */ if (_unlikely_(ftwbuf->level == 1 && tflag == FTW_D && streq(fpath, "/run/initramfs"))) return FTW_SKIP_SUBTREE; return FTW_CONTINUE; }; int mount_setup(bool loaded_policy) { int r; unsigned i; for (i = 0; i < ELEMENTSOF(mount_table); i ++) { r = mount_one(mount_table + i, true); if (r < 0) return r; } /* Nodes in devtmpfs and /run need to be manually updated for * the appropriate labels, after mounting. The other virtual * API file systems like /sys and /proc do not need that, they * use the same label for all their files. */ if (loaded_policy) { usec_t before_relabel, after_relabel; char timespan[FORMAT_TIMESPAN_MAX]; before_relabel = now(CLOCK_MONOTONIC); nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); after_relabel = now(CLOCK_MONOTONIC); log_info("Relabelled /dev and /run in %s.", format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0)); } /* Create a few default symlinks, which are normally created * by udevd, but some scripts might need them before we start * udevd. */ dev_setup(NULL); /* Mark the root directory as shared in regards to mount * propagation. The kernel defaults to "private", but we think * it makes more sense to have a default of "shared" so that * nspawn and the container tools work out of the box. If * specific setups need other settings they can reset the * propagation mode to private if needed. */ if (detect_container(NULL) <= 0) if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) log_warning("Failed to set up the root directory for shared mount propagation: %m"); /* Create a few directories we always want around, Note that * sd_booted() checks for /run/systemd/system, so this mkdir * really needs to stay for good, otherwise software that * copied sd-daemon.c into their sources will misdetect * systemd. */ mkdir_label("/run/systemd", 0755); mkdir_label("/run/systemd/system", 0755); mkdir_label("/run/systemd/inaccessible", 0000); return 0; }
int switch_root(const char *new_root) { /* Don't try to unmount/move the old "/", there's no way to do it. */ static const char move_mounts[] = "/dev\0" "/proc\0" "/sys\0" "/run\0"; _cleanup_close_ int old_root_fd = -1; struct stat new_root_stat; bool old_root_remove; const char *i, *temporary_old_root; if (path_equal(new_root, "/")) return 0; /* When using pivot_root() we assume that /mnt exists as place * we can temporarily move the old root to. As we immediately * unmount it from there it doesn't matter much which * directory we choose for this, but it should be more likely * than not that /mnt exists and is suitable as mount point * and is on the same fs as the old root dir */ temporary_old_root = strappenda(new_root, "/mnt"); old_root_remove = in_initrd(); if (stat(new_root, &new_root_stat) < 0) { log_error("Failed to stat directory %s: %m", new_root); return -errno; } /* Work-around for a kernel bug: for some reason the kernel * refuses switching root if any file systems are mounted * MS_SHARED. Hence remount them MS_PRIVATE here as a * work-around. * * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) log_warning("Failed to make \"/\" private mount: %m"); NULSTR_FOREACH(i, move_mounts) { char new_mount[PATH_MAX]; struct stat sb; snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i); char_array_0(new_mount); if ((stat(new_mount, &sb) < 0) || sb.st_dev != new_root_stat.st_dev) { /* Mount point seems to be mounted already or * stat failed. Unmount the old mount * point. */ if (umount2(i, MNT_DETACH) < 0) log_warning("Failed to unmount %s: %m", i); continue; } if (mount(i, new_mount, NULL, MS_MOVE, NULL) < 0) { log_error("Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); if (umount2(i, MNT_FORCE) < 0) log_warning("Failed to unmount %s: %m", i); } }
static int create_disk( const char *name, const char *device, const char *password, const char *options) { _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL; _cleanup_fclose_ FILE *f = NULL; bool noauto, nofail, tmp, swap; char *from; int r; assert(name); assert(device); noauto = has_option(options, "noauto"); nofail = has_option(options, "nofail"); tmp = has_option(options, "tmp"); swap = has_option(options, "swap"); if (tmp && swap) { log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); return -EINVAL; } e = unit_name_escape(name); if (!e) return log_oom(); n = unit_name_build("systemd-cryptsetup", e, ".service"); if (!n) return log_oom(); p = strjoin(arg_dest, "/", n, NULL); if (!p) return log_oom(); u = fstab_node_to_udev_node(device); if (!u) return log_oom(); d = unit_name_from_path(u, ".device"); if (!d) return log_oom(); f = fopen(p, "wxe"); if (!f) { log_error("Failed to create unit file %s: %m", p); return -errno; } fputs( "# Automatically generated by systemd-cryptsetup-generator\n\n" "[Unit]\n" "Description=Cryptography Setup for %I\n" "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:[email protected](8)\n" "SourcePath=/etc/crypttab\n" "DefaultDependencies=no\n" "Conflicts=umount.target\n" "BindsTo=dev-mapper-%i.device\n" "IgnoreOnIsolate=true\n" "After=systemd-readahead-collect.service systemd-readahead-replay.service\n", f); if (!nofail) fprintf(f, "Before=cryptsetup.target\n"); if (password) { if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) fputs("After=systemd-random-seed.service\n", f); else if (!streq(password, "-") && !streq(password, "none")) { _cleanup_free_ char *uu; uu = fstab_node_to_udev_node(password); if (!uu) return log_oom(); if (is_device_path(uu)) { _cleanup_free_ char *dd; dd = unit_name_from_path(uu, ".device"); if (!dd) return log_oom(); fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); } else fprintf(f, "RequiresMountsFor=%s\n", password); } } if (is_device_path(u)) fprintf(f, "BindsTo=%s\n" "After=%s\n" "Before=umount.target\n", d, d); else fprintf(f, "RequiresMountsFor=%s\n", u); fprintf(f, "\n[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", name, u, strempty(password), strempty(options), name); if (tmp) fprintf(f, "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", name); if (swap) fprintf(f, "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", name); fflush(f); if (ferror(f)) { log_error("Failed to write file %s: %m", p); return -errno; } from = strappenda("../", n); if (!noauto) { to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) { log_error("Failed to create symlink %s: %m", to); return -errno; } free(to); if (!nofail) to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); else to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) { log_error("Failed to create symlink %s: %m", to); return -errno; } } free(to); to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); if (!to) return log_oom(); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) { log_error("Failed to create symlink %s: %m", to); return -errno; } if (!noauto && !nofail) { free(p); p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); if (!p) return log_oom(); mkdir_parents_label(p, 0755); r = write_string_file(p, "# Automatically generated by systemd-cryptsetup-generator\n\n" "[Unit]\n" "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */ if (r < 0) { log_error("Failed to write device drop-in: %s", strerror(-r)); return r; } } 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; }
int pager_open(bool jump_to_end) { int fd[2]; const char *pager; pid_t parent_pid; int r; if (pager_pid > 0) return 1; if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER"))) if (!*pager || streq(pager, "cat")) return 0; if (!on_tty()) return 0; /* Determine and cache number of columns before we spawn the * pager so that we get the value from the actual tty */ columns(); if (pipe(fd) < 0) { log_error("Failed to create pager pipe: %m"); return -errno; } parent_pid = getpid(); pager_pid = fork(); if (pager_pid < 0) { r = -errno; log_error("Failed to fork pager: %m"); safe_close_pair(fd); return r; } /* In the child start the pager */ if (pager_pid == 0) { const char* less_opts; dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); less_opts = getenv("SYSTEMD_LESS"); if (!less_opts) less_opts = "FRSXMK"; if (jump_to_end) less_opts = strappenda(less_opts, " +G"); setenv("LESS", less_opts, 1); /* Make sure the pager goes away when the parent dies */ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) _exit(EXIT_FAILURE); /* Check whether our parent died before we were able * to set the death signal */ if (getppid() != parent_pid) _exit(EXIT_SUCCESS); if (pager) { execlp(pager, pager, NULL); execl("/bin/sh", "sh", "-c", pager, NULL); } /* Debian's alternatives command for pagers is * called 'pager'. Note that we do not call * sensible-pagers here, since that is just a * shell script that implements a logic that * is similar to this one anyway, but is * Debian-specific. */ execlp("pager", "pager", NULL); execlp("less", "less", NULL); execlp("more", "more", NULL); pager_fallback(); /* not reached */ } /* Return in the parent */ if (dup2(fd[1], STDOUT_FILENO) < 0) { log_error("Failed to duplicate pager pipe: %m"); return -errno; } safe_close_pair(fd); return 1; }
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) { /* Don't try to unmount/move the old "/", there's no way to do it. */ static const char move_mounts[] = "/dev\0" "/proc\0" "/sys\0" "/run\0"; _cleanup_close_ int old_root_fd = -1; struct stat new_root_stat; bool old_root_remove; const char *i, *temporary_old_root; if (path_equal(new_root, "/")) return 0; temporary_old_root = strappenda(new_root, oldroot); mkdir_p_label(temporary_old_root, 0755); old_root_remove = in_initrd(); if (stat(new_root, &new_root_stat) < 0) { log_error("Failed to stat directory %s: %m", new_root); return -errno; } /* Work-around for kernel design: the kernel refuses switching * root if any file systems are mounted MS_SHARED. Hence * remount them MS_PRIVATE here as a work-around. * * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) log_warning("Failed to make \"/\" private mount: %m"); NULSTR_FOREACH(i, move_mounts) { char new_mount[PATH_MAX]; struct stat sb; snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i); char_array_0(new_mount); mkdir_p_label(new_mount, 0755); if ((stat(new_mount, &sb) < 0) || sb.st_dev != new_root_stat.st_dev) { /* Mount point seems to be mounted already or * stat failed. Unmount the old mount * point. */ if (umount2(i, MNT_DETACH) < 0) log_warning("Failed to unmount %s: %m", i); continue; } if (mount(i, new_mount, NULL, mountflags, NULL) < 0) { if (mountflags & MS_MOVE) { log_error("Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); if (umount2(i, MNT_FORCE) < 0) log_warning("Failed to unmount %s: %m", i); } if (mountflags & MS_BIND) log_error("Failed to bind mount %s to %s: %m", i, new_mount); } }
static int parse_fstab(const char *prefix, bool initrd) { char *fstab_path; _cleanup_endmntent_ FILE *f; int r = 0; struct mntent *me; fstab_path = strappenda(strempty(prefix), "/etc/fstab"); f = setmntent(fstab_path, "r"); if (!f) { if (errno == ENOENT) return 0; log_error("Failed to open %s/etc/fstab: %m", strempty(prefix)); return -errno; } while ((me = getmntent(f))) { _cleanup_free_ char *where = NULL, *what = NULL; int k; if (initrd && !mount_in_initrd(me)) continue; what = fstab_node_to_udev_node(me->mnt_fsname); where = strjoin(strempty(prefix), me->mnt_dir, NULL); if (!what || !where) return log_oom(); if (is_path(where)) path_kill_slashes(where); log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type); if (streq(me->mnt_type, "swap")) k = add_swap(what, me); else { bool noauto, nofail, automount; const char *post; noauto = !!hasmntopt(me, "noauto"); nofail = !!hasmntopt(me, "nofail"); automount = hasmntopt(me, "comment=systemd.automount") || hasmntopt(me, "x-systemd.automount"); if (initrd) { post = SPECIAL_INITRD_FS_TARGET; } else if (mount_in_initrd(me)) { post = SPECIAL_INITRD_ROOT_FS_TARGET; } else if (mount_is_network(me)) { post = SPECIAL_REMOTE_FS_TARGET; } else { post = SPECIAL_LOCAL_FS_TARGET; } k = add_mount(what, where, me->mnt_type, me->mnt_opts, me->mnt_passno, noauto, nofail, automount, post, fstab_path); } if (k < 0) r = k; } return r; }