int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Network *network = userdata; assert(filename); assert(section); assert(lvalue); assert(rvalue); assert(data); if (streq(rvalue, "high")) network->router_preference = SD_NDISC_PREFERENCE_HIGH; else if (STR_IN_SET(rvalue, "medium", "normal", "default")) network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; else if (streq(rvalue, "low")) network->router_preference = SD_NDISC_PREFERENCE_LOW; else log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); return 0; }
static int parse_proc_cmdline_item(const char *key, const char *value) { int r; if (STR_IN_SET(key, "modules-load", "rd.modules-load") && value) { r = add_modules(value); if (r < 0) return r; } return 0; }
static int parse_proc_cmdline_item(const char *key, const char *value) { int r; if (STR_IN_SET(key, "luks", "rd.luks") && value) { r = parse_boolean(value); if (r < 0) log_warning("Failed to parse luks switch %s. Ignoring.", value); else arg_enabled = r; } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) { r = parse_boolean(value); if (r < 0) log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value); else arg_read_crypttab = r; } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) { if (strv_extend(&arg_disks, value) < 0) return log_oom(); } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) { if (strv_extend(&arg_options, value) < 0) return log_oom(); } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) { free(arg_keyfile); arg_keyfile = strdup(value); if (!arg_keyfile) return log_oom(); } else if (startswith(key, "luks.") || startswith(key, "rd.luks.")) log_warning("Unknown kernel switch %s. Ignoring.", key); return 0; }
static int clean_posix_mq(uid_t uid) { _cleanup_closedir_ DIR *dir = NULL; struct dirent *de; int ret = 0; dir = opendir("/dev/mqueue"); if (!dir) { if (errno == ENOENT) return 0; log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); return -errno; } FOREACH_DIRENT(de, dir, goto fail) { struct stat st; char fn[1+strlen(de->d_name)+1]; if (STR_IN_SET(de->d_name, "..", ".")) continue; if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { if (errno == ENOENT) continue; ret = log_warning_errno(errno, "Failed to stat() MQ segment %s: %m", de->d_name); continue; } if (st.st_uid != uid) continue; fn[0] = '/'; strcpy(fn+1, de->d_name); if (mq_unlink(fn) < 0) { if (errno == ENOENT) continue; ret = log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn); } } return ret; fail: log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); return -errno; }
bool network_is_online(void) { _cleanup_free_ char *state = NULL; int r; r = sd_network_get_operational_state(&state); if (r < 0) /* if we don't know anything, we consider the system online */ return true; if (STR_IN_SET(state, "routable", "degraded")) return true; return false; }
static void run_context_check_done(RunContext *c) { bool done = true; assert(c); if (c->match) done = done && (c->active_state && STR_IN_SET(c->active_state, "inactive", "failed")); if (c->forward) done = done && pty_forward_is_done(c->forward); if (done) sd_event_exit(c->event, EXIT_SUCCESS); }
static int parse_proc_cmdline_item(const char *key, const char *value) { int r; /* root= and roofstype= may occur more than once, the last * instance should take precedence. In the case of multiple * rootflags= the arguments should be concatenated */ if (STR_IN_SET(key, "fstab", "rd.fstab") && value) { r = parse_boolean(value); if (r < 0) log_warning("Failed to parse fstab switch %s. Ignoring.", value); else arg_fstab_enabled = r; } else if (streq(key, "root") && value) { free(arg_root_what); arg_root_what = strdup(value); if (!arg_root_what) return log_oom(); } else if (streq(key, "rootfstype") && value) { free(arg_root_fstype); arg_root_fstype = strdup(value); if (!arg_root_fstype) return log_oom(); } else if (streq(key, "rootflags") && value) { char *o; o = arg_root_options ? strjoin(arg_root_options, ",", value, NULL) : strdup(value); if (!o) return log_oom(); free(arg_root_options); arg_root_options = o; } else if (streq(key, "rw") && !value) arg_root_rw = true; else if (streq(key, "ro") && !value) arg_root_rw = false; return 0; }
static int set_limit(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; uint64_t limit; int r; r = acquire_bus(&bus); if (r < 0) return r; (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); if (STR_IN_SET(argv[argc-1], "-", "none", "infinity")) limit = (uint64_t) -1; else { r = parse_size(argv[argc-1], 1024, &limit); if (r < 0) return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]); } if (argc > 2) /* With two arguments changes the quota limit of the specified image */ r = sd_bus_call_method( bus, "org.freedesktop.portable1", "/org/freedesktop/portable1", "org.freedesktop.portable1.Manager", "SetImageLimit", &error, NULL, "st", argv[1], limit); else /* With one argument changes the pool quota limit */ r = sd_bus_call_method( bus, "org.freedesktop.portable1", "/org/freedesktop/portable1", "org.freedesktop.portable1.Manager", "SetPoolLimit", &error, NULL, "t", limit); if (r < 0) return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r)); return 0; }
/* Retrieve existing subsystems. This function is called in a new cgroup * namespace. */ static int get_process_controllers(Set **ret) { _cleanup_set_free_free_ Set *controllers = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(ret); controllers = set_new(&string_hash_ops); if (!controllers) return -ENOMEM; f = fopen("/proc/self/cgroup", "re"); if (!f) return errno == ENOENT ? -ESRCH : -errno; for (;;) { _cleanup_free_ char *line = NULL; char *e, *l; r = read_line(f, LONG_LINE_MAX, &line); if (r < 0) return r; if (r == 0) break; l = strchr(line, ':'); if (!l) continue; l++; e = strchr(l, ':'); if (!e) continue; *e = 0; if (STR_IN_SET(l, "", "name=systemd", "name=unified")) continue; r = set_put_strdup(controllers, l); if (r < 0) return r; } *ret = TAKE_PTR(controllers); return 0; }
bool is_locale_utf8(void) { const char *set; static int cached_answer = -1; /* Note that we default to 'true' here, since today UTF8 is * pretty much supported everywhere. */ if (cached_answer >= 0) goto out; if (!setlocale(LC_ALL, "")) { cached_answer = true; goto out; } set = nl_langinfo(CODESET); if (!set) { cached_answer = true; goto out; } if (streq(set, "UTF-8")) { cached_answer = true; goto out; } /* For LC_CTYPE=="C" return true, because CTYPE is effectly * unset and everything can do to UTF-8 nowadays. */ set = setlocale(LC_CTYPE, NULL); if (!set) { cached_answer = true; goto out; } /* Check result, but ignore the result if C was set * explicitly. */ cached_answer = STR_IN_SET(set, "C", "POSIX") && !getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"); out: return (bool) cached_answer; }
static int get_current_runlevel(Context *c) { static const struct { const int runlevel; const char *special; } table[] = { /* The first target of this list that is active or has * a job scheduled wins. We prefer runlevels 5 and 3 * here over the others, since these are the main * runlevels used on Fedora. It might make sense to * change the order on some distributions. */ { '5', SPECIAL_GRAPHICAL_TARGET }, { '3', SPECIAL_MULTI_USER_TARGET }, { '1', SPECIAL_RESCUE_TARGET }, }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; unsigned i; assert(c); for (i = 0; i < ELEMENTSOF(table); i++) { _cleanup_free_ char *state = NULL, *path = NULL; path = unit_dbus_path_from_name(table[i].special); if (!path) return log_oom(); r = sd_bus_get_property_string( c->bus, "org.freedesktop.systemd1", path, "org.freedesktop.systemd1.Unit", "ActiveState", &error, &state); if (r < 0) return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r)); if (STR_IN_SET(state, "active", "reloading")) return table[i].runlevel; } return 0; }
static int clean_posix_shm_internal(DIR *dir, uid_t uid) { struct dirent *de; int ret = 0, r; assert(dir); FOREACH_DIRENT(de, dir, goto fail) { struct stat st; if (STR_IN_SET(de->d_name, "..", ".")) continue; if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { if (errno == ENOENT) continue; log_warning("Failed to stat() POSIX shared memory segment %s: %m", de->d_name); ret = -errno; continue; } if (st.st_uid != uid) continue; if (S_ISDIR(st.st_mode)) { _cleanup_closedir_ DIR *kid; kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); if (!kid) { if (errno != ENOENT) { log_warning("Failed to enter shared memory directory %s: %m", de->d_name); ret = -errno; } } else { r = clean_posix_shm_internal(kid, uid); if (r < 0) ret = r; } if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) { if (errno == ENOENT) continue; log_warning("Failed to remove POSIX shared memory directory %s: %m", de->d_name); ret = -errno; } } else { if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { if (errno == ENOENT) continue; log_warning("Failed to remove POSIX shared memory segment %s: %m", de->d_name); ret = -errno; } } } return ret; fail: log_warning("Failed to read /dev/shm: %m"); return -errno; }
int pager_open(bool no_pager, bool jump_to_end) { _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; pid_t parent_pid; if (no_pager) return 0; if (pager_pid > 0) return 1; if (terminal_is_dumb()) return 0; pager = getenv("SYSTEMD_PAGER"); if (!pager) pager = getenv("PAGER"); /* If the pager is explicitly turned off, honour it */ if (pager && STR_IN_SET(pager, "", "cat")) return 0; /* Determine and cache number of columns before we spawn the * pager so that we get the value from the actual tty */ (void) columns(); if (pipe(fd) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); parent_pid = getpid(); pager_pid = fork(); if (pager_pid < 0) return log_error_errno(errno, "Failed to fork pager: %m"); /* In the child start the pager */ if (pager_pid == 0) { const char* less_opts, *less_charset; (void) reset_all_signal_handlers(); (void) reset_signal_mask(); (void) dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); /* Initialize a good set of less options */ less_opts = getenv("SYSTEMD_LESS"); if (!less_opts) less_opts = "FRSXMK"; if (jump_to_end) less_opts = strjoina(less_opts, " +G"); setenv("LESS", less_opts, 1); /* Initialize a good charset for less. This is * particularly important if we output UTF-8 * characters. */ less_charset = getenv("SYSTEMD_LESSCHARSET"); if (!less_charset && is_locale_utf8()) less_charset = "utf-8"; if (less_charset) setenv("LESSCHARSET", less_charset, 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) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); if (dup2(fd[1], STDERR_FILENO) < 0) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); return 1; }
static int process( const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration, Group **ret) { Group *g; int r, all_unified; assert(controller); assert(path); assert(a); all_unified = cg_all_unified(); if (all_unified < 0) return all_unified; g = hashmap_get(a, path); if (!g) { g = hashmap_get(b, path); if (!g) { g = new0(Group, 1); if (!g) return -ENOMEM; g->path = strdup(path); if (!g->path) { group_free(g); return -ENOMEM; } r = hashmap_put(a, g->path, g); if (r < 0) { group_free(g); return r; } } else { r = hashmap_move_one(a, b, path); if (r < 0) return r; g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { _cleanup_fclose_ FILE *f = NULL; pid_t pid; r = cg_enumerate_processes(controller, path, &f); if (r == -ENOENT) return 0; if (r < 0) return r; g->n_tasks = 0; while (cg_read_pid(f, &pid) > 0) { if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) continue; g->n_tasks++; } if (g->n_tasks > 0) g->n_tasks_valid = true; } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { if (is_root_cgroup(path)) { r = procfs_tasks_get_current(&g->n_tasks); if (r < 0) return r; } else { _cleanup_free_ char *p = NULL, *v = NULL; r = cg_get_path(controller, path, "pids.current", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &g->n_tasks); if (r < 0) return r; } if (g->n_tasks > 0) g->n_tasks_valid = true; } else if (STR_IN_SET(controller, "cpu", "cpuacct")) { _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; nsec_t timestamp; if (is_root_cgroup(path)) { r = procfs_cpu_get_usage(&new_usage); if (r < 0) return r; } else if (all_unified) { _cleanup_free_ char *val = NULL; if (!streq(controller, "cpu")) return 0; r = cg_get_keyed_attribute("cpu", path, "cpu.stat", STRV_MAKE("usage_usec"), &val); if (IN_SET(r, -ENOENT, -ENXIO)) return 0; if (r < 0) return r; r = safe_atou64(val, &new_usage); if (r < 0) return r; new_usage *= NSEC_PER_USEC; } else { if (!streq(controller, "cpuacct")) return 0; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); if (r < 0) return r; } timestamp = now_nsec(CLOCK_MONOTONIC); if (g->cpu_iteration == iteration - 1 && (nsec_t) new_usage > g->cpu_usage) { nsec_t x, y; x = timestamp - g->cpu_timestamp; if (x < 1) x = 1; y = (nsec_t) new_usage - g->cpu_usage; g->cpu_fraction = (double) y / (double) x; g->cpu_valid = true; } g->cpu_usage = (nsec_t) new_usage; g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { if (is_root_cgroup(path)) { r = procfs_memory_get_current(&g->memory); if (r < 0) return r; } else { _cleanup_free_ char *p = NULL, *v = NULL; if (all_unified) r = cg_get_path(controller, path, "memory.current", &p); else r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); if (r < 0) return r; } if (g->memory > 0) g->memory_valid = true; } else if ((streq(controller, "io") && all_unified) || (streq(controller, "blkio") && !all_unified)) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; uint64_t wr = 0, rd = 0; nsec_t timestamp; r = cg_get_path(controller, path, all_unified ? "io.stat" : "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); if (!f) { if (errno == ENOENT) return 0; return -errno; } for (;;) { char line[LINE_MAX], *l; uint64_t k, *q; if (!fgets(line, sizeof(line), f)) break; /* Trim and skip the device */ l = strstrip(line); l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); if (all_unified) { while (!isempty(l)) { if (sscanf(l, "rbytes=%" SCNu64, &k)) rd += k; else if (sscanf(l, "wbytes=%" SCNu64, &k)) wr += k; l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); } } else { if (first_word(l, "Read")) { l += 4; q = &rd; } else if (first_word(l, "Write")) { l += 5; q = ≀ } else continue; l += strspn(l, WHITESPACE); r = safe_atou64(l, &k); if (r < 0) continue; *q += k; } } timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; x = (uint64_t) (timestamp - g->io_timestamp); if (x < 1) x = 1; if (rd > g->io_input) yr = rd - g->io_input; else yr = 0; if (wr > g->io_output) yw = wr - g->io_output; else yw = 0; if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; } } g->io_input = rd; g->io_output = wr; g->io_timestamp = timestamp; g->io_iteration = iteration; } if (ret) *ret = g; return 0; }
static int start_transient_service( sd_bus *bus, char **argv, int *retval) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; _cleanup_free_ char *service = NULL, *pty_path = NULL; _cleanup_close_ int master = -1; int r; assert(bus); assert(argv); assert(retval); if (arg_pty) { if (arg_transport == BUS_TRANSPORT_LOCAL) { master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); if (master < 0) return log_error_errno(errno, "Failed to acquire pseudo tty: %m"); r = ptsname_malloc(master, &pty_path); if (r < 0) return log_error_errno(r, "Failed to determine tty name: %m"); if (unlockpt(master) < 0) return log_error_errno(errno, "Failed to unlock tty: %m"); } else if (arg_transport == BUS_TRANSPORT_MACHINE) { _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL; const char *s; r = sd_bus_default_system(&system_bus); if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); r = sd_bus_call_method(system_bus, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "OpenMachinePTY", &error, &pty_reply, "s", arg_host); if (r < 0) { log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); return r; } r = sd_bus_message_read(pty_reply, "hs", &master, &s); if (r < 0) return bus_log_parse_error(r); master = fcntl(master, F_DUPFD_CLOEXEC, 3); if (master < 0) return log_error_errno(errno, "Failed to duplicate master fd: %m"); pty_path = strdup(s); if (!pty_path) return log_oom(); } else assert_not_reached("Can't allocate tty via ssh"); } if (!arg_no_block) { r = bus_wait_for_jobs_new(bus, &w); if (r < 0) return log_error_errno(r, "Could not watch jobs: %m"); } if (arg_unit) { r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); } else { r = make_unit_name(bus, UNIT_SERVICE, &service); if (r < 0) return r; } r = sd_bus_message_new_method_call( bus, &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); if (r < 0) return bus_log_create_error(r); /* Name and mode */ r = sd_bus_message_append(m, "ss", service, "fail"); if (r < 0) return bus_log_create_error(r); /* Properties */ r = sd_bus_message_open_container(m, 'a', "(sv)"); if (r < 0) return bus_log_create_error(r); r = transient_service_set_properties(m, argv, pty_path); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); /* Auxiliary units */ r = sd_bus_message_append(m, "a(sa(sv))", 0); if (r < 0) return bus_log_create_error(r); polkit_agent_open_if_enabled(); r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) return log_error_errno(r, "Failed to start transient service unit: %s", bus_error_message(&error, r)); if (w) { const char *object; r = sd_bus_message_read(reply, "o", &object); if (r < 0) return bus_log_parse_error(r); r = bus_wait_for_jobs_one(w, object, arg_quiet); if (r < 0) return r; } if (!arg_quiet) log_info("Running as unit: %s", service); if (arg_wait || master >= 0) { _cleanup_(run_context_free) RunContext c = {}; c.bus = sd_bus_ref(bus); r = sd_event_default(&c.event); if (r < 0) return log_error_errno(r, "Failed to get event loop: %m"); if (master >= 0) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); (void) sd_event_add_signal(c.event, NULL, SIGINT, NULL, NULL); (void) sd_event_add_signal(c.event, NULL, SIGTERM, NULL, NULL); if (!arg_quiet) log_info("Press ^] three times within 1s to disconnect TTY."); r = pty_forward_new(c.event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c.forward); if (r < 0) return log_error_errno(r, "Failed to create PTY forwarder: %m"); pty_forward_set_handler(c.forward, pty_forward_handler, &c); } if (arg_wait) { _cleanup_free_ char *path = NULL; const char *mt; path = unit_dbus_path_from_name(service); if (!path) return log_oom(); mt = strjoina("type='signal'," "sender='org.freedesktop.systemd1'," "path='", path, "'," "interface='org.freedesktop.DBus.Properties'," "member='PropertiesChanged'"); r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c); if (r < 0) return log_error_errno(r, "Failed to add properties changed signal."); r = sd_bus_attach_event(bus, c.event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop."); } r = sd_event_loop(c.event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); if (c.forward) { char last_char = 0; r = pty_forward_get_last_char(c.forward, &last_char); if (r >= 0 && !arg_quiet && last_char != '\n') fputc('\n', stdout); } if (!arg_quiet) { if (!isempty(c.result)) log_info("Finished with result: %s", strna(c.result)); if (c.exit_code == CLD_EXITED) log_info("Main processes terminated with: code=%s/status=%i", sigchld_code_to_string(c.exit_code), c.exit_status); else if (c.exit_code > 0) log_info("Main processes terminated with: code=%s/status=%s", sigchld_code_to_string(c.exit_code), signal_to_string(c.exit_status)); if (c.inactive_enter_usec > 0 && c.inactive_enter_usec != USEC_INFINITY && c.inactive_exit_usec > 0 && c.inactive_exit_usec != USEC_INFINITY && c.inactive_enter_usec > c.inactive_exit_usec) { char ts[FORMAT_TIMESPAN_MAX]; log_info("Service runtime: %s", format_timespan(ts, sizeof(ts), c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC)); } if (c.cpu_usage_nsec > 0 && c.cpu_usage_nsec != NSEC_INFINITY) { char ts[FORMAT_TIMESPAN_MAX]; log_info("CPU time consumed: %s", format_timespan(ts, sizeof(ts), (c.cpu_usage_nsec + NSEC_PER_USEC - 1) / NSEC_PER_USEC, USEC_PER_MSEC)); } } /* Try to propagate the service's return value */ if (c.result && STR_IN_SET(c.result, "success", "exit-code") && c.exit_code == CLD_EXITED) *retval = c.exit_status; else *retval = EXIT_FAILURE; } return 0; }
int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { const char *eq, *field; int r; assert(m); assert(assignment); eq = strchr(assignment, '='); if (!eq) { log_error("Not an assignment: %s", assignment); return -EINVAL; } field = strndupa(assignment, eq - assignment); eq ++; if (streq(field, "CPUQuota")) { if (isempty(eq)) { r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec"); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_append(m, "v", "t", USEC_INFINITY); } else if (endswith(eq, "%")) { double percent; if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { log_error("CPU quota '%s' invalid.", eq); return -EINVAL; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec"); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100); } else { log_error("CPU quota needs to be in percent."); return -EINVAL; } if (r < 0) return bus_log_create_error(r); return 0; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); if (r < 0) return bus_log_create_error(r); if (STR_IN_SET(field, "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "SendSIGHUP", "SendSIGKILL")) { r = parse_boolean(eq); if (r < 0) { log_error("Failed to parse boolean assignment %s.", assignment); return -EINVAL; } r = sd_bus_message_append(m, "v", "b", r); } else if (streq(field, "MemoryLimit")) { off_t bytes; r = parse_size(eq, 1024, &bytes); if (r < 0) { log_error("Failed to parse bytes specification %s", assignment); return -EINVAL; } r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes); } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) { uint64_t u; r = safe_atou64(eq, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } r = sd_bus_message_append(m, "v", "t", u); } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode")) r = sd_bus_message_append(m, "v", "s", eq); else if (streq(field, "DeviceAllow")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(ss)", 0); else { const char *path, *rwm, *e; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); rwm = e+1; } else { path = eq; rwm = ""; } if (!path_startswith(path, "/dev")) { log_error("%s is not a device file in /dev.", path); return -EINVAL; } r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); } } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(st)", 0); else { const char *path, *bandwidth, *e; off_t bytes; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); bandwidth = e+1; } else { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } if (!path_startswith(path, "/dev")) { log_error("%s is not a device file in /dev.", path); return -EINVAL; } r = parse_size(bandwidth, 1000, &bytes); if (r < 0) { log_error("Failed to parse byte value %s.", bandwidth); return -EINVAL; } r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes); } } else if (streq(field, "BlockIODeviceWeight")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(st)", 0); else { const char *path, *weight, *e; uint64_t u; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); weight = e+1; } else { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } if (!path_startswith(path, "/dev")) { log_error("%s is not a device file in /dev.", path); return -EINVAL; } r = safe_atou64(weight, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, weight); return -EINVAL; } r = sd_bus_message_append(m, "v", "a(st)", path, u); } } else if (rlimit_from_string(field) >= 0) { uint64_t rl; if (streq(eq, "infinity")) rl = (uint64_t) -1; else { r = safe_atou64(eq, &rl); if (r < 0) { log_error("Invalid resource limit: %s", eq); return -EINVAL; } } r = sd_bus_message_append(m, "v", "t", rl); } else if (streq(field, "Nice")) { int32_t i; r = safe_atoi32(eq, &i); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } r = sd_bus_message_append(m, "v", "i", i); } else if (streq(field, "Environment")) { r = sd_bus_message_append(m, "v", "as", 1, eq); } else if (streq(field, "KillSignal")) { int sig; sig = signal_from_string_try_harder(eq); if (sig < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } r = sd_bus_message_append(m, "v", "i", sig); } else { log_error("Unknown assignment %s.", assignment); return -EINVAL; } if (r < 0) return bus_log_create_error(r); return 0; }
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, *e = NULL, *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL; _cleanup_fclose_ FILE *f = NULL; const char *dmname; bool noauto, nofail, tmp, swap, netdev; int r; assert(name); assert(device); noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); tmp = fstab_test_option(options, "tmp\0"); swap = fstab_test_option(options, "swap\0"); netdev = fstab_test_option(options, "_netdev\0"); if (tmp && swap) { log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); return -EINVAL; } name_escaped = specifier_escape(name); if (!name_escaped) return log_oom(); e = unit_name_escape(name); if (!e) return log_oom(); r = unit_name_build("systemd-cryptsetup", e, ".service", &n); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n); if (!p) return log_oom(); u = fstab_node_to_udev_node(device); if (!u) return log_oom(); u_escaped = specifier_escape(u); if (!u_escaped) return log_oom(); r = unit_name_from_path(u, ".device", &d); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); password_escaped = specifier_escape(password); if (!password_escaped) return log_oom(); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", p); fprintf(f, "# 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" "IgnoreOnIsolate=true\n" "After=%s\n", netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target"); if (!nofail) fprintf(f, "Before=%s\n", netdev ? "remote-cryptsetup.target" : "cryptsetup.target"); if (password) { if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) fputs_unlocked("After=systemd-random-seed.service\n", f); else if (!STR_IN_SET(password, "-", "none")) { _cleanup_free_ char *uu; uu = fstab_node_to_udev_node(password); if (!uu) return log_oom(); if (!path_equal(uu, "/dev/null")) { if (path_startswith(uu, "/dev/")) { _cleanup_free_ char *dd = NULL; r = unit_name_from_path(uu, ".device", &dd); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); } else fprintf(f, "RequiresMountsFor=%s\n", password_escaped); } } } if (path_startswith(u, "/dev/")) { fprintf(f, "BindsTo=%s\n" "After=%s\n" "Before=umount.target\n", d, d); if (swap) fputs_unlocked("Before=dev-mapper-%i.swap\n", f); } else fprintf(f, "RequiresMountsFor=%s\n", u_escaped); r = generator_write_timeouts(arg_dest, device, name, options, &filtered); if (r < 0) return r; filtered_escaped = specifier_escape(filtered); if (!filtered_escaped) return log_oom(); fprintf(f, "\n[Service]\n" "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ "KeyringMode=shared\n" /* make sure we can share cached keys among instances */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped), name_escaped); if (tmp) fprintf(f, "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", name_escaped); if (swap) fprintf(f, "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", name_escaped); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write file %s: %m", p); if (!noauto) { r = generator_add_symlink(arg_dest, d, "wants", n); if (r < 0) return r; r = generator_add_symlink(arg_dest, netdev ? "remote-cryptsetup.target" : "cryptsetup.target", nofail ? "wants" : "requires", n); if (r < 0) return r; } dmname = strjoina("dev-mapper-", e, ".device"); r = generator_add_symlink(arg_dest, dmname, "requires", n); if (r < 0) return r; if (!noauto && !nofail) { r = write_drop_in(arg_dest, dmname, 90, "device-timeout", "# Automatically generated by systemd-cryptsetup-generator \n\n" "[Unit]\nJobTimeoutSec=0"); if (r < 0) return log_error_errno(r, "Failed to write device drop-in: %m"); } return 0; }
static void test_str_in_set(void) { assert_se(STR_IN_SET("x", "x", "y", "z")); assert_se(!STR_IN_SET("X", "x", "y", "z")); assert_se(!STR_IN_SET("", "x", "y", "z")); assert_se(STR_IN_SET("x", "w", "x")); }
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, *filtered = NULL; _cleanup_fclose_ FILE *f = NULL; bool noauto, nofail, tmp, swap; char *from; int r; assert(name); assert(device); noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); tmp = fstab_test_option(options, "tmp\0"); swap = fstab_test_option(options, "swap\0"); 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(); r = unit_name_build("systemd-cryptsetup", e, ".service", &n); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); p = strjoin(arg_dest, "/", n, NULL); if (!p) return log_oom(); u = fstab_node_to_udev_node(device); if (!u) return log_oom(); r = unit_name_from_path(u, ".device", &d); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", p); 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=cryptsetup-pre.target\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 (!path_equal(uu, "/dev/null")) { if (is_device_path(uu)) { _cleanup_free_ char *dd = NULL; r = unit_name_from_path(uu, ".device", &dd); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); 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); r = generator_write_timeouts(arg_dest, device, name, options, &filtered); if (r < 0) return r; 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(filtered), 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); r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write file %s: %m", p); from = strjoina("../", 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) return log_error_errno(errno, "Failed to create symlink %s: %m", to); 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) return log_error_errno(errno, "Failed to create symlink %s: %m", to); } 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) return log_error_errno(errno, "Failed to create symlink %s: %m", to); if (!noauto && !nofail) { _cleanup_free_ char *dmname; dmname = strjoin("dev-mapper-", e, ".device", NULL); if (!dmname) return log_oom(); r = write_drop_in(arg_dest, dmname, 90, "device-timeout", "# Automatically generated by systemd-cryptsetup-generator \n\n" "[Unit]\nJobTimeoutSec=0"); if (r < 0) return log_error_errno(r, "Failed to write device drop-in: %m"); } return 0; }