chiark / gitweb /
34cd720517d7ea260d1330b0110534dc37fe0aea
[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                 /* The root disk is not an option */
193                 automount = false;
194                 noauto = false;
195                 nofail = false;
196         }
197
198         name = unit_name_from_path(where, ".mount");
199         if (!name)
200                 return log_oom();
201
202         unit = strjoin(arg_dest, "/", name, NULL);
203         if (!unit)
204                 return log_oom();
205
206         f = fopen(unit, "wxe");
207         if (!f) {
208                 if (errno == EEXIST)
209                         log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
210                 else
211                         log_error("Failed to create unit file %s: %m", unit);
212                 return -errno;
213         }
214
215         fprintf(f,
216                 "# Automatically generated by systemd-fstab-generator\n\n"
217                 "[Unit]\n"
218                 "SourcePath=%s\n"
219                 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
220                 source);
221
222         if (post && !noauto && !nofail && !automount)
223                 fprintf(f, "Before=%s\n", post);
224
225         if (passno != 0) {
226                 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
227                 if (r < 0)
228                         return r;
229         }
230
231         fprintf(f,
232                 "\n"
233                 "[Mount]\n"
234                 "What=%s\n"
235                 "Where=%s\n",
236                 what,
237                 where);
238
239         if (!isempty(fstype) && !streq(fstype, "auto"))
240                 fprintf(f, "Type=%s\n", fstype);
241
242         if (!isempty(opts) && !streq(opts, "defaults"))
243                 fprintf(f, "Options=%s\n", opts);
244
245         fflush(f);
246         if (ferror(f)) {
247                 log_error("Failed to write unit file %s: %m", unit);
248                 return -errno;
249         }
250
251         if (!noauto && 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         if (automount) {
264                 automount_name = unit_name_from_path(where, ".automount");
265                 if (!automount_name)
266                         return log_oom();
267
268                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
269                 if (!automount_unit)
270                         return log_oom();
271
272                 fclose(f);
273                 f = fopen(automount_unit, "wxe");
274                 if (!f) {
275                         log_error("Failed to create unit file %s: %m", automount_unit);
276                         return -errno;
277                 }
278
279                 fprintf(f,
280                         "# Automatically generated by systemd-fstab-generator\n\n"
281                         "[Unit]\n"
282                         "SourcePath=%s\n"
283                         "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
284                         source);
285
286                 if (post)
287                         fprintf(f,
288                                 "Before=%s\n",
289                                 post);
290
291                 fprintf(f,
292                         "[Automount]\n"
293                         "Where=%s\n",
294                         where);
295
296                 fflush(f);
297                 if (ferror(f)) {
298                         log_error("Failed to write unit file %s: %m", automount_unit);
299                         return -errno;
300                 }
301
302                 free(lnk);
303                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
304                 if (!lnk)
305                         return log_oom();
306
307                 mkdir_parents_label(lnk, 0755);
308                 if (symlink(automount_unit, lnk) < 0) {
309                         log_error("Failed to create symlink %s: %m", lnk);
310                         return -errno;
311                 }
312         }
313
314         return 0;
315 }
316
317 static int parse_fstab(bool initrd) {
318         _cleanup_endmntent_ FILE *f = NULL;
319         const char *fstab_path;
320         struct mntent *me;
321         int r = 0;
322
323         fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
324         f = setmntent(fstab_path, "re");
325         if (!f) {
326                 if (errno == ENOENT)
327                         return 0;
328
329                 log_error("Failed to open %s: %m", fstab_path);
330                 return -errno;
331         }
332
333         while ((me = getmntent(f))) {
334                 _cleanup_free_ char *where = NULL, *what = NULL;
335                 int k;
336
337                 if (initrd && !mount_in_initrd(me))
338                         continue;
339
340                 what = fstab_node_to_udev_node(me->mnt_fsname);
341                 if (!what)
342                         return log_oom();
343
344                 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
345                 if (!where)
346                         return log_oom();
347
348                 if (is_path(where))
349                         path_kill_slashes(where);
350
351                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
352
353                 if (streq(me->mnt_type, "swap"))
354                         k = add_swap(what, me);
355                 else {
356                         bool noauto, nofail, automount;
357                         const char *post;
358
359                         noauto = !!hasmntopt(me, "noauto");
360                         nofail = !!hasmntopt(me, "nofail");
361                         automount =
362                                   hasmntopt(me, "comment=systemd.automount") ||
363                                   hasmntopt(me, "x-systemd.automount");
364
365                         if (initrd)
366                                 post = SPECIAL_INITRD_FS_TARGET;
367                         else if (mount_in_initrd(me))
368                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
369                         else if (mount_is_network(me))
370                                 post = SPECIAL_REMOTE_FS_TARGET;
371                         else
372                                 post = SPECIAL_LOCAL_FS_TARGET;
373
374                         k = add_mount(what,
375                                       where,
376                                       me->mnt_type,
377                                       me->mnt_opts,
378                                       me->mnt_passno,
379                                       noauto,
380                                       nofail,
381                                       automount,
382                                       post,
383                                       fstab_path);
384                 }
385
386                 if (k < 0)
387                         r = k;
388         }
389
390         return r;
391 }
392
393 static int add_root_mount(void) {
394         _cleanup_free_ char *o = NULL, *what = NULL;
395
396         if (isempty(arg_root_what)) {
397                 log_debug("Could not find a root= entry on the kernel commandline.");
398                 return 0;
399         }
400
401         what = fstab_node_to_udev_node(arg_root_what);
402         if (!path_is_absolute(what)) {
403                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
404                 return 0;
405         }
406
407         if (!arg_root_options)
408                 o = strdup(arg_root_rw > 0 ? "rw" : "ro");
409         else {
410                 if (arg_root_rw >= 0 ||
411                     (!mount_test_option(arg_root_options, "ro") &&
412                      !mount_test_option(arg_root_options, "rw")))
413                         o = strjoin(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro", NULL);
414                 else
415                         o = strdup(arg_root_options);
416         }
417         if (!o)
418                 return log_oom();
419
420         log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
421         return add_mount(what,
422                          "/sysroot",
423                          arg_root_fstype,
424                          o,
425                          1,
426                          false,
427                          false,
428                          false,
429                          SPECIAL_INITRD_ROOT_FS_TARGET,
430                          "/proc/cmdline");
431 }
432
433 static int parse_proc_cmdline_item(const char *key, const char *value) {
434         int r;
435
436         /* root= and roofstype= may occur more than once, the last
437          * instance should take precedence.  In the case of multiple
438          * rootflags= the arguments should be concatenated */
439
440         if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
441
442                 r = parse_boolean(value);
443                 if (r < 0)
444                         log_warning("Failed to parse fstab switch %s. Ignoring.", value);
445                 else
446                         arg_fstab_enabled = r;
447
448         } else if (streq(key, "root") && value) {
449
450                 free(arg_root_what);
451                 arg_root_what = strdup(value);
452                 if (!arg_root_what)
453                         return log_oom();
454
455         } else if (streq(key, "rootfstype") && value) {
456
457                 free(arg_root_fstype);
458                 arg_root_fstype = strdup(value);
459                 if (!arg_root_fstype)
460                         return log_oom();
461
462         } else if (streq(key, "rootflags") && value) {
463                 char *o;
464
465                 o = arg_root_options ?
466                         strjoin(arg_root_options, ",", value, NULL) :
467                         strdup(value);
468                 if (!o)
469                         return log_oom();
470
471                 free(arg_root_options);
472                 arg_root_options = o;
473
474         } else if (streq(key, "rw") && !value)
475                 arg_root_rw = true;
476         else if (streq(key, "ro") && !value)
477                 arg_root_rw = false;
478         else if (startswith(key, "fstab.") || startswith(key, "rd.fstab."))
479                 log_warning("Unknown kernel switch %s. Ignoring.", key);
480
481         return 0;
482 }
483
484 int main(int argc, char *argv[]) {
485         int r = 0;
486
487         if (argc > 1 && argc != 4) {
488                 log_error("This program takes three or no arguments.");
489                 return EXIT_FAILURE;
490         }
491
492         if (argc > 1)
493                 arg_dest = argv[1];
494
495         log_set_target(LOG_TARGET_SAFE);
496         log_parse_environment();
497         log_open();
498
499         umask(0022);
500
501         if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
502                 return EXIT_FAILURE;
503
504         /* Always honour root= in the kernel command line if we are in an initrd */
505         if (in_initrd())
506                 r = add_root_mount();
507
508         /* Honour /etc/fstab only when that's enabled */
509         if (arg_fstab_enabled) {
510                 int k;
511
512                 /* Parse the local /etc/fstab, possibly from the initrd */
513                 k = parse_fstab(false);
514                 if (k < 0)
515                         r = k;
516
517                 /* If running in the initrd also parse the /etc/fstab from the host */
518                 if (in_initrd()) {
519                         k = parse_fstab(true);
520                         if (k < 0)
521                                 r = k;
522                 }
523         }
524
525         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
526 }