blob: 80e424f7b6aa4f9b58ecef7b3a548fca388fbb85 [file] [log] [blame]
Lennart Poettering5008d582010-09-28 02:34:02 +02001/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <unistd.h>
23#include <fcntl.h>
24#include <errno.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <limits.h>
28#include <dirent.h>
29#include <grp.h>
30#include <pwd.h>
31
32#include "log.h"
33#include "util.h"
34#include "strv.h"
35#include "label.h"
36
37/* This reads all files listed in /etc/tempfiles.d/?*.conf and creates
38 * them in the file system. This is intended to be used to create
39 * properly owned directories beneath /tmp, /var/tmp, /var/run and
40 * /var/lock which are volatile and hence need to be recreated on
41 * bootup. */
42
43static void process_line(const char *fname, unsigned line, const char *buffer, const char *prefix) {
44 char type;
45 char *path = NULL;
46 unsigned mode;
47 char *user = NULL, *group = NULL;
48 uid_t uid;
49 gid_t gid;
50 bool uid_set = false, gid_set = false;
51 int n, fd = -1;
52
53 assert(fname);
54 assert(line >= 1);
55 assert(buffer);
56
57 if ((n = sscanf(buffer,
58 "%c "
59 "%ms "
60 "%o "
61 "%ms "
62 "%ms ",
63 &type,
64 &path,
65 &mode,
66 &user,
67 &group)) < 2) {
68 log_error("[%s:%u] Syntax error.", fname, line);
69 goto finish;
70 }
71
72 if (type != 'f' && type != 'd') {
73 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
74 goto finish;
75 }
76
77 if (prefix && !path_startswith(path, prefix))
78 goto finish;
79
80 if (user && !streq(user, "-")) {
81 unsigned long lu;
82 struct passwd *p;
83
84 if (streq(user, "root") || streq(user, "0"))
85 uid = 0;
86 else if (safe_atolu(user, &lu) >= 0)
87 uid = (uid_t) lu;
88 else if ((p = getpwnam(user)))
89 uid = p->pw_uid;
90 else {
91 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
92 goto finish;
93 }
94
95 uid_set = true;
96 }
97
98 if (group && !streq(group, "-")) {
99 unsigned long lu;
100 struct group *g;
101
102 if (streq(group, "root") || streq(group, "0"))
103 gid = 0;
104 else if (safe_atolu(group, &lu) >= 0)
105 gid = (gid_t) lu;
106 else if ((g = getgrnam(group)))
107 gid = g->gr_gid;
108 else {
109 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
110 goto finish;
111 }
112
113 gid_set = true;
114 }
115
116 if (n < 3)
117 mode = type == 'f' ? 0644 : 0755;
118
119 if (type == 'f') {
120 mode_t u;
121 struct stat st;
122
123 u = umask(0);
124 fd = open(path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, mode);
125 umask(u);
126
127 if (fd < 0) {
128 log_error("Failed to create file %s: %m", path);
129 goto finish;
130 }
131
132 if (fstat(fd, &st) < 0) {
133 log_error("stat(%s) failed: %m", path);
134 goto finish;
135 }
136
137 if (!S_ISREG(st.st_mode)) {
138 log_error("%s is not a file.", path);
139 goto finish;
140 }
141
142 if (fchmod(fd, mode) < 0) {
143 log_error("chmod(%s) failed: %m", path);
144 goto finish;
145 }
146
147 if (uid_set || gid_set) {
148
149 if (fchown(fd,
150 uid_set ? uid : (uid_t) -1,
151 gid_set ? gid : (gid_t) -1) < 0) {
152 log_error("chown(%s) failed: %m", path);
153 goto finish;
154 }
155 }
156
157 } else if (type == 'd') {
158 mode_t u;
159 int r;
160 struct stat st;
161
162 u = umask(0);
163 r = mkdir(path, mode);
164 umask(u);
165
166 if (r < 0 && errno != EEXIST) {
167 log_error("Failed to create directory %s: %m", path);
168 goto finish;
169 }
170
171 if (stat(path, &st) < 0) {
172 log_error("stat(%s) failed: %m", path);
173 goto finish;
174 }
175
176 if (!S_ISDIR(st.st_mode)) {
177 log_error("%s is not a directory.", path);
178 goto finish;
179 }
180
181 if (chmod(path, mode) < 0) {
182 log_error("chmod(%s) failed: %m", path);
183 goto finish;
184 }
185
186 if (uid_set || gid_set) {
187
188 if (chown(path,
189 uid_set ? uid : (uid_t) -1,
190 gid_set ? gid : (gid_t) -1) < 0) {
191 log_error("chown(%s) failed: %m", path);
192 goto finish;
193 }
194 }
195 }
196
197 label_fix(path);
198
199 log_debug("%s created successfully.", path);
200
201finish:
202 free(path);
203 free(user);
204 free(group);
205
206 if (fd >= 0)
207 close_nointr_nofail(fd);
208}
209
210static int scandir_filter(const struct dirent *d) {
211 assert(d);
212
213 if (ignore_file(d->d_name))
214 return 0;
215
216 if (d->d_type != DT_REG &&
217 d->d_type != DT_LNK)
218 return 0;
219
220 return endswith(d->d_name, ".conf");
221}
222
223int main(int argc, char *argv[]) {
224 struct dirent **de = NULL;
225 int r = EXIT_FAILURE, n, i;
226 const char *prefix = NULL;
227
228 if (argc > 2) {
229 log_error("This program takes no more than one argument.");
230 return EXIT_FAILURE;
231 } else if (argc > 1)
232 prefix = argv[1];
233 else
234 prefix = "/";
235
236 log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
237 log_parse_environment();
238 log_open();
239
240 label_init();
241
242 if ((n = scandir("/etc/tempfiles.d/", &de, scandir_filter, alphasort)) < 0) {
243
244 if (errno == ENOENT)
245 r = EXIT_SUCCESS;
246 else
247 log_error("Failed to enumerate /etc/tempfiles.d/ files: %m");
248
249 goto finish;
250 }
251
252 r = EXIT_SUCCESS;
253
254 for (i = 0; i < n; i++) {
255 int k;
256 char *fn;
257 FILE *f;
258 unsigned j;
259
260 k = asprintf(&fn, "/etc/tempfiles.d/%s", de[i]->d_name);
261 free(de[i]);
262
263 if (k < 0) {
264 log_error("Failed to allocate file name.");
265 r = EXIT_FAILURE;
266 continue;
267 }
268
269 f = fopen(fn, "re");
270 free(fn);
271
272 if (!f) {
273 log_error("Failed to open %s: %m", fn);
274 r = EXIT_FAILURE;
275 continue;
276 }
277
278 j = 0;
279 for (;;) {
280 char line[LINE_MAX], *l;
281
282 if (!(fgets(line, sizeof(line), f)))
283 break;
284
285 j++;
286
287 l = strstrip(line);
288 if (*l == '#' || *l == 0)
289 continue;
290
291 process_line(de[i]->d_name, j, l, prefix);
292 }
293
294 if (ferror(f)) {
295 r = EXIT_FAILURE;
296 log_error("Failed to read from file: %m");
297 }
298
299 fclose(f);
300 }
301
302 free(de);
303
304finish:
305
306 return r;
307}