chiark / gitweb /
78d7609c6c14b83842f9eabe118cb0bda4b17454
[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                 if (streq(where, "/")) {
214                         lnk = strjoin(arg_dest, "/", SPECIAL_LOCAL_FS_TARGET, ".wants/", "systemd-fsck-root.service", NULL);
215                         if (!lnk)
216                                 return log_oom();
217
218                         mkdir_parents_label(lnk, 0755);
219                         if (symlink("systemd-fsck-root.service", lnk) < 0) {
220                                 log_error("Failed to create symlink %s: %m", lnk);
221                                 return -errno;
222                         }
223                 } else {
224                         _cleanup_free_ char *fsck = NULL;
225
226                         fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
227                         if (!fsck)
228                                 return log_oom();
229
230                         fprintf(f,
231                                 "Requires=%s\n"
232                                 "After=%s\n",
233                                 fsck,
234                                 fsck);
235                 }
236         }
237
238
239         fprintf(f,
240                 "\n"
241                 "[Mount]\n"
242                 "What=%s\n"
243                 "Where=%s\n"
244                 "Type=%s\n",
245                 what,
246                 where,
247                 type);
248
249         if (!isempty(opts) &&
250             !streq(opts, "defaults"))
251                 fprintf(f,
252                         "Options=%s\n",
253                         opts);
254
255         fflush(f);
256         if (ferror(f)) {
257                 log_error("Failed to write unit file %s: %m", unit);
258                 return -errno;
259         }
260
261         if (!noauto) {
262                 if (post) {
263                         free(lnk);
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
276         if (automount && !path_equal(where, "/")) {
277                 automount_name = unit_name_from_path(where, ".automount");
278                 if (!automount_name)
279                         return log_oom();
280
281                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
282                 if (!automount_unit)
283                         return log_oom();
284
285                 fclose(f);
286                 f = fopen(automount_unit, "wxe");
287                 if (!f) {
288                         log_error("Failed to create unit file %s: %m", automount_unit);
289                         return -errno;
290                 }
291
292                 fprintf(f,
293                         "# Automatically generated by systemd-fstab-generator\n\n"
294                         "[Unit]\n"
295                         "SourcePath=%s\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(const char *prefix, bool initrd) {
330         char *fstab_path;
331         _cleanup_endmntent_ FILE *f;
332         int r = 0;
333         struct mntent *me;
334
335         fstab_path = strappenda(strempty(prefix), "/etc/fstab");
336         f = setmntent(fstab_path, "r");
337         if (!f) {
338                 if (errno == ENOENT)
339                         return 0;
340
341                 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
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                 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
354                 if (!what || !where)
355                         return log_oom();
356
357                 if (is_path(where))
358                         path_kill_slashes(where);
359
360                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
361
362                 if (streq(me->mnt_type, "swap"))
363                         k = add_swap(what, me);
364                 else {
365                         bool noauto, nofail, automount;
366                         const char *post;
367
368                         noauto = !!hasmntopt(me, "noauto");
369                         nofail = !!hasmntopt(me, "nofail");
370                         automount =
371                                   hasmntopt(me, "comment=systemd.automount") ||
372                                   hasmntopt(me, "x-systemd.automount");
373
374                         if (initrd) {
375                                 post = SPECIAL_INITRD_FS_TARGET;
376                         } else if (mount_in_initrd(me)) {
377                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
378                         } else if (mount_is_network(me)) {
379                                 post = SPECIAL_REMOTE_FS_TARGET;
380                         } else {
381                                 post = SPECIAL_LOCAL_FS_TARGET;
382                         }
383
384                         k = add_mount(what, where, me->mnt_type, me->mnt_opts,
385                                       me->mnt_passno, noauto, nofail, automount,
386                                       post, fstab_path);
387                 }
388
389                 if (k < 0)
390                         r = k;
391         }
392
393         return r;
394 }
395
396 static int parse_new_root_from_proc_cmdline(void) {
397         _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
398         char *w, *state;
399         int r;
400         size_t l;
401         bool noauto, nofail;
402
403         r = read_one_line_file("/proc/cmdline", &line);
404         if (r < 0) {
405                 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
406                 return 0;
407         }
408
409         opts = strdup("ro");
410         type = strdup("auto");
411         if (!opts || !type)
412                 return log_oom();
413
414         /* root= and roofstype= may occur more than once, the last instance should take precedence.
415          * In the case of multiple rootflags= the arguments should be concatenated */
416         FOREACH_WORD_QUOTED(w, l, line, state) {
417                 _cleanup_free_ char *word;
418
419                 word = strndup(w, l);
420                 if (!word)
421                         return log_oom();
422
423                 else if (startswith(word, "root=")) {
424                         free(what);
425                         what = fstab_node_to_udev_node(word+5);
426                         if (!what)
427                                 return log_oom();
428
429                 } else if (startswith(word, "rootfstype=")) {
430                         free(type);
431                         type = strdup(word + 11);
432                         if (!type)
433                                 return log_oom();
434
435                 } else if (startswith(word, "rootflags=")) {
436                         char *o;
437
438                         o = strjoin(opts, ",", word + 10, NULL);
439                         if (!o)
440                                 return log_oom();
441
442                         free(opts);
443                         opts = o;
444
445                 } else if (streq(word, "ro") || streq(word, "rw")) {
446                         char *o;
447
448                         o = strjoin(opts, ",", word, NULL);
449                         if (!o)
450                                 return log_oom();
451
452                         free(opts);
453                         opts = o;
454                 }
455         }
456
457         noauto = !!strstr(opts, "noauto");
458         nofail = !!strstr(opts, "nofail");
459
460         if (!what) {
461                 log_debug("Could not find a root= entry on the kernel commandline.");
462                 return 0;
463         }
464
465         if (what[0] != '/') {
466                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
467                 return 0;
468         }
469
470         log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
471         r = add_mount(what, "/sysroot", type, opts, 1, noauto, nofail, false,
472                       SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
473
474         return (r < 0) ? r : 0;
475 }
476
477 static int parse_proc_cmdline(void) {
478         _cleanup_free_ char *line = NULL;
479         char *w, *state;
480         int r;
481         size_t l;
482
483         if (detect_container(NULL) > 0)
484                 return 0;
485
486         r = read_one_line_file("/proc/cmdline", &line);
487         if (r < 0) {
488                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
489                 return 0;
490         }
491
492         FOREACH_WORD_QUOTED(w, l, line, state) {
493                 _cleanup_free_ char *word = NULL;
494
495                 word = strndup(w, l);
496                 if (!word)
497                         return log_oom();
498
499                 if (startswith(word, "fstab=")) {
500                         r = parse_boolean(word + 6);
501                         if (r < 0)
502                                 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
503                         else
504                                 arg_enabled = r;
505
506                 } else if (startswith(word, "rd.fstab=")) {
507
508                         if (in_initrd()) {
509                                 r = parse_boolean(word + 9);
510                                 if (r < 0)
511                                         log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
512                                 else
513                                         arg_enabled = r;
514                         }
515
516                 } else if (startswith(word, "fstab.") ||
517                            (in_initrd() && startswith(word, "rd.fstab."))) {
518
519                         log_warning("Unknown kernel switch %s. Ignoring.", word);
520                 }
521         }
522
523         return 0;
524 }
525
526 int main(int argc, char *argv[]) {
527         int r = 0, k, l = 0;
528
529         if (argc > 1 && argc != 4) {
530                 log_error("This program takes three or no arguments.");
531                 return EXIT_FAILURE;
532         }
533
534         if (argc > 1)
535                 arg_dest = argv[1];
536
537         log_set_target(LOG_TARGET_SAFE);
538         log_parse_environment();
539         log_open();
540
541         umask(0022);
542
543         if (parse_proc_cmdline() < 0)
544                 return EXIT_FAILURE;
545
546         if (in_initrd())
547                 r = parse_new_root_from_proc_cmdline();
548
549         if (!arg_enabled)
550                 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
551
552         k = parse_fstab(NULL, false);
553
554         if (in_initrd())
555                 l = parse_fstab("/sysroot", true);
556
557         return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
558 }