X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Ftmpfiles%2Ftmpfiles.c;h=d5639897905f6cb24471df28ae13e90cad537c7b;hp=332ddcea766c7a3b01ef4d178841c8d451c05478;hb=081043cf06cc67a327f55806039639c55acdd439;hpb=2695c5c44e028d1bc07404baf3357e5a6017fc57 diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 332ddcea7..d56398979 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -4,6 +4,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering, Kay Sievers + Copyright 2015 Zbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -23,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -34,10 +34,11 @@ #include #include #include -#include -#include #include #include +#include +#include +#include #include #include "log.h" @@ -56,6 +57,7 @@ #include "copy.h" #include "selinux-util.h" #include "btrfs-util.h" +#include "acl-util.h" /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates * them in the file system. This is intended to be used to create @@ -75,6 +77,7 @@ typedef enum ItemType { CREATE_BLOCK_DEVICE = 'b', COPY_FILES = 'C', SET_XATTR = 't', + SET_ACL = 'a', /* These ones take globs */ WRITE_FILE = 'w', @@ -93,6 +96,10 @@ typedef struct Item { char *path; char *argument; char **xattrs; +#ifdef HAVE_ACL + acl_t acl_access; + acl_t acl_default; +#endif uid_t uid; gid_t gid; mode_t mode; @@ -113,6 +120,12 @@ typedef struct Item { bool done:1; } Item; +typedef struct ItemArray { + Item *items; + size_t count; + size_t size; +} ItemArray; + static bool arg_create = false; static bool arg_clean = false; static bool arg_remove = false; @@ -141,13 +154,40 @@ static bool needs_glob(ItemType t) { RECURSIVE_RELABEL_PATH); } +static bool takes_ownership(ItemType t) { + return IN_SET(t, + CREATE_FILE, + TRUNCATE_FILE, + CREATE_DIRECTORY, + TRUNCATE_DIRECTORY, + CREATE_SUBVOLUME, + CREATE_FIFO, + CREATE_SYMLINK, + CREATE_CHAR_DEVICE, + CREATE_BLOCK_DEVICE, + COPY_FILES, + + WRITE_FILE, + IGNORE_PATH, + IGNORE_DIRECTORY_PATH, + REMOVE_PATH, + RECURSIVE_REMOVE_PATH); +} + static struct Item* find_glob(Hashmap *h, const char *match) { - Item *j; + ItemArray *j; Iterator i; - HASHMAP_FOREACH(j, h, i) - if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) - return j; + HASHMAP_FOREACH(j, h, i) { + unsigned n; + + for (n = 0; n < j->count; n++) { + Item *item = j->items + n; + + if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) + return item; + } + } return NULL; } @@ -480,11 +520,9 @@ static int item_set_perms(Item *i, const char *path) { (i->uid_set || i->gid_set)) if (chown(path, i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID) < 0) { + i->gid_set ? i->gid : GID_INVALID) < 0) - log_error_errno(errno, "chown(%s) failed: %m", path); - return -errno; - } + return log_error_errno(errno, "chown(%s) failed: %m", path); return label_fix(path, false, false); } @@ -495,36 +533,36 @@ static int get_xattrs_from_arg(Item *i) { int r; assert(i); + assert(i->argument); - if (!i->argument) { - log_error("%s: Argument can't be empty!", i->path); - return -EBADMSG; - } p = i->argument; while ((r = unquote_first_word(&p, &xattr, false)) > 0) { - _cleanup_free_ char *tmp = NULL, *name = NULL, *value = NULL; + _cleanup_free_ char *tmp = NULL, *name = NULL, + *value = NULL, *value2 = NULL, *_xattr = xattr; + r = split_pair(xattr, "=", &name, &value); if (r < 0) { log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr); - free(xattr); continue; } - free(xattr); - if (streq(name, "") || streq(value, "")) { - log_warning("Malformed xattr found: \"%s=%s\" - ignoring.", name, value); + + if (strempty(name) || strempty(value)) { + log_warning("Malformed xattr found: \"%s\" - ignoring.", xattr); continue; } + tmp = unquote(value, "\""); if (!tmp) return log_oom(); - free(value); - value = cunescape(tmp); - if (!value) + + value2 = cunescape(tmp); + if (!value2) return log_oom(); - if (strv_consume_pair(&i->xattrs, name, value) < 0) + + if (strv_push_pair(&i->xattrs, name, value2) < 0) return log_oom(); - name = value = NULL; + name = value2 = NULL; } return r; @@ -536,20 +574,72 @@ static int item_set_xattrs(Item *i, const char *path) { assert(i); assert(path); - if (strv_isempty(i->xattrs)) - return 0; - STRV_FOREACH_PAIR(name, value, i->xattrs) { int n; + n = strlen(*value); if (lsetxattr(path, *name, *value, n, 0) < 0) { - log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); + log_error("Setting extended attribute %s=%s on %s failed: %m", + *name, *value, path); return -errno; } } return 0; } +static int get_acls_from_arg(Item *item) { +#ifdef HAVE_ACL + int r; + _cleanup_(acl_freep) acl_t a = NULL, d = NULL; + + assert(item); + + r = parse_acl(item->argument, &item->acl_access, &item->acl_default); + if (r < 0) + log_warning_errno(errno, "Failed to parse ACL \"%s\": %m. Ignoring", + item->argument); +#else + log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring"); +#endif + + return 0; +} + +static int item_set_acl(Item *item, const char *path) { +#ifdef HAVE_ACL + int r; + + assert(item); + assert(path); + + if (item->acl_access) { + r = acl_set_file(path, ACL_TYPE_ACCESS, item->acl_access); + if (r < 0) { + _cleanup_(acl_free_charpp) char *t; + + t = acl_to_any_text(item->acl_access, NULL, ',', TEXT_ABBREVIATE); + return log_error_errno(errno, + "Setting access ACL \"%s\" on %s failed: %m", + strna(t), path); + } + } + + if (item->acl_default) { + r = acl_set_file(path, ACL_TYPE_DEFAULT, item->acl_default); + if (r < 0) { + _cleanup_(acl_free_charpp) char *t; + + t = acl_to_any_text(item->acl_default, NULL, ',', TEXT_ABBREVIATE); + return log_error_errno(errno, + "Setting default ACL \"%s\" on %s failed: %m", + strna(t), path); + } + } +#endif + + return 0; +} + static int write_one_file(Item *i, const char *path) { _cleanup_close_ int fd = -1; int flags, r = 0; @@ -607,14 +697,12 @@ static int write_one_file(Item *i, const char *path) { if (r < 0) return r; - r = item_set_xattrs(i, i->path); - if (r < 0) - return r; - return 0; } -static int item_set_perms_children(Item *i, const char *path) { +typedef int (*action_t)(Item *, const char *); + +static int item_do_children(Item *i, const char *path, action_t action) { _cleanup_closedir_ DIR *d; int r = 0; @@ -649,12 +737,12 @@ static int item_set_perms_children(Item *i, const char *path) { if (!p) return -ENOMEM; - q = item_set_perms(i, p); + q = action(i, p); if (q < 0 && q != -ENOENT && r == 0) r = q; if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { - q = item_set_perms_children(i, p); + q = item_do_children(i, p, action); if (q < 0 && r == 0) r = q; } @@ -663,42 +751,26 @@ static int item_set_perms_children(Item *i, const char *path) { return r; } -static int item_set_perms_recursive(Item *i, const char *path) { - int r, q; - - assert(i); - assert(path); - - r = item_set_perms(i, path); - if (r < 0) - return r; - - q = item_set_perms_children(i, path); - if (q < 0 && r == 0) - r = q; - - return r; -} - -static int glob_item(Item *i, int (*action)(Item *, const char *)) { +static int glob_item(Item *i, action_t action, bool recursive) { _cleanup_globfree_ glob_t g = {}; int r = 0, k; char **fn; errno = 0; k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); - if (k != 0 && k != GLOB_NOMATCH) { - if (errno == 0) - errno = EIO; - - log_error_errno(errno, "glob(%s) failed: %m", i->path); - return -errno; - } + if (k != 0 && k != GLOB_NOMATCH) + return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path); STRV_FOREACH(fn, g.gl_pathv) { k = action(i, *fn); if (k < 0 && r == 0) r = k; + + if (recursive) { + k = item_do_children(i, *fn, action); + if (k < 0 && r == 0) + r = k; + } } return r; @@ -752,7 +824,7 @@ static int create_item(Item *i) { break; case WRITE_FILE: - r = glob_item(i, write_one_file); + r = glob_item(i, write_one_file, false); if (r < 0) return r; @@ -771,7 +843,7 @@ static int create_item(Item *i) { } else r = 0; - if (i->type == CREATE_DIRECTORY || i->type == TRUNCATE_DIRECTORY || r == -ENOTTY) { + if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) { RUN_WITH_UMASK(0000) r = mkdir_label(i->path, i->mode); } @@ -793,10 +865,6 @@ static int create_item(Item *i) { if (r < 0) return r; - r = item_set_xattrs(i, i->path); - if (r < 0) - return r; - break; case CREATE_FIFO: @@ -837,10 +905,6 @@ static int create_item(Item *i) { if (r < 0) return r; - r = item_set_xattrs(i, i->path); - if (r < 0) - return r; - break; case CREATE_SYMLINK: @@ -872,10 +936,6 @@ static int create_item(Item *i) { } } - r = item_set_xattrs(i, i->path); - if (r < 0) - return r; - break; case CREATE_BLOCK_DEVICE: @@ -936,24 +996,20 @@ static int create_item(Item *i) { if (r < 0) return r; - r = item_set_xattrs(i, i->path); - if (r < 0) - return r; - break; } case ADJUST_MODE: case RELABEL_PATH: - r = glob_item(i, item_set_perms); + r = glob_item(i, item_set_perms, false); if (r < 0) return r; break; case RECURSIVE_RELABEL_PATH: - r = glob_item(i, item_set_perms_recursive); + r = glob_item(i, item_set_perms, true); if (r < 0) return r; break; @@ -963,6 +1019,11 @@ static int create_item(Item *i) { if (r < 0) return r; break; + + case SET_ACL: + r = item_set_acl(i, i->path); + if (r < 0) + return r; } log_debug("%s created successfully.", i->path); @@ -993,11 +1054,12 @@ static int remove_item_instance(Item *i, const char *instance) { case WRITE_FILE: case COPY_FILES: case SET_XATTR: + case SET_ACL: break; case REMOVE_PATH: if (remove(instance) < 0 && errno != ENOENT) - return log_error_errno(errno, "remove(%s): %m", instance); + return log_error_errno(errno, "rm(%s): %m", instance); break; @@ -1038,12 +1100,13 @@ static int remove_item(Item *i) { case WRITE_FILE: case COPY_FILES: case SET_XATTR: + case SET_ACL: break; case REMOVE_PATH: case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - r = glob_item(i, remove_item_instance); + r = glob_item(i, remove_item_instance, false); break; } @@ -1110,7 +1173,7 @@ static int clean_item(Item *i) { clean_item_instance(i, i->path); break; case IGNORE_DIRECTORY_PATH: - r = glob_item(i, clean_item_instance); + r = glob_item(i, clean_item_instance, false); break; default: break; @@ -1119,6 +1182,8 @@ static int clean_item(Item *i) { return r; } +static int process_item_array(ItemArray *array); + static int process_item(Item *i) { int r, q, p, t = 0; _cleanup_free_ char *prefix = NULL; @@ -1135,13 +1200,13 @@ static int process_item(Item *i) { return log_oom(); PATH_FOREACH_PREFIX(prefix, i->path) { - Item *j; + ItemArray *j; j = hashmap_get(items, prefix); if (j) { int s; - s = process_item(j); + s = process_item_array(j); if (s < 0 && t == 0) t = s; } @@ -1157,57 +1222,71 @@ static int process_item(Item *i) { p; } -static void item_free(Item *i) { +static int process_item_array(ItemArray *array) { + unsigned n; + int r = 0, k; + + assert(array); - if (!i) - return; + for (n = 0; n < array->count; n++) { + k = process_item(array->items + n); + if (k < 0 && r == 0) + r = k; + } + return r; +} + +static void item_free_contents(Item *i) { + assert(i); free(i->path); free(i->argument); strv_free(i->xattrs); - free(i); + +#ifdef HAVE_ACL + acl_free(i->acl_access); + acl_free(i->acl_default); +#endif } -DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free); +static void item_array_free(ItemArray *a) { + unsigned n; + + if (!a) + return; + + for (n = 0; n < a->count; n++) + item_free_contents(a->items + n); + free(a->items); + free(a); +} -static bool item_equal(Item *a, Item *b) { +static bool item_compatible(Item *a, Item *b) { assert(a); assert(b); + assert(streq(a->path, b->path)); - if (!streq_ptr(a->path, b->path)) - return false; + if (takes_ownership(a->type) && takes_ownership(b->type)) + /* check if the items are the same */ + return streq_ptr(a->argument, b->argument) && - if (a->type != b->type) - return false; + a->uid_set == b->uid_set && + a->uid == b->uid && - if (a->uid_set != b->uid_set || - (a->uid_set && a->uid != b->uid)) - return false; + a->gid_set == b->gid_set && + a->gid == b->gid && - if (a->gid_set != b->gid_set || - (a->gid_set && a->gid != b->gid)) - return false; + a->mode_set == b->mode_set && + a->mode == b->mode && - if (a->mode_set != b->mode_set || - (a->mode_set && a->mode != b->mode)) - return false; + a->age_set == b->age_set && + a->age == b->age && - if (a->age_set != b->age_set || - (a->age_set && a->age != b->age)) - return false; + a->mask_perms == b->mask_perms && - if ((a->type == CREATE_FILE || - a->type == TRUNCATE_FILE || - a->type == WRITE_FILE || - a->type == CREATE_SYMLINK || - a->type == COPY_FILES) && - !streq_ptr(a->argument, b->argument)) - return false; + a->keep_first_level == b->keep_first_level && - if ((a->type == CREATE_CHAR_DEVICE || - a->type == CREATE_BLOCK_DEVICE) && - a->major_minor != b->major_minor) - return false; + a->major_minor == b->major_minor; return true; } @@ -1239,11 +1318,11 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { }; _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL; - _cleanup_(item_freep) Item *i = NULL; - Item *existing; - char type; + _cleanup_(item_free_contents) Item i = {}; + ItemArray *existing; Hashmap *h; - int r, n = -1; + int r, c = -1, pos; + bool force = false, boot = false; assert(fname); assert(line >= 1); @@ -1257,7 +1336,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { &user, &group, &age, - &n); + &c); if (r < 2) { log_error("[%s:%u] Syntax error.", fname, line); return -EIO; @@ -1268,38 +1347,40 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return -EINVAL; } - if (strlen(action) > 1 && !in_charset(action+1, "!+")) { - log_error("[%s:%u] Unknown modifiers in command '%s'", fname, line, action); - return -EINVAL; + for (pos = 1; action[pos]; pos++) { + if (action[pos] == '!' && !boot) + boot = true; + else if (action[pos] == '+' && !force) + force = true; + else { + log_error("[%s:%u] Unknown modifiers in command '%s'", + fname, line, action); + return -EINVAL; + } } - if (strchr(action+1, '!') && !arg_boot) + if (boot && !arg_boot) return 0; - type = action[0]; - - i = new0(Item, 1); - if (!i) - return log_oom(); - - i->force = !!strchr(action+1, '+'); + i.type = action[0]; + i.force = force; - r = specifier_printf(path, specifier_table, NULL, &i->path); + r = specifier_printf(path, specifier_table, NULL, &i.path); if (r < 0) { log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path); return r; } - 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) + if (c >= 0) { + c += strspn(buffer+c, WHITESPACE); + if (buffer[c] != 0 && (buffer[c] != '-' || buffer[c+1] != 0)) { + i.argument = unquote(buffer+c, "\""); + if (!i.argument) return log_oom(); } } - switch (type) { + switch (i.type) { case CREATE_FILE: case TRUNCATE_FILE: @@ -1317,113 +1398,119 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { break; case CREATE_SYMLINK: - if (!i->argument) { - i->argument = strappend("/usr/share/factory", i->path); - if (!i->argument) + if (!i.argument) { + i.argument = strappend("/usr/share/factory/", i.path); + if (!i.argument) return log_oom(); } break; case WRITE_FILE: - if (!i->argument) { + if (!i.argument) { log_error("[%s:%u] Write file requires argument.", fname, line); return -EBADMSG; } break; case COPY_FILES: - if (!i->argument) { - i->argument = strappend("/usr/share/factory", i->path); - if (!i->argument) + if (!i.argument) { + i.argument = strappend("/usr/share/factory/", i.path); + if (!i.argument) return log_oom(); - } - - if (!path_is_absolute(i->argument)) { + } else if (!path_is_absolute(i.argument)) { log_error("[%s:%u] Source path is not absolute.", fname, line); return -EBADMSG; } - path_kill_slashes(i->argument); + path_kill_slashes(i.argument); break; case CREATE_CHAR_DEVICE: case CREATE_BLOCK_DEVICE: { unsigned major, minor; - if (!i->argument) { + if (!i.argument) { log_error("[%s:%u] Device file requires argument.", fname, line); return -EBADMSG; } - 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); + 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); return -EBADMSG; } - i->major_minor = makedev(major, minor); + i.major_minor = makedev(major, minor); break; } case SET_XATTR: - if (!i->argument) { + if (!i.argument) { log_error("[%s:%u] Set extended attribute requires argument.", fname, line); return -EBADMSG; } - r = get_xattrs_from_arg(i); + r = get_xattrs_from_arg(&i); + if (r < 0) + return r; + break; + + case SET_ACL: + if (!i.argument) { + log_error("[%s:%u] Set ACLs requires argument.", fname, line); + return -EBADMSG; + } + r = get_acls_from_arg(&i); if (r < 0) return r; break; default: - log_error("[%s:%u] Unknown command type '%c'.", fname, line, type); + log_error("[%s:%u] Unknown command type '%c'.", fname, line, i.type); return -EBADMSG; } - i->type = type; - - if (!path_is_absolute(i->path)) { - log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path); + if (!path_is_absolute(i.path)) { + log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path); return -EBADMSG; } - path_kill_slashes(i->path); + path_kill_slashes(i.path); - if (!should_include_path(i->path)) + if (!should_include_path(i.path)) return 0; if (arg_root) { char *p; - p = strappend(arg_root, i->path); + p = strappend(arg_root, i.path); if (!p) return log_oom(); - free(i->path); - i->path = p; + free(i.path); + i.path = p; } if (user && !streq(user, "-")) { const char *u = user; - r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); + r = get_user_creds(&u, &i.uid, NULL, NULL, NULL); if (r < 0) { log_error("[%s:%u] Unknown user '%s'.", fname, line, user); return r; } - i->uid_set = true; + i.uid_set = true; } if (group && !streq(group, "-")) { const char *g = group; - r = get_group_creds(&g, &i->gid); + r = get_group_creds(&g, &i.gid); if (r < 0) { log_error("[%s:%u] Unknown group '%s'.", fname, line, group); return r; } - i->gid_set = true; + i.gid_set = true; } if (mode && !streq(mode, "-")) { @@ -1431,7 +1518,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { unsigned m; if (*mm == '~') { - i->mask_perms = true; + i.mask_perms = true; mm++; } @@ -1440,65 +1527,51 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return -ENOENT; } - i->mode = m; - i->mode_set = true; + i.mode = m; + i.mode_set = true; } else - i->mode = - i->type == CREATE_DIRECTORY || - i->type == CREATE_SUBVOLUME || - i->type == TRUNCATE_DIRECTORY ? 0755 : 0644; + i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY) + ? 0755 : 0644; if (age && !streq(age, "-")) { const char *a = age; if (*a == '~') { - i->keep_first_level = true; + i.keep_first_level = true; a++; } - if (parse_sec(a, &i->age) < 0) { + if (parse_sec(a, &i.age) < 0) { log_error("[%s:%u] Invalid age '%s'.", fname, line, age); return -EBADMSG; } - i->age_set = true; + i.age_set = true; } - h = needs_glob(i->type) ? globs : items; + h = needs_glob(i.type) ? globs : items; - existing = hashmap_get(h, i->path); + existing = hashmap_get(h, i.path); if (existing) { - if (i->type == SET_XATTR) { - r = strv_extend_strv(&existing->xattrs, i->xattrs); - if (r < 0) - return log_oom(); - return 0; - } else if (existing->type == SET_XATTR) { - r = strv_extend_strv(&i->xattrs, existing->xattrs); - if (r < 0) - return log_oom(); - r = hashmap_replace(h, i->path, i); - if (r < 0) { - log_error("Failed to replace item for %s.", i->path); - return r; - } - item_free(existing); - } else { - /* Two identical items are fine */ - if (!item_equal(existing, i)) - log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path); - return 0; + unsigned n; + + for (n = 0; n < existing->count; n++) { + if (!item_compatible(existing->items + n, &i)) + log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.", + fname, line, i.path); } } else { - r = hashmap_put(h, i->path, i); - if (r < 0) { - log_error("Failed to insert item %s: %s", i->path, strerror(-r)); - return r; - } + existing = new0(ItemArray, 1); + r = hashmap_put(h, i.path, existing); + if (r < 0) + return log_oom(); } - i = NULL; /* avoid cleanup */ + if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1)) + return log_oom(); + memcpy(existing->items + existing->count++, &i, sizeof(i)); + zero(i); return 0; } @@ -1683,7 +1756,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { int main(int argc, char *argv[]) { int r, k; - Item *i; + ItemArray *a; Iterator iterator; r = parse_argv(argc, argv); @@ -1734,24 +1807,24 @@ int main(int argc, char *argv[]) { } } - HASHMAP_FOREACH(i, globs, iterator) { - k = process_item(i); + HASHMAP_FOREACH(a, globs, iterator) { + k = process_item_array(a); if (k < 0 && r == 0) r = k; } - HASHMAP_FOREACH(i, items, iterator) { - k = process_item(i); + HASHMAP_FOREACH(a, items, iterator) { + k = process_item_array(a); if (k < 0 && r == 0) r = k; } finish: - while ((i = hashmap_steal_first(items))) - item_free(i); + while ((a = hashmap_steal_first(items))) + item_array_free(a); - while ((i = hashmap_steal_first(globs))) - item_free(i); + while ((a = hashmap_steal_first(globs))) + item_array_free(a); hashmap_free(items); hashmap_free(globs);