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