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 "alloc-util.h"
43 #include "btrfs-util.h"
44 #include "capability-util.h"
45 #include "chattr-util.h"
46 #include "conf-files.h"
52 #include "formats-util.h"
54 #include "glob-util.h"
61 #include "mount-util.h"
62 #include "parse-util.h"
63 #include "path-util.h"
65 #include "selinux-util.h"
67 #include "specifier.h"
68 #include "stat-util.h"
69 #include "stdio-util.h"
70 #include "string-table.h"
71 #include "string-util.h"
73 #include "umask-util.h"
74 #include "user-util.h"
77 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
78 * them in the file system. This is intended to be used to create
79 * properly owned directories beneath /tmp, /var/tmp, /run, which are
80 * volatile and hence need to be recreated on bootup. */
82 typedef enum ItemType
{
83 /* These ones take file names */
86 CREATE_DIRECTORY
= 'd',
87 TRUNCATE_DIRECTORY
= 'D',
88 CREATE_SUBVOLUME
= 'v',
89 CREATE_SUBVOLUME_INHERIT_QUOTA
= 'q',
90 CREATE_SUBVOLUME_NEW_QUOTA
= 'Q',
93 CREATE_CHAR_DEVICE
= 'c',
94 CREATE_BLOCK_DEVICE
= 'b',
97 /* These ones take globs */
100 RECURSIVE_SET_XATTR
= 'T',
102 RECURSIVE_SET_ACL
= 'A',
104 RECURSIVE_SET_ATTRIBUTE
= 'H',
106 IGNORE_DIRECTORY_PATH
= 'X',
108 RECURSIVE_REMOVE_PATH
= 'R',
110 RECURSIVE_RELABEL_PATH
= 'Z',
111 ADJUST_MODE
= 'm', /* legacy, 'z' is identical to this */
114 typedef struct Item
{
130 unsigned attribute_value
;
131 unsigned attribute_mask
;
138 bool attribute_set
:1;
140 bool keep_first_level
:1;
147 typedef struct ItemArray
{
153 static bool arg_create
= false;
154 static bool arg_clean
= false;
155 static bool arg_remove
= false;
156 static bool arg_boot
= false;
158 static char **arg_include_prefixes
= NULL
;
159 static char **arg_exclude_prefixes
= NULL
;
160 static char *arg_root
= NULL
;
162 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("tmpfiles");
164 #define MAX_DEPTH 256
166 static OrderedHashmap
*items
= NULL
, *globs
= NULL
;
167 static Set
*unix_sockets
= NULL
;
169 static const Specifier specifier_table
[] = {
170 { 'm', specifier_machine_id
, NULL
},
171 { 'b', specifier_boot_id
, NULL
},
172 { 'H', specifier_host_name
, NULL
},
173 { 'v', specifier_kernel_release
, NULL
},
177 static bool needs_glob(ItemType t
) {
181 IGNORE_DIRECTORY_PATH
,
183 RECURSIVE_REMOVE_PATH
,
186 RECURSIVE_RELABEL_PATH
,
192 RECURSIVE_SET_ATTRIBUTE
);
195 static bool takes_ownership(ItemType t
) {
202 CREATE_SUBVOLUME_INHERIT_QUOTA
,
203 CREATE_SUBVOLUME_NEW_QUOTA
,
211 IGNORE_DIRECTORY_PATH
,
213 RECURSIVE_REMOVE_PATH
);
216 static struct Item
* find_glob(OrderedHashmap
*h
, const char *match
) {
220 ORDERED_HASHMAP_FOREACH(j
, h
, i
) {
223 for (n
= 0; n
< j
->count
; n
++) {
224 Item
*item
= j
->items
+ n
;
226 if (fnmatch(item
->path
, match
, FNM_PATHNAME
|FNM_PERIOD
) == 0)
234 static void load_unix_sockets(void) {
235 _cleanup_fclose_
FILE *f
= NULL
;
241 /* We maintain a cache of the sockets we found in
242 * /proc/net/unix to speed things up a little. */
244 unix_sockets
= set_new(&string_hash_ops
);
248 f
= fopen("/proc/net/unix", "re");
253 if (!fgets(line
, sizeof(line
), f
))
260 if (!fgets(line
, sizeof(line
), f
))
265 p
= strchr(line
, ':');
273 p
+= strspn(p
, WHITESPACE
);
274 p
+= strcspn(p
, WHITESPACE
); /* skip one more word */
275 p
+= strspn(p
, WHITESPACE
);
284 path_kill_slashes(s
);
286 k
= set_consume(unix_sockets
, s
);
287 if (k
< 0 && k
!= -EEXIST
)
294 set_free_free(unix_sockets
);
298 static bool unix_socket_alive(const char *fn
) {
304 return !!set_get(unix_sockets
, (char*) fn
);
306 /* We don't know, so assume yes */
310 static int dir_is_mount_point(DIR *d
, const char *subdir
) {
312 union file_handle_union h
= FILE_HANDLE_INIT
;
313 int mount_id_parent
, mount_id
;
316 r_p
= name_to_handle_at(dirfd(d
), ".", &h
.handle
, &mount_id_parent
, 0);
320 h
.handle
.handle_bytes
= MAX_HANDLE_SZ
;
321 r
= name_to_handle_at(dirfd(d
), subdir
, &h
.handle
, &mount_id
, 0);
325 /* got no handle; make no assumptions, return error */
326 if (r_p
< 0 && r
< 0)
329 /* got both handles; if they differ, it is a mount point */
330 if (r_p
>= 0 && r
>= 0)
331 return mount_id_parent
!= mount_id
;
333 /* got only one handle; assume different mount points if one
334 * of both queries was not supported by the filesystem */
335 if (r_p
== -ENOSYS
|| r_p
== -EOPNOTSUPP
|| r
== -ENOSYS
|| r
== -EOPNOTSUPP
)
344 static DIR* xopendirat_nomod(int dirfd
, const char *path
) {
347 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
|O_NOATIME
);
351 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
355 dir
= xopendirat(dirfd
, path
, O_NOFOLLOW
);
357 log_debug_errno(errno
, "Cannot open %sdirectory \"%s\": %m", dirfd
== AT_FDCWD
? "" : "sub", path
);
362 static DIR* opendir_nomod(const char *path
) {
363 return xopendirat_nomod(AT_FDCWD
, path
);
366 static int dir_cleanup(
370 const struct stat
*ds
,
375 bool keep_this_level
) {
378 struct timespec times
[2];
379 bool deleted
= false;
382 while ((dent
= readdir(d
))) {
385 _cleanup_free_
char *sub_path
= NULL
;
387 if (STR_IN_SET(dent
->d_name
, ".", ".."))
390 if (fstatat(dirfd(d
), dent
->d_name
, &s
, AT_SYMLINK_NOFOLLOW
) < 0) {
394 /* FUSE, NFS mounts, SELinux might return EACCES */
396 log_debug_errno(errno
, "stat(%s/%s) failed: %m", p
, dent
->d_name
);
398 log_error_errno(errno
, "stat(%s/%s) failed: %m", p
, dent
->d_name
);
403 /* Stay on the same filesystem */
404 if (s
.st_dev
!= rootdev
) {
405 log_debug("Ignoring \"%s/%s\": different filesystem.", p
, dent
->d_name
);
409 /* Try to detect bind mounts of the same filesystem instance; they
410 * do not differ in device major/minors. This type of query is not
411 * supported on all kernels or filesystem types though. */
412 if (S_ISDIR(s
.st_mode
) && dir_is_mount_point(d
, dent
->d_name
) > 0) {
413 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
418 /* Do not delete read-only files owned by root */
419 if (s
.st_uid
== 0 && !(s
.st_mode
& S_IWUSR
)) {
420 log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p
, dent
->d_name
);
424 sub_path
= strjoin(p
, "/", dent
->d_name
, NULL
);
430 /* Is there an item configured for this path? */
431 if (ordered_hashmap_get(items
, sub_path
)) {
432 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path
);
436 if (find_glob(globs
, sub_path
)) {
437 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path
);
441 if (S_ISDIR(s
.st_mode
)) {
444 streq(dent
->d_name
, "lost+found") &&
446 log_debug("Ignoring \"%s\".", sub_path
);
451 log_warning("Reached max depth on \"%s\".", sub_path
);
453 _cleanup_closedir_
DIR *sub_dir
;
456 sub_dir
= xopendirat_nomod(dirfd(d
), dent
->d_name
);
459 r
= log_error_errno(errno
, "opendir(%s) failed: %m", sub_path
);
464 q
= dir_cleanup(i
, sub_path
, sub_dir
, &s
, cutoff
, rootdev
, false, maxdepth
-1, false);
469 /* Note: if you are wondering why we don't
470 * support the sticky bit for excluding
471 * directories from cleaning like we do it for
472 * other file system objects: well, the sticky
473 * bit already has a meaning for directories,
474 * so we don't want to overload that. */
476 if (keep_this_level
) {
477 log_debug("Keeping \"%s\".", sub_path
);
481 /* Ignore ctime, we change it when deleting */
482 age
= timespec_load(&s
.st_mtim
);
484 char a
[FORMAT_TIMESTAMP_MAX
];
485 /* Follows spelling in stat(1). */
486 log_debug("Directory \"%s\": modify time %s is too new.",
488 format_timestamp_us(a
, sizeof(a
), age
));
492 age
= timespec_load(&s
.st_atim
);
494 char a
[FORMAT_TIMESTAMP_MAX
];
495 log_debug("Directory \"%s\": access time %s is too new.",
497 format_timestamp_us(a
, sizeof(a
), age
));
501 log_debug("Removing directory \"%s\".", sub_path
);
502 if (unlinkat(dirfd(d
), dent
->d_name
, AT_REMOVEDIR
) < 0)
503 if (errno
!= ENOENT
&& errno
!= ENOTEMPTY
) {
504 log_error_errno(errno
, "rmdir(%s): %m", sub_path
);
509 /* Skip files for which the sticky bit is
510 * set. These are semantics we define, and are
511 * unknown elsewhere. See XDG_RUNTIME_DIR
512 * specification for details. */
513 if (s
.st_mode
& S_ISVTX
) {
514 log_debug("Skipping \"%s\": sticky bit set.", sub_path
);
518 if (mountpoint
&& S_ISREG(s
.st_mode
))
519 if (s
.st_uid
== 0 && STR_IN_SET(dent
->d_name
,
523 log_debug("Skipping \"%s\".", sub_path
);
527 /* Ignore sockets that are listed in /proc/net/unix */
528 if (S_ISSOCK(s
.st_mode
) && unix_socket_alive(sub_path
)) {
529 log_debug("Skipping \"%s\": live socket.", sub_path
);
533 /* Ignore device nodes */
534 if (S_ISCHR(s
.st_mode
) || S_ISBLK(s
.st_mode
)) {
535 log_debug("Skipping \"%s\": a device.", sub_path
);
539 /* Keep files on this level around if this is
541 if (keep_this_level
) {
542 log_debug("Keeping \"%s\".", sub_path
);
546 age
= timespec_load(&s
.st_mtim
);
548 char a
[FORMAT_TIMESTAMP_MAX
];
549 /* Follows spelling in stat(1). */
550 log_debug("File \"%s\": modify time %s is too new.",
552 format_timestamp_us(a
, sizeof(a
), age
));
556 age
= timespec_load(&s
.st_atim
);
558 char a
[FORMAT_TIMESTAMP_MAX
];
559 log_debug("File \"%s\": access time %s is too new.",
561 format_timestamp_us(a
, sizeof(a
), age
));
565 age
= timespec_load(&s
.st_ctim
);
567 char a
[FORMAT_TIMESTAMP_MAX
];
568 log_debug("File \"%s\": change time %s is too new.",
570 format_timestamp_us(a
, sizeof(a
), age
));
574 log_debug("unlink \"%s\"", sub_path
);
576 if (unlinkat(dirfd(d
), dent
->d_name
, 0) < 0)
578 r
= log_error_errno(errno
, "unlink(%s): %m", sub_path
);
587 char a
[FORMAT_TIMESTAMP_MAX
], b
[FORMAT_TIMESTAMP_MAX
];
589 /* Restore original directory timestamps */
590 times
[0] = ds
->st_atim
;
591 times
[1] = ds
->st_mtim
;
593 age1
= timespec_load(&ds
->st_atim
);
594 age2
= timespec_load(&ds
->st_mtim
);
595 log_debug("Restoring access and modification time on \"%s\": %s, %s",
597 format_timestamp_us(a
, sizeof(a
), age1
),
598 format_timestamp_us(b
, sizeof(b
), age2
));
599 if (futimens(dirfd(d
), times
) < 0)
600 log_error_errno(errno
, "utimensat(%s): %m", p
);
606 static int path_set_perms(Item
*i
, const char *path
) {
607 _cleanup_close_
int fd
= -1;
613 /* We open the file with O_PATH here, to make the operation
614 * somewhat atomic. Also there's unfortunately no fchmodat()
615 * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
618 fd
= open(path
, O_RDONLY
|O_NOFOLLOW
|O_CLOEXEC
|O_PATH
|O_NOATIME
);
620 return log_error_errno(errno
, "Adjusting owner and mode for %s failed: %m", path
);
622 if (fstatat(fd
, "", &st
, AT_EMPTY_PATH
) < 0)
623 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
625 if (S_ISLNK(st
.st_mode
))
626 log_debug("Skipping mode an owner fix for symlink %s.", path
);
628 char fn
[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
629 xsprintf(fn
, "/proc/self/fd/%i", fd
);
631 /* not using i->path directly because it may be a glob */
636 if (!(st
.st_mode
& 0111))
638 if (!(st
.st_mode
& 0222))
640 if (!(st
.st_mode
& 0444))
642 if (!S_ISDIR(st
.st_mode
))
643 m
&= ~07000; /* remove sticky/sgid/suid bit, unless directory */
646 if (m
== (st
.st_mode
& 07777))
647 log_debug("\"%s\" has right mode %o", path
, st
.st_mode
);
649 log_debug("chmod \"%s\" to mode %o", path
, m
);
650 if (chmod(fn
, m
) < 0)
651 return log_error_errno(errno
, "chmod(%s) failed: %m", path
);
655 if ((i
->uid
!= st
.st_uid
|| i
->gid
!= st
.st_gid
) &&
656 (i
->uid_set
|| i
->gid_set
)) {
657 log_debug("chown \"%s\" to "UID_FMT
"."GID_FMT
,
659 i
->uid_set
? i
->uid
: UID_INVALID
,
660 i
->gid_set
? i
->gid
: GID_INVALID
);
662 i
->uid_set
? i
->uid
: UID_INVALID
,
663 i
->gid_set
? i
->gid
: GID_INVALID
) < 0)
664 return log_error_errno(errno
, "chown(%s) failed: %m", path
);
670 return label_fix(path
, false, false);
673 static int parse_xattrs_from_arg(Item
*i
) {
683 _cleanup_free_
char *name
= NULL
, *value
= NULL
, *xattr
= NULL
, *xattr_replaced
= NULL
;
685 r
= extract_first_word(&p
, &xattr
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
687 log_warning_errno(r
, "Failed to parse extended attribute '%s', ignoring: %m", p
);
691 r
= specifier_printf(xattr
, specifier_table
, NULL
, &xattr_replaced
);
693 return log_error_errno(r
, "Failed to replace specifiers in extended attribute '%s': %m", xattr
);
695 r
= split_pair(xattr_replaced
, "=", &name
, &value
);
697 log_warning_errno(r
, "Failed to parse extended attribute, ignoring: %s", xattr
);
701 if (isempty(name
) || isempty(value
)) {
702 log_warning("Malformed extended attribute found, ignoring: %s", xattr
);
706 if (strv_push_pair(&i
->xattrs
, name
, value
) < 0)
715 static int path_set_xattrs(Item
*i
, const char *path
) {
716 char **name
, **value
;
721 STRV_FOREACH_PAIR(name
, value
, i
->xattrs
) {
725 log_debug("Setting extended attribute '%s=%s' on %s.", *name
, *value
, path
);
726 if (lsetxattr(path
, *name
, *value
, n
, 0) < 0) {
727 log_error("Setting extended attribute %s=%s on %s failed: %m", *name
, *value
, path
);
734 static int parse_acls_from_arg(Item
*item
) {
740 /* If force (= modify) is set, we will not modify the acl
741 * afterwards, so the mask can be added now if necessary. */
743 r
= parse_acl(item
->argument
, &item
->acl_access
, &item
->acl_default
, !item
->force
);
745 log_warning_errno(r
, "Failed to parse ACL \"%s\": %m. Ignoring", item
->argument
);
747 log_warning_errno(ENOSYS
, "ACLs are not supported. Ignoring");
754 static int path_set_acl(const char *path
, const char *pretty
, acl_type_t type
, acl_t acl
, bool modify
) {
755 _cleanup_(acl_free_charpp
) char *t
= NULL
;
756 _cleanup_(acl_freep
) acl_t dup
= NULL
;
759 /* Returns 0 for success, positive error if already warned,
760 * negative error otherwise. */
763 r
= acls_for_file(path
, type
, acl
, &dup
);
767 r
= calc_acl_mask_if_needed(&dup
);
775 /* the mask was already added earlier if needed */
778 r
= add_base_acls_if_needed(&dup
, path
);
782 t
= acl_to_any_text(dup
, NULL
, ',', TEXT_ABBREVIATE
);
783 log_debug("Setting %s ACL %s on %s.",
784 type
== ACL_TYPE_ACCESS
? "access" : "default",
787 r
= acl_set_file(path
, type
, dup
);
789 /* Return positive to indicate we already warned */
790 return -log_error_errno(errno
,
791 "Setting %s ACL \"%s\" on %s failed: %m",
792 type
== ACL_TYPE_ACCESS
? "access" : "default",
799 static int path_set_acls(Item
*item
, const char *path
) {
802 char fn
[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
803 _cleanup_close_
int fd
= -1;
809 fd
= open(path
, O_RDONLY
|O_NOFOLLOW
|O_CLOEXEC
|O_PATH
|O_NOATIME
);
811 return log_error_errno(errno
, "Adjusting ACL of %s failed: %m", path
);
813 if (fstatat(fd
, "", &st
, AT_EMPTY_PATH
) < 0)
814 return log_error_errno(errno
, "Failed to fstat() file %s: %m", path
);
816 if (S_ISLNK(st
.st_mode
)) {
817 log_debug("Skipping ACL fix for symlink %s.", path
);
821 xsprintf(fn
, "/proc/self/fd/%i", fd
);
823 if (item
->acl_access
)
824 r
= path_set_acl(fn
, path
, ACL_TYPE_ACCESS
, item
->acl_access
, item
->force
);
826 if (r
== 0 && item
->acl_default
)
827 r
= path_set_acl(fn
, path
, ACL_TYPE_DEFAULT
, item
->acl_default
, item
->force
);
830 return -r
; /* already warned */
831 else if (r
== -EOPNOTSUPP
) {
832 log_debug_errno(r
, "ACLs not supported by file system at %s", path
);
835 log_error_errno(r
, "ACL operation on \"%s\" failed: %m", path
);
840 #define ATTRIBUTES_ALL \
849 FS_JOURNAL_DATA_FL | \
856 static int parse_attribute_from_arg(Item
*item
) {
858 static const struct {
862 { 'A', FS_NOATIME_FL
}, /* do not update atime */
863 { 'S', FS_SYNC_FL
}, /* Synchronous updates */
864 { 'D', FS_DIRSYNC_FL
}, /* dirsync behaviour (directories only) */
865 { 'a', FS_APPEND_FL
}, /* writes to file may only append */
866 { 'c', FS_COMPR_FL
}, /* Compress file */
867 { 'd', FS_NODUMP_FL
}, /* do not dump file */
868 { 'e', FS_EXTENT_FL
}, /* Top of directory hierarchies*/
869 { 'i', FS_IMMUTABLE_FL
}, /* Immutable file */
870 { 'j', FS_JOURNAL_DATA_FL
}, /* Reserved for ext3 */
871 { 's', FS_SECRM_FL
}, /* Secure deletion */
872 { 'u', FS_UNRM_FL
}, /* Undelete */
873 { 't', FS_NOTAIL_FL
}, /* file tail should not be merged */
874 { 'T', FS_TOPDIR_FL
}, /* Top of directory hierarchies*/
875 { 'C', FS_NOCOW_FL
}, /* Do not cow file */
884 unsigned value
= 0, mask
= 0;
894 } else if (*p
== '-') {
897 } else if (*p
== '=') {
903 if (isempty(p
) && mode
!= MODE_SET
) {
904 log_error("Setting file attribute on '%s' needs an attribute specification.", item
->path
);
908 for (; p
&& *p
; p
++) {
911 for (i
= 0; i
< ELEMENTSOF(attributes
); i
++)
912 if (*p
== attributes
[i
].character
)
915 if (i
>= ELEMENTSOF(attributes
)) {
916 log_error("Unknown file attribute '%c' on '%s'.", *p
, item
->path
);
920 v
= attributes
[i
].value
;
922 if (mode
== MODE_ADD
|| mode
== MODE_SET
)
930 if (mode
== MODE_SET
)
931 mask
|= ATTRIBUTES_ALL
;
935 item
->attribute_mask
= mask
;
936 item
->attribute_value
= value
;
937 item
->attribute_set
= true;
942 static int path_set_attribute(Item
*item
, const char *path
) {
943 _cleanup_close_
int fd
= -1;
948 if (!item
->attribute_set
|| item
->attribute_mask
== 0)
951 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_CLOEXEC
|O_NOATIME
|O_NOFOLLOW
);
954 return log_error_errno(errno
, "Skipping file attributes adjustment on symlink %s.", path
);
956 return log_error_errno(errno
, "Cannot open '%s': %m", path
);
959 if (fstat(fd
, &st
) < 0)
960 return log_error_errno(errno
, "Cannot stat '%s': %m", path
);
962 /* Issuing the file attribute ioctls on device nodes is not
963 * safe, as that will be delivered to the drivers, not the
964 * file system containing the device node. */
965 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
)) {
966 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path
);
970 f
= item
->attribute_value
& item
->attribute_mask
;
972 /* Mask away directory-specific flags */
973 if (!S_ISDIR(st
.st_mode
))
976 r
= chattr_fd(fd
, f
, item
->attribute_mask
);
978 log_full_errno(r
== -ENOTTY
? LOG_DEBUG
: LOG_WARNING
,
980 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
981 path
, item
->attribute_value
, item
->attribute_mask
);
986 static int write_one_file(Item
*i
, const char *path
) {
987 _cleanup_close_
int fd
= -1;
994 flags
= i
->type
== CREATE_FILE
? O_CREAT
|O_APPEND
|O_NOFOLLOW
:
995 i
->type
== TRUNCATE_FILE
? O_CREAT
|O_TRUNC
|O_NOFOLLOW
: 0;
997 RUN_WITH_UMASK(0000) {
998 mac_selinux_create_file_prepare(path
, S_IFREG
);
999 fd
= open(path
, flags
|O_NDELAY
|O_CLOEXEC
|O_WRONLY
|O_NOCTTY
, i
->mode
);
1000 mac_selinux_create_file_clear();
1004 if (i
->type
== WRITE_FILE
&& errno
== ENOENT
) {
1005 log_debug_errno(errno
, "Not writing \"%s\": %m", path
);
1010 if (!i
->argument
&& errno
== EROFS
&& stat(path
, &st
) == 0 &&
1011 (i
->type
== CREATE_FILE
|| st
.st_size
== 0))
1014 return log_error_errno(r
, "Failed to create file %s: %m", path
);
1018 _cleanup_free_
char *unescaped
= NULL
, *replaced
= NULL
;
1020 log_debug("%s to \"%s\".", i
->type
== CREATE_FILE
? "Appending" : "Writing", path
);
1022 r
= cunescape(i
->argument
, 0, &unescaped
);
1024 return log_error_errno(r
, "Failed to unescape parameter to write: %s", i
->argument
);
1026 r
= specifier_printf(unescaped
, specifier_table
, NULL
, &replaced
);
1028 return log_error_errno(r
, "Failed to replace specifiers in parameter to write '%s': %m", unescaped
);
1030 r
= loop_write(fd
, replaced
, strlen(replaced
), false);
1032 return log_error_errno(r
, "Failed to write file \"%s\": %m", path
);
1034 log_debug("\"%s\" has been created.", path
);
1036 fd
= safe_close(fd
);
1038 if (stat(path
, &st
) < 0)
1039 return log_error_errno(errno
, "stat(%s) failed: %m", path
);
1042 if (!S_ISREG(st
.st_mode
)) {
1043 log_error("%s is not a file.", path
);
1047 r
= path_set_perms(i
, path
);
1054 typedef int (*action_t
)(Item
*, const char *);
1056 static int item_do_children(Item
*i
, const char *path
, action_t action
) {
1057 _cleanup_closedir_
DIR *d
;
1063 /* This returns the first error we run into, but nevertheless
1066 d
= opendir_nomod(path
);
1068 return errno
== ENOENT
|| errno
== ENOTDIR
? 0 : -errno
;
1071 _cleanup_free_
char *p
= NULL
;
1078 if (errno
!= 0 && r
== 0)
1084 if (STR_IN_SET(de
->d_name
, ".", ".."))
1087 p
= strjoin(path
, "/", de
->d_name
, NULL
);
1092 if (q
< 0 && q
!= -ENOENT
&& r
== 0)
1095 if (IN_SET(de
->d_type
, DT_UNKNOWN
, DT_DIR
)) {
1096 q
= item_do_children(i
, p
, action
);
1097 if (q
< 0 && r
== 0)
1105 static int glob_item(Item
*i
, action_t action
, bool recursive
) {
1106 _cleanup_globfree_ glob_t g
= {
1107 .gl_closedir
= (void (*)(void *)) closedir
,
1108 .gl_readdir
= (struct dirent
*(*)(void *)) readdir
,
1109 .gl_opendir
= (void *(*)(const char *)) opendir_nomod
,
1117 k
= glob(i
->path
, GLOB_NOSORT
|GLOB_BRACE
|GLOB_ALTDIRFUNC
, NULL
, &g
);
1118 if (k
!= 0 && k
!= GLOB_NOMATCH
)
1119 return log_error_errno(errno
?: EIO
, "glob(%s) failed: %m", i
->path
);
1121 STRV_FOREACH(fn
, g
.gl_pathv
) {
1123 if (k
< 0 && r
== 0)
1127 k
= item_do_children(i
, *fn
, action
);
1128 if (k
< 0 && r
== 0)
1141 _CREATION_MODE_INVALID
= -1
1144 static const char *creation_mode_verb_table
[_CREATION_MODE_MAX
] = {
1145 [CREATION_NORMAL
] = "Created",
1146 [CREATION_EXISTING
] = "Found existing",
1147 [CREATION_FORCE
] = "Created replacement",
1150 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb
, CreationMode
);
1152 static int create_item(Item
*i
) {
1153 _cleanup_free_
char *resolved
= NULL
;
1156 CreationMode creation
;
1160 log_debug("Running create action for entry %c %s", (char) i
->type
, i
->path
);
1165 case IGNORE_DIRECTORY_PATH
:
1167 case RECURSIVE_REMOVE_PATH
:
1172 r
= write_one_file(i
, i
->path
);
1178 r
= specifier_printf(i
->argument
, specifier_table
, NULL
, &resolved
);
1180 return log_error_errno(r
, "Failed to substitute specifiers in copy source %s: %m", i
->argument
);
1182 log_debug("Copying tree \"%s\" to \"%s\".", resolved
, i
->path
);
1183 r
= copy_tree(resolved
, i
->path
, false);
1185 if (r
== -EROFS
&& stat(i
->path
, &st
) == 0)
1192 return log_error_errno(r
, "Failed to copy files to %s: %m", i
->path
);
1194 if (stat(resolved
, &a
) < 0)
1195 return log_error_errno(errno
, "stat(%s) failed: %m", resolved
);
1197 if (stat(i
->path
, &b
) < 0)
1198 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1200 if ((a
.st_mode
^ b
.st_mode
) & S_IFMT
) {
1201 log_debug("Can't copy to %s, file exists already and is of different type", i
->path
);
1206 r
= path_set_perms(i
, i
->path
);
1213 r
= glob_item(i
, write_one_file
, false);
1219 case CREATE_DIRECTORY
:
1220 case TRUNCATE_DIRECTORY
:
1221 case CREATE_SUBVOLUME
:
1222 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1223 case CREATE_SUBVOLUME_NEW_QUOTA
:
1225 RUN_WITH_UMASK(0000)
1226 mkdir_parents_label(i
->path
, 0755);
1228 if (IN_SET(i
->type
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
)) {
1229 RUN_WITH_UMASK((~i
->mode
) & 0777)
1230 r
= btrfs_subvol_make(i
->path
);
1234 if (IN_SET(i
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
) || r
== -ENOTTY
)
1235 RUN_WITH_UMASK(0000)
1236 r
= mkdir_label(i
->path
, i
->mode
);
1241 if (r
!= -EEXIST
&& r
!= -EROFS
)
1242 return log_error_errno(r
, "Failed to create directory or subvolume \"%s\": %m", i
->path
);
1244 k
= is_dir(i
->path
, false);
1245 if (k
== -ENOENT
&& r
== -EROFS
)
1246 return log_error_errno(r
, "%s does not exist and cannot be created as the file system is read-only.", i
->path
);
1248 return log_error_errno(k
, "Failed to check if %s exists: %m", i
->path
);
1250 log_warning("\"%s\" already exists and is not a directory.", i
->path
);
1254 creation
= CREATION_EXISTING
;
1256 creation
= CREATION_NORMAL
;
1258 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1260 if (IN_SET(i
->type
, CREATE_SUBVOLUME_NEW_QUOTA
, CREATE_SUBVOLUME_INHERIT_QUOTA
)) {
1261 r
= btrfs_subvol_auto_qgroup(i
->path
, 0, i
->type
== CREATE_SUBVOLUME_NEW_QUOTA
);
1263 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
);
1267 log_debug_errno(r
, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i
->path
);
1271 return log_error_errno(r
, "Failed to adjust quota for subvolume \"%s\": %m", i
->path
);
1273 log_debug("Adjusted quota for subvolume \"%s\".", i
->path
);
1275 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i
->path
);
1278 r
= path_set_perms(i
, i
->path
);
1286 RUN_WITH_UMASK(0000) {
1287 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1288 r
= mkfifo(i
->path
, i
->mode
);
1289 mac_selinux_create_file_clear();
1293 if (errno
!= EEXIST
)
1294 return log_error_errno(errno
, "Failed to create fifo %s: %m", i
->path
);
1296 if (lstat(i
->path
, &st
) < 0)
1297 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1299 if (!S_ISFIFO(st
.st_mode
)) {
1302 RUN_WITH_UMASK(0000) {
1303 mac_selinux_create_file_prepare(i
->path
, S_IFIFO
);
1304 r
= mkfifo_atomic(i
->path
, i
->mode
);
1305 mac_selinux_create_file_clear();
1309 return log_error_errno(r
, "Failed to create fifo %s: %m", i
->path
);
1310 creation
= CREATION_FORCE
;
1312 log_warning("\"%s\" already exists and is not a fifo.", i
->path
);
1316 creation
= CREATION_EXISTING
;
1318 creation
= CREATION_NORMAL
;
1319 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1321 r
= path_set_perms(i
, i
->path
);
1328 case CREATE_SYMLINK
: {
1329 r
= specifier_printf(i
->argument
, specifier_table
, NULL
, &resolved
);
1331 return log_error_errno(r
, "Failed to substitute specifiers in symlink target %s: %m", i
->argument
);
1333 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1334 r
= symlink(resolved
, i
->path
);
1335 mac_selinux_create_file_clear();
1338 _cleanup_free_
char *x
= NULL
;
1340 if (errno
!= EEXIST
)
1341 return log_error_errno(errno
, "symlink(%s, %s) failed: %m", resolved
, i
->path
);
1343 r
= readlink_malloc(i
->path
, &x
);
1344 if (r
< 0 || !streq(resolved
, x
)) {
1347 mac_selinux_create_file_prepare(i
->path
, S_IFLNK
);
1348 r
= symlink_atomic(resolved
, i
->path
);
1349 mac_selinux_create_file_clear();
1352 return log_error_errno(r
, "symlink(%s, %s) failed: %m", resolved
, i
->path
);
1354 creation
= CREATION_FORCE
;
1356 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i
->path
);
1360 creation
= CREATION_EXISTING
;
1363 creation
= CREATION_NORMAL
;
1364 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation
), i
->path
);
1368 case CREATE_BLOCK_DEVICE
:
1369 case CREATE_CHAR_DEVICE
: {
1372 if (have_effective_cap(CAP_MKNOD
) == 0) {
1373 /* In a container we lack CAP_MKNOD. We
1374 shouldn't attempt to create the device node in
1375 that case to avoid noise, and we don't support
1376 virtualized devices in containers anyway. */
1378 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i
->path
);
1382 file_type
= i
->type
== CREATE_BLOCK_DEVICE
? S_IFBLK
: S_IFCHR
;
1384 RUN_WITH_UMASK(0000) {
1385 mac_selinux_create_file_prepare(i
->path
, file_type
);
1386 r
= mknod(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1387 mac_selinux_create_file_clear();
1391 if (errno
== EPERM
) {
1392 log_debug("We lack permissions, possibly because of cgroup configuration; "
1393 "skipping creation of device node %s.", i
->path
);
1397 if (errno
!= EEXIST
)
1398 return log_error_errno(errno
, "Failed to create device node %s: %m", i
->path
);
1400 if (lstat(i
->path
, &st
) < 0)
1401 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1403 if ((st
.st_mode
& S_IFMT
) != file_type
) {
1407 RUN_WITH_UMASK(0000) {
1408 mac_selinux_create_file_prepare(i
->path
, file_type
);
1409 r
= mknod_atomic(i
->path
, i
->mode
| file_type
, i
->major_minor
);
1410 mac_selinux_create_file_clear();
1414 return log_error_errno(r
, "Failed to create device node \"%s\": %m", i
->path
);
1415 creation
= CREATION_FORCE
;
1417 log_debug("%s is not a device node.", i
->path
);
1421 creation
= CREATION_EXISTING
;
1423 creation
= CREATION_NORMAL
;
1425 log_debug("%s %s device node \"%s\" %u:%u.",
1426 creation_mode_verb_to_string(creation
),
1427 i
->type
== CREATE_BLOCK_DEVICE
? "block" : "char",
1428 i
->path
, major(i
->mode
), minor(i
->mode
));
1430 r
= path_set_perms(i
, i
->path
);
1439 r
= glob_item(i
, path_set_perms
, false);
1444 case RECURSIVE_RELABEL_PATH
:
1445 r
= glob_item(i
, path_set_perms
, true);
1451 r
= glob_item(i
, path_set_xattrs
, false);
1456 case RECURSIVE_SET_XATTR
:
1457 r
= glob_item(i
, path_set_xattrs
, true);
1463 r
= glob_item(i
, path_set_acls
, false);
1468 case RECURSIVE_SET_ACL
:
1469 r
= glob_item(i
, path_set_acls
, true);
1475 r
= glob_item(i
, path_set_attribute
, false);
1480 case RECURSIVE_SET_ATTRIBUTE
:
1481 r
= glob_item(i
, path_set_attribute
, true);
1490 static int remove_item_instance(Item
*i
, const char *instance
) {
1498 if (remove(instance
) < 0 && errno
!= ENOENT
)
1499 return log_error_errno(errno
, "rm(%s): %m", instance
);
1503 case TRUNCATE_DIRECTORY
:
1504 case RECURSIVE_REMOVE_PATH
:
1505 /* FIXME: we probably should use dir_cleanup() here
1506 * instead of rm_rf() so that 'x' is honoured. */
1507 log_debug("rm -rf \"%s\"", instance
);
1508 r
= rm_rf(instance
, (i
->type
== RECURSIVE_REMOVE_PATH
? REMOVE_ROOT
|REMOVE_SUBVOLUME
: 0) | REMOVE_PHYSICAL
);
1509 if (r
< 0 && r
!= -ENOENT
)
1510 return log_error_errno(r
, "rm_rf(%s): %m", instance
);
1515 assert_not_reached("wut?");
1521 static int remove_item(Item
*i
) {
1526 log_debug("Running remove action for entry %c %s", (char) i
->type
, i
->path
);
1532 case CREATE_DIRECTORY
:
1533 case CREATE_SUBVOLUME
:
1534 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1535 case CREATE_SUBVOLUME_NEW_QUOTA
:
1537 case CREATE_SYMLINK
:
1538 case CREATE_CHAR_DEVICE
:
1539 case CREATE_BLOCK_DEVICE
:
1541 case IGNORE_DIRECTORY_PATH
:
1544 case RECURSIVE_RELABEL_PATH
:
1548 case RECURSIVE_SET_XATTR
:
1550 case RECURSIVE_SET_ACL
:
1552 case RECURSIVE_SET_ATTRIBUTE
:
1556 case TRUNCATE_DIRECTORY
:
1557 case RECURSIVE_REMOVE_PATH
:
1558 r
= glob_item(i
, remove_item_instance
, false);
1565 static int clean_item_instance(Item
*i
, const char* instance
) {
1566 _cleanup_closedir_
DIR *d
= NULL
;
1570 char timestamp
[FORMAT_TIMESTAMP_MAX
];
1577 n
= now(CLOCK_REALTIME
);
1581 cutoff
= n
- i
->age
;
1583 d
= opendir_nomod(instance
);
1585 if (errno
== ENOENT
|| errno
== ENOTDIR
) {
1586 log_debug_errno(errno
, "Directory \"%s\": %m", instance
);
1590 log_error_errno(errno
, "Failed to open directory %s: %m", instance
);
1594 if (fstat(dirfd(d
), &s
) < 0)
1595 return log_error_errno(errno
, "stat(%s) failed: %m", i
->path
);
1597 if (!S_ISDIR(s
.st_mode
)) {
1598 log_error("%s is not a directory.", i
->path
);
1602 if (fstatat(dirfd(d
), "..", &ps
, AT_SYMLINK_NOFOLLOW
) != 0)
1603 return log_error_errno(errno
, "stat(%s/..) failed: %m", i
->path
);
1605 mountpoint
= s
.st_dev
!= ps
.st_dev
|| s
.st_ino
== ps
.st_ino
;
1607 log_debug("Cleanup threshold for %s \"%s\" is %s",
1608 mountpoint
? "mount point" : "directory",
1610 format_timestamp_us(timestamp
, sizeof(timestamp
), cutoff
));
1612 return dir_cleanup(i
, instance
, d
, &s
, cutoff
, s
.st_dev
, mountpoint
,
1613 MAX_DEPTH
, i
->keep_first_level
);
1616 static int clean_item(Item
*i
) {
1621 log_debug("Running clean action for entry %c %s", (char) i
->type
, i
->path
);
1624 case CREATE_DIRECTORY
:
1625 case CREATE_SUBVOLUME
:
1626 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1627 case CREATE_SUBVOLUME_NEW_QUOTA
:
1628 case TRUNCATE_DIRECTORY
:
1631 clean_item_instance(i
, i
->path
);
1633 case IGNORE_DIRECTORY_PATH
:
1634 r
= glob_item(i
, clean_item_instance
, false);
1643 static int process_item_array(ItemArray
*array
);
1645 static int process_item(Item
*i
) {
1647 _cleanup_free_
char *prefix
= NULL
;
1656 prefix
= malloc(strlen(i
->path
) + 1);
1660 PATH_FOREACH_PREFIX(prefix
, i
->path
) {
1663 j
= ordered_hashmap_get(items
, prefix
);
1667 s
= process_item_array(j
);
1668 if (s
< 0 && t
== 0)
1673 r
= arg_create
? create_item(i
) : 0;
1674 q
= arg_remove
? remove_item(i
) : 0;
1675 p
= arg_clean
? clean_item(i
) : 0;
1683 static int process_item_array(ItemArray
*array
) {
1689 for (n
= 0; n
< array
->count
; n
++) {
1690 k
= process_item(array
->items
+ n
);
1691 if (k
< 0 && r
== 0)
1698 static void item_free_contents(Item
*i
) {
1702 strv_free(i
->xattrs
);
1705 acl_free(i
->acl_access
);
1706 acl_free(i
->acl_default
);
1710 static void item_array_free(ItemArray
*a
) {
1716 for (n
= 0; n
< a
->count
; n
++)
1717 item_free_contents(a
->items
+ n
);
1722 static int item_compare(const void *a
, const void *b
) {
1723 const Item
*x
= a
, *y
= b
;
1725 /* Make sure that the ownership taking item is put first, so
1726 * that we first create the node, and then can adjust it */
1728 if (takes_ownership(x
->type
) && !takes_ownership(y
->type
))
1730 if (!takes_ownership(x
->type
) && takes_ownership(y
->type
))
1733 return (int) x
->type
- (int) y
->type
;
1736 static bool item_compatible(Item
*a
, Item
*b
) {
1739 assert(streq(a
->path
, b
->path
));
1741 if (takes_ownership(a
->type
) && takes_ownership(b
->type
))
1742 /* check if the items are the same */
1743 return streq_ptr(a
->argument
, b
->argument
) &&
1745 a
->uid_set
== b
->uid_set
&&
1748 a
->gid_set
== b
->gid_set
&&
1751 a
->mode_set
== b
->mode_set
&&
1752 a
->mode
== b
->mode
&&
1754 a
->age_set
== b
->age_set
&&
1757 a
->mask_perms
== b
->mask_perms
&&
1759 a
->keep_first_level
== b
->keep_first_level
&&
1761 a
->major_minor
== b
->major_minor
;
1766 static bool should_include_path(const char *path
) {
1769 STRV_FOREACH(prefix
, arg_exclude_prefixes
)
1770 if (path_startswith(path
, *prefix
)) {
1771 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1776 STRV_FOREACH(prefix
, arg_include_prefixes
)
1777 if (path_startswith(path
, *prefix
)) {
1778 log_debug("Entry \"%s\" matches include prefix \"%s\".", path
, *prefix
);
1782 /* no matches, so we should include this path only if we
1783 * have no whitelist at all */
1784 if (strv_length(arg_include_prefixes
) == 0)
1787 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path
);
1791 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1793 _cleanup_free_
char *action
= NULL
, *mode
= NULL
, *user
= NULL
, *group
= NULL
, *age
= NULL
, *path
= NULL
;
1794 _cleanup_(item_free_contents
) Item i
= {};
1795 ItemArray
*existing
;
1798 bool force
= false, boot
= false;
1804 r
= extract_many_words(
1816 return log_error_errno(r
, "[%s:%u] Failed to parse line: %m", fname
, line
);
1818 log_error("[%s:%u] Syntax error.", fname
, line
);
1822 if (!isempty(buffer
) && !streq(buffer
, "-")) {
1823 i
.argument
= strdup(buffer
);
1828 if (isempty(action
)) {
1829 log_error("[%s:%u] Command too short '%s'.", fname
, line
, action
);
1833 for (pos
= 1; action
[pos
]; pos
++) {
1834 if (action
[pos
] == '!' && !boot
)
1836 else if (action
[pos
] == '+' && !force
)
1839 log_error("[%s:%u] Unknown modifiers in command '%s'",
1840 fname
, line
, action
);
1845 if (boot
&& !arg_boot
) {
1846 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1854 r
= specifier_printf(path
, specifier_table
, NULL
, &i
.path
);
1856 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, path
);
1862 case CREATE_DIRECTORY
:
1863 case CREATE_SUBVOLUME
:
1864 case CREATE_SUBVOLUME_INHERIT_QUOTA
:
1865 case CREATE_SUBVOLUME_NEW_QUOTA
:
1866 case TRUNCATE_DIRECTORY
:
1869 case IGNORE_DIRECTORY_PATH
:
1871 case RECURSIVE_REMOVE_PATH
:
1874 case RECURSIVE_RELABEL_PATH
:
1876 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname
, line
, i
.type
);
1884 case CREATE_SYMLINK
:
1886 i
.argument
= strappend("/usr/share/factory/", i
.path
);
1894 log_error("[%s:%u] Write file requires argument.", fname
, line
);
1901 i
.argument
= strappend("/usr/share/factory/", i
.path
);
1904 } else if (!path_is_absolute(i
.argument
)) {
1905 log_error("[%s:%u] Source path is not absolute.", fname
, line
);
1909 path_kill_slashes(i
.argument
);
1912 case CREATE_CHAR_DEVICE
:
1913 case CREATE_BLOCK_DEVICE
: {
1914 unsigned major
, minor
;
1917 log_error("[%s:%u] Device file requires argument.", fname
, line
);
1921 if (sscanf(i
.argument
, "%u:%u", &major
, &minor
) != 2) {
1922 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname
, line
, i
.argument
);
1926 i
.major_minor
= makedev(major
, minor
);
1931 case RECURSIVE_SET_XATTR
:
1933 log_error("[%s:%u] Set extended attribute requires argument.", fname
, line
);
1936 r
= parse_xattrs_from_arg(&i
);
1942 case RECURSIVE_SET_ACL
:
1944 log_error("[%s:%u] Set ACLs requires argument.", fname
, line
);
1947 r
= parse_acls_from_arg(&i
);
1953 case RECURSIVE_SET_ATTRIBUTE
:
1955 log_error("[%s:%u] Set file attribute requires argument.", fname
, line
);
1958 r
= parse_attribute_from_arg(&i
);
1964 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, (char) i
.type
);
1968 if (!path_is_absolute(i
.path
)) {
1969 log_error("[%s:%u] Path '%s' not absolute.", fname
, line
, i
.path
);
1973 path_kill_slashes(i
.path
);
1975 if (!should_include_path(i
.path
))
1981 p
= prefix_root(arg_root
, i
.path
);
1989 if (!isempty(user
) && !streq(user
, "-")) {
1990 const char *u
= user
;
1992 r
= get_user_creds(&u
, &i
.uid
, NULL
, NULL
, NULL
);
1994 log_error("[%s:%u] Unknown user '%s'.", fname
, line
, user
);
2001 if (!isempty(group
) && !streq(group
, "-")) {
2002 const char *g
= group
;
2004 r
= get_group_creds(&g
, &i
.gid
);
2006 log_error("[%s:%u] Unknown group '%s'.", fname
, line
, group
);
2013 if (!isempty(mode
) && !streq(mode
, "-")) {
2014 const char *mm
= mode
;
2018 i
.mask_perms
= true;
2022 if (parse_mode(mm
, &m
) < 0) {
2023 log_error("[%s:%u] Invalid mode '%s'.", fname
, line
, mode
);
2030 i
.mode
= IN_SET(i
.type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
) ? 0755 : 0644;
2032 if (!isempty(age
) && !streq(age
, "-")) {
2033 const char *a
= age
;
2036 i
.keep_first_level
= true;
2040 if (parse_sec(a
, &i
.age
) < 0) {
2041 log_error("[%s:%u] Invalid age '%s'.", fname
, line
, age
);
2048 h
= needs_glob(i
.type
) ? globs
: items
;
2050 existing
= ordered_hashmap_get(h
, i
.path
);
2054 for (n
= 0; n
< existing
->count
; n
++) {
2055 if (!item_compatible(existing
->items
+ n
, &i
)) {
2056 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2057 fname
, line
, i
.path
);
2062 existing
= new0(ItemArray
, 1);
2063 r
= ordered_hashmap_put(h
, i
.path
, existing
);
2068 if (!GREEDY_REALLOC(existing
->items
, existing
->size
, existing
->count
+ 1))
2071 memcpy(existing
->items
+ existing
->count
++, &i
, sizeof(i
));
2073 /* Sort item array, to enforce stable ordering of application */
2074 qsort_safe(existing
->items
, existing
->count
, sizeof(Item
), item_compare
);
2080 static void help(void) {
2081 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2082 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2083 " -h --help Show this help\n"
2084 " --version Show package version\n"
2085 " --create Create marked files/directories\n"
2086 " --clean Clean up marked directories\n"
2087 " --remove Remove marked files/directories\n"
2088 " --boot Execute actions only safe at boot\n"
2089 " --prefix=PATH Only apply rules with the specified prefix\n"
2090 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2091 " --root=PATH Operate on an alternate filesystem root\n",
2092 program_invocation_short_name
);
2095 static int parse_argv(int argc
, char *argv
[]) {
2098 ARG_VERSION
= 0x100,
2108 static const struct option options
[] = {
2109 { "help", no_argument
, NULL
, 'h' },
2110 { "version", no_argument
, NULL
, ARG_VERSION
},
2111 { "create", no_argument
, NULL
, ARG_CREATE
},
2112 { "clean", no_argument
, NULL
, ARG_CLEAN
},
2113 { "remove", no_argument
, NULL
, ARG_REMOVE
},
2114 { "boot", no_argument
, NULL
, ARG_BOOT
},
2115 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
2116 { "exclude-prefix", required_argument
, NULL
, ARG_EXCLUDE_PREFIX
},
2117 { "root", required_argument
, NULL
, ARG_ROOT
},
2126 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
2154 if (strv_push(&arg_include_prefixes
, optarg
) < 0)
2158 case ARG_EXCLUDE_PREFIX
:
2159 if (strv_push(&arg_exclude_prefixes
, optarg
) < 0)
2164 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
2173 assert_not_reached("Unhandled option");
2176 if (!arg_clean
&& !arg_create
&& !arg_remove
) {
2177 log_error("You need to specify at least one of --clean, --create or --remove.");
2184 static int read_config_file(const char *fn
, bool ignore_enoent
) {
2185 _cleanup_fclose_
FILE *f
= NULL
;
2186 char line
[LINE_MAX
];
2194 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &f
);
2196 if (ignore_enoent
&& r
== -ENOENT
) {
2197 log_debug_errno(r
, "Failed to open \"%s\": %m", fn
);
2201 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
2203 log_debug("Reading config file \"%s\".", fn
);
2205 FOREACH_LINE(line
, f
, break) {
2212 if (*l
== '#' || *l
== 0)
2215 k
= parse_line(fn
, v
, l
);
2216 if (k
< 0 && r
== 0)
2220 /* we have to determine age parameter for each entry of type X */
2221 ORDERED_HASHMAP_FOREACH(i
, globs
, iterator
) {
2223 Item
*j
, *candidate_item
= NULL
;
2225 if (i
->type
!= IGNORE_DIRECTORY_PATH
)
2228 ORDERED_HASHMAP_FOREACH(j
, items
, iter
) {
2229 if (!IN_SET(j
->type
, CREATE_DIRECTORY
, TRUNCATE_DIRECTORY
, CREATE_SUBVOLUME
, CREATE_SUBVOLUME_INHERIT_QUOTA
, CREATE_SUBVOLUME_NEW_QUOTA
))
2232 if (path_equal(j
->path
, i
->path
)) {
2237 if ((!candidate_item
&& path_startswith(i
->path
, j
->path
)) ||
2238 (candidate_item
&& path_startswith(j
->path
, candidate_item
->path
) && (fnmatch(i
->path
, j
->path
, FNM_PATHNAME
| FNM_PERIOD
) == 0)))
2242 if (candidate_item
&& candidate_item
->age_set
) {
2243 i
->age
= candidate_item
->age
;
2249 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
2257 int main(int argc
, char *argv
[]) {
2262 r
= parse_argv(argc
, argv
);
2266 log_set_target(LOG_TARGET_AUTO
);
2267 log_parse_environment();
2272 mac_selinux_init(NULL
);
2274 items
= ordered_hashmap_new(&string_hash_ops
);
2275 globs
= ordered_hashmap_new(&string_hash_ops
);
2277 if (!items
|| !globs
) {
2284 if (optind
< argc
) {
2287 for (j
= optind
; j
< argc
; j
++) {
2288 k
= read_config_file(argv
[j
], false);
2289 if (k
< 0 && r
== 0)
2294 _cleanup_strv_free_
char **files
= NULL
;
2297 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
2299 log_error_errno(r
, "Failed to enumerate tmpfiles.d files: %m");
2303 STRV_FOREACH(f
, files
) {
2304 k
= read_config_file(*f
, true);
2305 if (k
< 0 && r
== 0)
2310 /* The non-globbing ones usually create things, hence we apply
2312 ORDERED_HASHMAP_FOREACH(a
, items
, iterator
) {
2313 k
= process_item_array(a
);
2314 if (k
< 0 && r
== 0)
2318 /* The globbing ones usually alter things, hence we apply them
2320 ORDERED_HASHMAP_FOREACH(a
, globs
, iterator
) {
2321 k
= process_item_array(a
);
2322 if (k
< 0 && r
== 0)
2327 while ((a
= ordered_hashmap_steal_first(items
)))
2330 while ((a
= ordered_hashmap_steal_first(globs
)))
2333 ordered_hashmap_free(items
);
2334 ordered_hashmap_free(globs
);
2336 free(arg_include_prefixes
);
2337 free(arg_exclude_prefixes
);
2340 set_free_free(unix_sockets
);
2342 mac_selinux_finish();
2344 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;