Exemple #1
0
static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
                                   bool last, struct unit_times *times, struct boot_times *boot) {
        unsigned int i;
        char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];

        for (i = level; i != 0; i--)
                printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));

        printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));

        if (times) {
                if (times->time)
                        printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
                               format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
                               format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
                else if (times->activated > boot->userspace_time)
                        printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
                else
                        printf("%s", name);
        } else
                printf("%s", name);
        printf("\n");

        return 0;
}
Exemple #2
0
static int prompt_root_password(void) {
        const char *msg1, *msg2, *etc_shadow;
        int r;

        if (arg_root_password)
                return 0;

        if (!arg_prompt_root_password)
                return 0;

        etc_shadow = prefix_roota("/etc/shadow");
        if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
                return 0;

        print_welcome();
        putchar('\n');

        msg1 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
        msg2 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");

        for (;;) {
                _cleanup_free_ char *a = NULL, *b = NULL;

                r = ask_password_tty(msg1, 0, false, NULL, &a);
                if (r < 0) {
                        log_error("Failed to query root password: %s", strerror(-r));
                        return r;
                }

                if (isempty(a)) {
                        log_warning("No password entered, skipping.");
                        break;
                }

                r = ask_password_tty(msg2, 0, false, NULL, &b);
                if (r < 0) {
                        log_error("Failed to query root password: %s", strerror(-r));
                        clear_string(a);
                        return r;
                }

                if (!streq(a, b)) {
                        log_error("Entered passwords did not match, please try again.");
                        clear_string(a);
                        clear_string(b);
                        continue;
                }

                clear_string(b);
                arg_root_password = a;
                a = NULL;
                break;
        }

        return 0;
}
Exemple #3
0
static int notify_override_extended(const char *top, const char *bottom) {
        if (!(arg_flags & SHOW_EXTENDED))
               return 0;

        printf("%s%s%s   %s %s %s\n",
               ansi_highlight(), "[EXTENDED]", ansi_normal(),
               top, draw_special_char(DRAW_ARROW), bottom);
        return 1;
}
Exemple #4
0
static int notify_override_overridden(const char *top, const char *bottom) {
        if (!(arg_flags & SHOW_OVERRIDDEN))
                return 0;

        printf("%s%s%s %s %s %s\n",
               ansi_highlight(), "[OVERRIDDEN]", ansi_normal(),
               top, draw_special_char(DRAW_ARROW), bottom);
        return 1;
}
Exemple #5
0
static int notify_override_equivalent(const char *top, const char *bottom) {
        if (!(arg_flags & SHOW_EQUIVALENT))
                return 0;

        printf("%s%s%s %s %s %s\n",
               ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(),
               top, draw_special_char(DRAW_ARROW), bottom);
        return 1;
}
Exemple #6
0
static int notify_override_redirected(const char *top, const char *bottom) {
        if (!(arg_flags & SHOW_REDIRECTED))
                return 0;

        printf("%s%s%s   %s %s %s\n",
               ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(),
               top, draw_special_char(DRAW_ARROW), bottom);
        return 1;
}
Exemple #7
0
static int notify_override_masked(const char *top, const char *bottom) {
        if (!(arg_flags & SHOW_MASKED))
                return 0;

        printf("%s%s%s     %s %s %s\n",
               ansi_lightred(), "[MASKED]", ansi_highlight_off(),
               top, draw_special_char(DRAW_ARROW), bottom);
        return 1;
}
Exemple #8
0
static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) {
        int r;

        assert(text);
        assert(is_valid);
        assert(ret);

        for (;;) {
                _cleanup_free_ char *p = NULL;
                unsigned u;

                r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET), text);
                if (r < 0) {
                        log_error("Failed to query user: %s", strerror(-r));
                        return r;
                }

                if (isempty(p)) {
                        log_warning("No data entered, skipping.");
                        return 0;
                }

                r = safe_atou(p, &u);
                if (r >= 0) {
                        char *c;

                        if (u <= 0 || u > strv_length(l)) {
                                log_error("Specified entry number out of range.");
                                continue;
                        }

                        log_info("Selected '%s'.", l[u-1]);

                        c = strdup(l[u-1]);
                        if (!c)
                                return log_oom();

                        free(*ret);
                        *ret = c;
                        return 0;
                }

                if (!is_valid(p)) {
                        log_error("Entered data invalid.");
                        continue;
                }

                free(*ret);
                *ret = p;
                p = 0;
                return 0;
        }
}
Exemple #9
0
int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) {
        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
        _cleanup_udev_unref_ struct udev *udev = NULL;
        struct udev_list_entry *first = NULL;
        int r;

        if (n_columns <= 0)
                n_columns = columns();

        if (!prefix)
                prefix = "";

        if (isempty(seat))
                seat = "seat0";

        udev = udev_new();
        if (!udev)
                return -ENOMEM;

        e = udev_enumerate_new(udev);
        if (!e)
                return -ENOMEM;

        if (!streq(seat, "seat0"))
                r = udev_enumerate_add_match_tag(e, seat);
        else
                r = udev_enumerate_add_match_tag(e, "seat");
        if (r < 0)
                return r;

        r = udev_enumerate_add_match_is_initialized(e);
        if (r < 0)
                return r;

        r = udev_enumerate_scan_devices(e);
        if (r < 0)
                return r;

        first = udev_enumerate_get_list_entry(e);
        if (first)
                show_sysfs_one(udev, seat, &first, "/", prefix, n_columns);
        else
                printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), "(none)");

        return r;
}
Exemple #10
0
static int prompt_hostname(void) {
        int r;

        if (arg_hostname)
                return 0;

        if (!arg_prompt_hostname)
                return 0;

        print_welcome();
        putchar('\n');

        for (;;) {
                _cleanup_free_ char *h = NULL;

                r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET));
                if (r < 0)
                        return log_error_errno(r, "Failed to query hostname: %m");

                if (isempty(h)) {
                        log_warning("No hostname entered, skipping.");
                        break;
                }

                if (!hostname_is_valid(h, true)) {
                        log_error("Specified hostname invalid.");
                        continue;
                }

                /* Get rid of the trailing dot that we allow, but don't want to see */
                arg_hostname = hostname_cleanup(h);
                h = NULL;
                break;
        }

        return 0;
}
Exemple #11
0
static int prompt_hostname(void) {
        int r;

        if (arg_hostname)
                return 0;

        if (!arg_prompt_hostname)
                return 0;

        print_welcome();
        putchar('\n');

        for (;;) {
                _cleanup_free_ char *h = NULL;

                r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET));
                if (r < 0) {
                        log_error("Failed to query hostname: %s", strerror(-r));
                        return r;
                }

                if (isempty(h)) {
                        log_warning("No hostname entered, skipping.");
                        break;
                }

                if (!hostname_is_valid(h)) {
                        log_error("Specified hostname invalid.");
                        continue;
                }

                arg_hostname = h;
                h = NULL;
                break;
        }

        return 0;
}
Exemple #12
0
static int show_sysfs_one(
                struct udev *udev,
                const char *seat,
                struct udev_list_entry **item,
                const char *sub,
                const char *prefix,
                unsigned n_columns) {

        assert(udev);
        assert(seat);
        assert(item);
        assert(prefix);

        while (*item) {
                struct udev_list_entry *next, *lookahead;
                struct udev_device *d;
                const char *sn, *name, *sysfs, *subsystem, *sysname;
                char *l, *k;
                bool is_master;

                sysfs = udev_list_entry_get_name(*item);
                if (!path_startswith(sysfs, sub))
                        return 0;

                d = udev_device_new_from_syspath(udev, sysfs);
                if (!d) {
                        *item = udev_list_entry_get_next(*item);
                        continue;
                }

                sn = udev_device_get_property_value(d, "ID_SEAT");
                if (isempty(sn))
                        sn = "seat0";

                /* Explicitly also check for tag 'seat' here */
                if (!streq(seat, sn) || !udev_device_has_tag(d, "seat")) {
                        udev_device_unref(d);
                        *item = udev_list_entry_get_next(*item);
                        continue;
                }

                is_master = udev_device_has_tag(d, "seat-master");

                name = udev_device_get_sysattr_value(d, "name");
                if (!name)
                        name = udev_device_get_sysattr_value(d, "id");
                subsystem = udev_device_get_subsystem(d);
                sysname = udev_device_get_sysname(d);

                /* Look if there's more coming after this */
                lookahead = next = udev_list_entry_get_next(*item);
                while (lookahead) {
                        const char *lookahead_sysfs;

                        lookahead_sysfs = udev_list_entry_get_name(lookahead);

                        if (path_startswith(lookahead_sysfs, sub) &&
                            !path_startswith(lookahead_sysfs, sysfs)) {
                                struct udev_device *lookahead_d;

                                lookahead_d = udev_device_new_from_syspath(udev, lookahead_sysfs);
                                if (lookahead_d) {
                                        const char *lookahead_sn;
                                        bool found;

                                        lookahead_sn = udev_device_get_property_value(d, "ID_SEAT");
                                        if (isempty(lookahead_sn))
                                                lookahead_sn = "seat0";

                                        found = streq(seat, lookahead_sn) && udev_device_has_tag(lookahead_d, "seat");
                                        udev_device_unref(lookahead_d);

                                        if (found)
                                                break;
                                }
                        }

                        lookahead = udev_list_entry_get_next(lookahead);
                }

                k = ellipsize(sysfs, n_columns, 20);
                printf("%s%s%s\n", prefix, draw_special_char(lookahead ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT),
                                   k ? k : sysfs);
                free(k);

                if (asprintf(&l,
                             "%s%s:%s%s%s%s",
                             is_master ? "[MASTER] " : "",
                             subsystem, sysname,
                             name ? " \"" : "", name ? name : "", name ? "\"" : "") < 0) {
                        udev_device_unref(d);
                        return -ENOMEM;
                }

                k = ellipsize(l, n_columns, 70);
                printf("%s%s%s\n", prefix, lookahead ? draw_special_char(DRAW_TREE_VERT) : "  ",
                                   k ? k : l);
                free(k);
                free(l);

                *item = next;
                if (*item) {
                        char *p;

                        p = strappend(prefix, lookahead ? draw_special_char(DRAW_TREE_VERT) : "  ");
                        show_sysfs_one(udev, seat, item, sysfs, p ? p : prefix, n_columns - 2);
                        free(p);
                }

                udev_device_unref(d);
        }

        return 0;
}
Exemple #13
0
static int show_status(char **args, unsigned n) {
        char buf[64];
        struct boot_info *info;
        int err;

        err = boot_info_new(&info);
        if (err < 0)
                return -ENOMEM;

        err = boot_info_query(info);

        printf("System:\n");
        printf("   Machine ID: %s\n", sd_id128_to_string(info->machine_id, buf));
        printf("      Boot ID: %s\n", sd_id128_to_string(info->boot_id, buf));
        if (info->fw_type)
                printf("     Firmware: %s (%s)\n", info->fw_type, strna(info->fw_info));
        if (info->fw_secure_boot >= 0)
                printf("  Secure Boot: %s\n", info->fw_secure_boot ? "enabled" : "disabled");
        if (info->fw_secure_boot_setup_mode >= 0)
                printf("   Setup Mode: %s\n", info->fw_secure_boot_setup_mode ? "setup" : "user");
        printf("\n");

        if (info->fw_entry_active >= 0) {
                printf("Selected Firmware Entry:\n");
                printf("        Title: %s\n", strna(info->fw_entries[info->fw_entry_active].title));
                if (!sd_id128_equal(info->fw_entries[info->fw_entry_active].part_uuid, SD_ID128_NULL))
                        printf("    Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
                               SD_ID128_FORMAT_VAL(info->fw_entries[info->fw_entry_active].part_uuid));
                else
                        printf("    Partition: n/a\n");
                if (info->fw_entries[info->fw_entry_active].path)
                        printf("         File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), info->fw_entries[info->fw_entry_active].path);
        }
        printf("\n");

        if (info->loader) {
                printf("Boot Loader:\n");
                printf("      Product: %s\n", info->loader);
                if (!sd_id128_equal(info->loader_part_uuid, SD_ID128_NULL))
                        printf("    Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
                               SD_ID128_FORMAT_VAL(info->loader_part_uuid));
                        else
                                printf("    Partition: n/a\n");
                printf("         File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(info->loader_image_path));
                printf("\n");

                if (info->loader_entry_active >= 0) {
                        printf("Selected Boot Loader Entry:\n");
                        printf("        Title: %s\n", strna(info->loader_entries[info->loader_entry_active].title));
                        printf("         File: %s\n", info->loader_entries[info->loader_entry_active].path);
                        if (info->loader_options_added)
                                printf("      Options: %s\n", info->loader_options_added);
                }
        } else
                printf("No suitable data is provided by the boot manager. See:\n"
                       "  http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface\n"
                       "  http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec\n"
                       "for details.\n");
        printf("\n");

        boot_info_free(info);
        return err;
}
Exemple #14
0
static int bootctl_main(int argc, char*argv[]) {
        enum action {
                ACTION_STATUS,
                ACTION_INSTALL,
                ACTION_UPDATE,
                ACTION_REMOVE
        } arg_action = ACTION_STATUS;
        static const struct {
                const char* verb;
                enum action action;
        } verbs[] = {
                { "status",  ACTION_STATUS },
                { "install", ACTION_INSTALL },
                { "update",  ACTION_UPDATE },
                { "remove",  ACTION_REMOVE },
        };

        sd_id128_t uuid = {};
        uint32_t part = 0;
        uint64_t pstart = 0, psize = 0;
        int r, q;

        if (argv[optind]) {
                unsigned i;

                for (i = 0; i < ELEMENTSOF(verbs); i++) {
                        if (!streq(argv[optind], verbs[i].verb))
                                continue;
                        arg_action = verbs[i].action;
                        break;
                }
                if (i >= ELEMENTSOF(verbs)) {
                        log_error("Unknown operation \"%s\"", argv[optind]);
                        return -EINVAL;
                }
        }

        if (geteuid() != 0)
                return log_error_errno(EPERM, "Need to be root.");

        r = verify_esp(arg_path, &part, &pstart, &psize, &uuid);
        if (r == -ENODEV && !arg_path)
                log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot.");
        if (r < 0)
                return r;

        switch (arg_action) {
        case ACTION_STATUS: {
                _cleanup_free_ char *fw_type = NULL;
                _cleanup_free_ char *fw_info = NULL;
                _cleanup_free_ char *loader = NULL;
                _cleanup_free_ char *loader_path = NULL;
                sd_id128_t loader_part_uuid = {};

                if (is_efi_boot()) {
                        read_loader_efi_var("LoaderFirmwareType", &fw_type);
                        read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
                        read_loader_efi_var("LoaderInfo", &loader);
                        read_loader_efi_var("LoaderImageIdentifier", &loader_path);
                        if (loader_path)
                                efi_tilt_backslashes(loader_path);
                        r = efi_loader_get_device_part_uuid(&loader_part_uuid);
                        if (r < 0 && r == -ENOENT)
                                log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m");

                        printf("System:\n");
                        printf("     Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));

                        r = is_efi_secure_boot();
                        if (r < 0)
                                log_warning_errno(r, "Failed to query secure boot status: %m");
                        else
                                printf("  Secure Boot: %s\n", r ? "enabled" : "disabled");

                        r = is_efi_secure_boot_setup_mode();
                        if (r < 0)
                                log_warning_errno(r, "Failed to query secure boot mode: %m");
                        else
                                printf("   Setup Mode: %s\n", r ? "setup" : "user");
                        printf("\n");

                        printf("Loader:\n");
                        printf("      Product: %s\n", strna(loader));
                        if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL))
                                printf("    Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
                                       SD_ID128_FORMAT_VAL(loader_part_uuid));
                        else
                                printf("    Partition: n/a\n");
                        printf("         File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path));
                        printf("\n");
                } else
                        printf("System:\n    Not booted with EFI\n");

                r = status_binaries(arg_path, uuid);
                if (r < 0)
                        return r;

                if (arg_touch_variables)
                        r = status_variables();
                break;
        }

        case ACTION_INSTALL:
        case ACTION_UPDATE:
                umask(0002);

                r = install_binaries(arg_path, arg_action == ACTION_INSTALL);
                if (r < 0)
                        return r;

                if (arg_action == ACTION_INSTALL) {
                        r = install_loader_config(arg_path);
                        if (r < 0)
                                return r;
                }

                if (arg_touch_variables)
                        r = install_variables(arg_path,
                                              part, pstart, psize, uuid,
                                              "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
                                              arg_action == ACTION_INSTALL);
                break;

        case ACTION_REMOVE:
                r = remove_binaries(arg_path);

                if (arg_touch_variables) {
                        q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
                        if (q < 0 && r == 0)
                                r = q;
                }
                break;
        }

        return r;
}
Exemple #15
0
static int bootctl_main(int argc, char*argv[]) {
        enum action {
                ACTION_STATUS,
                ACTION_INSTALL,
                ACTION_UPDATE,
                ACTION_REMOVE
        } arg_action = ACTION_STATUS;
        static const struct {
                const char* verb;
                enum action action;
        } verbs[] = {
                { "status",  ACTION_STATUS },
                { "install", ACTION_INSTALL },
                { "update",  ACTION_UPDATE },
                { "remove",  ACTION_REMOVE },
        };

        sd_id128_t uuid = {};
        uint32_t part = 0;
        uint64_t pstart = 0;
        uint64_t psize = 0;
        unsigned int i;
        int q;
        int r;

        if (argv[optind]) {
                for (i = 0; i < ELEMENTSOF(verbs); i++) {
                        if (!streq(argv[optind], verbs[i].verb))
                                continue;
                        arg_action = verbs[i].action;
                        break;
                }
                if (i >= ELEMENTSOF(verbs)) {
                        fprintf(stderr, "Unknown operation %s\n", argv[optind]);
                        r = -EINVAL;
                        goto finish;
                }
        }

        if (!arg_path)
                arg_path = "/boot";

        if (geteuid() != 0) {
                fprintf(stderr, "Need to be root.\n");
                r = -EPERM;
                goto finish;
        }

        r = verify_esp(arg_path, &part, &pstart, &psize, &uuid);
        if (r == -ENODEV && !arg_path)
                fprintf(stderr, "You might want to use --path= to indicate the path to your ESP, in case it is not mounted to /boot.\n");
        if (r < 0)
                goto finish;

        switch (arg_action) {
        case ACTION_STATUS: {
                _cleanup_free_ char *fw_type = NULL;
                _cleanup_free_ char *fw_info = NULL;
                _cleanup_free_ char *loader = NULL;
                _cleanup_free_ char *loader_path = NULL;
                sd_id128_t loader_part_uuid = {};

                efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType", &fw_type);
                efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo", &fw_info);
                efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &loader);
                if (efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier", &loader_path) > 0)
                        efi_tilt_backslashes(loader_path);
                efi_loader_get_device_part_uuid(&loader_part_uuid);

                printf("System:\n");
                printf("     Firmware: %s (%s)\n", fw_type, strna(fw_info));
                printf("  Secure Boot: %s\n", is_efi_secure_boot() ? "enabled" : "disabled");
                printf("   Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
                printf("\n");

                printf("Loader:\n");
                printf("      Product: %s\n", strna(loader));
                if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL))
                        printf("    Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
                               SD_ID128_FORMAT_VAL(loader_part_uuid));
                else
                        printf("    Partition: n/a\n");
                printf("         File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path));
                printf("\n");

                r = status_binaries(arg_path, uuid);
                if (r < 0)
                        goto finish;

                if (arg_touch_variables)
                        r = status_variables();
                break;
        }

        case ACTION_INSTALL:
        case ACTION_UPDATE:
                umask(0002);

                r = install_binaries(arg_path, arg_action == ACTION_INSTALL);
                if (r < 0)
                        goto finish;

                if (arg_action == ACTION_INSTALL)
                        install_loader_config(arg_path);

                if (arg_touch_variables)
                        r = install_variables(arg_path,
                                              part, pstart, psize, uuid,
                                              "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
                                              arg_action == ACTION_INSTALL);
                break;

        case ACTION_REMOVE:
                r = remove_binaries(arg_path);

                if (arg_touch_variables) {
                        q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
                        if (q < 0 && r == 0)
                                r = q;
                }
                break;
        }

finish:
        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
Exemple #16
0
static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
        _cleanup_free_ char *unit = NULL;
        _cleanup_free_ char *path = NULL;
        _cleanup_strv_free_ char **list = NULL;
        char **file;
        char *c;
        int r;

        assert(!endswith(drop, "/"));

        path = strjoin(toppath, "/", drop, NULL);
        if (!path)
                return -ENOMEM;

        log_debug("Looking at %s", path);

        unit = strdup(drop);
        if (!unit)
                return -ENOMEM;

        c = strrchr(unit, '.');
        if (!c)
                return -EINVAL;
        *c = 0;

        r = get_files_in_directory(path, &list);
        if (r < 0)
                return log_error_errno(r, "Failed to enumerate %s: %m", path);

        STRV_FOREACH(file, list) {
                Hashmap *h;
                int k;
                char *p;
                char *d;

                if (!endswith(*file, ".conf"))
                        continue;

                p = strjoin(path, "/", *file, NULL);
                if (!p)
                        return -ENOMEM;
                d = p + strlen(toppath) + 1;

                log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
                k = hashmap_put(top, d, p);
                if (k >= 0) {
                        p = strdup(p);
                        if (!p)
                                return -ENOMEM;
                        d = p + strlen(toppath) + 1;
                } else if (k != -EEXIST) {
                        free(p);
                        return k;
                }

                log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
                free(hashmap_remove(bottom, d));
                k = hashmap_put(bottom, d, p);
                if (k < 0) {
                        free(p);
                        return k;
                }

                h = hashmap_get(drops, unit);
                if (!h) {
                        h = hashmap_new(&string_hash_ops);
                        if (!h)
                                return -ENOMEM;
                        hashmap_put(drops, unit, h);
                        unit = strdup(unit);
                        if (!unit)
                                return -ENOMEM;
                }

                p = strdup(p);
                if (!p)
                        return -ENOMEM;

                log_debug("Adding to drops: %s %s %s %s %s",
                          unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p);
                k = hashmap_put(h, basename(p), p);
                if (k < 0) {
                        free(p);
                        if (k != -EEXIST)
                                return k;
                }
        }