1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "conf-files.h"
29 #include "dirent-util.h"
35 #include "path-util.h"
36 #include "stat-util.h"
37 #include "string-util.h"
41 static int files_add(Hashmap
*h
, const char *suffix
, const char *root
, unsigned flags
, const char *path
) {
42 _cleanup_closedir_
DIR *dir
= NULL
;
49 dirpath
= prefix_roota(root
, path
);
51 dir
= opendir(dirpath
);
58 FOREACH_DIRENT(de
, dir
, return -errno
) {
61 if (!dirent_is_file_with_suffix(de
, suffix
)) {
62 log_debug("Ignoring %s/%s, because it's not a regular file with suffix %s.", dirpath
, de
->d_name
, strna(suffix
));
66 if (flags
& CONF_FILES_EXECUTABLE
) {
69 /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK)
70 * here, as we care about whether the file is marked executable at all, and not whether it is
71 * executable for us, because if such errors are stuff we should log about. */
73 if (fstatat(dirfd(dir
), de
->d_name
, &st
, 0) < 0) {
74 log_debug_errno(errno
, "Failed to stat %s/%s, ignoring: %m", dirpath
, de
->d_name
);
78 if (!null_or_empty(&st
)) {
79 /* A mask is a symlink to /dev/null or an empty file. It does not even
80 * have to be executable. Other entries must be regular executable files
81 * or symlinks to them. */
82 if (S_ISREG(st
.st_mode
)) {
83 if ((st
.st_mode
& 0111) == 0) { /* not executable */
84 log_debug("Ignoring %s/%s, as it is not marked executable.",
89 log_debug("Ignoring %s/%s, as it is neither a regular file nor a mask.",
96 p
= strjoin(dirpath
, "/", de
->d_name
);
100 r
= hashmap_put(h
, basename(p
), p
);
102 log_debug("Skipping overridden file: %s.", p
);
108 log_debug("Duplicate file %s", p
);
116 static int base_cmp(const void *a
, const void *b
) {
119 s1
= *(char * const *)a
;
120 s2
= *(char * const *)b
;
121 return strcmp(basename(s1
), basename(s2
));
124 static int conf_files_list_strv_internal(char ***strv
, const char *suffix
, const char *root
, unsigned flags
, char **dirs
) {
125 _cleanup_hashmap_free_ Hashmap
*fh
= NULL
;
131 /* This alters the dirs string array */
132 if (!path_strv_resolve_uniq(dirs
, root
))
135 fh
= hashmap_new(&string_hash_ops
);
139 STRV_FOREACH(p
, dirs
) {
140 r
= files_add(fh
, suffix
, root
, flags
, *p
);
144 log_debug_errno(r
, "Failed to search for files in %s, ignoring: %m", *p
);
147 files
= hashmap_get_strv(fh
);
151 qsort_safe(files
, hashmap_size(fh
), sizeof(char *), base_cmp
);
157 int conf_files_list_strv(char ***strv
, const char *suffix
, const char *root
, unsigned flags
, const char* const* dirs
) {
158 _cleanup_strv_free_
char **copy
= NULL
;
162 copy
= strv_copy((char**) dirs
);
166 return conf_files_list_strv_internal(strv
, suffix
, root
, flags
, copy
);
169 int conf_files_list(char ***strv
, const char *suffix
, const char *root
, unsigned flags
, const char *dir
, ...) {
170 _cleanup_strv_free_
char **dirs
= NULL
;
176 dirs
= strv_new_ap(dir
, ap
);
182 return conf_files_list_strv_internal(strv
, suffix
, root
, flags
, dirs
);
185 int conf_files_list_nulstr(char ***strv
, const char *suffix
, const char *root
, unsigned flags
, const char *d
) {
186 _cleanup_strv_free_
char **dirs
= NULL
;
190 dirs
= strv_split_nulstr(d
);
194 return conf_files_list_strv_internal(strv
, suffix
, root
, flags
, dirs
);