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