]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
util: simplify proc_cmdline() to reuse get_process_cmdline()
[thirdparty/systemd.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 r = parse_proc_cmdline(parse_proc_cmdline_item);
597 if (r < 0)
598 log_warning("Failed to parse kernel command line, ignoring: %s", strerror(-r));
599
600 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
601 if (in_initrd()) {
602 r = add_root_mount();
603 if (r == 0)
604 r = add_usr_mount();
605 }
606
607 /* Honour /etc/fstab only when that's enabled */
608 if (arg_fstab_enabled) {
609 int k;
610
611 log_debug("Parsing /etc/fstab");
612
613 /* Parse the local /etc/fstab, possibly from the initrd */
614 k = parse_fstab(false);
615 if (k < 0)
616 r = k;
617
618 /* If running in the initrd also parse the /etc/fstab from the host */
619 if (in_initrd()) {
620 log_debug("Parsing /sysroot/etc/fstab");
621
622 k = parse_fstab(true);
623 if (k < 0)
624 r = k;
625 }
626 }
627
628 free(arg_root_what);
629
630 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
631 }