chiark / gitweb /
tmpfiles: rename tempfiles to tmpfiles since this isn't windows
[elogind.git] / src / tmpfiles.c
1 /*-*- 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
43 static 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
201 finish:
202         free(path);
203         free(user);
204         free(group);
205
206         if (fd >= 0)
207                 close_nointr_nofail(fd);
208 }
209
210 static 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
223 int 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
304 finish:
305
306         return r;
307 }