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