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