chiark / gitweb /
fstab-generator: add missing strempty() calls
[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 bool mount_in_initrd(struct mntent *me) {
195         assert(me);
196
197         return
198                 hasmntopt(me, "x-initrd.mount") ||
199                 streq(me->mnt_dir, "/usr");
200 }
201
202 static bool mount_is_rootfs(struct mntent *me) {
203         assert(me);
204
205         return hasmntopt(me, "x-initrd.rootfs");
206 }
207
208 static int add_mount(const char *what, const char *where, const char *type, const char *opts,
209                      int passno, bool noauto, bool nofail, bool automount, bool isbind,
210                      const char *pre, const char *post, const char *source) {
211         char _cleanup_free_
212                 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
213                 *automount_name = NULL, *automount_unit = NULL;
214         FILE _cleanup_fclose_ *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               "DefaultDependencies=no\n",
257               source);
258
259         if (!path_equal(where, "/")) {
260                 if (pre)
261                         fprintf(f,
262                                 "After=%s\n"
263                                 "Wants=%s\n",
264                                 pre,
265                                 pre);
266                 fprintf(f,
267                         "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
268                         "Before=" SPECIAL_UMOUNT_TARGET "\n");
269         }
270
271         if (post && !noauto && !nofail && !automount)
272                 fprintf(f,
273                         "Before=%s\n",
274                         post);
275
276         fprintf(f,
277                 "\n"
278                 "[Mount]\n"
279                 "What=%s\n"
280                 "Where=%s\n"
281                 "Type=%s\n"
282                 "FsckPassNo=%i\n",
283                 what,
284                 where,
285                 type,
286                 passno);
287
288         if (!isempty(opts) &&
289             !streq(opts, "defaults"))
290                 fprintf(f,
291                         "Options=%s\n",
292                         opts);
293
294         fflush(f);
295         if (ferror(f)) {
296                 log_error("Failed to write unit file %s: %m", unit);
297                 return -errno;
298         }
299
300         if (!noauto) {
301                 if (post) {
302                         lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
303                         if (!lnk)
304                                 return log_oom();
305
306                         mkdir_parents_label(lnk, 0755);
307                         if (symlink(unit, lnk) < 0) {
308                                 log_error("Failed to create symlink %s: %m", lnk);
309                                 return -errno;
310                         }
311                 }
312
313                 if (!isbind &&
314                     !path_equal(where, "/")) {
315
316                         r = device_name(what, &device);
317                         if (r < 0)
318                                 return r;
319
320                         if (r > 0) {
321                                 free(lnk);
322                                 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
323                                 if (!lnk)
324                                         return log_oom();
325
326                                 mkdir_parents_label(lnk, 0755);
327                                 if (symlink(unit, lnk) < 0) {
328                                         log_error("Failed to create symlink %s: %m", lnk);
329                                         return -errno;
330                                 }
331                         }
332                 }
333         }
334
335         if (automount && !path_equal(where, "/")) {
336                 automount_name = unit_name_from_path(where, ".automount");
337                 if (!name)
338                         return log_oom();
339
340                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
341                 if (!automount_unit)
342                         return log_oom();
343
344                 fclose(f);
345                 f = fopen(automount_unit, "wxe");
346                 if (!f) {
347                         log_error("Failed to create unit file %s: %m", automount_unit);
348                         return -errno;
349                 }
350
351                 fprintf(f,
352                         "# Automatically generated by systemd-fstab-generator\n\n"
353                         "[Unit]\n"
354                         "SourcePath=%s\n"
355                         "DefaultDependencies=no\n"
356                         "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
357                         "Before=" SPECIAL_UMOUNT_TARGET "\n",
358                         source);
359
360                 if (post)
361                         fprintf(f,
362                                 "Before= %s\n",
363                                 post);
364
365                 fprintf(f,
366                         "[Automount]\n"
367                         "Where=%s\n",
368                         where);
369
370                 fflush(f);
371                 if (ferror(f)) {
372                         log_error("Failed to write unit file %s: %m", automount_unit);
373                         return -errno;
374                 }
375
376                 free(lnk);
377                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
378                 if (!lnk)
379                         return log_oom();
380
381                 mkdir_parents_label(lnk, 0755);
382                 if (symlink(automount_unit, lnk) < 0) {
383                         log_error("Failed to create symlink %s: %m", lnk);
384                         return -errno;
385                 }
386         }
387
388         return 0;
389 }
390
391 static int parse_fstab(const char *prefix, bool initrd) {
392         _cleanup_free_ char *fstab_path = NULL;
393         FILE *f;
394         int r = 0;
395         struct mntent *me;
396
397         fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
398         if (!fstab_path)
399                 return log_oom();
400
401         f = setmntent(fstab_path, "r");
402         if (!f) {
403                 if (errno == ENOENT)
404                         return 0;
405
406                 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
407                 return -errno;
408         }
409
410         while ((me = getmntent(f))) {
411                 char _cleanup_free_ *where = NULL, *what = NULL;
412                 int k;
413
414                 if (initrd && !mount_in_initrd(me))
415                         continue;
416
417                 what = fstab_node_to_udev_node(me->mnt_fsname);
418                 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
419                 if (!what || !where) {
420                         r = log_oom();
421                         goto finish;
422                 }
423
424                 if (is_path(where))
425                         path_kill_slashes(where);
426
427                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
428
429                 if (streq(me->mnt_type, "swap"))
430                         k = add_swap(what, me);
431                 else {
432                         bool noauto, nofail, automount, isbind;
433                         const char *pre, *post;
434
435                         noauto = !!hasmntopt(me, "noauto");
436                         nofail = !!hasmntopt(me, "nofail");
437                         automount =
438                                   hasmntopt(me, "comment=systemd.automount") ||
439                                   hasmntopt(me, "x-systemd.automount");
440                         isbind = mount_is_bind(me);
441
442                         if (initrd) {
443                                 post = SPECIAL_INITRD_FS_TARGET;
444                                 pre = NULL;
445                         } else if (mount_is_rootfs(me)) {
446                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
447                                 pre = NULL;
448                         } else if (mount_is_network(me)) {
449                                 post = SPECIAL_REMOTE_FS_TARGET;
450                                 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
451                         } else {
452                                 post = SPECIAL_LOCAL_FS_TARGET;
453                                 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
454                         }
455
456                         k = add_mount(what, where, me->mnt_type, me->mnt_opts,
457                                      me->mnt_passno, noauto, nofail, automount,
458                                      isbind, pre, post, fstab_path);
459                 }
460
461                 if (k < 0)
462                         r = k;
463         }
464
465 finish:
466         endmntent(f);
467         return r;
468 }
469
470 static int parse_new_root_from_proc_cmdline(void) {
471         _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
472         char *w, *state;
473         int r;
474         size_t l;
475
476         r = read_one_line_file("/proc/cmdline", &line);
477         if (r < 0) {
478                 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
479                 return 0;
480         }
481
482         opts = strdup("ro");
483         type = strdup("auto");
484         if (!opts || !type)
485                 return log_oom();
486
487         /* root= and roofstype= may occur more than once, the last instance should take precedence.
488          * In the case of multiple rootflags= the arguments should be concatenated */
489         FOREACH_WORD_QUOTED(w, l, line, state) {
490                 _cleanup_free_ char *word;
491
492                 word = strndup(w, l);
493                 if (!word)
494                         return log_oom();
495
496                 else if (startswith(word, "root=")) {
497                         free(what);
498                         what = fstab_node_to_udev_node(word+5);
499                         if (!what)
500                                 return log_oom();
501
502                 } else if (startswith(word, "rootfstype=")) {
503                         free(type);
504                         type = strdup(word + 11);
505                         if (!type)
506                                 return log_oom();
507
508                 } else if (startswith(word, "rootflags=")) {
509                         char *o;
510
511                         o = strjoin(opts, ",", word + 10, NULL);
512                         if (!o)
513                                 return log_oom();
514
515                         free(opts);
516                         opts = o;
517
518                 } else if (streq(word, "ro") || streq(word, "rw")) {
519                         char *o;
520
521                         o = strjoin(opts, ",", word, NULL);
522                         if (!o)
523                                 return log_oom();
524
525                         free(opts);
526                         opts = o;
527                 }
528         }
529
530         if (!what) {
531                 log_error("Could not find a root= entry on the kernel commandline.");
532                 return 0;
533         }
534
535         if (what[0] != '/') {
536                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
537                 return 0;
538         }
539
540         log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
541         r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
542                       false, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
543
544         return (r < 0) ? r : 0;
545 }
546
547 static int parse_proc_cmdline(void) {
548         char _cleanup_free_ *line = NULL;
549         char *w, *state;
550         int r;
551         size_t l;
552
553         if (detect_container(NULL) > 0)
554                 return 0;
555
556         r = read_one_line_file("/proc/cmdline", &line);
557         if (r < 0) {
558                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
559                 return 0;
560         }
561
562         FOREACH_WORD_QUOTED(w, l, line, state) {
563                 char _cleanup_free_ *word = NULL;
564
565                 word = strndup(w, l);
566                 if (!word)
567                         return log_oom();
568
569                 if (startswith(word, "fstab=")) {
570                         r = parse_boolean(word + 6);
571                         if (r < 0)
572                                 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
573                         else
574                                 arg_enabled = r;
575
576                 } else if (startswith(word, "rd.fstab=")) {
577
578                         if (in_initrd()) {
579                                 r = parse_boolean(word + 6);
580                                 if (r < 0)
581                                         log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
582                                 else
583                                         arg_enabled = r;
584                         }
585
586                 } else if (startswith(word, "fstab.") ||
587                            (in_initrd() && startswith(word, "rd.fstab."))) {
588
589                         log_warning("Unknown kernel switch %s. Ignoring.", word);
590                 }
591         }
592
593         return 0;
594 }
595
596 int main(int argc, char *argv[]) {
597         int r = 0, k, l = 0;
598
599         if (argc > 1 && argc != 4) {
600                 log_error("This program takes three or no arguments.");
601                 return EXIT_FAILURE;
602         }
603
604         if (argc > 1)
605                 arg_dest = argv[1];
606
607         log_set_target(LOG_TARGET_SAFE);
608         log_parse_environment();
609         log_open();
610
611         umask(0022);
612
613         if (parse_proc_cmdline() < 0)
614                 return EXIT_FAILURE;
615
616         if (in_initrd())
617                 r = parse_new_root_from_proc_cmdline();
618
619         if (!arg_enabled)
620                 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
621
622         k = parse_fstab(NULL, false);
623
624         if (in_initrd())
625                 l = parse_fstab("/sysroot", true);
626
627         return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
628 }