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