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