blob: 13e830db56b63ca3a9d593d174d3c46eab10f17e [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
Andreas Jaeger01000472010-09-29 10:08:24 +020037/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
Lennart Poettering5008d582010-09-28 02:34:02 +020038 * 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
Lennart Poettering4aa8b152010-09-28 22:32:05 +020043static int process_line(const char *fname, unsigned line, const char *buffer, const char *prefix) {
Lennart Poettering5008d582010-09-28 02:34:02 +020044 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;
Lennart Poettering4aa8b152010-09-28 22:32:05 +020051 int n, fd = -1, r;
Lennart Poettering5008d582010-09-28 02:34:02 +020052
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);
Lennart Poettering4aa8b152010-09-28 22:32:05 +020069 r = -EIO;
Lennart Poettering5008d582010-09-28 02:34:02 +020070 goto finish;
71 }
72
73 if (type != 'f' && type != 'd') {
74 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
Lennart Poettering4aa8b152010-09-28 22:32:05 +020075 r = -EBADMSG;
Lennart Poettering5008d582010-09-28 02:34:02 +020076 goto finish;
77 }
78
Lennart Poettering4aa8b152010-09-28 22:32:05 +020079 if (prefix && !path_startswith(path, prefix)) {
80 r = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +020081 goto finish;
Lennart Poettering4aa8b152010-09-28 22:32:05 +020082 }
Lennart Poettering5008d582010-09-28 02:34:02 +020083
84 if (user && !streq(user, "-")) {
85 unsigned long lu;
86 struct passwd *p;
87
88 if (streq(user, "root") || streq(user, "0"))
89 uid = 0;
90 else if (safe_atolu(user, &lu) >= 0)
91 uid = (uid_t) lu;
92 else if ((p = getpwnam(user)))
93 uid = p->pw_uid;
94 else {
95 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
Lennart Poettering4aa8b152010-09-28 22:32:05 +020096 r = -ENOENT;
Lennart Poettering5008d582010-09-28 02:34:02 +020097 goto finish;
98 }
99
100 uid_set = true;
101 }
102
103 if (group && !streq(group, "-")) {
104 unsigned long lu;
105 struct group *g;
106
107 if (streq(group, "root") || streq(group, "0"))
108 gid = 0;
109 else if (safe_atolu(group, &lu) >= 0)
110 gid = (gid_t) lu;
111 else if ((g = getgrnam(group)))
112 gid = g->gr_gid;
113 else {
114 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200115 r = -ENOENT;
Lennart Poettering5008d582010-09-28 02:34:02 +0200116 goto finish;
117 }
118
119 gid_set = true;
120 }
121
122 if (n < 3)
123 mode = type == 'f' ? 0644 : 0755;
124
125 if (type == 'f') {
126 mode_t u;
127 struct stat st;
128
129 u = umask(0);
130 fd = open(path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, mode);
131 umask(u);
132
133 if (fd < 0) {
134 log_error("Failed to create file %s: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200135 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200136 goto finish;
137 }
138
139 if (fstat(fd, &st) < 0) {
140 log_error("stat(%s) failed: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200141 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200142 goto finish;
143 }
144
145 if (!S_ISREG(st.st_mode)) {
146 log_error("%s is not a file.", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200147 r = -EEXIST;
Lennart Poettering5008d582010-09-28 02:34:02 +0200148 goto finish;
149 }
150
151 if (fchmod(fd, mode) < 0) {
152 log_error("chmod(%s) failed: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200153 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200154 goto finish;
155 }
156
157 if (uid_set || gid_set) {
158
159 if (fchown(fd,
160 uid_set ? uid : (uid_t) -1,
161 gid_set ? gid : (gid_t) -1) < 0) {
162 log_error("chown(%s) failed: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200163 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200164 goto finish;
165 }
166 }
167
168 } else if (type == 'd') {
169 mode_t u;
Lennart Poettering5008d582010-09-28 02:34:02 +0200170 struct stat st;
171
172 u = umask(0);
173 r = mkdir(path, mode);
174 umask(u);
175
176 if (r < 0 && errno != EEXIST) {
177 log_error("Failed to create directory %s: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200178 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200179 goto finish;
180 }
181
182 if (stat(path, &st) < 0) {
183 log_error("stat(%s) failed: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200184 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200185 goto finish;
186 }
187
188 if (!S_ISDIR(st.st_mode)) {
189 log_error("%s is not a directory.", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200190 r = -EEXIST;
Lennart Poettering5008d582010-09-28 02:34:02 +0200191 goto finish;
192 }
193
194 if (chmod(path, mode) < 0) {
195 log_error("chmod(%s) failed: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200196 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200197 goto finish;
198 }
199
200 if (uid_set || gid_set) {
201
202 if (chown(path,
203 uid_set ? uid : (uid_t) -1,
204 gid_set ? gid : (gid_t) -1) < 0) {
205 log_error("chown(%s) failed: %m", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200206 r = -errno;
Lennart Poettering5008d582010-09-28 02:34:02 +0200207 goto finish;
208 }
209 }
210 }
211
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200212 if ((r = label_fix(path)) < 0)
213 goto finish;
Lennart Poettering5008d582010-09-28 02:34:02 +0200214
215 log_debug("%s created successfully.", path);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200216 r = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +0200217
218finish:
219 free(path);
220 free(user);
221 free(group);
222
223 if (fd >= 0)
224 close_nointr_nofail(fd);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200225
226 return r;
Lennart Poettering5008d582010-09-28 02:34:02 +0200227}
228
229static int scandir_filter(const struct dirent *d) {
230 assert(d);
231
232 if (ignore_file(d->d_name))
233 return 0;
234
235 if (d->d_type != DT_REG &&
236 d->d_type != DT_LNK)
237 return 0;
238
239 return endswith(d->d_name, ".conf");
240}
241
242int main(int argc, char *argv[]) {
243 struct dirent **de = NULL;
244 int r = EXIT_FAILURE, n, i;
245 const char *prefix = NULL;
246
247 if (argc > 2) {
248 log_error("This program takes no more than one argument.");
249 return EXIT_FAILURE;
250 } else if (argc > 1)
251 prefix = argv[1];
252 else
253 prefix = "/";
254
255 log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
256 log_parse_environment();
257 log_open();
258
259 label_init();
260
Lennart Poettering7fcde282010-09-28 22:32:27 +0200261 if ((n = scandir("/etc/tmpfiles.d/", &de, scandir_filter, alphasort)) < 0) {
Lennart Poettering5008d582010-09-28 02:34:02 +0200262
263 if (errno == ENOENT)
264 r = EXIT_SUCCESS;
265 else
Lennart Poettering7fcde282010-09-28 22:32:27 +0200266 log_error("Failed to enumerate /etc/tmpfiles.d/ files: %m");
Lennart Poettering5008d582010-09-28 02:34:02 +0200267
268 goto finish;
269 }
270
271 r = EXIT_SUCCESS;
272
273 for (i = 0; i < n; i++) {
274 int k;
275 char *fn;
276 FILE *f;
277 unsigned j;
278
Lennart Poettering7fcde282010-09-28 22:32:27 +0200279 k = asprintf(&fn, "/etc/tmpfiles.d/%s", de[i]->d_name);
Lennart Poettering5008d582010-09-28 02:34:02 +0200280 free(de[i]);
281
282 if (k < 0) {
283 log_error("Failed to allocate file name.");
284 r = EXIT_FAILURE;
285 continue;
286 }
287
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200288 if (!(f = fopen(fn, "re"))) {
Lennart Poettering5008d582010-09-28 02:34:02 +0200289 log_error("Failed to open %s: %m", fn);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200290 free(fn);
Lennart Poettering5008d582010-09-28 02:34:02 +0200291 r = EXIT_FAILURE;
292 continue;
293 }
294
295 j = 0;
296 for (;;) {
297 char line[LINE_MAX], *l;
298
299 if (!(fgets(line, sizeof(line), f)))
300 break;
301
302 j++;
303
304 l = strstrip(line);
305 if (*l == '#' || *l == 0)
306 continue;
307
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200308 if (process_line(fn, j, l, prefix) < 0)
309 r = EXIT_FAILURE;
Lennart Poettering5008d582010-09-28 02:34:02 +0200310 }
311
312 if (ferror(f)) {
313 r = EXIT_FAILURE;
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200314 log_error("Failed to read from file %s: %m", fn);
Lennart Poettering5008d582010-09-28 02:34:02 +0200315 }
316
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200317 free(fn);
318
Lennart Poettering5008d582010-09-28 02:34:02 +0200319 fclose(f);
320 }
321
322 free(de);
323
324finish:
325
326 return r;
327}