int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { const char *p; char *mp; int r; static __thread bool good = false; assert(controller); assert(fs); /* This is a very minimal lookup from controller names to * paths. Since we have mounted most hierarchies ourselves * should be kinda safe, but eventually we might want to * extend this to have a fallback to actually check * /proc/mounts. Might need caching then. */ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) p = "systemd"; else if (startswith(controller, "name=")) p = controller + 5; else p = controller; if (asprintf(&mp, "/sys/fs/cgroup/%s", p) < 0) return -ENOMEM; if (!good) { if ((r = path_is_mount_point(mp)) <= 0) { free(mp); return r < 0 ? r : -ENOENT; } /* Cache this to save a few stat()s */ good = true; } if (path && suffix) r = asprintf(fs, "%s/%s/%s", mp, path, suffix); else if (path) r = asprintf(fs, "%s/%s", mp, path); else if (suffix) r = asprintf(fs, "%s/%s", mp, suffix); else { path_kill_slashes(mp); *fs = mp; return 0; } free(mp); path_kill_slashes(*fs); return r < 0 ? -ENOMEM : 0; }
static int join_path(const char *controller, const char *path, const char *suffix, char **fs) { char *t = NULL; if (!(controller || path)) return -EINVAL; if (controller) { if (path && suffix) t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); else if (path) t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL); else if (suffix) t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL); else t = strjoin("/sys/fs/cgroup/", controller, NULL); } else { if (path && suffix) t = strjoin(path, "/", suffix, NULL); else if (path) t = strdup(path); } if (!t) return -ENOMEM; path_kill_slashes(t); *fs = t; return 0; }
static int refresh_one( const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration, unsigned depth) { DIR *d = NULL; int r; assert(controller); assert(path); assert(a); if (depth > arg_depth) return 0; r = process(controller, path, a, b, iteration); if (r < 0) return r; r = cg_enumerate_subgroups(controller, path, &d); if (r < 0) { if (r == -ENOENT) return 0; return r; } for (;;) { char *fn, *p; r = cg_read_subgroup(d, &fn); if (r <= 0) goto finish; p = strjoin(path, "/", fn, NULL); free(fn); if (!p) { r = -ENOMEM; goto finish; } path_kill_slashes(p); r = refresh_one(controller, p, a, b, iteration, depth + 1); free(p); if (r < 0) goto finish; } finish: if (d) closedir(d); return r; }
static int join_path(const char *controller, const char *path, const char *suffix, char **fs) { char *t = NULL; if (!isempty(controller)) { if (!isempty(path) && !isempty(suffix)) t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); else if (!isempty(path)) t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL); else if (!isempty(suffix)) t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL); else t = strappend("/sys/fs/cgroup/", controller); } else { if (!isempty(path) && !isempty(suffix)) t = strjoin(path, "/", suffix, NULL); else if (!isempty(path)) t = strdup(path); else return -EINVAL; } if (!t) return -ENOMEM; path_kill_slashes(t); *fs = t; return 0; }
static int enumerate_dir(Hashmap *top, Hashmap *bottom, const char *path) { _cleanup_closedir_ DIR *d; assert(top); assert(bottom); assert(path); d = opendir(path); if (!d) { if (errno == ENOENT) return 0; log_error("Failed to enumerate %s: %m", path); return -errno; } for (;;) { struct dirent *de; union dirent_storage buf; int k; char *p; k = readdir_r(d, &buf.de, &de); if (k != 0) return -k; if (!de) break; if (!dirent_is_file(de)) continue; p = strjoin(path, "/", de->d_name, NULL); if (!p) return -ENOMEM; path_kill_slashes(p); k = hashmap_put(top, path_get_file_name(p), p); if (k >= 0) { p = strdup(p); if (!p) return -ENOMEM; } else if (k != -EEXIST) { free(p); return k; } free(hashmap_remove(bottom, path_get_file_name(p))); k = hashmap_put(bottom, path_get_file_name(p), p); if (k < 0) { free(p); return k; } } return 0; }
static int parse_fstab(void) { FILE *f; int r = 0; struct mntent *me; errno = 0; f = setmntent("/etc/fstab", "r"); if (!f) { if (errno == ENOENT) return 0; log_error("Failed to open /etc/fstab: %m"); return -errno; } while ((me = getmntent(f))) { char _cleanup_free_ *where = NULL, *what = NULL; int k; what = fstab_node_to_udev_node(me->mnt_fsname); where = strdup(me->mnt_dir); if (!what || !where) { r = log_oom(); goto finish; } 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, isbind, isnetwork; noauto = !!hasmntopt(me, "noauto"); nofail = !!hasmntopt(me, "nofail"); automount = hasmntopt(me, "comment=systemd.automount") || hasmntopt(me, "x-systemd.automount"); isbind = mount_is_bind(me); isnetwork = mount_is_network(me); k = add_mount(what, where, me->mnt_type, me->mnt_opts, me->mnt_passno, false, noauto, nofail, automount, isbind, isnetwork, "/etc/fstab"); } if (k < 0) r = k; } finish: endmntent(f); return r; }
static int parse_fstab(void) { FILE *f; int r = 0; struct mntent *me; errno = 0; f = setmntent("/etc/fstab", "r"); if (!f) { if (errno == ENOENT) return 0; log_error("Failed to open /etc/fstab: %m"); return -errno; } while ((me = getmntent(f))) { char *where, *what; int k; what = fstab_node_to_udev_node(me->mnt_fsname); if (!what) { r = log_oom(); goto finish; } where = strdup(me->mnt_dir); if (!where) { r = log_oom(); free(what); goto finish; } 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 k = add_mount(what, where, me); free(what); free(where); if (k < 0) r = k; } finish: endmntent(f); return r; }
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { const char *p; char *t; static __thread bool good = false; assert(controller); assert(fs); if (_unlikely_(!good)) { int r; r = path_is_mount_point("/sys/fs/cgroup", false); if (r <= 0) return r < 0 ? r : -ENOENT; /* Cache this to save a few stat()s */ good = true; } if (isempty(controller)) return -EINVAL; /* This is a very minimal lookup from controller names to * paths. Since we have mounted most hierarchies ourselves * should be kinda safe, but eventually we might want to * extend this to have a fallback to actually check * /proc/mounts. Might need caching then. */ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) p = "systemd"; else if (startswith(controller, "name=")) p = controller + 5; else p = controller; if (path && suffix) t = join("/sys/fs/cgroup/", p, "/", path, "/", suffix, NULL); else if (path) t = join("/sys/fs/cgroup/", p, "/", path, NULL); else if (suffix) t = join("/sys/fs/cgroup/", p, "/", suffix, NULL); else t = join("/sys/fs/cgroup/", p, NULL); if (!t) return -ENOMEM; path_kill_slashes(t); *fs = t; return 0; }
static int automount_set_where(Automount *a) { int r; assert(a); if (a->where) return 0; r = unit_name_to_path(UNIT(a)->id, &a->where); if (r < 0) return r; path_kill_slashes(a->where); return 1; }
static int image_new( ImageType t, const char *pretty, const char *path, const char *filename, bool read_only, usec_t crtime, usec_t mtime, Image **ret) { _cleanup_(image_unrefp) Image *i = NULL; assert(t >= 0); assert(t < _IMAGE_TYPE_MAX); assert(pretty); assert(filename); assert(ret); i = new0(Image, 1); if (!i) return -ENOMEM; i->type = t; i->read_only = read_only; i->crtime = crtime; i->mtime = mtime; i->usage = i->usage_exclusive = (uint64_t) -1; i->limit = i->limit_exclusive = (uint64_t) -1; i->name = strdup(pretty); if (!i->name) return -ENOMEM; if (path) i->path = strjoin(path, "/", filename); else i->path = strdup(filename); if (!i->path) return -ENOMEM; path_kill_slashes(i->path); *ret = i; i = NULL; return 0; }
int config_parse_path(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) { char **s = data; char *n; int offset; assert(filename); assert(lvalue); assert(rvalue); assert(data); if (!utf8_is_valid(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); return 0; } offset = rvalue[0] == '-' && (streq(lvalue, "InaccessibleDirectories") || streq(lvalue, "ReadOnlyDirectories")); if (!path_is_absolute(rvalue + offset)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue); return 0; } n = strdup(rvalue); if (!n) return log_oom(); path_kill_slashes(n); free(*s); *s = n; return 0; }
static int automount_load(Unit *u) { Automount *a = AUTOMOUNT(u); int r; assert(u); assert(u->load_state == UNIT_STUB); /* Load a .automount file */ r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; if (u->load_state == UNIT_LOADED) { Unit *x; if (!a->where) { a->where = unit_name_to_path(u->id); if (!a->where) return -ENOMEM; } path_kill_slashes(a->where); r = unit_load_related_unit(u, ".mount", &x); if (r < 0) return r; r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); if (r < 0) return r; r = automount_add_mount_links(a); if (r < 0) return r; if (UNIT(a)->default_dependencies) { r = automount_add_default_dependencies(a); if (r < 0) return r; } } return automount_verify(a); }
int config_parse_path(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { char **s = data; char *n; assert(filename); assert(lvalue); assert(rvalue); assert(data); if (!utf8_is_valid(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); return 0; } if (!path_is_absolute(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue); return 0; } n = strdup(rvalue); if (!n) return log_oom(); path_kill_slashes(n); free(*s); *s = n; return 0; }
int path_make_relative(const char *from_dir, const char *to_path, char **_r) { char *r, *p; unsigned n_parents; assert(from_dir); assert(to_path); assert(_r); /* Strips the common part, and adds ".." elements as necessary. */ if (!path_is_absolute(from_dir)) return -EINVAL; if (!path_is_absolute(to_path)) return -EINVAL; /* Skip the common part. */ for (;;) { size_t a; size_t b; from_dir += strspn(from_dir, "/"); to_path += strspn(to_path, "/"); if (!*from_dir) { if (!*to_path) /* from_dir equals to_path. */ r = strdup("."); else /* from_dir is a parent directory of to_path. */ r = strdup(to_path); if (!r) return -ENOMEM; path_kill_slashes(r); *_r = r; return 0; } if (!*to_path) break; a = strcspn(from_dir, "/"); b = strcspn(to_path, "/"); if (a != b) break; if (memcmp(from_dir, to_path, a) != 0) break; from_dir += a; to_path += b; } /* If we're here, then "from_dir" has one or more elements that need to * be replaced with "..". */ /* Count the number of necessary ".." elements. */ for (n_parents = 0;;) { from_dir += strspn(from_dir, "/"); if (!*from_dir) break; from_dir += strcspn(from_dir, "/"); n_parents++; } r = malloc(n_parents * 3 + strlen(to_path) + 1); if (!r) return -ENOMEM; for (p = r; n_parents > 0; n_parents--, p += 3) memcpy(p, "../", 3); strcpy(p, to_path); path_kill_slashes(r); *_r = r; return 0; }
static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_ROOT, ARG_LOCALE, ARG_LOCALE_MESSAGES, ARG_TIMEZONE, ARG_HOSTNAME, ARG_MACHINE_ID, ARG_ROOT_PASSWORD, ARG_ROOT_PASSWORD_FILE, ARG_PROMPT, ARG_PROMPT_LOCALE, ARG_PROMPT_TIMEZONE, ARG_PROMPT_HOSTNAME, ARG_PROMPT_ROOT_PASSWORD, ARG_COPY, ARG_COPY_LOCALE, ARG_COPY_TIMEZONE, ARG_COPY_ROOT_PASSWORD, ARG_SETUP_MACHINE_ID, }; static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "root", required_argument, NULL, ARG_ROOT }, { "locale", required_argument, NULL, ARG_LOCALE }, { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, { "timezone", required_argument, NULL, ARG_TIMEZONE }, { "hostname", required_argument, NULL, ARG_HOSTNAME }, { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, { "prompt", no_argument, NULL, ARG_PROMPT }, { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, { "copy", no_argument, NULL, ARG_COPY }, { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, {} }; int r, c; assert(argc >= 0); assert(argv); while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) switch (c) { case 'h': help(); return 0; case ARG_VERSION: return version(); case ARG_ROOT: free(arg_root); arg_root = path_make_absolute_cwd(optarg); if (!arg_root) return log_oom(); path_kill_slashes(arg_root); if (path_equal(arg_root, "/")) arg_root = mfree(arg_root); break; case ARG_LOCALE: if (!locale_is_valid(optarg)) { log_error("Locale %s is not valid.", optarg); return -EINVAL; } r = free_and_strdup(&arg_locale, optarg); if (r < 0) return log_oom(); break; case ARG_LOCALE_MESSAGES: if (!locale_is_valid(optarg)) { log_error("Locale %s is not valid.", optarg); return -EINVAL; } r = free_and_strdup(&arg_locale_messages, optarg); if (r < 0) return log_oom(); break; case ARG_TIMEZONE: if (!timezone_is_valid(optarg)) { log_error("Timezone %s is not valid.", optarg); return -EINVAL; } r = free_and_strdup(&arg_timezone, optarg); if (r < 0) return log_oom(); break; case ARG_ROOT_PASSWORD: r = free_and_strdup(&arg_root_password, optarg); if (r < 0) return log_oom(); break; case ARG_ROOT_PASSWORD_FILE: arg_root_password = mfree(arg_root_password); r = read_one_line_file(optarg, &arg_root_password); if (r < 0) return log_error_errno(r, "Failed to read %s: %m", optarg); break; case ARG_HOSTNAME: if (!hostname_is_valid(optarg, true)) { log_error("Host name %s is not valid.", optarg); return -EINVAL; } hostname_cleanup(optarg); r = free_and_strdup(&arg_hostname, optarg); if (r < 0) return log_oom(); break; case ARG_MACHINE_ID: if (sd_id128_from_string(optarg, &arg_machine_id) < 0) { log_error("Failed to parse machine id %s.", optarg); return -EINVAL; } break; case ARG_PROMPT: arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; break; case ARG_PROMPT_LOCALE: arg_prompt_locale = true; break; case ARG_PROMPT_TIMEZONE: arg_prompt_timezone = true; break; case ARG_PROMPT_HOSTNAME: arg_prompt_hostname = true; break; case ARG_PROMPT_ROOT_PASSWORD: arg_prompt_root_password = true; break; case ARG_COPY: arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true; break; case ARG_COPY_LOCALE: arg_copy_locale = true; break; case ARG_COPY_TIMEZONE: arg_copy_timezone = true; break; case ARG_COPY_ROOT_PASSWORD: arg_copy_root_password = true; break; case ARG_SETUP_MACHINE_ID: r = sd_id128_randomize(&arg_machine_id); if (r < 0) return log_error_errno(r, "Failed to generate randomized machine ID: %m"); break; case '?': return -EINVAL; default: assert_not_reached("Unhandled option"); } return 1; }
static int refresh_one( const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration, unsigned depth, Group **ret) { _cleanup_closedir_ DIR *d = NULL; Group *ours = NULL; int r; assert(controller); assert(path); assert(a); if (depth > arg_depth) return 0; r = process(controller, path, a, b, iteration, &ours); if (r < 0) return r; r = cg_enumerate_subgroups(controller, path, &d); if (r == -ENOENT) return 0; if (r < 0) return r; for (;;) { _cleanup_free_ char *fn = NULL, *p = NULL; Group *child = NULL; r = cg_read_subgroup(d, &fn); if (r < 0) return r; if (r == 0) break; p = strjoin(path, "/", fn, NULL); if (!p) return -ENOMEM; path_kill_slashes(p); r = refresh_one(controller, p, a, b, iteration, depth + 1, &child); if (r < 0) return r; if (arg_recursive && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) && child && child->n_tasks_valid && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { /* Recursively sum up processes */ if (ours->n_tasks_valid) ours->n_tasks += child->n_tasks; else { ours->n_tasks = child->n_tasks; ours->n_tasks_valid = true; } } } if (ret) *ret = ours; return 1; }
int main(int argc, char *argv[]) { int r, output_flags; log_parse_environment(); log_open(); r = parse_argv(argc, argv); if (r <= 0) goto finish; r = pager_open(arg_no_pager, false); if (r > 0 && arg_full < 0) arg_full = true; output_flags = arg_all * OUTPUT_SHOW_ALL | (arg_full > 0) * OUTPUT_FULL_WIDTH | arg_kernel_threads * OUTPUT_KERNEL_THREADS; if (arg_names) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *root = NULL; char **name; STRV_FOREACH(name, arg_names) { int q; if (arg_show_unit != SHOW_UNIT_NONE) { /* Command line arguments are unit names */ _cleanup_free_ char *cgroup = NULL; if (!bus) { /* Connect to the bus only if necessary */ r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, arg_show_unit == SHOW_UNIT_USER, &bus); if (r < 0) { log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } } q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup); if (q < 0) goto failed; if (isempty(cgroup)) { log_warning("Unit %s not found.", *name); q = -ENOENT; goto failed; } printf("Unit %s (%s):\n", *name, cgroup); fflush(stdout); q = show_cgroup_by_path(cgroup, NULL, 0, output_flags); } else if (path_startswith(*name, "/sys/fs/cgroup")) { printf("Directory %s:\n", *name); fflush(stdout); q = show_cgroup_by_path(*name, NULL, 0, output_flags); } else { _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; const char *controller, *path; if (!root) { /* Query root only if needed, treat error as fatal */ r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root); if (r < 0) goto finish; } q = cg_split_spec(*name, &c, &p); if (q < 0) { log_error_errno(q, "Failed to split argument %s: %m", *name); goto failed; } controller = c ?: SYSTEMD_CGROUP_CONTROLLER; if (p) { j = strjoin(root, "/", p); if (!j) { r = log_oom(); goto finish; } path_kill_slashes(j); path = j; } else path = root; show_cg_info(controller, path); q = show_cgroup(controller, path, NULL, 0, output_flags); } failed: if (q < 0 && r >= 0) r = q; } } else {
static int parse_fstab(const char *prefix, bool initrd) { _cleanup_free_ char *fstab_path = NULL; FILE *f; int r = 0; struct mntent *me; fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL); if (!fstab_path) return log_oom(); 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) { r = log_oom(); goto finish; } 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, isbind; const char *pre, *pre2, *post, *online; noauto = !!hasmntopt(me, "noauto"); nofail = !!hasmntopt(me, "nofail"); automount = hasmntopt(me, "comment=systemd.automount") || hasmntopt(me, "x-systemd.automount"); isbind = mount_is_bind(me); if (initrd) { pre = pre2 = online = NULL; post = SPECIAL_INITRD_FS_TARGET; } else if (mount_in_initrd(me)) { pre = pre2 = online = NULL; post = SPECIAL_INITRD_ROOT_FS_TARGET; } else if (mount_is_network(me)) { pre = SPECIAL_REMOTE_FS_PRE_TARGET; pre2 = SPECIAL_NETWORK_TARGET; online = SPECIAL_NETWORK_ONLINE_TARGET; post = SPECIAL_REMOTE_FS_TARGET; } else { pre = SPECIAL_LOCAL_FS_PRE_TARGET; pre2 = online = NULL; post = SPECIAL_LOCAL_FS_TARGET; } k = add_mount(what, where, me->mnt_type, me->mnt_opts, me->mnt_passno, noauto, nofail, automount, isbind, pre, pre2, online, post, fstab_path); } if (k < 0) r = k; } finish: endmntent(f); return r; }
int main(int argc, char *argv[]) { int r, output_flags; log_parse_environment(); log_open(); r = parse_argv(argc, argv); if (r <= 0) goto finish; if (!arg_no_pager) { r = pager_open(false); if (r > 0 && arg_full < 0) arg_full = true; } output_flags = arg_all * OUTPUT_SHOW_ALL | (arg_full > 0) * OUTPUT_FULL_WIDTH; if (optind < argc) { _cleanup_free_ char *root = NULL; int i; r = get_cgroup_root(&root); if (r < 0) goto finish; for (i = optind; i < argc; i++) { int q; if (path_startswith(argv[i], "/sys/fs/cgroup")) { printf("Directory %s:\n", argv[i]); fflush(stdout); q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); } else { _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; const char *controller, *path; r = cg_split_spec(argv[i], &c, &p); if (r < 0) { log_error_errno(r, "Failed to split argument %s: %m", argv[i]); goto finish; } controller = c ?: SYSTEMD_CGROUP_CONTROLLER; if (p) { j = strjoin(root, "/", p, NULL); if (!j) { r = log_oom(); goto finish; } path_kill_slashes(j); path = j; } else path = root; show_cg_info(controller, path); q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); } if (q < 0) r = q; } } else {
static int parse_argv(int argc, char *argv[]) { enum { ARG_PERCPU = 0x100, }; static const struct option options[] = { {"rel", no_argument, NULL, 'r' }, {"freq", required_argument, NULL, 'f' }, {"samples", required_argument, NULL, 'n' }, {"pss", no_argument, NULL, 'p' }, {"output", required_argument, NULL, 'o' }, {"init", required_argument, NULL, 'i' }, {"no-filter", no_argument, NULL, 'F' }, {"cmdline", no_argument, NULL, 'C' }, {"control-group", no_argument, NULL, 'c' }, {"help", no_argument, NULL, 'h' }, {"scale-x", required_argument, NULL, 'x' }, {"scale-y", required_argument, NULL, 'y' }, {"entropy", no_argument, NULL, 'e' }, {"per-cpu", no_argument, NULL, ARG_PERCPU}, {} }; int c, r; if (getpid() == 1) opterr = 0; while ((c = getopt_long(argc, argv, "erpf:n:o:i:FCchx:y:", options, NULL)) >= 0) switch (c) { case 'r': arg_relative = true; break; case 'f': r = safe_atod(optarg, &arg_hz); if (r < 0) log_warning_errno(r, "failed to parse --freq/-f argument '%s': %m", optarg); break; case 'F': arg_filter = false; break; case 'C': arg_show_cmdline = true; break; case 'c': arg_show_cgroup = true; break; case 'n': r = safe_atoi(optarg, &arg_samples_len); if (r < 0) log_warning_errno(r, "failed to parse --samples/-n argument '%s': %m", optarg); break; case 'o': path_kill_slashes(optarg); strscpy(arg_output_path, sizeof(arg_output_path), optarg); break; case 'i': path_kill_slashes(optarg); strscpy(arg_init_path, sizeof(arg_init_path), optarg); break; case 'p': arg_pss = true; break; case 'x': r = safe_atod(optarg, &arg_scale_x); if (r < 0) log_warning_errno(r, "failed to parse --scale-x/-x argument '%s': %m", optarg); break; case 'y': r = safe_atod(optarg, &arg_scale_y); if (r < 0) log_warning_errno(r, "failed to parse --scale-y/-y argument '%s': %m", optarg); break; case 'e': arg_entropy = true; break; case ARG_PERCPU: arg_percpu = true; break; case 'h': help(); return 0; case '?': if (getpid() != 1) return -EINVAL; else return 0; default: assert_not_reached("Unhandled option code."); } if (arg_hz <= 0) { log_error("Frequency needs to be > 0"); return -EINVAL; } return 1; }
int main(int argc, char *argv[]) { _cleanup_free_ char *build = NULL; struct sigaction sig; struct ps_struct *ps; char output_file[PATH_MAX]; char datestr[200]; time_t t = 0; const char *fn; _cleanup_fclose_ FILE *f; int gind; int i, r; char *init = NULL, *output = NULL; struct rlimit rlim; const ConfigTableItem items[] = { { "Bootchart", "Samples", config_parse_int, 0, &arg_samples_len }, { "Bootchart", "Frequency", config_parse_double, 0, &arg_hz }, { "Bootchart", "Relative", config_parse_bool, 0, &arg_relative }, { "Bootchart", "Filter", config_parse_bool, 0, &arg_filter }, { "Bootchart", "Output", config_parse_path, 0, &output }, { "Bootchart", "Init", config_parse_path, 0, &init }, { "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &arg_pss }, { "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &arg_entropy }, { "Bootchart", "ScaleX", config_parse_double, 0, &arg_scale_x }, { "Bootchart", "ScaleY", config_parse_double, 0, &arg_scale_y }, { NULL, NULL, NULL, 0, NULL } }; fn = "/etc/systemd/bootchart.conf"; f = fopen(fn, "re"); if (f) { r = config_parse(fn, f, NULL, config_item_table_lookup, (void*) items, true, NULL); if (r < 0) log_warning("Failed to parse configuration file: %s", strerror(-r)); if (init != NULL) strscpy(arg_init_path, sizeof(arg_init_path), init); if (output != NULL) strscpy(arg_output_path, sizeof(arg_output_path), output); } while (1) { static struct option opts[] = { {"rel", no_argument, NULL, 'r'}, {"freq", required_argument, NULL, 'f'}, {"samples", required_argument, NULL, 'n'}, {"pss", no_argument, NULL, 'p'}, {"output", required_argument, NULL, 'o'}, {"init", required_argument, NULL, 'i'}, {"no-filter", no_argument, NULL, 'F'}, {"cmdline", no_argument, NULL, 'C'}, {"help", no_argument, NULL, 'h'}, {"scale-x", required_argument, NULL, 'x'}, {"scale-y", required_argument, NULL, 'y'}, {"entropy", no_argument, NULL, 'e'}, {NULL, 0, NULL, 0} }; gind = 0; i = getopt_long(argc, argv, "erpf:n:o:i:FChx:y:", opts, &gind); if (i == -1) break; switch (i) { case 'r': arg_relative = true; break; case 'f': r = safe_atod(optarg, &arg_hz); if (r < 0) log_warning("failed to parse --freq/-f argument '%s': %s", optarg, strerror(-r)); break; case 'F': arg_filter = false; break; case 'C': arg_show_cmdline = true; break; case 'n': r = safe_atoi(optarg, &arg_samples_len); if (r < 0) log_warning("failed to parse --samples/-n argument '%s': %s", optarg, strerror(-r)); break; case 'o': path_kill_slashes(optarg); strscpy(arg_output_path, sizeof(arg_output_path), optarg); break; case 'i': path_kill_slashes(optarg); strscpy(arg_init_path, sizeof(arg_init_path), optarg); break; case 'p': arg_pss = true; break; case 'x': r = safe_atod(optarg, &arg_scale_x); if (r < 0) log_warning("failed to parse --scale-x/-x argument '%s': %s", optarg, strerror(-r)); break; case 'y': r = safe_atod(optarg, &arg_scale_y); if (r < 0) log_warning("failed to parse --scale-y/-y argument '%s': %s", optarg, strerror(-r)); break; case 'e': arg_entropy = true; break; case 'h': fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]); fprintf(stderr, " --rel, -r Record time relative to recording\n"); fprintf(stderr, " --freq, -f f Sample frequency [%f]\n", arg_hz); fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", arg_samples_len); fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", arg_scale_x); fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", arg_scale_y); fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n"); fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n"); fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", arg_output_path); fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", arg_init_path); fprintf(stderr, " --no-filter, -F Disable filtering of processes from the graph\n"); fprintf(stderr, " that are of less importance or short-lived\n"); fprintf(stderr, " --cmdline, -C Display the full command line with arguments\n"); fprintf(stderr, " of processes, instead of only the process name\n"); fprintf(stderr, " --help, -h Display this message\n"); fprintf(stderr, "See bootchart.conf for more information.\n"); exit (EXIT_SUCCESS); break; default: break; } } if (arg_samples_len > MAXSAMPLES) { fprintf(stderr, "Error: samples exceeds maximum\n"); exit(EXIT_FAILURE); } if (arg_hz <= 0.0) { fprintf(stderr, "Error: Frequency needs to be > 0\n"); exit(EXIT_FAILURE); } /* * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then * fork: * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1 * - child logs data */ if (getpid() == 1) { if (fork()) { /* parent */ execl(arg_init_path, arg_init_path, NULL); } } argv[0][0] = '@'; rlim.rlim_cur = 4096; rlim.rlim_max = 4096; (void) setrlimit(RLIMIT_NOFILE, &rlim); /* start with empty ps LL */ ps_first = calloc(1, sizeof(struct ps_struct)); if (!ps_first) { perror("calloc(ps_struct)"); exit(EXIT_FAILURE); } /* handle TERM/INT nicely */ memset(&sig, 0, sizeof(struct sigaction)); sig.sa_handler = signal_handler; sigaction(SIGHUP, &sig, NULL); interval = (1.0 / arg_hz) * 1000000000.0; log_uptime(); /* main program loop */ while (!exiting) { int res; double sample_stop; struct timespec req; time_t newint_s; long newint_ns; double elapsed; double timeleft; sampletime[samples] = gettime_ns(); if (!of && (access(arg_output_path, R_OK|W_OK|X_OK) == 0)) { t = time(NULL); strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t)); snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr); of = fopen(output_file, "w"); } if (sysfd < 0) sysfd = open("/sys", O_RDONLY); if (!build) parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &build, NULL); /* wait for /proc to become available, discarding samples */ if (!(graph_start > 0.0)) log_uptime(); else log_sample(samples); sample_stop = gettime_ns(); elapsed = (sample_stop - sampletime[samples]) * 1000000000.0; timeleft = interval - elapsed; newint_s = (time_t)(timeleft / 1000000000.0); newint_ns = (long)(timeleft - (newint_s * 1000000000.0)); /* * check if we have not consumed our entire timeslice. If we * do, don't sleep and take a new sample right away. * we'll lose all the missed samples and overrun our total * time */ if ((newint_ns > 0) || (newint_s > 0)) { req.tv_sec = newint_s; req.tv_nsec = newint_ns; res = nanosleep(&req, NULL); if (res) { if (errno == EINTR) { /* caught signal, probably HUP! */ break; } perror("nanosleep()"); exit (EXIT_FAILURE); } } else { overrun++; /* calculate how many samples we lost and scrap them */ arg_samples_len = arg_samples_len + ((int)(newint_ns / interval)); } samples++; if (samples > arg_samples_len) break; } /* do some cleanup, close fd's */ ps = ps_first; while (ps->next_ps) { ps = ps->next_ps; if (ps->schedstat) close(ps->schedstat); if (ps->sched) close(ps->sched); if (ps->smaps) fclose(ps->smaps); } if (!of) { t = time(NULL); strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t)); snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr); of = fopen(output_file, "w"); } if (!of) { fprintf(stderr, "opening output file '%s': %m\n", output_file); exit (EXIT_FAILURE); } svg_do(build); fprintf(stderr, "systemd-bootchart wrote %s\n", output_file); if (of) fclose(of); closedir(proc); if (sysfd >= 0) close(sysfd); /* nitpic cleanups */ ps = ps_first; while (ps->next_ps) { struct ps_struct *old = ps; ps = ps->next_ps; free(old->sample); free(old); } free(ps->sample); free(ps); /* don't complain when overrun once, happens most commonly on 1st sample */ if (overrun > 1) fprintf(stderr, "systemd-boochart: Warning: sample time overrun %i times\n", overrun); return 0; }
static int parse_args(int argc, char *argv[]) { static struct option options[] = { {"rel", no_argument, NULL, 'r'}, {"freq", required_argument, NULL, 'f'}, {"samples", required_argument, NULL, 'n'}, {"pss", no_argument, NULL, 'p'}, {"output", required_argument, NULL, 'o'}, {"init", required_argument, NULL, 'i'}, {"no-filter", no_argument, NULL, 'F'}, {"cmdline", no_argument, NULL, 'C'}, {"help", no_argument, NULL, 'h'}, {"scale-x", required_argument, NULL, 'x'}, {"scale-y", required_argument, NULL, 'y'}, {"entropy", no_argument, NULL, 'e'}, {NULL, 0, NULL, 0} }; int c; while ((c = getopt_long(argc, argv, "erpf:n:o:i:FChx:y:", options, NULL)) >= 0) { int r; switch (c) { case 'r': arg_relative = true; break; case 'f': r = safe_atod(optarg, &arg_hz); if (r < 0) log_warning("failed to parse --freq/-f argument '%s': %s", optarg, strerror(-r)); break; case 'F': arg_filter = false; break; case 'C': arg_show_cmdline = true; break; case 'n': r = safe_atoi(optarg, &arg_samples_len); if (r < 0) log_warning("failed to parse --samples/-n argument '%s': %s", optarg, strerror(-r)); break; case 'o': path_kill_slashes(optarg); strscpy(arg_output_path, sizeof(arg_output_path), optarg); break; case 'i': path_kill_slashes(optarg); strscpy(arg_init_path, sizeof(arg_init_path), optarg); break; case 'p': arg_pss = true; break; case 'x': r = safe_atod(optarg, &arg_scale_x); if (r < 0) log_warning("failed to parse --scale-x/-x argument '%s': %s", optarg, strerror(-r)); break; case 'y': r = safe_atod(optarg, &arg_scale_y); if (r < 0) log_warning("failed to parse --scale-y/-y argument '%s': %s", optarg, strerror(-r)); break; case 'e': arg_entropy = true; break; case 'h': fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]); fprintf(stderr, " --rel, -r Record time relative to recording\n"); fprintf(stderr, " --freq, -f f Sample frequency [%f]\n", arg_hz); fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", arg_samples_len); fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", arg_scale_x); fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", arg_scale_y); fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n"); fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n"); fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", arg_output_path); fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", arg_init_path); fprintf(stderr, " --no-filter, -F Disable filtering of processes from the graph\n"); fprintf(stderr, " that are of less importance or short-lived\n"); fprintf(stderr, " --cmdline, -C Display the full command line with arguments\n"); fprintf(stderr, " of processes, instead of only the process name\n"); fprintf(stderr, " --help, -h Display this message\n"); fprintf(stderr, "See bootchart.conf for more information.\n"); exit (EXIT_SUCCESS); break; default: break; } } if (arg_hz <= 0.0) { fprintf(stderr, "Error: Frequency needs to be > 0\n"); return -EINVAL; } return 0; }
static void load_unix_sockets(void) { FILE *f = NULL; char line[LINE_MAX]; if (unix_sockets) return; /* We maintain a cache of the sockets we found in * /proc/net/unix to speed things up a little. */ unix_sockets = set_new(string_hash_func, string_compare_func); if (!unix_sockets) return; f = fopen("/proc/net/unix", "re"); if (!f) return; /* Skip header */ if (!fgets(line, sizeof(line), f)) goto fail; for (;;) { char *p, *s; int k; if (!fgets(line, sizeof(line), f)) break; truncate_nl(line); p = strchr(line, ':'); if (!p) continue; if (strlen(p) < 37) continue; p += 37; p += strspn(p, WHITESPACE); p += strcspn(p, WHITESPACE); /* skip one more word */ p += strspn(p, WHITESPACE); if (*p != '/') continue; s = strdup(p); if (!s) goto fail; path_kill_slashes(s); k = set_put(unix_sockets, s); if (k < 0) { free(s); if (k != -EEXIST) goto fail; } } fclose(f); return; fail: set_free_free(unix_sockets); unix_sockets = NULL; if (f) fclose(f); }
static int parse_line(const char *fname, unsigned line, const char *buffer) { Item *i, *existing; char *mode = NULL, *user = NULL, *group = NULL, *age = NULL; char type; Hashmap *h; int r, n = -1; assert(fname); assert(line >= 1); assert(buffer); i = new0(Item, 1); if (!i) { log_error("Out of memory"); return -ENOMEM; } if (sscanf(buffer, "%c " "%ms " "%ms " "%ms " "%ms " "%ms " "%n", &type, &i->path, &mode, &user, &group, &age, &n) < 2) { log_error("[%s:%u] Syntax error.", fname, line); r = -EIO; goto finish; } if (n >= 0) { n += strspn(buffer+n, WHITESPACE); if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) { i->argument = unquote(buffer+n, "\""); if (!i->argument) { log_error("Out of memory"); return -ENOMEM; } } } switch(type) { case CREATE_FILE: case TRUNCATE_FILE: case CREATE_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: case REMOVE_PATH: case RECURSIVE_REMOVE_PATH: case RELABEL_PATH: case RECURSIVE_RELABEL_PATH: break; case CREATE_SYMLINK: if (!i->argument) { log_error("[%s:%u] Symlink file requires argument.", fname, line); r = -EBADMSG; goto finish; } break; case WRITE_FILE: if (!i->argument) { log_error("[%s:%u] Write file requires argument.", fname, line); r = -EBADMSG; goto finish; } break; case CREATE_CHAR_DEVICE: case CREATE_BLOCK_DEVICE: { unsigned major, minor; if (!i->argument) { log_error("[%s:%u] Device file requires argument.", fname, line); r = -EBADMSG; goto finish; } if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) { log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument); r = -EBADMSG; goto finish; } i->major_minor = makedev(major, minor); break; } default: log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); r = -EBADMSG; goto finish; } i->type = type; if (!path_is_absolute(i->path)) { log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path); r = -EBADMSG; goto finish; } path_kill_slashes(i->path); if (arg_prefix && !path_startswith(i->path, arg_prefix)) { r = 0; goto finish; } if (user && !streq(user, "-")) { const char *u = user; r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); if (r < 0) { log_error("[%s:%u] Unknown user '%s'.", fname, line, user); goto finish; } i->uid_set = true; } if (group && !streq(group, "-")) { const char *g = group; r = get_group_creds(&g, &i->gid); if (r < 0) { log_error("[%s:%u] Unknown group '%s'.", fname, line, group); goto finish; } i->gid_set = true; } if (mode && !streq(mode, "-")) { unsigned m; if (sscanf(mode, "%o", &m) != 1) { log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); r = -ENOENT; goto finish; } i->mode = m; i->mode_set = true; } else i->mode = i->type == CREATE_DIRECTORY || i->type == TRUNCATE_DIRECTORY ? 0755 : 0644; if (age && !streq(age, "-")) { const char *a = age; if (*a == '~') { i->keep_first_level = true; a++; } if (parse_usec(a, &i->age) < 0) { log_error("[%s:%u] Invalid age '%s'.", fname, line, age); r = -EBADMSG; goto finish; } i->age_set = true; } h = needs_glob(i->type) ? globs : items; existing = hashmap_get(h, i->path); if (existing) { /* Two identical items are fine */ if (!item_equal(existing, i)) log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path); r = 0; goto finish; } r = hashmap_put(h, i->path, i); if (r < 0) { log_error("Failed to insert item %s: %s", i->path, strerror(-r)); goto finish; } i = NULL; r = 0; finish: free(user); free(group); free(mode); free(age); if (i) item_free(i); return r; }
static int parse_fstab(bool initrd) { _cleanup_endmntent_ FILE *f = NULL; const char *fstab_path; struct mntent *me; int r = 0; fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab"; f = setmntent(fstab_path, "re"); if (!f) { if (errno == ENOENT) return 0; log_error_errno(errno, "Failed to open %s: %m", fstab_path); return -errno; } while ((me = getmntent(f))) { _cleanup_free_ char *where = NULL, *what = NULL; bool noauto, nofail; int k; if (initrd && !mount_in_initrd(me)) continue; what = fstab_node_to_udev_node(me->mnt_fsname); if (!what) return log_oom(); if (is_device_path(what) && path_is_read_only_fs("sys") > 0) { log_info("Running in a container, ignoring fstab device entry for %s.", what); continue; } where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir); if (!where) return log_oom(); if (is_path(where)) path_kill_slashes(where); noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s", what, where, me->mnt_type, yes_no(noauto), yes_no(nofail)); if (streq(me->mnt_type, "swap")) k = add_swap(what, me, noauto, nofail); else { bool automount; const char *post; automount = fstab_test_option(me->mnt_opts, "comment=systemd.automount\0" "x-systemd.automount\0"); 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; }