]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/conf-files.c
Merge pull request #7388 from keszybz/doc-tweak
[thirdparty/systemd.git] / src / basic / conf-files.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <dirent.h>
21 #include <errno.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "conf-files.h"
28 #include "dirent-util.h"
29 #include "fd-util.h"
30 #include "hashmap.h"
31 #include "log.h"
32 #include "macro.h"
33 #include "missing.h"
34 #include "path-util.h"
35 #include "stat-util.h"
36 #include "string-util.h"
37 #include "strv.h"
38 #include "util.h"
39
40 static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) {
41 _cleanup_closedir_ DIR *dir = NULL;
42 const char *dirpath;
43 struct dirent *de;
44 int r;
45
46 assert(path);
47
48 dirpath = prefix_roota(root, path);
49
50 dir = opendir(dirpath);
51 if (!dir) {
52 if (errno == ENOENT)
53 return 0;
54 return -errno;
55 }
56
57 FOREACH_DIRENT(de, dir, return -errno) {
58 char *p;
59
60 if (!dirent_is_file_with_suffix(de, suffix)) {
61 log_debug("Ignoring %s/%s, because it's not a regular file with suffix %s.", dirpath, de->d_name, strna(suffix));
62 continue;
63 }
64
65 if (flags & CONF_FILES_EXECUTABLE) {
66 struct stat st;
67
68 /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK)
69 * here, as we care about whether the file is marked executable at all, and not whether it is
70 * executable for us, because if such errors are stuff we should log about. */
71
72 if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) {
73 log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", dirpath, de->d_name);
74 continue;
75 }
76
77 if (!null_or_empty(&st)) {
78 /* A mask is a symlink to /dev/null or an empty file. It does not even
79 * have to be executable. Other entries must be regular executable files
80 * or symlinks to them. */
81 if (S_ISREG(st.st_mode)) {
82 if ((st.st_mode & 0111) == 0) { /* not executable */
83 log_debug("Ignoring %s/%s, as it is not marked executable.",
84 dirpath, de->d_name);
85 continue;
86 }
87 } else {
88 log_debug("Ignoring %s/%s, as it is neither a regular file nor a mask.",
89 dirpath, de->d_name);
90 continue;
91 }
92 }
93 }
94
95 p = strjoin(dirpath, "/", de->d_name);
96 if (!p)
97 return -ENOMEM;
98
99 r = hashmap_put(h, basename(p), p);
100 if (r == -EEXIST) {
101 log_debug("Skipping overridden file: %s.", p);
102 free(p);
103 } else if (r < 0) {
104 free(p);
105 return r;
106 } else if (r == 0) {
107 log_debug("Duplicate file %s", p);
108 free(p);
109 }
110 }
111
112 return 0;
113 }
114
115 static int base_cmp(const void *a, const void *b) {
116 const char *s1, *s2;
117
118 s1 = *(char * const *)a;
119 s2 = *(char * const *)b;
120 return strcmp(basename(s1), basename(s2));
121 }
122
123 static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, unsigned flags, char **dirs) {
124 _cleanup_hashmap_free_ Hashmap *fh = NULL;
125 char **files, **p;
126 int r;
127
128 assert(strv);
129
130 /* This alters the dirs string array */
131 if (!path_strv_resolve_uniq(dirs, root))
132 return -ENOMEM;
133
134 fh = hashmap_new(&string_hash_ops);
135 if (!fh)
136 return -ENOMEM;
137
138 STRV_FOREACH(p, dirs) {
139 r = files_add(fh, suffix, root, flags, *p);
140 if (r == -ENOMEM)
141 return r;
142 if (r < 0)
143 log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p);
144 }
145
146 files = hashmap_get_strv(fh);
147 if (!files)
148 return -ENOMEM;
149
150 qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
151 *strv = files;
152
153 return 0;
154 }
155
156 int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) {
157 _cleanup_strv_free_ char **copy = NULL;
158
159 assert(strv);
160
161 copy = strv_copy((char**) dirs);
162 if (!copy)
163 return -ENOMEM;
164
165 return conf_files_list_strv_internal(strv, suffix, root, flags, copy);
166 }
167
168 int conf_files_list(char ***strv, const char *suffix, const char *root, unsigned flags, const char *dir, ...) {
169 _cleanup_strv_free_ char **dirs = NULL;
170 va_list ap;
171
172 assert(strv);
173
174 va_start(ap, dir);
175 dirs = strv_new_ap(dir, ap);
176 va_end(ap);
177
178 if (!dirs)
179 return -ENOMEM;
180
181 return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
182 }
183
184 int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *d) {
185 _cleanup_strv_free_ char **dirs = NULL;
186
187 assert(strv);
188
189 dirs = strv_split_nulstr(d);
190 if (!dirs)
191 return -ENOMEM;
192
193 return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
194 }