static int write_data_x11(void) { FILE *f; char *temp_path; int r; if (isempty(state.x11_layout) && isempty(state.x11_model) && isempty(state.x11_variant) && isempty(state.x11_options)) { if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) return errno == ENOENT ? 0 : -errno; return 0; } mkdir_p_label("/etc/X11/xorg.conf.d", 0755); r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); if (r < 0) return r; fchmod(fileno(f), 0644); fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" "# manually too freely.\n" "Section \"InputClass\"\n" " Identifier \"system-keyboard\"\n" " MatchIsKeyboard \"on\"\n", f); if (!isempty(state.x11_layout)) fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout); if (!isempty(state.x11_model)) fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model); if (!isempty(state.x11_variant)) fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant); if (!isempty(state.x11_options)) fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options); fputs("EndSection\n", f); fflush(f); if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { r = -errno; unlink("/etc/X11/xorg.conf.d/00-keyboard.conf"); unlink(temp_path); } else r = 0; fclose(f); free(temp_path); return r; }
static void automount_enter_running(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Unit *trigger; struct stat st; int r; assert(a); /* If the user masked our unit in the meantime, fail */ if (UNIT(a)->load_state != UNIT_LOADED) { log_unit_error(UNIT(a), "Suppressing automount event since unit is no longer loaded."); goto fail; } /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled."); automount_send_ready(a, a->tokens, -EHOSTDOWN); automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); return; } mkdir_p_label(a->where, a->directory_mode); /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); goto fail; } /* The mount unit may have been explicitly started before we got the * autofs request. Ack it to unblock anything waiting on the mount point. */ if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) { log_unit_info(UNIT(a), "Automount point already active?"); automount_send_ready(a, a->tokens, 0); return; } trigger = UNIT_TRIGGER(UNIT(a)); if (!trigger) { log_unit_error(UNIT(a), "Unit to trigger vanished."); goto fail; } r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); goto fail; } automount_set_state(a, AUTOMOUNT_RUNNING); return; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static int mount_one(const MountPoint *p, bool relabel) { int r; assert(p); if (p->condition_fn && !p->condition_fn()) return 0; /* Relabel first, just in case */ if (relabel) (void) label_fix(p->where, true, true); r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW); if (r < 0 && r != -ENOENT) { log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where); return (p->mode & MNT_FATAL) ? r : 0; } if (r > 0) return 0; /* Skip securityfs in a container */ if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0) return 0; /* The access mode here doesn't really matter too much, since * the mounted file system will take precedence anyway. */ if (relabel) (void) mkdir_p_label(p->where, 0755); else (void) mkdir_p(p->where, 0755); log_debug("Mounting %s to %s of type %s with options %s.", p->what, p->where, p->type, strna(p->options)); if (mount(p->what, p->where, p->type, p->flags, p->options) < 0) { log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where); return (p->mode & MNT_FATAL) ? -errno : 0; } /* Relabel again, since we now mounted something fresh here */ if (relabel) (void) label_fix(p->where, false, false); return 1; }
static int divert_coredump(void) { FILE *f; int r; log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/."); mkdir_p_label("/var/lib/systemd/coredump", 0755); f = fopen("/var/lib/systemd/coredump/core.systemd-journald", "we"); if (!f) { log_error("Failed to create coredump file: %m"); return -errno; } for (;;) { uint8_t buffer[4096]; size_t l, q; l = fread(buffer, 1, sizeof(buffer), stdin); if (l <= 0) { if (ferror(f)) { log_error("Failed to read coredump: %m"); r = -errno; goto finish; } r = 0; break; } q = fwrite(buffer, 1, l, f); if (q != l) { log_error("Failed to write coredump: %m"); r = -errno; goto finish; } } fflush(f); if (ferror(f)) { log_error("Failed to write coredump: %m"); r = -errno; } finish: fclose(f); return r; }
static int prepare_new_root(void) { static const char dirs[] = "/run/initramfs/oldroot\0" "/run/initramfs/proc\0" "/run/initramfs/sys\0" "/run/initramfs/dev\0" "/run/initramfs/run\0"; const char *dir; if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) { log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m"); return -errno; } if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) { log_error("Failed to make /run/initramfs private mount: %m"); return -errno; } NULSTR_FOREACH(dir, dirs) if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) { log_error("Failed to mkdir %s: %m", dir); return -errno; } if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) { log_error("Failed to mount bind /sys on /run/initramfs/sys: %m"); return -errno; } if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) { log_error("Failed to mount bind /proc on /run/initramfs/proc: %m"); return -errno; } if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) { log_error("Failed to mount bind /dev on /run/initramfs/dev: %m"); return -errno; } if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) { log_error("Failed to mount bind /run on /run/initramfs/run: %m"); return -errno; } return 0; }
static void automount_enter_runnning(Automount *a) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; int r; assert(a); /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { log_debug_unit(UNIT(a)->id, "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); automount_send_ready(a, -EHOSTDOWN); return; } mkdir_p_label(a->where, a->directory_mode); /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { log_warning_unit(UNIT(a)->id, "%s failed to stat automount point: %m", UNIT(a)->id); goto fail; } if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) log_info_unit(UNIT(a)->id, "%s's automount point already active?", UNIT(a)->id); else { r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); if (r < 0) { log_warning_unit(UNIT(a)->id, "%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error_message(&error, r)); goto fail; } } automount_set_state(a, AUTOMOUNT_RUNNING); return; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
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); } }
int x11_write_data(Context *c) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *temp_path = NULL; struct stat st; int r; if (isempty(c->x11_layout) && isempty(c->x11_model) && isempty(c->x11_variant) && isempty(c->x11_options)) { if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) return errno == ENOENT ? 0 : -errno; c->vc_mtime = USEC_INFINITY; return 0; } mkdir_p_label("/etc/X11/xorg.conf.d", 0755); r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); if (r < 0) return r; (void) __fsetlocking(f, FSETLOCKING_BYCALLER); (void) fchmod(fileno(f), 0644); fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n" "# probably wise not to edit this file manually. Use localectl(1) to\n" "# instruct systemd-localed to update it.\n" "Section \"InputClass\"\n" " Identifier \"system-keyboard\"\n" " MatchIsKeyboard \"on\"\n", f); if (!isempty(c->x11_layout)) fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout); if (!isempty(c->x11_model)) fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model); if (!isempty(c->x11_variant)) fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant); if (!isempty(c->x11_options)) fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options); fputs("EndSection\n", f); r = fflush_sync_and_check(f); if (r < 0) goto fail; if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { r = -errno; goto fail; } if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0) c->x11_mtime = timespec_load(&st.st_mtim); return 0; fail: if (temp_path) (void) unlink(temp_path); return r; }
static int x11_write_data(Context *c) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *temp_path = NULL; int r; if (isempty(c->x11_layout) && isempty(c->x11_model) && isempty(c->x11_variant) && isempty(c->x11_options)) { if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) return errno == ENOENT ? 0 : -errno; return 0; } mkdir_p_label("/etc/X11/xorg.conf.d", 0755); r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); if (r < 0) return r; fchmod(fileno(f), 0644); fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" "# manually too freely.\n" "Section \"InputClass\"\n" " Identifier \"system-keyboard\"\n" " MatchIsKeyboard \"on\"\n", f); if (!isempty(c->x11_layout)) fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout); if (!isempty(c->x11_model)) fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model); if (!isempty(c->x11_variant)) fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant); if (!isempty(c->x11_options)) fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options); fputs("EndSection\n", f); r = fflush_and_check(f); if (r < 0) goto fail; if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { r = -errno; goto fail; } return 0; fail: (void) unlink("/etc/X11/xorg.conf.d/00-keyboard.conf"); if (temp_path) (void) unlink(temp_path); return r; }
static int save_external_coredump( const char *info[_INFO_LEN], uid_t uid, char **ret_filename, int *ret_fd, uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; struct stat st; int r; assert(info); assert(ret_filename); assert(ret_fd); assert(ret_size); r = make_filename(info, &fn); if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); r = tempfn_random(fn, NULL, &tmp); if (r < 0) return log_error_errno(r, "Failed to determine temporary file name: %m"); mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); if (fd < 0) return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false); if (r == -EFBIG) { log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; } else if (r < 0) { log_error_errno(r, "Failed to dump coredump to file: %m"); goto fail; } if (fstat(fd, &st) < 0) { log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { log_error_errno(errno, "Failed to seek on %s: %m", tmp); goto fail; } #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ if (maybe_remove_external_coredump(NULL, st.st_size) == 0 && arg_compress) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; fn_compressed = strappend(fn, COMPRESSED_EXT); if (!fn_compressed) { log_oom(); goto uncompressed; } r = tempfn_random(fn_compressed, NULL, &tmp_compressed); if (r < 0) { log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed); goto uncompressed; } fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); if (fd_compressed < 0) { log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { log_error_errno(r, "Failed to compress %s: %m", tmp_compressed); goto fail_compressed; } r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid); if (r < 0) goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_fd = fd; /* uncompressed */ *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = -1; return 0; fail_compressed: unlink_noerrno(tmp_compressed); } uncompressed: #endif r = fix_permissions(fd, tmp, fn, info, uid); if (r < 0) goto fail; *ret_filename = fn; *ret_fd = fd; *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; return 0; fail: unlink_noerrno(tmp); return r; }
static void automount_enter_waiting(Automount *a) { _cleanup_close_ int ioctl_fd = -1; int p[2] = { -1, -1 }; char name[sizeof("systemd-")-1 + DECIMAL_STR_MAX(pid_t) + 1]; char options[sizeof("fd=,pgrp=,minproto=5,maxproto=5,direct")-1 + DECIMAL_STR_MAX(int) + DECIMAL_STR_MAX(gid_t) + 1]; bool mounted = false; int r, dev_autofs_fd; struct stat st; assert(a); assert(a->pipe_fd < 0); assert(a->where); set_clear(a->tokens); r = unit_fail_if_symlink(UNIT(a), a->where); if (r < 0) goto fail; (void) mkdir_p_label(a->where, 0555); unit_warn_if_dir_nonempty(UNIT(a), a->where); dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); if (dev_autofs_fd < 0) { r = dev_autofs_fd; goto fail; } if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { r = -errno; goto fail; } xsprintf(options, "fd=%i,pgrp="PID_FMT",minproto=5,maxproto=5,direct", p[1], getpgrp()); xsprintf(name, "systemd-"PID_FMT, getpid()); if (mount(name, a->where, "autofs", 0, options) < 0) { r = -errno; goto fail; } mounted = true; p[1] = safe_close(p[1]); if (stat(a->where, &st) < 0) { r = -errno; goto fail; } ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } r = autofs_protocol(dev_autofs_fd, ioctl_fd); if (r < 0) goto fail; r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec); if (r < 0) goto fail; /* Autofs fun fact: * * Unless we close the ioctl fd here, for some weird reason * the direct mount will not receive events from the * kernel. */ r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a); if (r < 0) goto fail; (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); a->pipe_fd = p[0]; a->dev_id = st.st_dev; automount_set_state(a, AUTOMOUNT_WAITING); return; fail: log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); safe_close_pair(p); if (mounted) { r = repeat_unmount(a->where, MNT_DETACH); if (r < 0) log_error_errno(r, "Failed to unmount, ignoring: %m"); } automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static int save_external_coredump( const char *context[_CONTEXT_MAX], int input_fd, char **ret_filename, int *ret_node_fd, int *ret_data_fd, uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; uint64_t rlimit, max_size; struct stat st; uid_t uid; int r; assert(context); assert(ret_filename); assert(ret_node_fd); assert(ret_data_fd); assert(ret_size); r = parse_uid(context[CONTEXT_UID], &uid); if (r < 0) return log_error_errno(r, "Failed to parse UID: %m"); r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); if (r < 0) return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); if (rlimit <= 0) { /* Is coredumping disabled? Then don't bother saving/processing the coredump */ log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); return -EBADSLT; } /* Never store more than the process configured, or than we actually shall keep or process */ max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max)); r = make_filename(context, &fn); if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); if (r == -EFBIG) { log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } else if (r < 0) { log_error_errno(r, "Failed to dump coredump to file: %m"); goto fail; } if (fstat(fd, &st) < 0) { log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); goto fail; } #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ if (maybe_remove_external_coredump(NULL, st.st_size) == 0 && arg_compress) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; fn_compressed = strappend(fn, COMPRESSED_EXT); if (!fn_compressed) { log_oom(); goto uncompressed; } fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); if (fd_compressed < 0) { log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); goto fail_compressed; } r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); if (r < 0) goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ if (tmp) unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_node_fd = fd_compressed; /* compressed */ *ret_data_fd = fd; /* uncompressed */ *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = fd_compressed = -1; return 0; fail_compressed: if (tmp_compressed) (void) unlink(tmp_compressed); } uncompressed: #endif r = fix_permissions(fd, tmp, fn, context, uid); if (r < 0) goto fail; *ret_filename = fn; *ret_data_fd = fd; *ret_node_fd = -1; *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; return 0; fail: if (tmp) (void) unlink(tmp); return r; }
static int save_external_coredump( const char *context[_CONTEXT_MAX], int input_fd, char **ret_filename, int *ret_node_fd, int *ret_data_fd, uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; uint64_t rlimit, max_size; struct stat st; uid_t uid; int r; assert(context); assert(ret_filename); assert(ret_node_fd); assert(ret_data_fd); assert(ret_size); r = parse_uid(context[CONTEXT_UID], &uid); if (r < 0) return log_error_errno(r, "Failed to parse UID: %m"); r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); if (r < 0) return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); if (rlimit < page_size()) { /* Is coredumping disabled? Then don't bother saving/processing the coredump. * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */ log_info("Resource limits disable core dumping for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); return -EBADSLT; } /* Never store more than the process configured, or than we actually shall keep or process */ max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max())); r = make_filename(context, &fn); if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); if (r < 0) { log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; } else if (r == 1) log_struct(LOG_INFO, LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size), "SIZE_LIMIT=%zu", max_size, LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE), NULL); if (fstat(fd, &st) < 0) { log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp)); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); goto fail; } #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; fn_compressed = strappend(fn, COMPRESSED_EXT); if (!fn_compressed) { log_oom(); goto uncompressed; } fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); if (fd_compressed < 0) { log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); goto fail_compressed; } r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); if (r < 0) goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ if (tmp) unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_node_fd = fd_compressed; /* compressed */ *ret_data_fd = fd; /* uncompressed */ *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = fd_compressed = -1; return 0; fail_compressed: if (tmp_compressed) (void) unlink(tmp_compressed); } uncompressed: #endif r = fix_permissions(fd, tmp, fn, context, uid); if (r < 0) goto fail; *ret_filename = fn; *ret_data_fd = fd; *ret_node_fd = -1; *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; return 0; fail: if (tmp) (void) unlink(tmp); return r; }
static int setup_machine_raw(uint64_t size, sd_bus_error *error) { _cleanup_free_ char *tmp = NULL; _cleanup_close_ int fd = -1; struct statvfs ss; pid_t pid = 0; siginfo_t si; int r; /* We want to be able to make use of btrfs-specific file * system features, in particular subvolumes, reflinks and * quota. Hence, if we detect that /var/lib/machines.raw is * not located on btrfs, let's create a loopback file, place a * btrfs file system into it, and mount it to * /var/lib/machines. */ fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd >= 0) { r = fd; fd = -1; return r; } if (errno != ENOENT) return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m"); r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp); if (r < 0) return r; (void) mkdir_p_label("/var/lib", 0755); fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600); if (fd < 0) return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m"); if (fstatvfs(fd, &ss) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m"); goto fail; } if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines."); goto fail; } if (ftruncate(fd, size) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m"); goto fail; } pid = fork(); if (pid < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m"); goto fail; } if (pid == 0) { /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); fd = safe_close(fd); execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); if (errno == ENOENT) return 99; _exit(EXIT_FAILURE); } r = wait_for_terminate(pid, &si); if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); goto fail; } pid = 0; if (si.si_code != CLD_EXITED) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally."); goto fail; } if (si.si_status == 99) { r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); goto fail; } if (si.si_status != 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status); goto fail; } r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw"); if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m"); goto fail; } r = fd; fd = -1; return r; fail: unlink_noerrno(tmp); if (pid > 1) kill_and_sigcont(pid, SIGKILL); return r; }
int setup_machine_directory(uint64_t size, sd_bus_error *error) { _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT; struct loop_info64 info = { .lo_flags = LO_FLAGS_AUTOCLEAR, }; _cleanup_close_ int fd = -1, control = -1, loop = -1; _cleanup_free_ char* loopdev = NULL; char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL; bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false; char buf[FORMAT_BYTES_MAX]; int r, nr = -1; /* btrfs cannot handle file systems < 16M, hence use this as minimum */ if (size == (uint64_t) -1) size = VAR_LIB_MACHINES_SIZE_START; else if (size < 16*1024*1024) size = 16*1024*1024; /* Make sure we only set the directory up once at a time */ r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file); if (r < 0) return r; r = check_btrfs(); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m"); if (r > 0) { (void) btrfs_subvol_make_label("/var/lib/machines"); r = btrfs_quota_enable("/var/lib/machines", true); if (r < 0) log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m"); r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true); if (r < 0) log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m"); return 1; } if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) { log_debug("/var/lib/machines is already a mount point, not creating loopback file for it."); return 0; } r = dir_is_populated("/var/lib/machines"); if (r < 0 && r != -ENOENT) return r; if (r > 0) { log_debug("/var/log/machines is already populated, not creating loopback file for it."); return 0; } r = mkfs_exists("btrfs"); if (r == -ENOENT) { log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines."); return 0; } if (r < 0) return r; fd = setup_machine_raw(size, error); if (fd < 0) return fd; control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); if (control < 0) return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m"); nr = ioctl(control, LOOP_CTL_GET_FREE); if (nr < 0) return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m"); if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) { r = -ENOMEM; goto fail; } loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK); if (loop < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m"); goto fail; } if (ioctl(loop, LOOP_SET_FD, fd) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m"); goto fail; } if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m"); goto fail; } /* We need to make sure the new /var/lib/machines directory * has an access mode of 0700 at the time it is first made * available. mkfs will create it with 0755 however. Hence, * let's mount the directory into an inaccessible directory * below /tmp first, fix the access mode, and move it to the * public place then. */ if (!mkdtemp(tmpdir)) { r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m"); goto fail; } tmpdir_made = true; mntdir = strjoina(tmpdir, "/mnt"); if (mkdir(mntdir, 0700) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m"); goto fail; } mntdir_made = true; if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m"); goto fail; } mntdir_mounted = true; r = btrfs_quota_enable(mntdir, true); if (r < 0) log_warning_errno(r, "Failed to enable quota, ignoring: %m"); r = btrfs_subvol_auto_qgroup(mntdir, 0, true); if (r < 0) log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m"); if (chmod(mntdir, 0700) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m"); goto fail; } (void) mkdir_p_label("/var/lib/machines", 0700); if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m"); goto fail; } (void) syncfs(fd); log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size)); (void) umount2(mntdir, MNT_DETACH); (void) rmdir(mntdir); (void) rmdir(tmpdir); return 1; fail: if (mntdir_mounted) (void) umount2(mntdir, MNT_DETACH); if (mntdir_made) (void) rmdir(mntdir); if (tmpdir_made) (void) rmdir(tmpdir); if (loop >= 0) { (void) ioctl(loop, LOOP_CLR_FD); loop = safe_close(loop); } if (control >= 0 && nr >= 0) (void) ioctl(control, LOOP_CTL_REMOVE, nr); return r; }
static void automount_enter_waiting(Automount *a) { int p[2] = { -1, -1 }; char name[32], options[128]; bool mounted = false; int r, ioctl_fd = -1, dev_autofs_fd; struct stat st; assert(a); assert(a->pipe_fd < 0); assert(a->where); if (a->tokens) set_clear(a->tokens); dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); if (dev_autofs_fd < 0) { r = dev_autofs_fd; goto fail; } /* We knowingly ignore the results of this call */ mkdir_p_label(a->where, 0555); warn_if_dir_nonempty(a->meta.id, a->where); if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { r = -errno; goto fail; } snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp()); char_array_0(options); snprintf(name, sizeof(name), "systemd-%u", (unsigned) getpid()); char_array_0(name); if (mount(name, a->where, "autofs", 0, options) < 0) { r = -errno; goto fail; } mounted = true; close_nointr_nofail(p[1]); p[1] = -1; if (stat(a->where, &st) < 0) { r = -errno; goto fail; } ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } r = autofs_protocol(dev_autofs_fd, ioctl_fd); if (r < 0) goto fail; r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300); if (r < 0) goto fail; /* Autofs fun fact: * * Unless we close the ioctl fd here, for some weird reason * the direct mount will not receive events from the * kernel. */ close_nointr_nofail(ioctl_fd); ioctl_fd = -1; r = sd_event_add_io(UNIT(a)->manager->event, p[0], EPOLLIN, automount_dispatch_io, a, &a->pipe_event_source); if (r < 0) goto fail; a->pipe_fd = p[0]; a->dev_id = st.st_dev; automount_set_state(a, AUTOMOUNT_WAITING); return; fail: assert_se(close_pipe(p) == 0); if (ioctl_fd >= 0) close_nointr_nofail(ioctl_fd); if (mounted) repeat_unmount(a->where); log_error_unit(UNIT(a)->id, "Failed to initialize automounter: %s", strerror(-r)); automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }