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