Beispiel #1
0
int main(int argc, char *argv[]) {
        bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
        bool in_container, use_watchdog = false;
        _cleanup_free_ char *cgroup = NULL;
        char *arguments[3];
        unsigned retries;
        int cmd, r;
        static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};

        log_parse_environment();
        r = parse_argv(argc, argv);
        if (r < 0)
                goto error;

        /* journald will die if not gone yet. The log target defaults
         * to console, but may have been changed by command line options. */

        log_close_console(); /* force reopen of /dev/console */
        log_open();

        umask(0022);

        if (getpid() != 1) {
                log_error("Not executed by init (PID 1).");
                r = -EPERM;
                goto error;
        }

        if (streq(arg_verb, "reboot"))
                cmd = RB_AUTOBOOT;
        else if (streq(arg_verb, "poweroff"))
                cmd = RB_POWER_OFF;
        else if (streq(arg_verb, "halt"))
                cmd = RB_HALT_SYSTEM;
        else if (streq(arg_verb, "kexec"))
                cmd = LINUX_REBOOT_CMD_KEXEC;
        else {
                r = -EINVAL;
                log_error("Unknown action '%s'.", arg_verb);
                goto error;
        }

        cg_get_root_path(&cgroup);

        use_watchdog = !!getenv("WATCHDOG_USEC");

        /* lock us into memory */
        mlockall(MCL_CURRENT|MCL_FUTURE);

        log_info("Sending SIGTERM to remaining processes...");
        broadcast_signal(SIGTERM, true, true);

        log_info("Sending SIGKILL to remaining processes...");
        broadcast_signal(SIGKILL, true, false);

        in_container = detect_container(NULL) > 0;

        need_umount = !in_container;
        need_swapoff = !in_container;
        need_loop_detach = !in_container;
        need_dm_detach = !in_container;

        /* Unmount all mountpoints, swaps, and loopback devices */
        for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
                bool changed = false;

                if (use_watchdog)
                        watchdog_ping();

                /* Let's trim the cgroup tree on each iteration so
                   that we leave an empty cgroup tree around, so that
                   container managers get a nice notify event when we
                   are down */
                if (cgroup)
                        cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);

                if (need_umount) {
                        log_info("Unmounting file systems.");
                        r = umount_all(&changed);
                        if (r == 0) {
                                need_umount = false;
                                log_info("All filesystems unmounted.");
                        } else if (r > 0)
                                log_info("Not all file systems unmounted, %d left.", r);
                        else
                                log_error_errno(r, "Failed to unmount file systems: %m");
                }

                if (need_swapoff) {
                        log_info("Deactivating swaps.");
                        r = swapoff_all(&changed);
                        if (r == 0) {
                                need_swapoff = false;
                                log_info("All swaps deactivated.");
                        } else if (r > 0)
                                log_info("Not all swaps deactivated, %d left.", r);
                        else
                                log_error_errno(r, "Failed to deactivate swaps: %m");
                }

                if (need_loop_detach) {
                        log_info("Detaching loop devices.");
                        r = loopback_detach_all(&changed);
                        if (r == 0) {
                                need_loop_detach = false;
                                log_info("All loop devices detached.");
                        } else if (r > 0)
                                log_info("Not all loop devices detached, %d left.", r);
                        else
                                log_error_errno(r, "Failed to detach loop devices: %m");
                }

                if (need_dm_detach) {
                        log_info("Detaching DM devices.");
                        r = dm_detach_all(&changed);
                        if (r == 0) {
                                need_dm_detach = false;
                                log_info("All DM devices detached.");
                        } else if (r > 0)
                                log_info("Not all DM devices detached, %d left.", r);
                        else
                                log_error_errno(r, "Failed to detach DM devices: %m");
                }

                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
                        if (retries > 0)
                                log_info("All filesystems, swaps, loop devices, DM devices detached.");
                        /* Yay, done */
                        goto initrd_jump;
                }

                /* If in this iteration we didn't manage to
                 * unmount/deactivate anything, we simply give up */
                if (!changed) {
                        log_info("Cannot finalize remaining%s%s%s%s continuing.",
                                 need_umount ? " file systems," : "",
                                 need_swapoff ? " swap devices," : "",
                                 need_loop_detach ? " loop devices," : "",
                                 need_dm_detach ? " DM devices," : "");
                        goto initrd_jump;
                }

                log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
                          retries + 1,
                          need_umount ? " file systems," : "",
                          need_swapoff ? " swap devices," : "",
                          need_loop_detach ? " loop devices," : "",
                          need_dm_detach ? " DM devices," : "");
        }

        log_error("Too many iterations, giving up.");

 initrd_jump:

        arguments[0] = NULL;
        arguments[1] = arg_verb;
        arguments[2] = NULL;
        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);

        if (!in_container && !in_initrd() &&
            access("/run/initramfs/shutdown", X_OK) == 0) {
                r = switch_root_initramfs();
                if (r >= 0) {
                        argv[0] = (char*) "/shutdown";

                        setsid();
                        make_console_stdio();

                        log_info("Successfully changed into root pivot.\n"
                                 "Returning to initrd...");

                        execv("/shutdown", argv);
                        log_error_errno(errno, "Failed to execute shutdown binary: %m");
                } else
                        log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");

        }

        if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
                log_error("Failed to finalize %s%s%s%s ignoring",
                          need_umount ? " file systems," : "",
                          need_swapoff ? " swap devices," : "",
                          need_loop_detach ? " loop devices," : "",
                          need_dm_detach ? " DM devices," : "");

        /* The kernel will automaticall flush ATA disks and suchlike
         * on reboot(), but the file systems need to be synce'd
         * explicitly in advance. So let's do this here, but not
         * needlessly slow down containers. */
        if (!in_container)
                sync();

        switch (cmd) {

        case LINUX_REBOOT_CMD_KEXEC:

                if (!in_container) {
                        /* We cheat and exec kexec to avoid doing all its work */
                        pid_t pid;

                        log_info("Rebooting with kexec.");

                        pid = fork();
                        if (pid < 0)
                                log_error_errno(errno, "Failed to fork: %m");
                        else if (pid == 0) {

                                const char * const args[] = {
                                        KEXEC, "-e", NULL
                                };

                                /* Child */

                                execv(args[0], (char * const *) args);
                                _exit(EXIT_FAILURE);
                        } else
                                wait_for_terminate_and_warn("kexec", pid, true);
                }

                cmd = RB_AUTOBOOT;
                /* Fall through */

        case RB_AUTOBOOT:

                if (!in_container) {
                        _cleanup_free_ char *param = NULL;

                        if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
                                log_info("Rebooting with argument '%s'.", param);
                                syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
                        }
                }

                log_info("Rebooting.");
                break;

        case RB_POWER_OFF:
                log_info("Powering off.");
                break;

        case RB_HALT_SYSTEM:
                log_info("Halting system.");
                break;

        default:
                assert_not_reached("Unknown magic");
        }

        reboot(cmd);
        if (errno == EPERM && in_container) {
                /* If we are in a container, and we lacked
                 * CAP_SYS_BOOT just exit, this will kill our
                 * container for good. */
                log_info("Exiting container.");
                exit(0);
        }

        log_error_errno(errno, "Failed to invoke reboot(): %m");
        r = -errno;

  error:
        log_emergency_errno(r, "Critical error while doing system shutdown: %m");

        freeze();
}
Beispiel #2
0
static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
        Group *g;
        int r;
        FILE *f = NULL;
        pid_t pid;
        unsigned n;

        assert(controller);
        assert(path);
        assert(a);

        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;
                }
        }

        /* Regardless which controller, let's find the maximum number
         * of processes in any of it */

        r = cg_enumerate_processes(controller, path, &f);
        if (r < 0)
                return r;

        n = 0;
        while (cg_read_pid(f, &pid) > 0)
                n++;
        fclose(f);

        if (n > 0) {
                if (g->n_tasks_valid)
                        g->n_tasks = MAX(g->n_tasks, n);
                else
                        g->n_tasks = n;

                g->n_tasks_valid = true;
        }

        if (streq(controller, "cpuacct")) {
                uint64_t new_usage;
                char *p, *v;
                struct timespec ts;

                r = cg_get_path(controller, path, "cpuacct.usage", &p);
                if (r < 0)
                        return r;

                r = read_one_line_file(p, &v);
                free(p);
                if (r < 0)
                        return r;

                r = safe_atou64(v, &new_usage);
                free(v);
                if (r < 0)
                        return r;

                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);

                if (g->cpu_iteration == iteration - 1) {
                        uint64_t x, y;

                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
                                ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);

                        y = new_usage - g->cpu_usage;

                        if (y > 0) {
                                g->cpu_fraction = (double) y / (double) x;
                                g->cpu_valid = true;
                        }
                }

                g->cpu_usage = new_usage;
                g->cpu_timestamp = ts;
                g->cpu_iteration = iteration;

        } else if (streq(controller, "memory")) {
                char *p, *v;

                r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
                if (r < 0)
                        return r;

                r = read_one_line_file(p, &v);
                free(p);
                if (r < 0)
                        return r;

                r = safe_atou64(v, &g->memory);
                free(v);
                if (r < 0)
                        return r;

                if (g->memory > 0)
                        g->memory_valid = true;

        } else if (streq(controller, "blkio")) {
                char *p;
                uint64_t wr = 0, rd = 0;
                struct timespec ts;

                r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
                if (r < 0)
                        return r;

                f = fopen(p, "re");
                free(p);

                if (!f)
                        return -errno;

                for (;;) {
                        char line[LINE_MAX], *l;
                        uint64_t k, *q;

                        if (!fgets(line, sizeof(line), f))
                                break;

                        l = strstrip(line);
                        l += strcspn(l, WHITESPACE);
                        l += strspn(l, WHITESPACE);

                        if (first_word(l, "Read")) {
                                l += 4;
                                q = &rd;
                        } else if (first_word(l, "Write")) {
                                l += 5;
                                q = &wr;
                        } else
                                continue;

                        l += strspn(l, WHITESPACE);
                        r = safe_atou64(l, &k);
                        if (r < 0)
                                continue;

                        *q += k;
                }

                fclose(f);

                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);

                if (g->io_iteration == iteration - 1) {
                        uint64_t x, yr, yw;

                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
                                ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);

                        yr = rd - g->io_input;
                        yw = wr - g->io_output;

                        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 = ts;
                g->io_iteration = iteration;
        }

        return 0;
}
static const char* fallback_chassis(void) {
        int r;
        char *type;
        unsigned t;
        int v;

        v = detect_virtualization(NULL);

        if (v == VIRTUALIZATION_VM)
                return "vm";
        if (v == VIRTUALIZATION_CONTAINER)
                return "container";

        r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
        if (r < 0)
                goto try_dmi;

        r = safe_atou(type, &t);
        free(type);
        if (r < 0)
                goto try_dmi;

        /* We only list the really obvious cases here as the ACPI data
         * is not really super reliable.
         *
         * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
         *
         * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
         */

        switch(t) {

        case 1:
        case 3:
        case 6:
                return "desktop";

        case 2:
                return "laptop";

        case 4:
        case 5:
        case 7:
                return "server";

        case 8:
                return "tablet";
        }

try_dmi:
        r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
        if (r < 0)
                return NULL;

        r = safe_atou(type, &t);
        free(type);
        if (r < 0)
                return NULL;

        /* We only list the really obvious cases here. The DMI data is
           unreliable enough, so let's not do any additional guesswork
           on top of that.

           See the SMBIOS Specification 2.7.1 section 7.4.1 for
           details about the values listed here:

           http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
         */

        switch (t) {

        case 0x3:
        case 0x4:
        case 0x6:
        case 0x7:
                return "desktop";

        case 0x8:
        case 0x9:
        case 0xA:
        case 0xE:
                return "laptop";

        case 0xB:
                return "handset";

        case 0x11:
        case 0x1C:
                return "server";
        }

        return NULL;
}
Beispiel #4
0
static const char* fallback_chassis(void) {
        char *type;
        unsigned t;
        int v, r;

        v = detect_virtualization();
        if (VIRTUALIZATION_IS_VM(v))
                return "vm";
        if (VIRTUALIZATION_IS_CONTAINER(v))
                return "container";

        r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
        if (r < 0)
                goto try_acpi;

        r = safe_atou(type, &t);
        free(type);
        if (r < 0)
                goto try_acpi;

        /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
           additional guesswork on top of that.

           See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here:

           https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
         */

        switch (t) {

        case 0x3: /* Desktop */
        case 0x4: /* Low Profile Desktop */
        case 0x6: /* Mini Tower */
        case 0x7: /* Tower */
                return "desktop";

        case 0x8: /* Portable */
        case 0x9: /* Laptop */
        case 0xA: /* Notebook */
        case 0xE: /* Sub Notebook */
                return "laptop";

        case 0xB: /* Hand Held */
                return "handset";

        case 0x11: /* Main Server Chassis */
        case 0x1C: /* Blade */
        case 0x1D: /* Blade Enclosure */
                return "server";

        case 0x1E: /* Tablet */
                return "tablet";

        case 0x1F: /* Convertible */
        case 0x20: /* Detachable */
                return "convertible";
        }

try_acpi:
        r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
        if (r < 0)
                return NULL;

        r = safe_atou(type, &t);
        free(type);
        if (r < 0)
                return NULL;

        /* We only list the really obvious cases here as the ACPI data is not really super reliable.
         *
         * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
         *
         * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
         */

        switch(t) {

        case 1: /* Desktop */
        case 3: /* Workstation */
        case 6: /* Appliance PC */
                return "desktop";

        case 2: /* Mobile */
                return "laptop";

        case 4: /* Enterprise Server */
        case 5: /* SOHO Server */
        case 7: /* Performance Server */
                return "server";

        case 8: /* Tablet */
                return "tablet";
        }

        return NULL;
}
Beispiel #5
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:
                        r = parse_path_argument_and_warn(optarg, true, &arg_root);
                        if (r < 0)
                                return r;
                        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;
}
Beispiel #6
0
static int process(
    const char *controller,
    const char *path,
    Hashmap *a,
    Hashmap *b,
    unsigned iteration,
    Group **ret) {

    Group *g;
    int r;

    assert(controller);
    assert(path);
    assert(a);

    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) {
        _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 (streq(controller, "cpuacct") && cg_unified() <= 0) {
        _cleanup_free_ char *p = NULL, *v = NULL;
        uint64_t new_usage;
        nsec_t timestamp;

        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")) {
        _cleanup_free_ char *p = NULL, *v = NULL;

        if (cg_unified() <= 0)
            r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
        else
            r = cg_get_path(controller, path, "memory.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->memory);
        if (r < 0)
            return r;

        if (g->memory > 0)
            g->memory_valid = true;

    } else if (streq(controller, "blkio") && cg_unified() <= 0) {
        _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, "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;

            l = strstrip(line);
            l += strcspn(l, WHITESPACE);
            l += strspn(l, WHITESPACE);

            if (first_word(l, "Read")) {
                l += 4;
                q = &rd;
            } else if (first_word(l, "Write")) {
                l += 5;
                q = &wr;
            } 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 dev_pci_slot(struct udev_device *dev, struct netnames *names) {
        struct udev *udev = udev_device_get_udev(names->pcidev);
        unsigned domain, bus, slot, func, dev_id = 0;
        size_t l;
        char *s;
        const char *attr;
        struct udev_device *pci = NULL;
        char slots[256], str[256];
        _cleanup_closedir_ DIR *dir = NULL;
        struct dirent *dent;
        int hotplug_slot = 0, err = 0;

        if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
                return -ENOENT;

        /* kernel provided port index for multiple ports on a single PCI function */
        attr = udev_device_get_sysattr_value(dev, "dev_id");
        if (attr) {
                dev_id = strtol(attr, NULL, 16);
                if (dev_id == 0) {
                        attr = udev_device_get_sysattr_value(dev, "dev_port");
                        if (attr)
                                dev_id = strtol(attr, NULL, 16);
                }
        }

        /* compose a name based on the raw kernel's PCI bus, slot numbers */
        s = names->pci_path;
        l = sizeof(names->pci_path);
        if (domain > 0)
                l = strpcpyf(&s, l, "P%u", domain);
        l = strpcpyf(&s, l, "p%us%u", bus, slot);
        if (func > 0 || is_pci_multifunction(names->pcidev))
                l = strpcpyf(&s, l, "f%d", func);
        if (dev_id > 0)
                l = strpcpyf(&s, l, "d%d", dev_id);
        if (l == 0)
                names->pci_path[0] = '\0';

        /* ACPI _SUN  -- slot user number */
        pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
        if (!pci) {
                err = -ENOENT;
                goto out;
        }
        snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
        dir = opendir(slots);
        if (!dir) {
                err = -errno;
                goto out;
        }

        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
                int i;
                char *rest;
                char *address;

                if (dent->d_name[0] == '.')
                        continue;
                i = strtol(dent->d_name, &rest, 10);
                if (rest[0] != '\0')
                        continue;
                if (i < 1)
                        continue;
                snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
                if (read_one_line_file(str, &address) >= 0) {
                        /* match slot address with device by stripping the function */
                        if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
                                hotplug_slot = i;
                        free(address);
                }

                if (hotplug_slot > 0)
                        break;
        }

        if (hotplug_slot > 0) {
                s = names->pci_slot;
                l = sizeof(names->pci_slot);
                if (domain > 0)
                        l = strpcpyf(&s, l, "P%d", domain);
                l = strpcpyf(&s, l, "s%d", hotplug_slot);
                if (func > 0 || is_pci_multifunction(names->pcidev))
                        l = strpcpyf(&s, l, "f%d", func);
                if (dev_id > 0)
                        l = strpcpyf(&s, l, "d%d", dev_id);
                if (l == 0)
                        names->pci_slot[0] = '\0';
        }
out:
        udev_device_unref(pci);
        return err;
}