chiark / gitweb /
treewide: another round of simplifications
[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 "fileio.h"
36 #include "generator.h"
37 #include "strv.h"
38 #include "virt.h"
39
40 static const char *arg_dest = "/tmp";
41 static bool arg_fstab_enabled = true;
42 static char *arg_root_what = NULL;
43 static char *arg_root_fstype = NULL;
44 static char *arg_root_options = NULL;
45 static int arg_root_rw = -1;
46 static char *arg_usr_what = NULL;
47 static char *arg_usr_fstype = NULL;
48 static char *arg_usr_options = NULL;
49
50 static int mount_find_pri(struct mntent *me, int *ret) {
51         char *end, *opt;
52         unsigned long r;
53
54         assert(me);
55         assert(ret);
56
57         opt = hasmntopt(me, "pri");
58         if (!opt)
59                 return 0;
60
61         opt += strlen("pri");
62         if (*opt != '=')
63                 return -EINVAL;
64
65         errno = 0;
66         r = strtoul(opt + 1, &end, 10);
67         if (errno > 0)
68                 return -errno;
69
70         if (end == opt + 1 || (*end != ',' && *end != 0))
71                 return -EINVAL;
72
73         *ret = (int) r;
74         return 1;
75 }
76
77 static int add_swap(
78                 const char *what,
79                 struct mntent *me,
80                 bool noauto,
81                 bool nofail) {
82
83         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
84         _cleanup_fclose_ FILE *f = NULL;
85         int r, pri = -1;
86
87         assert(what);
88         assert(me);
89
90         if (detect_container(NULL) > 0) {
91                 log_info("Running in a container, ignoring fstab swap entry for %s.", what);
92                 return 0;
93         }
94
95         r = mount_find_pri(me, &pri);
96         if (r < 0) {
97                 log_error("Failed to parse priority");
98                 return r;
99         }
100
101         name = unit_name_from_path(what, ".swap");
102         if (!name)
103                 return log_oom();
104
105         unit = strjoin(arg_dest, "/", name, NULL);
106         if (!unit)
107                 return log_oom();
108
109         f = fopen(unit, "wxe");
110         if (!f) {
111                 if (errno == EEXIST)
112                         log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
113                 else
114                         log_error_errno(errno, "Failed to create unit file %s: %m", unit);
115                 return -errno;
116         }
117
118         fprintf(f,
119                 "# Automatically generated by systemd-fstab-generator\n\n"
120                 "[Unit]\n"
121                 "SourcePath=/etc/fstab\n"
122                 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
123                 "[Swap]\n"
124                 "What=%s\n",
125                 what);
126
127         /* Note that we currently pass the priority field twice, once
128          * in Priority=, and once in Options= */
129         if (pri >= 0)
130                 fprintf(f, "Priority=%i\n", pri);
131
132         if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
133                 fprintf(f, "Options=%s\n", me->mnt_opts);
134
135         r = fflush_and_check(f);
136         if (r < 0)
137                 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
138
139         /* use what as where, to have a nicer error message */
140         r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
141         if (r < 0)
142                 return r;
143
144         if (!noauto) {
145                 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET,
146                               nofail ? ".wants/" : ".requires/", name, NULL);
147                 if (!lnk)
148                         return log_oom();
149
150                 mkdir_parents_label(lnk, 0755);
151                 if (symlink(unit, lnk) < 0)
152                         return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
153         }
154
155         return 0;
156 }
157
158 static bool mount_is_network(struct mntent *me) {
159         assert(me);
160
161         return
162                 hasmntopt(me, "_netdev") ||
163                 fstype_is_network(me->mnt_type);
164 }
165
166 static bool mount_in_initrd(struct mntent *me) {
167         assert(me);
168
169         return
170                 hasmntopt(me, "x-initrd.mount") ||
171                 streq(me->mnt_dir, "/usr");
172 }
173
174 static int add_mount(
175                 const char *what,
176                 const char *where,
177                 const char *fstype,
178                 const char *opts,
179                 int passno,
180                 bool noauto,
181                 bool nofail,
182                 bool automount,
183                 const char *post,
184                 const char *source) {
185
186         _cleanup_free_ char
187                 *name = NULL, *unit = NULL, *lnk = NULL,
188                 *automount_name = NULL, *automount_unit = NULL,
189                 *filtered = NULL;
190         _cleanup_fclose_ FILE *f = NULL;
191         int r;
192
193         assert(what);
194         assert(where);
195         assert(opts);
196         assert(source);
197
198         if (streq_ptr(fstype, "autofs"))
199                 return 0;
200
201         if (!is_path(where)) {
202                 log_warning("Mount point %s is not a valid path, ignoring.", where);
203                 return 0;
204         }
205
206         if (mount_point_is_api(where) ||
207             mount_point_ignore(where))
208                 return 0;
209
210         if (path_equal(where, "/")) {
211                 /* The root disk is not an option */
212                 automount = false;
213                 noauto = false;
214                 nofail = false;
215         }
216
217         name = unit_name_from_path(where, ".mount");
218         if (!name)
219                 return log_oom();
220
221         unit = strjoin(arg_dest, "/", name, NULL);
222         if (!unit)
223                 return log_oom();
224
225         f = fopen(unit, "wxe");
226         if (!f) {
227                 if (errno == EEXIST)
228                         log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
229                 else
230                         log_error_errno(errno, "Failed to create unit file %s: %m", unit);
231                 return -errno;
232         }
233
234         fprintf(f,
235                 "# Automatically generated by systemd-fstab-generator\n\n"
236                 "[Unit]\n"
237                 "SourcePath=%s\n"
238                 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
239                 source);
240
241         if (post && !noauto && !nofail && !automount)
242                 fprintf(f, "Before=%s\n", post);
243
244         if (passno != 0) {
245                 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
246                 if (r < 0)
247                         return r;
248         }
249
250         fprintf(f,
251                 "\n"
252                 "[Mount]\n"
253                 "What=%s\n"
254                 "Where=%s\n",
255                 what,
256                 where);
257
258         if (!isempty(fstype) && !streq(fstype, "auto"))
259                 fprintf(f, "Type=%s\n", fstype);
260
261         r = generator_write_timeouts(arg_dest, what, where, opts, &filtered);
262         if (r < 0)
263                 return r;
264
265         if (!isempty(filtered) && !streq(filtered, "defaults"))
266                 fprintf(f, "Options=%s\n", filtered);
267
268         fflush(f);
269         if (ferror(f))
270                 return log_error_errno(errno, "Failed to write unit file %s: %m", unit);
271
272         if (!noauto && post) {
273                 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
274                 if (!lnk)
275                         return log_oom();
276
277                 mkdir_parents_label(lnk, 0755);
278                 if (symlink(unit, lnk) < 0)
279                         return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
280         }
281
282         if (automount) {
283                 automount_name = unit_name_from_path(where, ".automount");
284                 if (!automount_name)
285                         return log_oom();
286
287                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
288                 if (!automount_unit)
289                         return log_oom();
290
291                 fclose(f);
292                 f = fopen(automount_unit, "wxe");
293                 if (!f)
294                         return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit);
295
296                 fprintf(f,
297                         "# Automatically generated by systemd-fstab-generator\n\n"
298                         "[Unit]\n"
299                         "SourcePath=%s\n"
300                         "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
301                         source);
302
303                 if (post)
304                         fprintf(f,
305                                 "Before=%s\n",
306                                 post);
307
308                 fprintf(f,
309                         "[Automount]\n"
310                         "Where=%s\n",
311                         where);
312
313                 fflush(f);
314                 if (ferror(f))
315                         return log_error_errno(errno, "Failed to write unit file %s: %m", automount_unit);
316
317                 free(lnk);
318                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
319                 if (!lnk)
320                         return log_oom();
321
322                 mkdir_parents_label(lnk, 0755);
323                 if (symlink(automount_unit, lnk) < 0)
324                         return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
325         }
326
327         return 0;
328 }
329
330 static int parse_fstab(bool initrd) {
331         _cleanup_endmntent_ FILE *f = NULL;
332         const char *fstab_path;
333         struct mntent *me;
334         int r = 0;
335
336         fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
337         f = setmntent(fstab_path, "re");
338         if (!f) {
339                 if (errno == ENOENT)
340                         return 0;
341
342                 log_error_errno(errno, "Failed to open %s: %m", fstab_path);
343                 return -errno;
344         }
345
346         while ((me = getmntent(f))) {
347                 _cleanup_free_ char *where = NULL, *what = NULL;
348                 bool noauto, nofail;
349                 int k;
350
351                 if (initrd && !mount_in_initrd(me))
352                         continue;
353
354                 what = fstab_node_to_udev_node(me->mnt_fsname);
355                 if (!what)
356                         return log_oom();
357
358                 if (detect_container(NULL) > 0 && is_device_path(what)) {
359                         log_info("Running in a container, ignoring fstab device entry for %s.", what);
360                         continue;
361                 }
362
363                 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
364                 if (!where)
365                         return log_oom();
366
367                 if (is_path(where))
368                         path_kill_slashes(where);
369
370                 noauto = !!hasmntopt(me, "noauto");
371                 nofail = !!hasmntopt(me, "nofail");
372                 log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
373                           what, where, me->mnt_type,
374                           yes_no(noauto), yes_no(nofail));
375
376                 if (streq(me->mnt_type, "swap"))
377                         k = add_swap(what, me, noauto, nofail);
378                 else {
379                         bool automount;
380                         const char *post;
381
382                         automount =
383                                   hasmntopt(me, "comment=systemd.automount") ||
384                                   hasmntopt(me, "x-systemd.automount");
385
386                         if (initrd)
387                                 post = SPECIAL_INITRD_FS_TARGET;
388                         else if (mount_in_initrd(me))
389                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
390                         else if (mount_is_network(me))
391                                 post = SPECIAL_REMOTE_FS_TARGET;
392                         else
393                                 post = SPECIAL_LOCAL_FS_TARGET;
394
395                         k = add_mount(what,
396                                       where,
397                                       me->mnt_type,
398                                       me->mnt_opts,
399                                       me->mnt_passno,
400                                       noauto,
401                                       nofail,
402                                       automount,
403                                       post,
404                                       fstab_path);
405                 }
406
407                 if (k < 0)
408                         r = k;
409         }
410
411         return r;
412 }
413
414 static int add_root_mount(void) {
415         _cleanup_free_ char *what = NULL;
416         const char *opts;
417
418         if (isempty(arg_root_what)) {
419                 log_debug("Could not find a root= entry on the kernel command line.");
420                 return 0;
421         }
422
423         what = fstab_node_to_udev_node(arg_root_what);
424         if (!path_is_absolute(what)) {
425                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
426                 return 0;
427         }
428
429         if (!arg_root_options)
430                 opts = arg_root_rw > 0 ? "rw" : "ro";
431         else if (arg_root_rw >= 0 ||
432                  (!mount_test_option(arg_root_options, "ro") &&
433                   !mount_test_option(arg_root_options, "rw")))
434                 opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
435         else
436                 opts = arg_root_options;
437
438         log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
439         return add_mount(what,
440                          "/sysroot",
441                          arg_root_fstype,
442                          opts,
443                          1,
444                          false,
445                          false,
446                          false,
447                          SPECIAL_INITRD_ROOT_FS_TARGET,
448                          "/proc/cmdline");
449 }
450
451 static int add_usr_mount(void) {
452         _cleanup_free_ char *what = NULL;
453         const char *opts;
454
455         if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
456                 return 0;
457
458         if (arg_root_what && !arg_usr_what) {
459                 arg_usr_what = strdup(arg_root_what);
460
461                 if (!arg_usr_what)
462                         return log_oom();
463         }
464
465         if (arg_root_fstype && !arg_usr_fstype) {
466                 arg_usr_fstype = strdup(arg_root_fstype);
467
468                 if (!arg_usr_fstype)
469                         return log_oom();
470         }
471
472         if (arg_root_options && !arg_usr_options) {
473                 arg_usr_options = strdup(arg_root_options);
474
475                 if (!arg_usr_options)
476                         return log_oom();
477         }
478
479         if (!arg_usr_what || !arg_usr_options)
480                 return 0;
481
482         what = fstab_node_to_udev_node(arg_usr_what);
483         if (!path_is_absolute(what)) {
484                 log_debug("Skipping entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
485                 return -1;
486         }
487
488         opts = arg_usr_options;
489
490         log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
491         return add_mount(what,
492                          "/sysroot/usr",
493                          arg_usr_fstype,
494                          opts,
495                          1,
496                          false,
497                          false,
498                          false,
499                          SPECIAL_INITRD_ROOT_FS_TARGET,
500                          "/proc/cmdline");
501 }
502
503 static int parse_proc_cmdline_item(const char *key, const char *value) {
504         int r;
505
506         /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
507          * instance should take precedence.  In the case of multiple rootflags=
508          * or usrflags= the arguments should be concatenated */
509
510         if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
511
512                 r = parse_boolean(value);
513                 if (r < 0)
514                         log_warning("Failed to parse fstab switch %s. Ignoring.", value);
515                 else
516                         arg_fstab_enabled = r;
517
518         } else if (streq(key, "root") && value) {
519
520                 if (free_and_strdup(&arg_root_what, value) < 0)
521                         return log_oom();
522
523         } else if (streq(key, "rootfstype") && value) {
524
525                 if (free_and_strdup(&arg_root_fstype, value) < 0)
526                         return log_oom();
527
528         } else if (streq(key, "rootflags") && value) {
529                 char *o;
530
531                 o = arg_root_options ?
532                         strjoin(arg_root_options, ",", value, NULL) :
533                         strdup(value);
534                 if (!o)
535                         return log_oom();
536
537                 free(arg_root_options);
538                 arg_root_options = o;
539
540         } else if (streq(key, "mount.usr") && value) {
541
542                 if (free_and_strdup(&arg_usr_what, value) < 0)
543                         return log_oom();
544
545         } else if (streq(key, "mount.usrfstype") && value) {
546
547                 if (free_and_strdup(&arg_usr_fstype, value) < 0)
548                         return log_oom();
549
550         } else if (streq(key, "mount.usrflags") && value) {
551                 char *o;
552
553                 o = arg_usr_options ?
554                         strjoin(arg_usr_options, ",", value, NULL) :
555                         strdup(value);
556                 if (!o)
557                         return log_oom();
558
559                 free(arg_usr_options);
560                 arg_usr_options = o;
561
562         } else if (streq(key, "rw") && !value)
563                 arg_root_rw = true;
564         else if (streq(key, "ro") && !value)
565                 arg_root_rw = false;
566
567         return 0;
568 }
569
570 int main(int argc, char *argv[]) {
571         int r = 0;
572
573         if (argc > 1 && argc != 4) {
574                 log_error("This program takes three or no arguments.");
575                 return EXIT_FAILURE;
576         }
577
578         if (argc > 1)
579                 arg_dest = argv[1];
580
581         log_set_target(LOG_TARGET_SAFE);
582         log_parse_environment();
583         log_open();
584
585         umask(0022);
586
587         r = parse_proc_cmdline(parse_proc_cmdline_item);
588         if (r < 0)
589                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
590
591         /* Always honour root= and usr= in the kernel command line if we are in an initrd */
592         if (in_initrd()) {
593                 r = add_root_mount();
594                 if (r == 0)
595                         r = add_usr_mount();
596         }
597
598         /* Honour /etc/fstab only when that's enabled */
599         if (arg_fstab_enabled) {
600                 int k;
601
602                 log_debug("Parsing /etc/fstab");
603
604                 /* Parse the local /etc/fstab, possibly from the initrd */
605                 k = parse_fstab(false);
606                 if (k < 0)
607                         r = k;
608
609                 /* If running in the initrd also parse the /etc/fstab from the host */
610                 if (in_initrd()) {
611                         log_debug("Parsing /sysroot/etc/fstab");
612
613                         k = parse_fstab(true);
614                         if (k < 0)
615                                 r = k;
616                 }
617         }
618
619         free(arg_root_what);
620
621         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
622 }