]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/conf-files.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / basic / conf-files.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
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.
11
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.
16
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/>.
19 ***/
20
21 #include <dirent.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "conf-files.h"
29 #include "dirent-util.h"
30 #include "fd-util.h"
31 #include "hashmap.h"
32 #include "log.h"
33 #include "macro.h"
34 #include "missing.h"
35 #include "path-util.h"
36 #include "stat-util.h"
37 #include "string-util.h"
38 #include "strv.h"
39 #include "util.h"
40
41 static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) {
42 _cleanup_closedir_ DIR *dir = NULL;
43 const char *dirpath;
44 struct dirent *de;
45 int r;
46
47 assert(path);
48
49 dirpath = prefix_roota(root, path);
50
51 dir = opendir(dirpath);
52 if (!dir) {
53 if (errno == ENOENT)
54 return 0;
55 return -errno;
56 }
57
58 FOREACH_DIRENT(de, dir, return -errno) {
59 char *p;
60
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));
63 continue;
64 }
65
66 if (flags & CONF_FILES_EXECUTABLE) {
67 struct stat st;
68
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. */
72
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);
75 continue;
76 }
77
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.",
85 dirpath, de->d_name);
86 continue;
87 }
88 } else {
89 log_debug("Ignoring %s/%s, as it is neither a regular file nor a mask.",
90 dirpath, de->d_name);
91 continue;
92 }
93 }
94 }
95
96 p = strjoin(dirpath, "/", de->d_name);
97 if (!p)
98 return -ENOMEM;
99
100 r = hashmap_put(h, basename(p), p);
101 if (r == -EEXIST) {
102 log_debug("Skipping overridden file: %s.", p);
103 free(p);
104 } else if (r < 0) {
105 free(p);
106 return r;
107 } else if (r == 0) {
108 log_debug("Duplicate file %s", p);
109 free(p);
110 }
111 }
112
113 return 0;
114 }
115
116 static int base_cmp(const void *a, const void *b) {
117 const char *s1, *s2;
118
119 s1 = *(char * const *)a;
120 s2 = *(char * const *)b;
121 return strcmp(basename(s1), basename(s2));
122 }
123
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;
126 char **files, **p;
127 int r;
128
129 assert(strv);
130
131 /* This alters the dirs string array */
132 if (!path_strv_resolve_uniq(dirs, root))
133 return -ENOMEM;
134
135 fh = hashmap_new(&string_hash_ops);
136 if (!fh)
137 return -ENOMEM;
138
139 STRV_FOREACH(p, dirs) {
140 r = files_add(fh, suffix, root, flags, *p);
141 if (r == -ENOMEM)
142 return r;
143 if (r < 0)
144 log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p);
145 }
146
147 files = hashmap_get_strv(fh);
148 if (!files)
149 return -ENOMEM;
150
151 qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
152 *strv = files;
153
154 return 0;
155 }
156
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;
159
160 assert(strv);
161
162 copy = strv_copy((char**) dirs);
163 if (!copy)
164 return -ENOMEM;
165
166 return conf_files_list_strv_internal(strv, suffix, root, flags, copy);
167 }
168
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;
171 va_list ap;
172
173 assert(strv);
174
175 va_start(ap, dir);
176 dirs = strv_new_ap(dir, ap);
177 va_end(ap);
178
179 if (!dirs)
180 return -ENOMEM;
181
182 return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
183 }
184
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;
187
188 assert(strv);
189
190 dirs = strv_split_nulstr(d);
191 if (!dirs)
192 return -ENOMEM;
193
194 return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
195 }