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