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
||
1589 (s
.st_dev
== ps
.st_dev
&& s
.st_ino
== ps
.st_ino
);
1591 log_debug("Cleanup threshold for %s \"%s\" is %s",
1592 mountpoint
? "mount point" : "directory",
1594 format_timestamp_us(timestamp
, sizeof(timestamp
), cutoff
));
1596 return dir_cleanup(i
, instance
, d
, &s
, cutoff
, s
.st_dev
, mountpoint
,
1597 MAX_DEPTH
, i
->keep_first_level
);
1600 static int clean_item(Item
*i
) {
1605 log_debug("Running clean action for entry %c %s", (char) i
->type
, i
->path
);
1608 case CREATE_DIRECTORY
:
1609 case CREATE_SUBVOLUME
:
1610 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1611 case CREATE_SUBVOLUME_NEW_QUOTA
:
1612 case TRUNCATE_DIRECTORY
:
1615 clean_item_instance(i
, i
->path
);
1617 case IGNORE_DIRECTORY_PATH
:
1618 r
= glob_item(i
, clean_item_instance
, false);
1627 static int process_item_array(ItemArray
*array
);
1629 static int process_item(Item
*i
) {
1631 _cleanup_free_
char *prefix
= NULL
;
1640 prefix
= malloc(strlen(i
->path
) + 1);
1644 PATH_FOREACH_PREFIX(prefix
, i
->path
) {
1647 j
= ordered_hashmap_get(items
, prefix
);
1651 s
= process_item_array(j
);
1652 if (s
< 0 && t
== 0)
1657 r
= arg_create
? create_item(i
) : 0;
1658 q
= arg_remove
? remove_item(i
) : 0;
1659 p
= arg_clean
? clean_item(i
) : 0;
1667 static int process_item_array(ItemArray
*array
) {
1673 for (n
= 0; n
< array
->count
; n
++) {
1674 k
= process_item(array
->items
+ n
);
1675 if (k
< 0 && r
== 0)
1682 static void item_free_contents(Item
*i
) {
1686 strv_free(i
->xattrs
);
1689 acl_free(i
->acl_access
);
1690 acl_free(i
->acl_default
);
1694 static void item_array_free(ItemArray
*a
) {
1700 for (n
= 0; n
< a
->count
; n
++)
1701 item_free_contents(a
->items
+ n
);
1706 static int item_compare(const void *a
, const void *b
) {
1707 const Item
*x
= a
, *y
= b
;
1709 /* Make sure that the ownership taking item is put first, so
1710 * that we first create the node, and then can adjust it */
1712 if (takes_ownership(x
->type
) && !takes_ownership(y
->type
))
1714 if (!takes_ownership(x
->type
) && takes_ownership(y
->type
))
1717 return (int) x
->type
- (int) y
->type
;
1720 static bool item_compatible(Item
*a
, Item
*b
) {
1723 assert(streq(a
->path
, b
->path
));
1725 if (takes_ownership(a
->type
) && takes_ownership(b
->type
))
1726 /* check if the items are the same */
1727 return streq_ptr(a
->argument
, b
->argument
) &&
1729 a
->uid_set
== b
->uid_set
&&
1732 a
->gid_set
== b
->gid_set
&&
1735 a
->mode_set
== b
->mode_set
&&
1736 a
->mode
== b
->mode
&&
1738 a
->age_set
== b
->age_set
&&
1741 a
->mask_perms
== b
->mask_perms
&&
1743 a
->keep_first_level
== b
->keep_first_level
&&
1745 a
->major_minor
== b
->major_minor
;
1750 static bool should_include_path(const char *path
) {
1753 STRV_FOREACH(prefix
, arg_exclude_prefixes
)
1754 if (path_startswith(path
, *prefix
)) {
1755 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1760 STRV_FOREACH(prefix
, arg_include_prefixes
)
1761 if (path_startswith(path
, *prefix
)) {
1762 log_debug("Entry \"%s\" matches include prefix \"%s\".", path
, *prefix
);
1766 /* no matches, so we should include this path only if we
1767 * have no whitelist at all */
1768 if (strv_length(arg_include_prefixes
) == 0)
1771 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path
);
1775 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1777 _cleanup_free_
char *action
= NULL
, *mode
= NULL
, *user
= NULL
, *group
= NULL
, *age
= NULL
, *path
= NULL
;
1778 _cleanup_(item_free_contents
) Item i
= {};
1779 ItemArray
*existing
;
1782 bool force
= false, boot
= false;
1788 r
= extract_many_words(
1800 return log_error_errno(r
, "[%s:%u] Failed to parse line: %m", fname
, line
);
1802 log_error("[%s:%u] Syntax error.", fname
, line
);
1806 if (!isempty(buffer
) && !streq(buffer
, "-")) {
1807 i
.argument
= strdup(buffer
);
1812 if (isempty(action
)) {
1813 log_error("[%s:%u] Command too short '%s'.", fname
, line
, action
);
1817 for (pos
= 1; action
[pos
]; pos
++) {
1818 if (action
[pos
] == '!' && !boot
)
1820 else if (action
[pos
] == '+' && !force
)
1823 log_error("[%s:%u] Unknown modifiers in command '%s'",
1824 fname
, line
, action
);
1829 if (boot
&& !arg_boot
) {
1830 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1838 r
= specifier_printf(path
, specifier_table
, NULL
, &i
.path
);
1840 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, path
);
1846 case CREATE_DIRECTORY
:
1847 case CREATE_SUBVOLUME
:
1848 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1849 case CREATE_SUBVOLUME_NEW_QUOTA
:
1850 case TRUNCATE_DIRECTORY
:
1853 case IGNORE_DIRECTORY_PATH
:
1855 case RECURSIVE_REMOVE_PATH
:
1858 case RECURSIVE_RELABEL_PATH
:
1860 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname
, line
, i
.type
);
1868 case CREATE_SYMLINK
:
1870 i
.argument
= strappend("/usr/share/factory/", i
.path
);
1878 log_error("[%s:%u] Write file requires argument.", fname
, line
);
1885 i
.argument
= strappend("/usr/share/factory/", i
.path
);
1888 } else if (!path_is_absolute(i
.argument
)) {
1889 log_error("[%s:%u] Source path is not absolute.", fname
, line
);
1893 path_kill_slashes(i
.argument
);
1896 case CREATE_CHAR_DEVICE
:
1897 case CREATE_BLOCK_DEVICE
: {
1898 unsigned major
, minor
;
1901 log_error("[%s:%u] Device file requires argument.", fname
, line
);
1905 if (sscanf(i
.argument
, "%u:%u", &major
, &minor
) != 2) {
1906 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname
, line
, i
.argument
);
1910 i
.major_minor
= makedev(major
, minor
);
1915 case RECURSIVE_SET_XATTR
:
1917 log_error("[%s:%u] Set extended attribute requires argument.", fname
, line
);
1920 r
= parse_xattrs_from_arg(&i
);
1926 case RECURSIVE_SET_ACL
:
1928 log_error("[%s:%u] Set ACLs requires argument.", fname
, line
);
1931 r
= parse_acls_from_arg(&i
);
1937 case RECURSIVE_SET_ATTRIBUTE
:
1939 log_error("[%s:%u] Set file attribute requires argument.", fname
, line
);
1942 r
= parse_attribute_from_arg(&i
);
1948 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, (char) i
.type
);
1952 if (!path_is_absolute(i
.path
)) {
1953 log_error("[%s:%u] Path '%s' not absolute.", fname
, line
, i
.path
);
1957 path_kill_slashes(i
.path
);
1959 if (!should_include_path(i
.path
))
1965 p
= prefix_root(arg_root
, i
.path
);
1973 if (!isempty(user
) && !streq(user
, "-")) {
1974 const char *u
= user
;
1976 r
= get_user_creds(&u
, &i
.uid
, NULL
, NULL
, NULL
);
1978 log_error("[%s:%u] Unknown user '%s'.", fname
, line
, user
);
1985 if (!isempty(group
) && !streq(group
, "-")) {
1986 const char *g
= group
;
1988 r
= get_group_creds(&g
, &i
.gid
);
1990 log_error("[%s:%u] Unknown group '%s'.", fname
, line
, group
);
1997 if (!isempty(mode
) && !streq(mode
, "-")) {
1998 const char *mm
= mode
;
2002 i
.mask_perms
= true;
2006 if (parse_mode(mm
, &m
) < 0) {
2007 log_error("[%s:%u] Invalid mode '%s'.", fname
, line
, mode
);
2014 i
.mode
= IN_SET(i
.type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
) ? 0755 : 0644;
2016 if (!isempty(age
) && !streq(age
, "-")) {
2017 const char *a
= age
;
2020 i
.keep_first_level
= true;
2024 if (parse_sec(a
, &i
.age
) < 0) {
2025 log_error("[%s:%u] Invalid age '%s'.", fname
, line
, age
);
2032 h
= needs_glob(i
.type
) ? globs
: items
;
2034 existing
= ordered_hashmap_get(h
, i
.path
);
2038 for (n
= 0; n
< existing
->count
; n
++) {
2039 if (!item_compatible(existing
->items
+ n
, &i
)) {
2040 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2041 fname
, line
, i
.path
);
2046 existing
= new0(ItemArray
, 1);
2047 r
= ordered_hashmap_put(h
, i
.path
, existing
);
2052 if (!GREEDY_REALLOC(existing
->items
, existing
->size
, existing
->count
+ 1))
2055 memcpy(existing
->items
+ existing
->count
++, &i
, sizeof(i
));
2057 /* Sort item array, to enforce stable ordering of application */
2058 qsort_safe(existing
->items
, existing
->count
, sizeof(Item
), item_compare
);
2064 static void help(void) {
2065 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2066 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2067 " -h --help Show this help\n"
2068 " --version Show package version\n"
2069 " --create Create marked files/directories\n"
2070 " --clean Clean up marked directories\n"
2071 " --remove Remove marked files/directories\n"
2072 " --boot Execute actions only safe at boot\n"
2073 " --prefix=PATH Only apply rules with the specified prefix\n"
2074 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2075 " --root=PATH Operate on an alternate filesystem root\n",
2076 program_invocation_short_name
);
2079 static int parse_argv(int argc
, char *argv
[]) {
2082 ARG_VERSION
= 0x100,
2092 static const struct option options
[] = {
2093 { "help", no_argument
, NULL
, 'h' },
2094 { "version", no_argument
, NULL
, ARG_VERSION
},
2095 { "create", no_argument
, NULL
, ARG_CREATE
},
2096 { "clean", no_argument
, NULL
, ARG_CLEAN
},
2097 { "remove", no_argument
, NULL
, ARG_REMOVE
},
2098 { "boot", no_argument
, NULL
, ARG_BOOT
},
2099 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
2100 { "exclude-prefix", required_argument
, NULL
, ARG_EXCLUDE_PREFIX
},
2101 { "root", required_argument
, NULL
, ARG_ROOT
},
2110 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
2138 if (strv_push(&arg_include_prefixes
, optarg
) < 0)
2142 case ARG_EXCLUDE_PREFIX
:
2143 if (strv_push(&arg_exclude_prefixes
, optarg
) < 0)
2149 arg_root
= path_make_absolute_cwd(optarg
);
2153 path_kill_slashes(arg_root
);
2160 assert_not_reached("Unhandled option");
2163 if (!arg_clean
&& !arg_create
&& !arg_remove
) {
2164 log_error("You need to specify at least one of --clean, --create or --remove.");
2171 static int read_config_file(const char *fn
, bool ignore_enoent
) {
2172 _cleanup_fclose_
FILE *f
= NULL
;
2173 char line
[LINE_MAX
];
2181 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &f
);
2183 if (ignore_enoent
&& r
== -ENOENT
) {
2184 log_debug_errno(r
, "Failed to open \"%s\": %m", fn
);
2188 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
2190 log_debug("Reading config file \"%s\".", fn
);
2192 FOREACH_LINE(line
, f
, break) {
2199 if (*l
== '#' || *l
== 0)
2202 k
= parse_line(fn
, v
, l
);
2203 if (k
< 0 && r
== 0)
2207 /* we have to determine age parameter for each entry of type X */
2208 ORDERED_HASHMAP_FOREACH(i
, globs
, iterator
) {
2210 Item
*j
, *candidate_item
= NULL
;
2212 if (i
->type
!= IGNORE_DIRECTORY_PATH
)
2215 ORDERED_HASHMAP_FOREACH(j
, items
, iter
) {
2216 if (!IN_SET(j
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
))
2219 if (path_equal(j
->path
, i
->path
)) {
2224 if ((!candidate_item
&& path_startswith(i
->path
, j
->path
)) ||
2225 (candidate_item
&& path_startswith(j
->path
, candidate_item
->path
) && (fnmatch(i
->path
, j
->path
, FNM_PATHNAME
| FNM_PERIOD
) == 0)))
2229 if (candidate_item
&& candidate_item
->age_set
) {
2230 i
->age
= candidate_item
->age
;
2236 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
2244 int main(int argc
, char *argv
[]) {
2249 r
= parse_argv(argc
, argv
);
2253 log_set_target(LOG_TARGET_AUTO
);
2254 log_parse_environment();
2259 mac_selinux_init(NULL
);
2261 items
= ordered_hashmap_new(&string_hash_ops
);
2262 globs
= ordered_hashmap_new(&string_hash_ops
);
2264 if (!items
|| !globs
) {
2271 if (optind
< argc
) {
2274 for (j
= optind
; j
< argc
; j
++) {
2275 k
= read_config_file(argv
[j
], false);
2276 if (k
< 0 && r
== 0)
2281 _cleanup_strv_free_
char **files
= NULL
;
2284 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
2286 log_error_errno(r
, "Failed to enumerate tmpfiles.d files: %m");
2290 STRV_FOREACH(f
, files
) {
2291 k
= read_config_file(*f
, true);
2292 if (k
< 0 && r
== 0)
2297 /* The non-globbing ones usually create things, hence we apply
2299 ORDERED_HASHMAP_FOREACH(a
, items
, iterator
) {
2300 k
= process_item_array(a
);
2301 if (k
< 0 && r
== 0)
2305 /* The globbing ones usually alter things, hence we apply them
2307 ORDERED_HASHMAP_FOREACH(a
, globs
, iterator
) {
2308 k
= process_item_array(a
);
2309 if (k
< 0 && r
== 0)
2314 while ((a
= ordered_hashmap_steal_first(items
)))
2317 while ((a
= ordered_hashmap_steal_first(globs
)))
2320 ordered_hashmap_free(items
);
2321 ordered_hashmap_free(globs
);
2323 free(arg_include_prefixes
);
2324 free(arg_exclude_prefixes
);
2327 set_free_free(unix_sockets
);
2329 mac_selinux_finish();
2331 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;