]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fstab-generator/fstab-generator.c
logind: close race on session state during logins
[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"
a5c32cff 35#include "fileio.h"
6b1dc2bd
LP
36
37static const char *arg_dest = "/tmp";
94734142 38static bool arg_enabled = true;
6b1dc2bd 39
6b1dc2bd
LP
40static int mount_find_pri(struct mntent *me, int *ret) {
41 char *end, *pri;
42 unsigned long r;
43
44 assert(me);
45 assert(ret);
46
47 pri = hasmntopt(me, "pri");
48 if (!pri)
49 return 0;
50
51 pri += 4;
52
53 errno = 0;
54 r = strtoul(pri, &end, 10);
8333c77e 55 if (errno > 0)
6b1dc2bd
LP
56 return -errno;
57
58 if (end == pri || (*end != ',' && *end != 0))
59 return -EINVAL;
60
61 *ret = (int) r;
62 return 1;
63}
64
65static int add_swap(const char *what, struct mntent *me) {
1dc2ced4 66 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
7fd1b19b 67 _cleanup_fclose_ FILE *f = NULL;
4e82fe52 68 bool noauto;
6b1dc2bd
LP
69 int r, pri = -1;
70
71 assert(what);
72 assert(me);
73
74 r = mount_find_pri(me, &pri);
75 if (r < 0) {
76 log_error("Failed to parse priority");
77 return pri;
78 }
79
4e82fe52
TG
80 noauto = !!hasmntopt(me, "noauto");
81
6b1dc2bd 82 name = unit_name_from_path(what, ".swap");
d0aa9ce5
ZJS
83 if (!name)
84 return log_oom();
6b1dc2bd 85
b7def684 86 unit = strjoin(arg_dest, "/", name, NULL);
d0aa9ce5
ZJS
87 if (!unit)
88 return log_oom();
6b1dc2bd
LP
89
90 f = fopen(unit, "wxe");
91 if (!f) {
67ab5f76
TG
92 if (errno == EEXIST)
93 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
94 else
95 log_error("Failed to create unit file %s: %m", unit);
d0aa9ce5 96 return -errno;
6b1dc2bd
LP
97 }
98
6b1dc2bd 99 fprintf(f,
64347fc2
TG
100 "# Automatically generated by systemd-fstab-generator\n\n"
101 "[Unit]\n"
4e82fe52 102 "SourcePath=/etc/fstab\n\n"
6b1dc2bd
LP
103 "[Swap]\n"
104 "What=%s\n",
105 what);
106
107 if (pri >= 0)
108 fprintf(f,
109 "Priority=%i\n",
110 pri);
111
112 fflush(f);
113 if (ferror(f)) {
40b8acd0 114 log_error("Failed to write unit file %s: %m", unit);
d0aa9ce5 115 return -errno;
6b1dc2bd
LP
116 }
117
4e82fe52
TG
118 if (!noauto) {
119 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
120 if (!lnk)
121 return log_oom();
122
123 mkdir_parents_label(lnk, 0755);
124 if (symlink(unit, lnk) < 0) {
125 log_error("Failed to create symlink %s: %m", lnk);
126 return -errno;
127 }
128 }
129
d0aa9ce5 130 return 0;
6b1dc2bd
LP
131}
132
6b1dc2bd
LP
133static bool mount_is_network(struct mntent *me) {
134 assert(me);
135
136 return
137 hasmntopt(me, "_netdev") ||
138 fstype_is_network(me->mnt_type);
139}
140
3d22d1ab
TG
141static bool mount_in_initrd(struct mntent *me) {
142 assert(me);
143
144 return
145 hasmntopt(me, "x-initrd.mount") ||
146 streq(me->mnt_dir, "/usr");
147}
148
94192cda
ZJS
149static int add_fsck(FILE *f, const char *what, const char *where, const char *type, int passno) {
150 assert(f);
151
152 if (passno == 0)
153 return 0;
154
e2f123b9
TB
155 if (!is_device_path(what)) {
156 log_warning("Checking was requested for \"%s\", but it is not a device.", what);
157 return 0;
158 }
159
94192cda
ZJS
160 if (type && !streq(type, "auto")) {
161 int r;
162 const char *checker;
163
164 checker = strappenda("/sbin/fsck.", type);
165 r = access(checker, X_OK);
166 if (r < 0) {
167 log_warning("Checking was requested for %s, but %s cannot be used: %m", what, checker);
168
169 /* treat missing check as essentially OK */
170 return errno == ENOENT ? 0 : -errno;
171 }
172 }
173
174 if (streq(where, "/")) {
175 char *lnk;
176
177 lnk = strappenda(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
178 mkdir_parents_label(lnk, 0755);
63a1b905 179 if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) {
94192cda
ZJS
180 log_error("Failed to create symlink %s: %m", lnk);
181 return -errno;
182 }
183 } else {
184 _cleanup_free_ char *fsck = NULL;
185
186 fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
187 if (!fsck)
188 return log_oom();
189
190 fprintf(f,
4e8da529 191 "RequiresOverridable=%s\n"
94192cda
ZJS
192 "After=%s\n",
193 fsck,
194 fsck);
195 }
196
197 return 0;
198}
199
e8d2f6cd
LP
200static int add_mount(
201 const char *what,
202 const char *where,
203 const char *type,
204 const char *opts,
205 int passno,
206 bool noauto,
207 bool nofail,
208 bool automount,
e8d2f6cd 209 const char *post,
e8d2f6cd 210 const char *source) {
7fd1b19b 211 _cleanup_free_ char
1dc2ced4 212 *name = NULL, *unit = NULL, *lnk = NULL,
d0aa9ce5 213 *automount_name = NULL, *automount_unit = NULL;
7fd1b19b 214 _cleanup_fclose_ FILE *f = NULL;
94192cda 215 int r;
6b1dc2bd
LP
216
217 assert(what);
218 assert(where);
5e398e54
TG
219 assert(type);
220 assert(opts);
221 assert(source);
6b1dc2bd 222
5e398e54 223 if (streq(type, "autofs"))
6b1dc2bd
LP
224 return 0;
225
226 if (!is_path(where)) {
227 log_warning("Mount point %s is not a valid path, ignoring.", where);
228 return 0;
229 }
230
231 if (mount_point_is_api(where) ||
232 mount_point_ignore(where))
233 return 0;
234
6b1dc2bd 235 name = unit_name_from_path(where, ".mount");
d0aa9ce5
ZJS
236 if (!name)
237 return log_oom();
6b1dc2bd 238
b7def684 239 unit = strjoin(arg_dest, "/", name, NULL);
d0aa9ce5
ZJS
240 if (!unit)
241 return log_oom();
6b1dc2bd
LP
242
243 f = fopen(unit, "wxe");
244 if (!f) {
67ab5f76
TG
245 if (errno == EEXIST)
246 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
247 else
248 log_error("Failed to create unit file %s: %m", unit);
d0aa9ce5 249 return -errno;
6b1dc2bd
LP
250 }
251
5e398e54
TG
252 fprintf(f,
253 "# Automatically generated by systemd-fstab-generator\n\n"
6b1dc2bd 254 "[Unit]\n"
80c3b720 255 "SourcePath=%s\n",
5e398e54 256 source);
6b1dc2bd 257
700e07ff 258 if (post && !noauto && !nofail && !automount)
6b1dc2bd
LP
259 fprintf(f,
260 "Before=%s\n",
261 post);
262
94192cda
ZJS
263 r = add_fsck(f, what, where, type, passno);
264 if (r < 0)
265 return r;
64e70e4b 266
6b1dc2bd
LP
267 fprintf(f,
268 "\n"
269 "[Mount]\n"
270 "What=%s\n"
271 "Where=%s\n"
64e70e4b 272 "Type=%s\n",
6b1dc2bd
LP
273 what,
274 where,
64e70e4b 275 type);
6b1dc2bd 276
5e398e54
TG
277 if (!isempty(opts) &&
278 !streq(opts, "defaults"))
6b1dc2bd
LP
279 fprintf(f,
280 "Options=%s\n",
5e398e54
TG
281 opts);
282
6b1dc2bd
LP
283 fflush(f);
284 if (ferror(f)) {
40b8acd0 285 log_error("Failed to write unit file %s: %m", unit);
d0aa9ce5 286 return -errno;
6b1dc2bd
LP
287 }
288
289 if (!noauto) {
700e07ff
HH
290 if (post) {
291 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
292 if (!lnk)
293 return log_oom();
6b1dc2bd 294
700e07ff
HH
295 mkdir_parents_label(lnk, 0755);
296 if (symlink(unit, lnk) < 0) {
297 log_error("Failed to create symlink %s: %m", lnk);
298 return -errno;
299 }
6b1dc2bd 300 }
6b1dc2bd
LP
301 }
302
303 if (automount && !path_equal(where, "/")) {
304 automount_name = unit_name_from_path(where, ".automount");
01264ad1 305 if (!automount_name)
d0aa9ce5 306 return log_oom();
6b1dc2bd 307
b7def684 308 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
d0aa9ce5
ZJS
309 if (!automount_unit)
310 return log_oom();
6b1dc2bd
LP
311
312 fclose(f);
313 f = fopen(automount_unit, "wxe");
314 if (!f) {
40b8acd0 315 log_error("Failed to create unit file %s: %m", automount_unit);
d0aa9ce5 316 return -errno;
6b1dc2bd
LP
317 }
318
319 fprintf(f,
320 "# Automatically generated by systemd-fstab-generator\n\n"
321 "[Unit]\n"
80c3b720 322 "SourcePath=%s\n",
700e07ff
HH
323 source);
324
325 if (post)
326 fprintf(f,
327 "Before= %s\n",
328 post);
329
330 fprintf(f,
6b1dc2bd
LP
331 "[Automount]\n"
332 "Where=%s\n",
6b1dc2bd
LP
333 where);
334
335 fflush(f);
336 if (ferror(f)) {
40b8acd0 337 log_error("Failed to write unit file %s: %m", automount_unit);
d0aa9ce5 338 return -errno;
6b1dc2bd
LP
339 }
340
341 free(lnk);
b7def684 342 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
d0aa9ce5
ZJS
343 if (!lnk)
344 return log_oom();
6b1dc2bd 345
d2e54fae 346 mkdir_parents_label(lnk, 0755);
6b1dc2bd 347 if (symlink(automount_unit, lnk) < 0) {
40b8acd0 348 log_error("Failed to create symlink %s: %m", lnk);
d0aa9ce5 349 return -errno;
6b1dc2bd
LP
350 }
351 }
352
d0aa9ce5 353 return 0;
6b1dc2bd
LP
354}
355
3d22d1ab 356static int parse_fstab(const char *prefix, bool initrd) {
5862d652
ZJS
357 char *fstab_path;
358 _cleanup_endmntent_ FILE *f;
6b1dc2bd
LP
359 int r = 0;
360 struct mntent *me;
361
5862d652 362 fstab_path = strappenda(strempty(prefix), "/etc/fstab");
3d22d1ab 363 f = setmntent(fstab_path, "r");
6b1dc2bd
LP
364 if (!f) {
365 if (errno == ENOENT)
366 return 0;
367
1b036593 368 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
6b1dc2bd
LP
369 return -errno;
370 }
371
372 while ((me = getmntent(f))) {
7fd1b19b 373 _cleanup_free_ char *where = NULL, *what = NULL;
6b1dc2bd
LP
374 int k;
375
3d22d1ab
TG
376 if (initrd && !mount_in_initrd(me))
377 continue;
378
6b1dc2bd 379 what = fstab_node_to_udev_node(me->mnt_fsname);
1b036593 380 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
5862d652
ZJS
381 if (!what || !where)
382 return log_oom();
6b1dc2bd 383
ec6ceb18 384 if (is_path(where))
6b1dc2bd
LP
385 path_kill_slashes(where);
386
387 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
388
389 if (streq(me->mnt_type, "swap"))
390 k = add_swap(what, me);
5e398e54 391 else {
5073f89f 392 bool noauto, nofail, automount;
80c3b720 393 const char *post;
5e398e54
TG
394
395 noauto = !!hasmntopt(me, "noauto");
396 nofail = !!hasmntopt(me, "nofail");
397 automount =
398 hasmntopt(me, "comment=systemd.automount") ||
399 hasmntopt(me, "x-systemd.automount");
700e07ff
HH
400
401 if (initrd) {
402 post = SPECIAL_INITRD_FS_TARGET;
a87586a1 403 } else if (mount_in_initrd(me)) {
9e5f0f92 404 post = SPECIAL_INITRD_ROOT_FS_TARGET;
700e07ff 405 } else if (mount_is_network(me)) {
0c17fbce 406 post = SPECIAL_REMOTE_FS_TARGET;
700e07ff 407 } else {
0c17fbce 408 post = SPECIAL_LOCAL_FS_TARGET;
700e07ff 409 }
5e398e54
TG
410
411 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
e8d2f6cd 412 me->mnt_passno, noauto, nofail, automount,
5073f89f 413 post, fstab_path);
5e398e54 414 }
6b1dc2bd 415
6b1dc2bd
LP
416 if (k < 0)
417 r = k;
418 }
419
6b1dc2bd
LP
420 return r;
421}
422
5e398e54 423static int parse_new_root_from_proc_cmdline(void) {
ac4785b0 424 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
74df0fca 425 bool noauto, nofail;
80c39ad2 426 char *w, *state;
5e398e54 427 size_t l;
74df0fca 428 int r;
5e398e54 429
74df0fca
LP
430 r = proc_cmdline(&line);
431 if (r < 0)
432 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
433 if (r <= 0)
5e398e54 434 return 0;
5e398e54 435
945d1442 436 opts = strdup("ro");
5e398e54 437 type = strdup("auto");
945d1442 438 if (!opts || !type)
5e398e54
TG
439 return log_oom();
440
b929bf04
TA
441 /* root= and roofstype= may occur more than once, the last instance should take precedence.
442 * In the case of multiple rootflags= the arguments should be concatenated */
5e398e54 443 FOREACH_WORD_QUOTED(w, l, line, state) {
80c39ad2 444 _cleanup_free_ char *word;
5e398e54
TG
445
446 word = strndup(w, l);
447 if (!word)
448 return log_oom();
449
450 else if (startswith(word, "root=")) {
451 free(what);
452 what = fstab_node_to_udev_node(word+5);
453 if (!what)
454 return log_oom();
455
456 } else if (startswith(word, "rootfstype=")) {
457 free(type);
458 type = strdup(word + 11);
459 if (!type)
460 return log_oom();
461
462 } else if (startswith(word, "rootflags=")) {
80c39ad2
LP
463 char *o;
464
465 o = strjoin(opts, ",", word + 10, NULL);
466 if (!o)
5e398e54
TG
467 return log_oom();
468
80c39ad2
LP
469 free(opts);
470 opts = o;
471
5e398e54 472 } else if (streq(word, "ro") || streq(word, "rw")) {
80c39ad2
LP
473 char *o;
474
475 o = strjoin(opts, ",", word, NULL);
476 if (!o)
5e398e54
TG
477 return log_oom();
478
80c39ad2
LP
479 free(opts);
480 opts = o;
533740e1 481 }
5e398e54
TG
482 }
483
21d1a678
WC
484 noauto = !!strstr(opts, "noauto");
485 nofail = !!strstr(opts, "nofail");
486
135b5212 487 if (!what) {
4e5ed9b6 488 log_debug("Could not find a root= entry on the kernel commandline.");
135b5212
HH
489 return 0;
490 }
5e398e54 491
135b5212
HH
492 if (what[0] != '/') {
493 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
494 return 0;
495 }
5e398e54 496
135b5212 497 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
7f5806d7 498 r = add_mount(what, "/sysroot", type, opts, 1, noauto, nofail, false,
5073f89f 499 SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
5e398e54 500
135b5212 501 return (r < 0) ? r : 0;
5e398e54
TG
502}
503
94734142 504static int parse_proc_cmdline(void) {
7fd1b19b 505 _cleanup_free_ char *line = NULL;
d0aa9ce5 506 char *w, *state;
94734142 507 size_t l;
74df0fca 508 int r;
94734142 509
74df0fca
LP
510 r = proc_cmdline(&line);
511 if (r < 0)
94734142 512 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
74df0fca 513 if (r <= 0)
94734142 514 return 0;
94734142
LP
515
516 FOREACH_WORD_QUOTED(w, l, line, state) {
7fd1b19b 517 _cleanup_free_ char *word = NULL;
94734142
LP
518
519 word = strndup(w, l);
d0aa9ce5
ZJS
520 if (!word)
521 return log_oom();
94734142
LP
522
523 if (startswith(word, "fstab=")) {
524 r = parse_boolean(word + 6);
525 if (r < 0)
526 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
527 else
528 arg_enabled = r;
529
530 } else if (startswith(word, "rd.fstab=")) {
531
532 if (in_initrd()) {
ef53700c 533 r = parse_boolean(word + 9);
94734142 534 if (r < 0)
ef53700c 535 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
94734142
LP
536 else
537 arg_enabled = r;
538 }
539
540 } else if (startswith(word, "fstab.") ||
541 (in_initrd() && startswith(word, "rd.fstab."))) {
542
543 log_warning("Unknown kernel switch %s. Ignoring.", word);
544 }
94734142
LP
545 }
546
d0aa9ce5 547 return 0;
94734142
LP
548}
549
6b1dc2bd 550int main(int argc, char *argv[]) {
ac4785b0 551 int r = 0, k, l = 0;
6b1dc2bd 552
07719a21
LP
553 if (argc > 1 && argc != 4) {
554 log_error("This program takes three or no arguments.");
6b1dc2bd
LP
555 return EXIT_FAILURE;
556 }
557
558 if (argc > 1)
559 arg_dest = argv[1];
560
a6903061 561 log_set_target(LOG_TARGET_SAFE);
6b1dc2bd
LP
562 log_parse_environment();
563 log_open();
564
6b1dc2bd
LP
565 umask(0022);
566
94734142
LP
567 if (parse_proc_cmdline() < 0)
568 return EXIT_FAILURE;
569
ac4785b0
HH
570 if (in_initrd())
571 r = parse_new_root_from_proc_cmdline();
5e398e54 572
ac4785b0
HH
573 if (!arg_enabled)
574 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
575
df3e57f5 576 k = parse_fstab(NULL, false);
ac4785b0
HH
577
578 if (in_initrd())
579 l = parse_fstab("/sysroot", true);
6b1dc2bd 580
3d22d1ab 581 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
6b1dc2bd 582}