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