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