chiark / gitweb /
swap: handle nofail/noauto in core
[elogind.git] / src / fstab-generator / fstab-generator.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <mntent.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "log.h"
29 #include "util.h"
30 #include "unit-name.h"
31 #include "path-util.h"
32 #include "mount-setup.h"
33 #include "special.h"
34 #include "mkdir.h"
35 #include "virt.h"
36 #include "fileio.h"
37
38 static const char *arg_dest = "/tmp";
39 static bool arg_enabled = true;
40
41 static int mount_find_pri(struct mntent *me, int *ret) {
42         char *end, *pri;
43         unsigned long r;
44
45         assert(me);
46         assert(ret);
47
48         pri = hasmntopt(me, "pri");
49         if (!pri)
50                 return 0;
51
52         pri += 4;
53
54         errno = 0;
55         r = strtoul(pri, &end, 10);
56         if (errno > 0)
57                 return -errno;
58
59         if (end == pri || (*end != ',' && *end != 0))
60                 return -EINVAL;
61
62         *ret = (int) r;
63         return 1;
64 }
65
66 static int add_swap(const char *what, struct mntent *me) {
67         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
68         _cleanup_fclose_ FILE *f = NULL;
69         int r, pri = -1;
70
71         assert(what);
72         assert(me);
73
74         r = mount_find_pri(me, &pri);
75         if (r < 0) {
76                 log_error("Failed to parse priority");
77                 return pri;
78         }
79
80         name = unit_name_from_path(what, ".swap");
81         if (!name)
82                 return log_oom();
83
84         unit = strjoin(arg_dest, "/", name, NULL);
85         if (!unit)
86                 return log_oom();
87
88         f = fopen(unit, "wxe");
89         if (!f) {
90                 if (errno == EEXIST)
91                         log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
92                 else
93                         log_error("Failed to create unit file %s: %m", unit);
94                 return -errno;
95         }
96
97         fprintf(f,
98                 "# Automatically generated by systemd-fstab-generator\n\n"
99                 "[Unit]\n"
100                 "SourcePath=/etc/fstab\n"
101                 "\n"
102                 "[Swap]\n"
103                 "What=%s\n",
104                 what);
105
106         if (pri >= 0)
107                 fprintf(f,
108                         "Priority=%i\n",
109                         pri);
110
111         fflush(f);
112         if (ferror(f)) {
113                 log_error("Failed to write unit file %s: %m", unit);
114                 return -errno;
115         }
116
117         return 0;
118 }
119
120 static bool mount_is_network(struct mntent *me) {
121         assert(me);
122
123         return
124                 hasmntopt(me, "_netdev") ||
125                 fstype_is_network(me->mnt_type);
126 }
127
128 static bool mount_in_initrd(struct mntent *me) {
129         assert(me);
130
131         return
132                 hasmntopt(me, "x-initrd.mount") ||
133                 streq(me->mnt_dir, "/usr");
134 }
135
136 static int add_mount(
137                 const char *what,
138                 const char *where,
139                 const char *type,
140                 const char *opts,
141                 int passno,
142                 bool noauto,
143                 bool nofail,
144                 bool automount,
145                 const char *post,
146                 const char *source) {
147         _cleanup_free_ char
148                 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
149                 *automount_name = NULL, *automount_unit = NULL;
150         _cleanup_fclose_ FILE *f = NULL;
151
152         assert(what);
153         assert(where);
154         assert(type);
155         assert(opts);
156         assert(source);
157
158         if (streq(type, "autofs"))
159                 return 0;
160
161         if (!is_path(where)) {
162                 log_warning("Mount point %s is not a valid path, ignoring.", where);
163                 return 0;
164         }
165
166         if (mount_point_is_api(where) ||
167             mount_point_ignore(where))
168                 return 0;
169
170         name = unit_name_from_path(where, ".mount");
171         if (!name)
172                 return log_oom();
173
174         unit = strjoin(arg_dest, "/", name, NULL);
175         if (!unit)
176                 return log_oom();
177
178         f = fopen(unit, "wxe");
179         if (!f) {
180                 if (errno == EEXIST)
181                         log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
182                 else
183                         log_error("Failed to create unit file %s: %m", unit);
184                 return -errno;
185         }
186
187         fprintf(f,
188               "# Automatically generated by systemd-fstab-generator\n\n"
189               "[Unit]\n"
190               "SourcePath=%s\n",
191               source);
192
193         if (post && !noauto && !nofail && !automount)
194                 fprintf(f,
195                         "Before=%s\n",
196                         post);
197
198         fprintf(f,
199                 "\n"
200                 "[Mount]\n"
201                 "What=%s\n"
202                 "Where=%s\n"
203                 "Type=%s\n"
204                 "FsckPassNo=%i\n",
205                 what,
206                 where,
207                 type,
208                 passno);
209
210         if (!isempty(opts) &&
211             !streq(opts, "defaults"))
212                 fprintf(f,
213                         "Options=%s\n",
214                         opts);
215
216         fflush(f);
217         if (ferror(f)) {
218                 log_error("Failed to write unit file %s: %m", unit);
219                 return -errno;
220         }
221
222         if (!noauto) {
223                 if (post) {
224                         lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
225                         if (!lnk)
226                                 return log_oom();
227
228                         mkdir_parents_label(lnk, 0755);
229                         if (symlink(unit, lnk) < 0) {
230                                 log_error("Failed to create symlink %s: %m", lnk);
231                                 return -errno;
232                         }
233                 }
234         }
235
236         if (automount && !path_equal(where, "/")) {
237                 automount_name = unit_name_from_path(where, ".automount");
238                 if (!automount_name)
239                         return log_oom();
240
241                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
242                 if (!automount_unit)
243                         return log_oom();
244
245                 fclose(f);
246                 f = fopen(automount_unit, "wxe");
247                 if (!f) {
248                         log_error("Failed to create unit file %s: %m", automount_unit);
249                         return -errno;
250                 }
251
252                 fprintf(f,
253                         "# Automatically generated by systemd-fstab-generator\n\n"
254                         "[Unit]\n"
255                         "SourcePath=%s\n",
256                         source);
257
258                 if (post)
259                         fprintf(f,
260                                 "Before= %s\n",
261                                 post);
262
263                 fprintf(f,
264                         "[Automount]\n"
265                         "Where=%s\n",
266                         where);
267
268                 fflush(f);
269                 if (ferror(f)) {
270                         log_error("Failed to write unit file %s: %m", automount_unit);
271                         return -errno;
272                 }
273
274                 free(lnk);
275                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
276                 if (!lnk)
277                         return log_oom();
278
279                 mkdir_parents_label(lnk, 0755);
280                 if (symlink(automount_unit, lnk) < 0) {
281                         log_error("Failed to create symlink %s: %m", lnk);
282                         return -errno;
283                 }
284         }
285
286         return 0;
287 }
288
289 static int parse_fstab(const char *prefix, bool initrd) {
290         _cleanup_free_ char *fstab_path = NULL;
291         FILE *f;
292         int r = 0;
293         struct mntent *me;
294
295         fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
296         if (!fstab_path)
297                 return log_oom();
298
299         f = setmntent(fstab_path, "r");
300         if (!f) {
301                 if (errno == ENOENT)
302                         return 0;
303
304                 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
305                 return -errno;
306         }
307
308         while ((me = getmntent(f))) {
309                 _cleanup_free_ char *where = NULL, *what = NULL;
310                 int k;
311
312                 if (initrd && !mount_in_initrd(me))
313                         continue;
314
315                 what = fstab_node_to_udev_node(me->mnt_fsname);
316                 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
317                 if (!what || !where) {
318                         r = log_oom();
319                         goto finish;
320                 }
321
322                 if (is_path(where))
323                         path_kill_slashes(where);
324
325                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
326
327                 if (streq(me->mnt_type, "swap"))
328                         k = add_swap(what, me);
329                 else {
330                         bool noauto, nofail, automount;
331                         const char *post;
332
333                         noauto = !!hasmntopt(me, "noauto");
334                         nofail = !!hasmntopt(me, "nofail");
335                         automount =
336                                   hasmntopt(me, "comment=systemd.automount") ||
337                                   hasmntopt(me, "x-systemd.automount");
338
339                         if (initrd) {
340                                 post = SPECIAL_INITRD_FS_TARGET;
341                         } else if (mount_in_initrd(me)) {
342                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
343                         } else if (mount_is_network(me)) {
344                                 post = SPECIAL_REMOTE_FS_TARGET;
345                         } else {
346                                 post = SPECIAL_LOCAL_FS_TARGET;
347                         }
348
349                         k = add_mount(what, where, me->mnt_type, me->mnt_opts,
350                                       me->mnt_passno, noauto, nofail, automount,
351                                       post, fstab_path);
352                 }
353
354                 if (k < 0)
355                         r = k;
356         }
357
358 finish:
359         endmntent(f);
360         return r;
361 }
362
363 static int parse_new_root_from_proc_cmdline(void) {
364         _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
365         char *w, *state;
366         int r;
367         size_t l;
368         bool noauto, nofail;
369
370         r = read_one_line_file("/proc/cmdline", &line);
371         if (r < 0) {
372                 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
373                 return 0;
374         }
375
376         opts = strdup("ro");
377         type = strdup("auto");
378         if (!opts || !type)
379                 return log_oom();
380
381         /* root= and roofstype= may occur more than once, the last instance should take precedence.
382          * In the case of multiple rootflags= the arguments should be concatenated */
383         FOREACH_WORD_QUOTED(w, l, line, state) {
384                 _cleanup_free_ char *word;
385
386                 word = strndup(w, l);
387                 if (!word)
388                         return log_oom();
389
390                 else if (startswith(word, "root=")) {
391                         free(what);
392                         what = fstab_node_to_udev_node(word+5);
393                         if (!what)
394                                 return log_oom();
395
396                 } else if (startswith(word, "rootfstype=")) {
397                         free(type);
398                         type = strdup(word + 11);
399                         if (!type)
400                                 return log_oom();
401
402                 } else if (startswith(word, "rootflags=")) {
403                         char *o;
404
405                         o = strjoin(opts, ",", word + 10, NULL);
406                         if (!o)
407                                 return log_oom();
408
409                         free(opts);
410                         opts = o;
411
412                 } else if (streq(word, "ro") || streq(word, "rw")) {
413                         char *o;
414
415                         o = strjoin(opts, ",", word, NULL);
416                         if (!o)
417                                 return log_oom();
418
419                         free(opts);
420                         opts = o;
421                 }
422         }
423
424         noauto = !!strstr(opts, "noauto");
425         nofail = !!strstr(opts, "nofail");
426
427         if (!what) {
428                 log_debug("Could not find a root= entry on the kernel commandline.");
429                 return 0;
430         }
431
432         if (what[0] != '/') {
433                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
434                 return 0;
435         }
436
437         log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
438         r = add_mount(what, "/sysroot", type, opts, 0, noauto, nofail, false,
439                       SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
440
441         return (r < 0) ? r : 0;
442 }
443
444 static int parse_proc_cmdline(void) {
445         _cleanup_free_ char *line = NULL;
446         char *w, *state;
447         int r;
448         size_t l;
449
450         if (detect_container(NULL) > 0)
451                 return 0;
452
453         r = read_one_line_file("/proc/cmdline", &line);
454         if (r < 0) {
455                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
456                 return 0;
457         }
458
459         FOREACH_WORD_QUOTED(w, l, line, state) {
460                 _cleanup_free_ char *word = NULL;
461
462                 word = strndup(w, l);
463                 if (!word)
464                         return log_oom();
465
466                 if (startswith(word, "fstab=")) {
467                         r = parse_boolean(word + 6);
468                         if (r < 0)
469                                 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
470                         else
471                                 arg_enabled = r;
472
473                 } else if (startswith(word, "rd.fstab=")) {
474
475                         if (in_initrd()) {
476                                 r = parse_boolean(word + 9);
477                                 if (r < 0)
478                                         log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
479                                 else
480                                         arg_enabled = r;
481                         }
482
483                 } else if (startswith(word, "fstab.") ||
484                            (in_initrd() && startswith(word, "rd.fstab."))) {
485
486                         log_warning("Unknown kernel switch %s. Ignoring.", word);
487                 }
488         }
489
490         return 0;
491 }
492
493 int main(int argc, char *argv[]) {
494         int r = 0, k, l = 0;
495
496         if (argc > 1 && argc != 4) {
497                 log_error("This program takes three or no arguments.");
498                 return EXIT_FAILURE;
499         }
500
501         if (argc > 1)
502                 arg_dest = argv[1];
503
504         log_set_target(LOG_TARGET_SAFE);
505         log_parse_environment();
506         log_open();
507
508         umask(0022);
509
510         if (parse_proc_cmdline() < 0)
511                 return EXIT_FAILURE;
512
513         if (in_initrd())
514                 r = parse_new_root_from_proc_cmdline();
515
516         if (!arg_enabled)
517                 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
518
519         k = parse_fstab(NULL, false);
520
521         if (in_initrd())
522                 l = parse_fstab("/sysroot", true);
523
524         return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
525 }