1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering, Kay Sievers
7 Copyright 2015 Zbigniew Jędrzejewski-Szmek
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
37 #include <sys/xattr.h>
42 #include "btrfs-util.h"
43 #include "capability.h"
44 #include "conf-files.h"
46 #include "formats-util.h"
52 #include "path-util.h"
54 #include "selinux-util.h"
56 #include "specifier.h"
60 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
61 * them in the file system. This is intended to be used to create
62 * properly owned directories beneath /tmp, /var/tmp, /run, which are
63 * volatile and hence need to be recreated on bootup. */
65 typedef enum ItemType
{
66 /* These ones take file names */
69 CREATE_DIRECTORY
= 'd',
70 TRUNCATE_DIRECTORY
= 'D',
71 CREATE_SUBVOLUME
= 'v',
72 CREATE_SUBVOLUME_INHERIT_QUOTA
= 'q',
73 CREATE_SUBVOLUME_NEW_QUOTA
= 'Q',
76 CREATE_CHAR_DEVICE
= 'c',
77 CREATE_BLOCK_DEVICE
= 'b',
80 /* These ones take globs */
83 RECURSIVE_SET_XATTR
= 'T',
85 RECURSIVE_SET_ACL
= 'A',
87 RECURSIVE_SET_ATTRIBUTE
= 'H',
89 IGNORE_DIRECTORY_PATH
= 'X',
91 RECURSIVE_REMOVE_PATH
= 'R',
93 RECURSIVE_RELABEL_PATH
= 'Z',
94 ADJUST_MODE
= 'm', /* legacy, 'z' is identical to this */
113 unsigned attribute_value
;
114 unsigned attribute_mask
;
121 bool attribute_set
:1;
123 bool keep_first_level
:1;
130 typedef struct ItemArray
{
136 static bool arg_create
= false;
137 static bool arg_clean
= false;
138 static bool arg_remove
= false;
139 static bool arg_boot
= false;
141 static char **arg_include_prefixes
= NULL
;
142 static char **arg_exclude_prefixes
= NULL
;
143 static char *arg_root
= NULL
;
145 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("tmpfiles");
147 #define MAX_DEPTH 256
149 static OrderedHashmap
*items
= NULL
, *globs
= NULL
;
150 static Set
*unix_sockets
= NULL
;
152 static const Specifier specifier_table
[] = {
153 { 'm', specifier_machine_id
, NULL
},
154 { 'b', specifier_boot_id
, NULL
},
155 { 'H', specifier_host_name
, NULL
},
156 { 'v', specifier_kernel_release
, NULL
},
160 static bool needs_glob(ItemType t
) {
164 IGNORE_DIRECTORY_PATH
,
166 RECURSIVE_REMOVE_PATH
,
169 RECURSIVE_RELABEL_PATH
,
175 RECURSIVE_SET_ATTRIBUTE
);
178 static bool takes_ownership(ItemType t
) {
185 CREATE_SUBVOLUME_INHERIT_QUOTA
,
186 CREATE_SUBVOLUME_NEW_QUOTA
,
194 IGNORE_DIRECTORY_PATH
,
196 RECURSIVE_REMOVE_PATH
);
199 static struct Item
* find_glob(OrderedHashmap
*h
, const char *match
) {
203 ORDERED_HASHMAP_FOREACH(j
, h
, i
) {
206 for (n
= 0; n
< j
->count
; n
++) {
207 Item
*item
= j
->items
+ n
;
209 if (fnmatch(item
->path
, match
, FNM_PATHNAME
|FNM_PERIOD
) == 0)
217 static void load_unix_sockets(void) {
218 _cleanup_fclose_
FILE *f
= NULL
;
224 /* We maintain a cache of the sockets we found in
225 * /proc/net/unix to speed things up a little. */
227 unix_sockets
= set_new(&string_hash_ops
);
231 f
= fopen("/proc/net/unix", "re");
236 if (!fgets(line
, sizeof(line
), f
))
243 if (!fgets(line
, sizeof(line
), f
))
248 p
= strchr(line
, ':');
256 p
+= strspn(p
, WHITESPACE
);
257 p
+= strcspn(p
, WHITESPACE
); /* skip one more word */
258 p
+= strspn(p
, WHITESPACE
);
267 path_kill_slashes(s
);
269 k
= set_consume(unix_sockets
, s
);
270 if (k
< 0 && k
!= -EEXIST
)
277 set_free_free(unix_sockets
);
281 static bool unix_socket_alive(const char *fn
) {
287 return !!set_get(unix_sockets
, (char*) fn
);
289 /* We don't know, so assume yes */
293 static int dir_is_mount_point(DIR *d
, const char *subdir
) {
295 union file_handle_union h
= FILE_HANDLE_INIT
;
296 int mount_id_parent
, mount_id
;
299 r_p
= name_to_handle_at(dirfd(d
), ".", &h
.handle
, &mount_id_parent
, 0);
303 h
.handle
.handle_bytes
= MAX_HANDLE_SZ
;
304 r
= name_to_handle_at(dirfd(d
), subdir
, &h
.handle
, &mount_id
, 0);
308 /* got no handle; make no assumptions, return error */
309 if (r_p
< 0 && r
< 0)
312 /* got both handles; if they differ, it is a mount point */
313 if (r_p
>= 0 && r
>= 0)
314 return mount_id_parent
!= mount_id
;
316 /* got only one handle; assume different mount points if one
317 * of both queries was not supported by the filesystem */
318 if (r_p
== -ENOSYS
|| r_p
== -EOPNOTSUPP
|| r
== -ENOSYS
|| r
== -EOPNOTSUPP
)
327 static DIR* xopendirat_nomod(int dirfd
, const char *path
) {
330 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
|O_NOATIME
);
334 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
338 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
);
340 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
345 static DIR* opendir_nomod(const char *path
) {
346 return xopendirat_nomod(AT_FDCWD
, path
);
349 static int dir_cleanup(
353 const struct stat
*ds
,
358 bool keep_this_level
) {
361 struct timespec times
[2];
362 bool deleted
= false;
365 while ((dent
= readdir(d
))) {
368 _cleanup_free_
char *sub_path
= NULL
;
370 if (STR_IN_SET(dent
->d_name
, ".", ".."))
373 if (fstatat(dirfd(d
), dent
->d_name
, &s
, AT_SYMLINK_NOFOLLOW
) < 0) {
377 /* FUSE, NFS mounts, SELinux might return EACCES */
379 log_debug_errno(errno
, "stat(%s/%s) failed: %m", p
, dent
->d_name
);
381 log_error_errno(errno
, "stat(%s/%s) failed: %m", p
, dent
->d_name
);
386 /* Stay on the same filesystem */
387 if (s
.st_dev
!= rootdev
) {
388 log_debug("Ignoring \"%s/%s\": different filesystem.", p
, dent
->d_name
);
392 /* Try to detect bind mounts of the same filesystem instance; they
393 * do not differ in device major/minors. This type of query is not
394 * supported on all kernels or filesystem types though. */
395 if (S_ISDIR(s
.st_mode
) && dir_is_mount_point(d
, dent
->d_name
) > 0) {
396 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
401 /* Do not delete read-only files owned by root */
402 if (s
.st_uid
== 0 && !(s
.st_mode
& S_IWUSR
)) {
403 log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p
, dent
->d_name
);
407 sub_path
= strjoin(p
, "/", dent
->d_name
, NULL
);
413 /* Is there an item configured for this path? */
414 if (ordered_hashmap_get(items
, sub_path
)) {
415 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path
);
419 if (find_glob(globs
, sub_path
)) {
420 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path
);
424 if (S_ISDIR(s
.st_mode
)) {
427 streq(dent
->d_name
, "lost+found") &&
429 log_debug("Ignoring \"%s\".", sub_path
);
434 log_warning("Reached max depth on \"%s\".", sub_path
);
436 _cleanup_closedir_
DIR *sub_dir
;
439 sub_dir
= xopendirat_nomod(dirfd(d
), dent
->d_name
);
442 r
= log_error_errno(errno
, "opendir(%s) failed: %m", sub_path
);
447 q
= dir_cleanup(i
, sub_path
, sub_dir
, &s
, cutoff
, rootdev
, false, maxdepth
-1, false);
452 /* Note: if you are wondering why we don't
453 * support the sticky bit for excluding
454 * directories from cleaning like we do it for
455 * other file system objects: well, the sticky
456 * bit already has a meaning for directories,
457 * so we don't want to overload that. */
459 if (keep_this_level
) {
460 log_debug("Keeping \"%s\".", sub_path
);
464 /* Ignore ctime, we change it when deleting */
465 age
= timespec_load(&s
.st_mtim
);
467 char a
[FORMAT_TIMESTAMP_MAX
];
468 /* Follows spelling in stat(1). */
469 log_debug("Directory \"%s\": modify time %s is too new.",
471 format_timestamp_us(a
, sizeof(a
), age
));
475 age
= timespec_load(&s
.st_atim
);
477 char a
[FORMAT_TIMESTAMP_MAX
];
478 log_debug("Directory \"%s\": access time %s is too new.",
480 format_timestamp_us(a
, sizeof(a
), age
));
484 log_debug("Removing directory \"%s\".", sub_path
);
485 if (unlinkat(dirfd(d
), dent
->d_name
, AT_REMOVEDIR
) < 0)
486 if (errno
!= ENOENT
&& errno
!= ENOTEMPTY
) {
487 log_error_errno(errno
, "rmdir(%s): %m", sub_path
);
492 /* Skip files for which the sticky bit is
493 * set. These are semantics we define, and are
494 * unknown elsewhere. See XDG_RUNTIME_DIR
495 * specification for details. */
496 if (s
.st_mode
& S_ISVTX
) {
497 log_debug("Skipping \"%s\": sticky bit set.", sub_path
);
501 if (mountpoint
&& S_ISREG(s
.st_mode
))
502 if (s
.st_uid
== 0 && STR_IN_SET(dent
->d_name
,
506 log_debug("Skipping \"%s\".", sub_path
);
510 /* Ignore sockets that are listed in /proc/net/unix */
511 if (S_ISSOCK(s
.st_mode
) && unix_socket_alive(sub_path
)) {
512 log_debug("Skipping \"%s\": live socket.", sub_path
);
516 /* Ignore device nodes */
517 if (S_ISCHR(s
.st_mode
) || S_ISBLK(s
.st_mode
)) {
518 log_debug("Skipping \"%s\": a device.", sub_path
);
522 /* Keep files on this level around if this is
524 if (keep_this_level
) {
525 log_debug("Keeping \"%s\".", sub_path
);
529 age
= timespec_load(&s
.st_mtim
);
531 char a
[FORMAT_TIMESTAMP_MAX
];
532 /* Follows spelling in stat(1). */
533 log_debug("File \"%s\": modify time %s is too new.",
535 format_timestamp_us(a
, sizeof(a
), age
));
539 age
= timespec_load(&s
.st_atim
);
541 char a
[FORMAT_TIMESTAMP_MAX
];
542 log_debug("File \"%s\": access time %s is too new.",
544 format_timestamp_us(a
, sizeof(a
), age
));
548 age
= timespec_load(&s
.st_ctim
);
550 char a
[FORMAT_TIMESTAMP_MAX
];
551 log_debug("File \"%s\": change time %s is too new.",
553 format_timestamp_us(a
, sizeof(a
), age
));
557 log_debug("unlink \"%s\"", sub_path
);
559 if (unlinkat(dirfd(d
), dent
->d_name
, 0) < 0)
561 r
= log_error_errno(errno
, "unlink(%s): %m", sub_path
);
570 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
];
572 /* Restore original directory timestamps */
573 times
[0] = ds
->st_atim
;
574 times
[1] = ds
->st_mtim
;
576 age1
= timespec_load(&ds
->st_atim
);
577 age2
= timespec_load(&ds
->st_mtim
);
578 log_debug("Restoring access and modification time on \"%s\": %s, %s",
580 format_timestamp_us(a
, sizeof(a
), age1
),
581 format_timestamp_us(b
, sizeof(b
), age2
));
582 if (futimens(dirfd(d
), times
) < 0)
583 log_error_errno(errno
, "utimensat(%s): %m", p
);
589 static int path_set_perms(Item
*i
, const char *path
) {
590 _cleanup_close_
int fd
= -1;
596 /* We open the file with O_PATH here, to make the operation
597 * somewhat atomic. Also there's unfortunately no fchmodat()
598 * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
601 fd
= open(path
, O_RDONLY
|O_NOFOLLOW
|O_CLOEXEC
|O_PATH
|O_NOATIME
);
603 return log_error_errno(errno
, "Adjusting owner and mode for %s failed: %m", path
);
605 if (fstatat(fd
, "", &st
, AT_EMPTY_PATH
) < 0)
606 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
608 if (S_ISLNK(st
.st_mode
))
609 log_debug("Skipping mode an owner fix for symlink %s.", path
);
611 char fn
[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
612 xsprintf(fn
, "/proc/self/fd/%i", fd
);
614 /* not using i->path directly because it may be a glob */
619 if (!(st
.st_mode
& 0111))
621 if (!(st
.st_mode
& 0222))
623 if (!(st
.st_mode
& 0444))
625 if (!S_ISDIR(st
.st_mode
))
626 m
&= ~07000; /* remove sticky/sgid/suid bit, unless directory */
629 if (m
== (st
.st_mode
& 07777))
630 log_debug("\"%s\" has right mode %o", path
, st
.st_mode
);
632 log_debug("chmod \"%s\" to mode %o", path
, m
);
633 if (chmod(fn
, m
) < 0)
634 return log_error_errno(errno
, "chmod(%s) failed: %m", path
);
638 if ((i
->uid
!= st
.st_uid
|| i
->gid
!= st
.st_gid
) &&
639 (i
->uid_set
|| i
->gid_set
)) {
640 log_debug("chown \"%s\" to "UID_FMT
"."GID_FMT
,
642 i
->uid_set
? i
->uid
: UID_INVALID
,
643 i
->gid_set
? i
->gid
: GID_INVALID
);
645 i
->uid_set
? i
->uid
: UID_INVALID
,
646 i
->gid_set
? i
->gid
: GID_INVALID
) < 0)
647 return log_error_errno(errno
, "chown(%s) failed: %m", path
);
653 return label_fix(path
, false, false);
656 static int parse_xattrs_from_arg(Item
*i
) {
666 _cleanup_free_
char *name
= NULL
, *value
= NULL
, *xattr
= NULL
, *xattr_replaced
= NULL
;
668 r
= extract_first_word(&p
, &xattr
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
670 log_warning_errno(r
, "Failed to parse extended attribute '%s', ignoring: %m", p
);
674 r
= specifier_printf(xattr
, specifier_table
, NULL
, &xattr_replaced
);
676 return log_error_errno(r
, "Failed to replace specifiers in extended attribute '%s': %m", xattr
);
678 r
= split_pair(xattr_replaced
, "=", &name
, &value
);
680 log_warning_errno(r
, "Failed to parse extended attribute, ignoring: %s", xattr
);
684 if (isempty(name
) || isempty(value
)) {
685 log_warning("Malformed extended attribute found, ignoring: %s", xattr
);
689 if (strv_push_pair(&i
->xattrs
, name
, value
) < 0)
698 static int path_set_xattrs(Item
*i
, const char *path
) {
699 char **name
, **value
;
704 STRV_FOREACH_PAIR(name
, value
, i
->xattrs
) {
708 log_debug("Setting extended attribute '%s=%s' on %s.", *name
, *value
, path
);
709 if (lsetxattr(path
, *name
, *value
, n
, 0) < 0) {
710 log_error("Setting extended attribute %s=%s on %s failed: %m", *name
, *value
, path
);
717 static int parse_acls_from_arg(Item
*item
) {
723 /* If force (= modify) is set, we will not modify the acl
724 * afterwards, so the mask can be added now if necessary. */
726 r
= parse_acl(item
->argument
, &item
->acl_access
, &item
->acl_default
, !item
->force
);
728 log_warning_errno(r
, "Failed to parse ACL \"%s\": %m. Ignoring", item
->argument
);
730 log_warning_errno(ENOSYS
, "ACLs are not supported. Ignoring");
737 static int path_set_acl(const char *path
, const char *pretty
, acl_type_t type
, acl_t acl
, bool modify
) {
738 _cleanup_(acl_free_charpp
) char *t
= NULL
;
739 _cleanup_(acl_freep
) acl_t dup
= NULL
;
742 /* Returns 0 for success, positive error if already warned,
743 * negative error otherwise. */
746 r
= acls_for_file(path
, type
, acl
, &dup
);
750 r
= calc_acl_mask_if_needed(&dup
);
758 /* the mask was already added earlier if needed */
761 r
= add_base_acls_if_needed(&dup
, path
);
765 t
= acl_to_any_text(dup
, NULL
, ',', TEXT_ABBREVIATE
);
766 log_debug("Setting %s ACL %s on %s.",
767 type
== ACL_TYPE_ACCESS
? "access" : "default",
770 r
= acl_set_file(path
, type
, dup
);
772 /* Return positive to indicate we already warned */
773 return -log_error_errno(errno
,
774 "Setting %s ACL \"%s\" on %s failed: %m",
775 type
== ACL_TYPE_ACCESS
? "access" : "default",
782 static int path_set_acls(Item
*item
, const char *path
) {
785 char fn
[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
786 _cleanup_close_
int fd
= -1;
792 fd
= open(path
, O_RDONLY
|O_NOFOLLOW
|O_CLOEXEC
|O_PATH
|O_NOATIME
);
794 return log_error_errno(errno
, "Adjusting ACL of %s failed: %m", path
);
796 if (fstatat(fd
, "", &st
, AT_EMPTY_PATH
) < 0)
797 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
799 if (S_ISLNK(st
.st_mode
)) {
800 log_debug("Skipping ACL fix for symlink %s.", path
);
804 xsprintf(fn
, "/proc/self/fd/%i", fd
);
806 if (item
->acl_access
)
807 r
= path_set_acl(fn
, path
, ACL_TYPE_ACCESS
, item
->acl_access
, item
->force
);
809 if (r
== 0 && item
->acl_default
)
810 r
= path_set_acl(fn
, path
, ACL_TYPE_DEFAULT
, item
->acl_default
, item
->force
);
813 return -r
; /* already warned */
814 else if (r
== -EOPNOTSUPP
) {
815 log_debug_errno(r
, "ACLs not supported by file system at %s", path
);
818 log_error_errno(r
, "ACL operation on \"%s\" failed: %m", path
);
823 #define ATTRIBUTES_ALL \
832 FS_JOURNAL_DATA_FL | \
839 static int parse_attribute_from_arg(Item
*item
) {
841 static const struct {
845 { 'A', FS_NOATIME_FL
}, /* do not update atime */
846 { 'S', FS_SYNC_FL
}, /* Synchronous updates */
847 { 'D', FS_DIRSYNC_FL
}, /* dirsync behaviour (directories only) */
848 { 'a', FS_APPEND_FL
}, /* writes to file may only append */
849 { 'c', FS_COMPR_FL
}, /* Compress file */
850 { 'd', FS_NODUMP_FL
}, /* do not dump file */
851 { 'e', FS_EXTENT_FL
}, /* Top of directory hierarchies*/
852 { 'i', FS_IMMUTABLE_FL
}, /* Immutable file */
853 { 'j', FS_JOURNAL_DATA_FL
}, /* Reserved for ext3 */
854 { 's', FS_SECRM_FL
}, /* Secure deletion */
855 { 'u', FS_UNRM_FL
}, /* Undelete */
856 { 't', FS_NOTAIL_FL
}, /* file tail should not be merged */
857 { 'T', FS_TOPDIR_FL
}, /* Top of directory hierarchies*/
858 { 'C', FS_NOCOW_FL
}, /* Do not cow file */
867 unsigned value
= 0, mask
= 0;
877 } else if (*p
== '-') {
880 } else if (*p
== '=') {
886 if (isempty(p
) && mode
!= MODE_SET
) {
887 log_error("Setting file attribute on '%s' needs an attribute specification.", item
->path
);
891 for (; p
&& *p
; p
++) {
894 for (i
= 0; i
< ELEMENTSOF(attributes
); i
++)
895 if (*p
== attributes
[i
].character
)
898 if (i
>= ELEMENTSOF(attributes
)) {
899 log_error("Unknown file attribute '%c' on '%s'.", *p
, item
->path
);
903 v
= attributes
[i
].value
;
905 if (mode
== MODE_ADD
|| mode
== MODE_SET
)
913 if (mode
== MODE_SET
)
914 mask
|= ATTRIBUTES_ALL
;
918 item
->attribute_mask
= mask
;
919 item
->attribute_value
= value
;
920 item
->attribute_set
= true;
925 static int path_set_attribute(Item
*item
, const char *path
) {
926 _cleanup_close_
int fd
= -1;
931 if (!item
->attribute_set
|| item
->attribute_mask
== 0)
934 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_CLOEXEC
|O_NOATIME
|O_NOFOLLOW
);
937 return log_error_errno(errno
, "Skipping file attributes adjustment on symlink %s.", path
);
939 return log_error_errno(errno
, "Cannot open '%s': %m", path
);
942 if (fstat(fd
, &st
) < 0)
943 return log_error_errno(errno
, "Cannot stat '%s': %m", path
);
945 /* Issuing the file attribute ioctls on device nodes is not
946 * safe, as that will be delivered to the drivers, not the
947 * file system containing the device node. */
948 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
)) {
949 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path
);
953 f
= item
->attribute_value
& item
->attribute_mask
;
955 /* Mask away directory-specific flags */
956 if (!S_ISDIR(st
.st_mode
))
959 r
= chattr_fd(fd
, f
, item
->attribute_mask
);
961 log_full_errno(r
== -ENOTTY
? LOG_DEBUG
: LOG_WARNING
,
963 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
964 path
, item
->attribute_value
, item
->attribute_mask
);
969 static int write_one_file(Item
*i
, const char *path
) {
970 _cleanup_close_
int fd
= -1;
977 flags
= i
->type
== CREATE_FILE
? O_CREAT
|O_APPEND
|O_NOFOLLOW
:
978 i
->type
== TRUNCATE_FILE
? O_CREAT
|O_TRUNC
|O_NOFOLLOW
: 0;
980 RUN_WITH_UMASK(0000) {
981 mac_selinux_create_file_prepare(path
, S_IFREG
);
982 fd
= open(path
, flags
|O_NDELAY
|O_CLOEXEC
|O_WRONLY
|O_NOCTTY
, i
->mode
);
983 mac_selinux_create_file_clear();
987 if (i
->type
== WRITE_FILE
&& errno
== ENOENT
) {
988 log_debug_errno(errno
, "Not writing \"%s\": %m", path
);
993 if (!i
->argument
&& errno
== EROFS
&& stat(path
, &st
) == 0 &&
994 (i
->type
== CREATE_FILE
|| st
.st_size
== 0))
997 return log_error_errno(r
, "Failed to create file %s: %m", path
);
1001 _cleanup_free_
char *unescaped
= NULL
, *replaced
= NULL
;
1003 log_debug("%s to \"%s\".", i
->type
== CREATE_FILE
? "Appending" : "Writing", path
);
1005 r
= cunescape(i
->argument
, 0, &unescaped
);
1007 return log_error_errno(r
, "Failed to unescape parameter to write: %s", i
->argument
);
1009 r
= specifier_printf(unescaped
, specifier_table
, NULL
, &replaced
);
1011 return log_error_errno(r
, "Failed to replace specifiers in parameter to write '%s': %m", unescaped
);
1013 r
= loop_write(fd
, replaced
, strlen(replaced
), false);
1015 return log_error_errno(r
, "Failed to write file \"%s\": %m", path
);
1017 log_debug("\"%s\" has been created.", path
);
1019 fd
= safe_close(fd
);
1021 if (stat(path
, &st
) < 0)
1022 return log_error_errno(errno
, "stat(%s) failed: %m", path
);
1025 if (!S_ISREG(st
.st_mode
)) {
1026 log_error("%s is not a file.", path
);
1030 r
= path_set_perms(i
, path
);
1037 typedef int (*action_t
)(Item
*, const char *);
1039 static int item_do_children(Item
*i
, const char *path
, action_t action
) {
1040 _cleanup_closedir_
DIR *d
;
1046 /* This returns the first error we run into, but nevertheless
1049 d
= opendir_nomod(path
);
1051 return errno
== ENOENT
|| errno
== ENOTDIR
? 0 : -errno
;
1054 _cleanup_free_
char *p
= NULL
;
1061 if (errno
!= 0 && r
== 0)
1067 if (STR_IN_SET(de
->d_name
, ".", ".."))
1070 p
= strjoin(path
, "/", de
->d_name
, NULL
);
1075 if (q
< 0 && q
!= -ENOENT
&& r
== 0)
1078 if (IN_SET(de
->d_type
, DT_UNKNOWN
, DT_DIR
)) {
1079 q
= item_do_children(i
, p
, action
);
1080 if (q
< 0 && r
== 0)
1088 static int glob_item(Item
*i
, action_t action
, bool recursive
) {
1089 _cleanup_globfree_ glob_t g
= {
1090 .gl_closedir
= (void (*)(void *)) closedir
,
1091 .gl_readdir
= (struct dirent
*(*)(void *)) readdir
,
1092 .gl_opendir
= (void *(*)(const char *)) opendir_nomod
,
1100 k
= glob(i
->path
, GLOB_NOSORT
|GLOB_BRACE
|GLOB_ALTDIRFUNC
, NULL
, &g
);
1101 if (k
!= 0 && k
!= GLOB_NOMATCH
)
1102 return log_error_errno(errno
?: EIO
, "glob(%s) failed: %m", i
->path
);
1104 STRV_FOREACH(fn
, g
.gl_pathv
) {
1106 if (k
< 0 && r
== 0)
1110 k
= item_do_children(i
, *fn
, action
);
1111 if (k
< 0 && r
== 0)
1124 _CREATION_MODE_INVALID
= -1
1127 static const char *creation_mode_verb_table
[_CREATION_MODE_MAX
] = {
1128 [CREATION_NORMAL
] = "Created",
1129 [CREATION_EXISTING
] = "Found existing",
1130 [CREATION_FORCE
] = "Created replacement",
1133 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb
, CreationMode
);
1135 static int create_item(Item
*i
) {
1136 _cleanup_free_
char *resolved
= NULL
;
1139 CreationMode creation
;
1143 log_debug("Running create action for entry %c %s", (char) i
->type
, i
->path
);
1148 case IGNORE_DIRECTORY_PATH
:
1150 case RECURSIVE_REMOVE_PATH
:
1155 r
= write_one_file(i
, i
->path
);
1161 r
= specifier_printf(i
->argument
, specifier_table
, NULL
, &resolved
);
1163 return log_error_errno(r
, "Failed to substitute specifiers in copy source %s: %m", i
->argument
);
1165 log_debug("Copying tree \"%s\" to \"%s\".", resolved
, i
->path
);
1166 r
= copy_tree(resolved
, i
->path
, false);
1168 if (r
== -EROFS
&& stat(i
->path
, &st
) == 0)
1175 return log_error_errno(r
, "Failed to copy files to %s: %m", i
->path
);
1177 if (stat(resolved
, &a
) < 0)
1178 return log_error_errno(errno
, "stat(%s) failed: %m", resolved
);
1180 if (stat(i
->path
, &b
) < 0)
1181 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1183 if ((a
.st_mode
^ b
.st_mode
) & S_IFMT
) {
1184 log_debug("Can't copy to %s, file exists already and is of different type", i
->path
);
1189 r
= path_set_perms(i
, i
->path
);
1196 r
= glob_item(i
, write_one_file
, false);
1202 case CREATE_DIRECTORY
:
1203 case TRUNCATE_DIRECTORY
:
1204 case CREATE_SUBVOLUME
:
1205 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1206 case CREATE_SUBVOLUME_NEW_QUOTA
:
1208 RUN_WITH_UMASK(0000)
1209 mkdir_parents_label(i
->path
, 0755);
1211 if (IN_SET(i
->type
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
)) {
1212 RUN_WITH_UMASK((~i
->mode
) & 0777)
1213 r
= btrfs_subvol_make(i
->path
);
1217 if (IN_SET(i
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
) || r
== -ENOTTY
)
1218 RUN_WITH_UMASK(0000)
1219 r
= mkdir_label(i
->path
, i
->mode
);
1224 if (r
!= -EEXIST
&& r
!= -EROFS
)
1225 return log_error_errno(r
, "Failed to create directory or subvolume \"%s\": %m", i
->path
);
1227 k
= is_dir(i
->path
, false);
1228 if (k
== -ENOENT
&& r
== -EROFS
)
1229 return log_error_errno(r
, "%s does not exist and cannot be created as the file system is read-only.", i
->path
);
1231 return log_error_errno(k
, "Failed to check if %s exists: %m", i
->path
);
1233 log_warning("\"%s\" already exists and is not a directory.", i
->path
);
1237 creation
= CREATION_EXISTING
;
1239 creation
= CREATION_NORMAL
;
1241 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1243 if (IN_SET(i
->type
, CREATE_SUBVOLUME_NEW_QUOTA
, CREATE_SUBVOLUME_INHERIT_QUOTA
)) {
1244 r
= btrfs_subvol_auto_qgroup(i
->path
, 0, i
->type
== CREATE_SUBVOLUME_NEW_QUOTA
);
1246 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i
->path
);
1250 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i
->path
);
1254 return log_error_errno(r
, "Failed to adjust quota for subvolume \"%s\": %m", i
->path
);
1256 log_debug("Adjusted quota for subvolume \"%s\".", i
->path
);
1258 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i
->path
);
1261 r
= path_set_perms(i
, i
->path
);
1269 RUN_WITH_UMASK(0000) {
1270 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1271 r
= mkfifo(i
->path
, i
->mode
);
1272 mac_selinux_create_file_clear();
1276 if (errno
!= EEXIST
)
1277 return log_error_errno(errno
, "Failed to create fifo %s: %m", i
->path
);
1279 if (lstat(i
->path
, &st
) < 0)
1280 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1282 if (!S_ISFIFO(st
.st_mode
)) {
1285 RUN_WITH_UMASK(0000) {
1286 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1287 r
= mkfifo_atomic(i
->path
, i
->mode
);
1288 mac_selinux_create_file_clear();
1292 return log_error_errno(r
, "Failed to create fifo %s: %m", i
->path
);
1293 creation
= CREATION_FORCE
;
1295 log_warning("\"%s\" already exists and is not a fifo.", i
->path
);
1299 creation
= CREATION_EXISTING
;
1301 creation
= CREATION_NORMAL
;
1302 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1304 r
= path_set_perms(i
, i
->path
);
1311 case CREATE_SYMLINK
: {
1312 r
= specifier_printf(i
->argument
, specifier_table
, NULL
, &resolved
);
1314 return log_error_errno(r
, "Failed to substitute specifiers in symlink target %s: %m", i
->argument
);
1316 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1317 r
= symlink(resolved
, i
->path
);
1318 mac_selinux_create_file_clear();
1321 _cleanup_free_
char *x
= NULL
;
1323 if (errno
!= EEXIST
)
1324 return log_error_errno(errno
, "symlink(%s, %s) failed: %m", resolved
, i
->path
);
1326 r
= readlink_malloc(i
->path
, &x
);
1327 if (r
< 0 || !streq(resolved
, x
)) {
1330 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1331 r
= symlink_atomic(resolved
, i
->path
);
1332 mac_selinux_create_file_clear();
1335 return log_error_errno(r
, "symlink(%s, %s) failed: %m", resolved
, i
->path
);
1337 creation
= CREATION_FORCE
;
1339 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i
->path
);
1343 creation
= CREATION_EXISTING
;
1346 creation
= CREATION_NORMAL
;
1347 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1351 case CREATE_BLOCK_DEVICE
:
1352 case CREATE_CHAR_DEVICE
: {
1355 if (have_effective_cap(CAP_MKNOD
) == 0) {
1356 /* In a container we lack CAP_MKNOD. We
1357 shouldn't attempt to create the device node in
1358 that case to avoid noise, and we don't support
1359 virtualized devices in containers anyway. */
1361 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i
->path
);
1365 file_type
= i
->type
== CREATE_BLOCK_DEVICE
? S_IFBLK
: S_IFCHR
;
1367 RUN_WITH_UMASK(0000) {
1368 mac_selinux_create_file_prepare(i
->path
, file_type
);
1369 r
= mknod(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1370 mac_selinux_create_file_clear();
1374 if (errno
== EPERM
) {
1375 log_debug("We lack permissions, possibly because of cgroup configuration; "
1376 "skipping creation of device node %s.", i
->path
);
1380 if (errno
!= EEXIST
)
1381 return log_error_errno(errno
, "Failed to create device node %s: %m", i
->path
);
1383 if (lstat(i
->path
, &st
) < 0)
1384 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1386 if ((st
.st_mode
& S_IFMT
) != file_type
) {
1390 RUN_WITH_UMASK(0000) {
1391 mac_selinux_create_file_prepare(i
->path
, file_type
);
1392 r
= mknod_atomic(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1393 mac_selinux_create_file_clear();
1397 return log_error_errno(r
, "Failed to create device node \"%s\": %m", i
->path
);
1398 creation
= CREATION_FORCE
;
1400 log_debug("%s is not a device node.", i
->path
);
1404 creation
= CREATION_EXISTING
;
1406 creation
= CREATION_NORMAL
;
1408 log_debug("%s %s device node \"%s\" %u:%u.",
1409 creation_mode_verb_to_string(creation
),
1410 i
->type
== CREATE_BLOCK_DEVICE
? "block" : "char",
1411 i
->path
, major(i
->mode
), minor(i
->mode
));
1413 r
= path_set_perms(i
, i
->path
);
1422 r
= glob_item(i
, path_set_perms
, false);
1427 case RECURSIVE_RELABEL_PATH
:
1428 r
= glob_item(i
, path_set_perms
, true);
1434 r
= glob_item(i
, path_set_xattrs
, false);
1439 case RECURSIVE_SET_XATTR
:
1440 r
= glob_item(i
, path_set_xattrs
, true);
1446 r
= glob_item(i
, path_set_acls
, false);
1451 case RECURSIVE_SET_ACL
:
1452 r
= glob_item(i
, path_set_acls
, true);
1458 r
= glob_item(i
, path_set_attribute
, false);
1463 case RECURSIVE_SET_ATTRIBUTE
:
1464 r
= glob_item(i
, path_set_attribute
, true);
1473 static int remove_item_instance(Item
*i
, const char *instance
) {
1481 if (remove(instance
) < 0 && errno
!= ENOENT
)
1482 return log_error_errno(errno
, "rm(%s): %m", instance
);
1486 case TRUNCATE_DIRECTORY
:
1487 case RECURSIVE_REMOVE_PATH
:
1488 /* FIXME: we probably should use dir_cleanup() here
1489 * instead of rm_rf() so that 'x' is honoured. */
1490 log_debug("rm -rf \"%s\"", instance
);
1491 r
= rm_rf(instance
, (i
->type
== RECURSIVE_REMOVE_PATH
? REMOVE_ROOT
|REMOVE_SUBVOLUME
: 0) | REMOVE_PHYSICAL
);
1492 if (r
< 0 && r
!= -ENOENT
)
1493 return log_error_errno(r
, "rm_rf(%s): %m", instance
);
1498 assert_not_reached("wut?");
1504 static int remove_item(Item
*i
) {
1509 log_debug("Running remove action for entry %c %s", (char) i
->type
, i
->path
);
1515 case CREATE_DIRECTORY
:
1516 case CREATE_SUBVOLUME
:
1517 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1518 case CREATE_SUBVOLUME_NEW_QUOTA
:
1520 case CREATE_SYMLINK
:
1521 case CREATE_CHAR_DEVICE
:
1522 case CREATE_BLOCK_DEVICE
:
1524 case IGNORE_DIRECTORY_PATH
:
1527 case RECURSIVE_RELABEL_PATH
:
1531 case RECURSIVE_SET_XATTR
:
1533 case RECURSIVE_SET_ACL
:
1535 case RECURSIVE_SET_ATTRIBUTE
:
1539 case TRUNCATE_DIRECTORY
:
1540 case RECURSIVE_REMOVE_PATH
:
1541 r
= glob_item(i
, remove_item_instance
, false);
1548 static int clean_item_instance(Item
*i
, const char* instance
) {
1549 _cleanup_closedir_
DIR *d
= NULL
;
1553 char timestamp
[FORMAT_TIMESTAMP_MAX
];
1560 n
= now(CLOCK_REALTIME
);
1564 cutoff
= n
- i
->age
;
1566 d
= opendir_nomod(instance
);
1568 if (errno
== ENOENT
|| errno
== ENOTDIR
) {
1569 log_debug_errno(errno
, "Directory \"%s\": %m", instance
);
1573 log_error_errno(errno
, "Failed to open directory %s: %m", instance
);
1577 if (fstat(dirfd(d
), &s
) < 0)
1578 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1580 if (!S_ISDIR(s
.st_mode
)) {
1581 log_error("%s is not a directory.", i
->path
);
1585 if (fstatat(dirfd(d
), "..", &ps
, AT_SYMLINK_NOFOLLOW
) != 0)
1586 return log_error_errno(errno
, "stat(%s/..) failed: %m", i
->path
);
1588 mountpoint
= s
.st_dev
!= ps
.st_dev
|| s
.st_ino
== ps
.st_ino
;
1590 log_debug("Cleanup threshold for %s \"%s\" is %s",
1591 mountpoint
? "mount point" : "directory",
1593 format_timestamp_us(timestamp
, sizeof(timestamp
), cutoff
));
1595 return dir_cleanup(i
, instance
, d
, &s
, cutoff
, s
.st_dev
, mountpoint
,
1596 MAX_DEPTH
, i
->keep_first_level
);
1599 static int clean_item(Item
*i
) {
1604 log_debug("Running clean action for entry %c %s", (char) i
->type
, i
->path
);
1607 case CREATE_DIRECTORY
:
1608 case CREATE_SUBVOLUME
:
1609 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1610 case CREATE_SUBVOLUME_NEW_QUOTA
:
1611 case TRUNCATE_DIRECTORY
:
1614 clean_item_instance(i
, i
->path
);
1616 case IGNORE_DIRECTORY_PATH
:
1617 r
= glob_item(i
, clean_item_instance
, false);
1626 static int process_item_array(ItemArray
*array
);
1628 static int process_item(Item
*i
) {
1630 _cleanup_free_
char *prefix
= NULL
;
1639 prefix
= malloc(strlen(i
->path
) + 1);
1643 PATH_FOREACH_PREFIX(prefix
, i
->path
) {
1646 j
= ordered_hashmap_get(items
, prefix
);
1650 s
= process_item_array(j
);
1651 if (s
< 0 && t
== 0)
1656 r
= arg_create
? create_item(i
) : 0;
1657 q
= arg_remove
? remove_item(i
) : 0;
1658 p
= arg_clean
? clean_item(i
) : 0;
1666 static int process_item_array(ItemArray
*array
) {
1672 for (n
= 0; n
< array
->count
; n
++) {
1673 k
= process_item(array
->items
+ n
);
1674 if (k
< 0 && r
== 0)
1681 static void item_free_contents(Item
*i
) {
1685 strv_free(i
->xattrs
);
1688 acl_free(i
->acl_access
);
1689 acl_free(i
->acl_default
);
1693 static void item_array_free(ItemArray
*a
) {
1699 for (n
= 0; n
< a
->count
; n
++)
1700 item_free_contents(a
->items
+ n
);
1705 static int item_compare(const void *a
, const void *b
) {
1706 const Item
*x
= a
, *y
= b
;
1708 /* Make sure that the ownership taking item is put first, so
1709 * that we first create the node, and then can adjust it */
1711 if (takes_ownership(x
->type
) && !takes_ownership(y
->type
))
1713 if (!takes_ownership(x
->type
) && takes_ownership(y
->type
))
1716 return (int) x
->type
- (int) y
->type
;
1719 static bool item_compatible(Item
*a
, Item
*b
) {
1722 assert(streq(a
->path
, b
->path
));
1724 if (takes_ownership(a
->type
) && takes_ownership(b
->type
))
1725 /* check if the items are the same */
1726 return streq_ptr(a
->argument
, b
->argument
) &&
1728 a
->uid_set
== b
->uid_set
&&
1731 a
->gid_set
== b
->gid_set
&&
1734 a
->mode_set
== b
->mode_set
&&
1735 a
->mode
== b
->mode
&&
1737 a
->age_set
== b
->age_set
&&
1740 a
->mask_perms
== b
->mask_perms
&&
1742 a
->keep_first_level
== b
->keep_first_level
&&
1744 a
->major_minor
== b
->major_minor
;
1749 static bool should_include_path(const char *path
) {
1752 STRV_FOREACH(prefix
, arg_exclude_prefixes
)
1753 if (path_startswith(path
, *prefix
)) {
1754 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1759 STRV_FOREACH(prefix
, arg_include_prefixes
)
1760 if (path_startswith(path
, *prefix
)) {
1761 log_debug("Entry \"%s\" matches include prefix \"%s\".", path
, *prefix
);
1765 /* no matches, so we should include this path only if we
1766 * have no whitelist at all */
1767 if (strv_length(arg_include_prefixes
) == 0)
1770 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path
);
1774 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1776 _cleanup_free_
char *action
= NULL
, *mode
= NULL
, *user
= NULL
, *group
= NULL
, *age
= NULL
, *path
= NULL
;
1777 _cleanup_(item_free_contents
) Item i
= {};
1778 ItemArray
*existing
;
1781 bool force
= false, boot
= false;
1787 r
= extract_many_words(
1799 return log_error_errno(r
, "[%s:%u] Failed to parse line: %m", fname
, line
);
1801 log_error("[%s:%u] Syntax error.", fname
, line
);
1805 if (!isempty(buffer
) && !streq(buffer
, "-")) {
1806 i
.argument
= strdup(buffer
);
1811 if (isempty(action
)) {
1812 log_error("[%s:%u] Command too short '%s'.", fname
, line
, action
);
1816 for (pos
= 1; action
[pos
]; pos
++) {
1817 if (action
[pos
] == '!' && !boot
)
1819 else if (action
[pos
] == '+' && !force
)
1822 log_error("[%s:%u] Unknown modifiers in command '%s'",
1823 fname
, line
, action
);
1828 if (boot
&& !arg_boot
) {
1829 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1837 r
= specifier_printf(path
, specifier_table
, NULL
, &i
.path
);
1839 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, path
);
1845 case CREATE_DIRECTORY
:
1846 case CREATE_SUBVOLUME
:
1847 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1848 case CREATE_SUBVOLUME_NEW_QUOTA
:
1849 case TRUNCATE_DIRECTORY
:
1852 case IGNORE_DIRECTORY_PATH
:
1854 case RECURSIVE_REMOVE_PATH
:
1857 case RECURSIVE_RELABEL_PATH
:
1859 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname
, line
, i
.type
);
1867 case CREATE_SYMLINK
:
1869 i
.argument
= strappend("/usr/share/factory/", i
.path
);
1877 log_error("[%s:%u] Write file requires argument.", fname
, line
);
1884 i
.argument
= strappend("/usr/share/factory/", i
.path
);
1887 } else if (!path_is_absolute(i
.argument
)) {
1888 log_error("[%s:%u] Source path is not absolute.", fname
, line
);
1892 path_kill_slashes(i
.argument
);
1895 case CREATE_CHAR_DEVICE
:
1896 case CREATE_BLOCK_DEVICE
: {
1897 unsigned major
, minor
;
1900 log_error("[%s:%u] Device file requires argument.", fname
, line
);
1904 if (sscanf(i
.argument
, "%u:%u", &major
, &minor
) != 2) {
1905 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname
, line
, i
.argument
);
1909 i
.major_minor
= makedev(major
, minor
);
1914 case RECURSIVE_SET_XATTR
:
1916 log_error("[%s:%u] Set extended attribute requires argument.", fname
, line
);
1919 r
= parse_xattrs_from_arg(&i
);
1925 case RECURSIVE_SET_ACL
:
1927 log_error("[%s:%u] Set ACLs requires argument.", fname
, line
);
1930 r
= parse_acls_from_arg(&i
);
1936 case RECURSIVE_SET_ATTRIBUTE
:
1938 log_error("[%s:%u] Set file attribute requires argument.", fname
, line
);
1941 r
= parse_attribute_from_arg(&i
);
1947 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, (char) i
.type
);
1951 if (!path_is_absolute(i
.path
)) {
1952 log_error("[%s:%u] Path '%s' not absolute.", fname
, line
, i
.path
);
1956 path_kill_slashes(i
.path
);
1958 if (!should_include_path(i
.path
))
1964 p
= prefix_root(arg_root
, i
.path
);
1972 if (!isempty(user
) && !streq(user
, "-")) {
1973 const char *u
= user
;
1975 r
= get_user_creds(&u
, &i
.uid
, NULL
, NULL
, NULL
);
1977 log_error("[%s:%u] Unknown user '%s'.", fname
, line
, user
);
1984 if (!isempty(group
) && !streq(group
, "-")) {
1985 const char *g
= group
;
1987 r
= get_group_creds(&g
, &i
.gid
);
1989 log_error("[%s:%u] Unknown group '%s'.", fname
, line
, group
);
1996 if (!isempty(mode
) && !streq(mode
, "-")) {
1997 const char *mm
= mode
;
2001 i
.mask_perms
= true;
2005 if (parse_mode(mm
, &m
) < 0) {
2006 log_error("[%s:%u] Invalid mode '%s'.", fname
, line
, mode
);
2013 i
.mode
= IN_SET(i
.type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
) ? 0755 : 0644;
2015 if (!isempty(age
) && !streq(age
, "-")) {
2016 const char *a
= age
;
2019 i
.keep_first_level
= true;
2023 if (parse_sec(a
, &i
.age
) < 0) {
2024 log_error("[%s:%u] Invalid age '%s'.", fname
, line
, age
);
2031 h
= needs_glob(i
.type
) ? globs
: items
;
2033 existing
= ordered_hashmap_get(h
, i
.path
);
2037 for (n
= 0; n
< existing
->count
; n
++) {
2038 if (!item_compatible(existing
->items
+ n
, &i
)) {
2039 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2040 fname
, line
, i
.path
);
2045 existing
= new0(ItemArray
, 1);
2046 r
= ordered_hashmap_put(h
, i
.path
, existing
);
2051 if (!GREEDY_REALLOC(existing
->items
, existing
->size
, existing
->count
+ 1))
2054 memcpy(existing
->items
+ existing
->count
++, &i
, sizeof(i
));
2056 /* Sort item array, to enforce stable ordering of application */
2057 qsort_safe(existing
->items
, existing
->count
, sizeof(Item
), item_compare
);
2063 static void help(void) {
2064 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2065 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2066 " -h --help Show this help\n"
2067 " --version Show package version\n"
2068 " --create Create marked files/directories\n"
2069 " --clean Clean up marked directories\n"
2070 " --remove Remove marked files/directories\n"
2071 " --boot Execute actions only safe at boot\n"
2072 " --prefix=PATH Only apply rules with the specified prefix\n"
2073 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2074 " --root=PATH Operate on an alternate filesystem root\n",
2075 program_invocation_short_name
);
2078 static int parse_argv(int argc
, char *argv
[]) {
2081 ARG_VERSION
= 0x100,
2091 static const struct option options
[] = {
2092 { "help", no_argument
, NULL
, 'h' },
2093 { "version", no_argument
, NULL
, ARG_VERSION
},
2094 { "create", no_argument
, NULL
, ARG_CREATE
},
2095 { "clean", no_argument
, NULL
, ARG_CLEAN
},
2096 { "remove", no_argument
, NULL
, ARG_REMOVE
},
2097 { "boot", no_argument
, NULL
, ARG_BOOT
},
2098 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
2099 { "exclude-prefix", required_argument
, NULL
, ARG_EXCLUDE_PREFIX
},
2100 { "root", required_argument
, NULL
, ARG_ROOT
},
2109 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
2137 if (strv_push(&arg_include_prefixes
, optarg
) < 0)
2141 case ARG_EXCLUDE_PREFIX
:
2142 if (strv_push(&arg_exclude_prefixes
, optarg
) < 0)
2147 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
2156 assert_not_reached("Unhandled option");
2159 if (!arg_clean
&& !arg_create
&& !arg_remove
) {
2160 log_error("You need to specify at least one of --clean, --create or --remove.");
2167 static int read_config_file(const char *fn
, bool ignore_enoent
) {
2168 _cleanup_fclose_
FILE *f
= NULL
;
2169 char line
[LINE_MAX
];
2177 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &f
);
2179 if (ignore_enoent
&& r
== -ENOENT
) {
2180 log_debug_errno(r
, "Failed to open \"%s\": %m", fn
);
2184 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
2186 log_debug("Reading config file \"%s\".", fn
);
2188 FOREACH_LINE(line
, f
, break) {
2195 if (*l
== '#' || *l
== 0)
2198 k
= parse_line(fn
, v
, l
);
2199 if (k
< 0 && r
== 0)
2203 /* we have to determine age parameter for each entry of type X */
2204 ORDERED_HASHMAP_FOREACH(i
, globs
, iterator
) {
2206 Item
*j
, *candidate_item
= NULL
;
2208 if (i
->type
!= IGNORE_DIRECTORY_PATH
)
2211 ORDERED_HASHMAP_FOREACH(j
, items
, iter
) {
2212 if (!IN_SET(j
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
))
2215 if (path_equal(j
->path
, i
->path
)) {
2220 if ((!candidate_item
&& path_startswith(i
->path
, j
->path
)) ||
2221 (candidate_item
&& path_startswith(j
->path
, candidate_item
->path
) && (fnmatch(i
->path
, j
->path
, FNM_PATHNAME
| FNM_PERIOD
) == 0)))
2225 if (candidate_item
&& candidate_item
->age_set
) {
2226 i
->age
= candidate_item
->age
;
2232 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
2240 int main(int argc
, char *argv
[]) {
2245 r
= parse_argv(argc
, argv
);
2249 log_set_target(LOG_TARGET_AUTO
);
2250 log_parse_environment();
2255 mac_selinux_init(NULL
);
2257 items
= ordered_hashmap_new(&string_hash_ops
);
2258 globs
= ordered_hashmap_new(&string_hash_ops
);
2260 if (!items
|| !globs
) {
2267 if (optind
< argc
) {
2270 for (j
= optind
; j
< argc
; j
++) {
2271 k
= read_config_file(argv
[j
], false);
2272 if (k
< 0 && r
== 0)
2277 _cleanup_strv_free_
char **files
= NULL
;
2280 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
2282 log_error_errno(r
, "Failed to enumerate tmpfiles.d files: %m");
2286 STRV_FOREACH(f
, files
) {
2287 k
= read_config_file(*f
, true);
2288 if (k
< 0 && r
== 0)
2293 /* The non-globbing ones usually create things, hence we apply
2295 ORDERED_HASHMAP_FOREACH(a
, items
, iterator
) {
2296 k
= process_item_array(a
);
2297 if (k
< 0 && r
== 0)
2301 /* The globbing ones usually alter things, hence we apply them
2303 ORDERED_HASHMAP_FOREACH(a
, globs
, iterator
) {
2304 k
= process_item_array(a
);
2305 if (k
< 0 && r
== 0)
2310 while ((a
= ordered_hashmap_steal_first(items
)))
2313 while ((a
= ordered_hashmap_steal_first(globs
)))
2316 ordered_hashmap_free(items
);
2317 ordered_hashmap_free(globs
);
2319 free(arg_include_prefixes
);
2320 free(arg_exclude_prefixes
);
2323 set_free_free(unix_sockets
);
2325 mac_selinux_finish();
2327 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;