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