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