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