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