chiark / gitweb /
systemctl: warn if user enables unit with no installation instructions
[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/tmpfiles.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 int 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, r;
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                 r = -EIO;
70                 goto finish;
71         }
72
73         if (type != 'f' && type != 'd') {
74                 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
75                 r = -EBADMSG;
76                 goto finish;
77         }
78
79         if (prefix && !path_startswith(path, prefix)) {
80                 r = 0;
81                 goto finish;
82         }
83
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);
96                         r = -ENOENT;
97                         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);
115                         r = -ENOENT;
116                         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);
135                         r = -errno;
136                         goto finish;
137                 }
138
139                 if (fstat(fd, &st) < 0) {
140                         log_error("stat(%s) failed: %m", path);
141                         r = -errno;
142                         goto finish;
143                 }
144
145                 if (!S_ISREG(st.st_mode)) {
146                         log_error("%s is not a file.", path);
147                         r = -EEXIST;
148                         goto finish;
149                 }
150
151                 if (fchmod(fd, mode) < 0) {
152                         log_error("chmod(%s) failed: %m", path);
153                         r = -errno;
154                         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);
163                                 r = -errno;
164                                 goto finish;
165                         }
166                 }
167
168         } else if (type == 'd') {
169                 mode_t u;
170                 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);
178                         r = -errno;
179                         goto finish;
180                 }
181
182                 if (stat(path, &st) < 0) {
183                         log_error("stat(%s) failed: %m", path);
184                         r = -errno;
185                         goto finish;
186                 }
187
188                 if (!S_ISDIR(st.st_mode)) {
189                         log_error("%s is not a directory.", path);
190                         r = -EEXIST;
191                         goto finish;
192                 }
193
194                 if (chmod(path, mode) < 0) {
195                         log_error("chmod(%s) failed: %m", path);
196                         r = -errno;
197                         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);
206                                 r = -errno;
207                                 goto finish;
208                         }
209                 }
210         }
211
212         if ((r = label_fix(path)) < 0)
213                 goto finish;
214
215         log_debug("%s created successfully.", path);
216         r = 0;
217
218 finish:
219         free(path);
220         free(user);
221         free(group);
222
223         if (fd >= 0)
224                 close_nointr_nofail(fd);
225
226         return r;
227 }
228
229 static 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
242 int 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
261         if ((n = scandir("/etc/tmpfiles.d/", &de, scandir_filter, alphasort)) < 0) {
262
263                 if (errno == ENOENT)
264                         r = EXIT_SUCCESS;
265                 else
266                         log_error("Failed to enumerate /etc/tmpfiles.d/ files: %m");
267
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
279                 k = asprintf(&fn, "/etc/tmpfiles.d/%s", de[i]->d_name);
280                 free(de[i]);
281
282                 if (k < 0) {
283                         log_error("Failed to allocate file name.");
284                         r = EXIT_FAILURE;
285                         continue;
286                 }
287
288                 if (!(f = fopen(fn, "re"))) {
289                         log_error("Failed to open %s: %m", fn);
290                         free(fn);
291                         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
308                         if (process_line(fn, j, l, prefix) < 0)
309                                 r = EXIT_FAILURE;
310                 }
311
312                 if (ferror(f)) {
313                         r = EXIT_FAILURE;
314                         log_error("Failed to read from file %s: %m", fn);
315                 }
316
317                 free(fn);
318
319                 fclose(f);
320         }
321
322         free(de);
323
324 finish:
325
326         return r;
327 }