+ i->age_set = true;
+ }
+
+ h = needs_glob(i->type) ? globs : items;
+
+ if ((existing = hashmap_get(h, i->path))) {
+
+ /* 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;
+ }
+
+ if ((r = hashmap_put(h, i->path, i)) < 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 help(void) {
+
+ printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+ "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
+ " -h --help Show this help\n"
+ " --create Create marked files/directories\n"
+ " --clean Clean up marked directories\n"
+ " --remove Remove marked files/directories\n"
+ " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_CREATE,
+ ARG_CLEAN,
+ ARG_REMOVE,
+ ARG_PREFIX
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "create", no_argument, NULL, ARG_CREATE },
+ { "clean", no_argument, NULL, ARG_CLEAN },
+ { "remove", no_argument, NULL, ARG_REMOVE },
+ { "prefix", required_argument, NULL, ARG_PREFIX },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int 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_CREATE:
+ arg_create = true;
+ break;
+
+ case ARG_CLEAN:
+ arg_clean = true;
+ break;
+
+ case ARG_REMOVE:
+ arg_remove = true;
+ break;
+
+ case ARG_PREFIX:
+ arg_prefix = optarg;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (!arg_clean && !arg_create && !arg_remove) {
+ log_error("You need to specify at least one of --clean, --create or --remove.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int read_config_file(const char *fn, bool ignore_enoent) {
+ FILE *f;
+ unsigned v = 0;
+ int r = 0;
+
+ assert(fn);
+
+ if (!(f = fopen(fn, "re"))) {
+
+ if (ignore_enoent && errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open %s: %m", fn);
+ return -errno;
+ }
+
+ log_debug("apply: %s\n", fn);
+ for (;;) {
+ char line[LINE_MAX], *l;
+ int k;
+
+ if (!(fgets(line, sizeof(line), f)))
+ break;
+
+ v++;
+
+ l = strstrip(line);
+ if (*l == '#' || *l == 0)
+ continue;
+
+ if ((k = parse_line(fn, v, l)) < 0)
+ if (r == 0)
+ r = k;
+ }
+
+ if (ferror(f)) {
+ log_error("Failed to read from file %s: %m", fn);
+ if (r == 0)
+ r = -EIO;
+ }
+
+ fclose(f);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ Item *i;
+ Iterator iterator;
+
+ if ((r = parse_argv(argc, argv)) <= 0)
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ label_init();
+
+ items = hashmap_new(string_hash_func, string_compare_func);
+ globs = hashmap_new(string_hash_func, string_compare_func);
+
+ if (!items || !globs) {
+ log_error("Out of memory");
+ r = EXIT_FAILURE;
+ goto finish;
+ }
+
+ r = EXIT_SUCCESS;
+
+ if (optind < argc) {
+ int j;
+
+ for (j = optind; j < argc; j++)
+ if (read_config_file(argv[j], false) < 0)
+ r = EXIT_FAILURE;
+
+ } else {
+ char **files, **f;
+
+ r = conf_files_list(&files, ".conf",
+ "/run/tmpfiles.d",
+ "/etc/tmpfiles.d",
+ "/usr/local/lib/tmpfiles.d",
+ "/usr/lib/tmpfiles.d",
+ NULL);
+ if (r < 0) {