]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
251a346c4d131a2c6ab81d18bd2fac7400d423fd
[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 "virt.h"
36
37 static const char *arg_dest = "/tmp";
38 static bool arg_enabled = true;
39
40 static int device_name(const char *path, char **unit) {
41 char *p;
42
43 assert(path);
44
45 if (!is_device_path(path))
46 return 0;
47
48 p = unit_name_from_path(path, ".device");
49 if (!p)
50 return log_oom();
51
52 *unit = p;
53 return 1;
54 }
55
56 static int mount_find_pri(struct mntent *me, int *ret) {
57 char *end, *pri;
58 unsigned long r;
59
60 assert(me);
61 assert(ret);
62
63 pri = hasmntopt(me, "pri");
64 if (!pri)
65 return 0;
66
67 pri += 4;
68
69 errno = 0;
70 r = strtoul(pri, &end, 10);
71 if (errno != 0)
72 return -errno;
73
74 if (end == pri || (*end != ',' && *end != 0))
75 return -EINVAL;
76
77 *ret = (int) r;
78 return 1;
79 }
80
81 static int add_swap(const char *what, struct mntent *me) {
82 char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
83 FILE *f = NULL;
84 bool noauto, nofail;
85 int r, pri = -1;
86
87 assert(what);
88 assert(me);
89
90 r = mount_find_pri(me, &pri);
91 if (r < 0) {
92 log_error("Failed to parse priority");
93 return pri;
94 }
95
96 noauto = !!hasmntopt(me, "noauto");
97 nofail = !!hasmntopt(me, "nofail");
98
99 name = unit_name_from_path(what, ".swap");
100 if (!name) {
101 r = log_oom();
102 goto finish;
103 }
104
105 unit = strjoin(arg_dest, "/", name, NULL);
106 if (!unit) {
107 r = log_oom();
108 goto finish;
109 }
110
111 f = fopen(unit, "wxe");
112 if (!f) {
113 r = -errno;
114 log_error("Failed to create unit file: %m");
115 goto finish;
116 }
117
118 fputs("# Automatically generated by systemd-fstab-generator\n\n"
119 "[Unit]\n"
120 "SourcePath=/etc/fstab\n"
121 "DefaultDependencies=no\n"
122 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
123 "Before=" SPECIAL_UMOUNT_TARGET "\n", f);
124
125 if (!noauto && !nofail)
126 fputs("Before=" SPECIAL_SWAP_TARGET "\n", f);
127
128 fprintf(f,
129 "\n"
130 "[Swap]\n"
131 "What=%s\n",
132 what);
133
134 if (pri >= 0)
135 fprintf(f,
136 "Priority=%i\n",
137 pri);
138
139 fflush(f);
140 if (ferror(f)) {
141 log_error("Failed to write unit file: %m");
142 r = -errno;
143 goto finish;
144 }
145
146 if (!noauto) {
147 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
148 if (!lnk) {
149 r = log_oom();
150 goto finish;
151 }
152
153 mkdir_parents_label(lnk, 0755);
154 if (symlink(unit, lnk) < 0) {
155 log_error("Failed to create symlink: %m");
156 r = -errno;
157 goto finish;
158 }
159
160 r = device_name(what, &device);
161 if (r < 0)
162 goto finish;
163
164 if (r > 0) {
165 free(lnk);
166 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
167 if (!lnk) {
168 r = log_oom();
169 goto finish;
170 }
171
172 mkdir_parents_label(lnk, 0755);
173 if (symlink(unit, lnk) < 0) {
174 log_error("Failed to create symlink: %m");
175 r = -errno;
176 goto finish;
177 }
178 }
179 }
180
181 r = 0;
182 finish:
183 if (f)
184 fclose(f);
185
186 free(unit);
187 free(lnk);
188 free(name);
189 free(device);
190
191 return r;
192 }
193
194 static bool mount_is_bind(struct mntent *me) {
195 assert(me);
196
197 return
198 hasmntopt(me, "bind") ||
199 streq(me->mnt_opts, "bind");
200 }
201
202 static bool mount_is_network(struct mntent *me) {
203 assert(me);
204
205 return
206 hasmntopt(me, "_netdev") ||
207 fstype_is_network(me->mnt_type);
208 }
209
210 static int add_mount(const char *what, const char *where, struct mntent *me) {
211 char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL, *automount_name = NULL, *automount_unit = NULL;
212 FILE *f = NULL;
213 bool noauto, nofail, automount, isbind, isnetwork;
214 int r;
215 const char *post, *pre;
216
217 assert(what);
218 assert(where);
219 assert(me);
220
221 if (streq(me->mnt_type, "autofs"))
222 return 0;
223
224 if (!is_path(where)) {
225 log_warning("Mount point %s is not a valid path, ignoring.", where);
226 return 0;
227 }
228
229 if (mount_point_is_api(where) ||
230 mount_point_ignore(where))
231 return 0;
232
233 isnetwork = mount_is_network(me);
234 isbind = mount_is_bind(me);
235
236 noauto = !!hasmntopt(me, "noauto");
237 nofail = !!hasmntopt(me, "nofail");
238 automount =
239 hasmntopt(me, "comment=systemd.automount") ||
240 hasmntopt(me, "x-systemd.automount");
241
242 if (isnetwork) {
243 post = SPECIAL_REMOTE_FS_TARGET;
244 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
245 } else {
246 post = SPECIAL_LOCAL_FS_TARGET;
247 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
248 }
249
250 name = unit_name_from_path(where, ".mount");
251 if (!name) {
252 r = log_oom();
253 goto finish;
254 }
255
256 unit = strjoin(arg_dest, "/", name, NULL);
257 if (!unit) {
258 r = log_oom();
259 goto finish;
260 }
261
262 f = fopen(unit, "wxe");
263 if (!f) {
264 r = -errno;
265 log_error("Failed to create unit file: %m");
266 goto finish;
267 }
268
269 fputs("# Automatically generated by systemd-fstab-generator\n\n"
270 "[Unit]\n"
271 "SourcePath=/etc/fstab\n"
272 "DefaultDependencies=no\n", f);
273
274 if (!path_equal(where, "/"))
275 fprintf(f,
276 "After=%s\n"
277 "Wants=%s\n"
278 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
279 "Before=" SPECIAL_UMOUNT_TARGET "\n",
280 pre,
281 pre);
282
283
284 if (!noauto && !nofail && !automount)
285 fprintf(f,
286 "Before=%s\n",
287 post);
288
289 fprintf(f,
290 "\n"
291 "[Mount]\n"
292 "What=%s\n"
293 "Where=%s\n"
294 "Type=%s\n"
295 "FsckPassNo=%i\n",
296 what,
297 where,
298 me->mnt_type,
299 me->mnt_passno);
300
301 if (!isempty(me->mnt_opts) &&
302 !streq(me->mnt_opts, "defaults"))
303 fprintf(f,
304 "Options=%s\n",
305 me->mnt_opts);
306
307 fflush(f);
308 if (ferror(f)) {
309 log_error("Failed to write unit file: %m");
310 r = -errno;
311 goto finish;
312 }
313
314 if (!noauto) {
315 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
316 if (!lnk) {
317 r = log_oom();
318 goto finish;
319 }
320
321 mkdir_parents_label(lnk, 0755);
322 if (symlink(unit, lnk) < 0) {
323 log_error("Failed to create symlink: %m");
324 r = -errno;
325 goto finish;
326 }
327
328 if (!isbind &&
329 !path_equal(where, "/")) {
330
331 r = device_name(what, &device);
332 if (r < 0)
333 goto finish;
334
335 if (r > 0) {
336 free(lnk);
337 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
338 if (!lnk) {
339 r = log_oom();
340 goto finish;
341 }
342
343 mkdir_parents_label(lnk, 0755);
344 if (symlink(unit, lnk) < 0) {
345 log_error("Failed to create symlink: %m");
346 r = -errno;
347 goto finish;
348 }
349 }
350 }
351 }
352
353 if (automount && !path_equal(where, "/")) {
354 automount_name = unit_name_from_path(where, ".automount");
355 if (!name) {
356 r = log_oom();
357 goto finish;
358 }
359
360 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
361 if (!automount_unit) {
362 r = log_oom();
363 goto finish;
364 }
365
366 fclose(f);
367 f = fopen(automount_unit, "wxe");
368 if (!f) {
369 r = -errno;
370 log_error("Failed to create unit file: %m");
371 goto finish;
372 }
373
374 fprintf(f,
375 "# Automatically generated by systemd-fstab-generator\n\n"
376 "[Unit]\n"
377 "SourcePath=/etc/fstab\n"
378 "DefaultDependencies=no\n"
379 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
380 "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
381 "\n"
382 "[Automount]\n"
383 "Where=%s\n",
384 post,
385 where);
386
387 fflush(f);
388 if (ferror(f)) {
389 log_error("Failed to write unit file: %m");
390 r = -errno;
391 goto finish;
392 }
393
394 free(lnk);
395 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
396 if (!lnk) {
397 r = log_oom();
398 goto finish;
399 }
400
401 mkdir_parents_label(lnk, 0755);
402 if (symlink(automount_unit, lnk) < 0) {
403 log_error("Failed to create symlink: %m");
404 r = -errno;
405 goto finish;
406 }
407 }
408
409 r = 0;
410 finish:
411 if (f)
412 fclose(f);
413
414 free(unit);
415 free(lnk);
416 free(name);
417 free(device);
418 free(automount_name);
419 free(automount_unit);
420
421 return r;
422 }
423
424 static int parse_fstab(void) {
425 FILE *f;
426 int r = 0;
427 struct mntent *me;
428
429 errno = 0;
430 f = setmntent("/etc/fstab", "r");
431 if (!f) {
432 if (errno == ENOENT)
433 return 0;
434
435 log_error("Failed to open /etc/fstab: %m");
436 return -errno;
437 }
438
439 while ((me = getmntent(f))) {
440 char *where, *what;
441 int k;
442
443 what = fstab_node_to_udev_node(me->mnt_fsname);
444 if (!what) {
445 r = log_oom();
446 goto finish;
447 }
448
449 where = strdup(me->mnt_dir);
450 if (!where) {
451 r = log_oom();
452 free(what);
453 goto finish;
454 }
455
456 if (is_path(where))
457 path_kill_slashes(where);
458
459 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
460
461 if (streq(me->mnt_type, "swap"))
462 k = add_swap(what, me);
463 else
464 k = add_mount(what, where, me);
465
466 free(what);
467 free(where);
468
469 if (k < 0)
470 r = k;
471 }
472
473 finish:
474 endmntent(f);
475 return r;
476 }
477
478 static int parse_proc_cmdline(void) {
479 char *line, *w, *state;
480 int r;
481 size_t l;
482
483 if (detect_container(NULL) > 0)
484 return 0;
485
486 r = read_one_line_file("/proc/cmdline", &line);
487 if (r < 0) {
488 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
489 return 0;
490 }
491
492 FOREACH_WORD_QUOTED(w, l, line, state) {
493 char *word;
494
495 word = strndup(w, l);
496 if (!word) {
497 r = log_oom();
498 goto finish;
499 }
500
501 if (startswith(word, "fstab=")) {
502 r = parse_boolean(word + 6);
503 if (r < 0)
504 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
505 else
506 arg_enabled = r;
507
508 } else if (startswith(word, "rd.fstab=")) {
509
510 if (in_initrd()) {
511 r = parse_boolean(word + 6);
512 if (r < 0)
513 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
514 else
515 arg_enabled = r;
516 }
517
518 } else if (startswith(word, "fstab.") ||
519 (in_initrd() && startswith(word, "rd.fstab."))) {
520
521 log_warning("Unknown kernel switch %s. Ignoring.", word);
522 }
523
524 free(word);
525 }
526
527 r = 0;
528
529 finish:
530 free(line);
531 return r;
532 }
533
534 int main(int argc, char *argv[]) {
535 int r;
536
537 if (argc > 1 && argc != 4) {
538 log_error("This program takes three or no arguments.");
539 return EXIT_FAILURE;
540 }
541
542 if (argc > 1)
543 arg_dest = argv[1];
544
545 log_set_target(LOG_TARGET_SAFE);
546 log_parse_environment();
547 log_open();
548
549 umask(0022);
550
551 if (parse_proc_cmdline() < 0)
552 return EXIT_FAILURE;
553
554 if (!arg_enabled)
555 return EXIT_SUCCESS;
556
557 r = parse_fstab();
558
559 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
560 }