chiark / gitweb /
dhcp: Add function to free DHCP client data
[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 (type && !streq(type, "auto")) {
156                 int r;
157                 const char *checker;
158
159                 checker = strappenda("/sbin/fsck.", type);
160                 r = access(checker, X_OK);
161                 if (r < 0) {
162                         log_warning("Checking was requested for %s, but %s cannot be used: %m", what, checker);
163
164                         /* treat missing check as essentially OK */
165                         return errno == ENOENT ? 0 : -errno;
166                 }
167         }
168
169         if (streq(where, "/")) {
170                 char *lnk;
171
172                 lnk = strappenda(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
173                 mkdir_parents_label(lnk, 0755);
174                 if (symlink("systemd-fsck-root.service", lnk) < 0) {
175                         log_error("Failed to create symlink %s: %m", lnk);
176                         return -errno;
177                 }
178         } else {
179                 _cleanup_free_ char *fsck = NULL;
180
181                 fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
182                 if (!fsck)
183                         return log_oom();
184
185                 fprintf(f,
186                         "RequiresOverridable=%s\n"
187                         "After=%s\n",
188                         fsck,
189                         fsck);
190         }
191
192         return 0;
193 }
194
195 static int add_mount(
196                 const char *what,
197                 const char *where,
198                 const char *type,
199                 const char *opts,
200                 int passno,
201                 bool noauto,
202                 bool nofail,
203                 bool automount,
204                 const char *post,
205                 const char *source) {
206         _cleanup_free_ char
207                 *name = NULL, *unit = NULL, *lnk = NULL,
208                 *automount_name = NULL, *automount_unit = NULL;
209         _cleanup_fclose_ FILE *f = NULL;
210         int r;
211
212         assert(what);
213         assert(where);
214         assert(type);
215         assert(opts);
216         assert(source);
217
218         if (streq(type, "autofs"))
219                 return 0;
220
221         if (!is_path(where)) {
222                 log_warning("Mount point %s is not a valid path, ignoring.", where);
223                 return 0;
224         }
225
226         if (mount_point_is_api(where) ||
227             mount_point_ignore(where))
228                 return 0;
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               source);
252
253         if (post && !noauto && !nofail && !automount)
254                 fprintf(f,
255                         "Before=%s\n",
256                         post);
257
258         r = add_fsck(f, what, where, type, passno);
259         if (r < 0)
260                 return r;
261
262         fprintf(f,
263                 "\n"
264                 "[Mount]\n"
265                 "What=%s\n"
266                 "Where=%s\n"
267                 "Type=%s\n",
268                 what,
269                 where,
270                 type);
271
272         if (!isempty(opts) &&
273             !streq(opts, "defaults"))
274                 fprintf(f,
275                         "Options=%s\n",
276                         opts);
277
278         fflush(f);
279         if (ferror(f)) {
280                 log_error("Failed to write unit file %s: %m", unit);
281                 return -errno;
282         }
283
284         if (!noauto) {
285                 if (post) {
286                         lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
287                         if (!lnk)
288                                 return log_oom();
289
290                         mkdir_parents_label(lnk, 0755);
291                         if (symlink(unit, lnk) < 0) {
292                                 log_error("Failed to create symlink %s: %m", lnk);
293                                 return -errno;
294                         }
295                 }
296         }
297
298         if (automount && !path_equal(where, "/")) {
299                 automount_name = unit_name_from_path(where, ".automount");
300                 if (!automount_name)
301                         return log_oom();
302
303                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
304                 if (!automount_unit)
305                         return log_oom();
306
307                 fclose(f);
308                 f = fopen(automount_unit, "wxe");
309                 if (!f) {
310                         log_error("Failed to create unit file %s: %m", automount_unit);
311                         return -errno;
312                 }
313
314                 fprintf(f,
315                         "# Automatically generated by systemd-fstab-generator\n\n"
316                         "[Unit]\n"
317                         "SourcePath=%s\n",
318                         source);
319
320                 if (post)
321                         fprintf(f,
322                                 "Before= %s\n",
323                                 post);
324
325                 fprintf(f,
326                         "[Automount]\n"
327                         "Where=%s\n",
328                         where);
329
330                 fflush(f);
331                 if (ferror(f)) {
332                         log_error("Failed to write unit file %s: %m", automount_unit);
333                         return -errno;
334                 }
335
336                 free(lnk);
337                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
338                 if (!lnk)
339                         return log_oom();
340
341                 mkdir_parents_label(lnk, 0755);
342                 if (symlink(automount_unit, lnk) < 0) {
343                         log_error("Failed to create symlink %s: %m", lnk);
344                         return -errno;
345                 }
346         }
347
348         return 0;
349 }
350
351 static int parse_fstab(const char *prefix, bool initrd) {
352         char *fstab_path;
353         _cleanup_endmntent_ FILE *f;
354         int r = 0;
355         struct mntent *me;
356
357         fstab_path = strappenda(strempty(prefix), "/etc/fstab");
358         f = setmntent(fstab_path, "r");
359         if (!f) {
360                 if (errno == ENOENT)
361                         return 0;
362
363                 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
364                 return -errno;
365         }
366
367         while ((me = getmntent(f))) {
368                 _cleanup_free_ char *where = NULL, *what = NULL;
369                 int k;
370
371                 if (initrd && !mount_in_initrd(me))
372                         continue;
373
374                 what = fstab_node_to_udev_node(me->mnt_fsname);
375                 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
376                 if (!what || !where)
377                         return log_oom();
378
379                 if (is_path(where))
380                         path_kill_slashes(where);
381
382                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
383
384                 if (streq(me->mnt_type, "swap"))
385                         k = add_swap(what, me);
386                 else {
387                         bool noauto, nofail, automount;
388                         const char *post;
389
390                         noauto = !!hasmntopt(me, "noauto");
391                         nofail = !!hasmntopt(me, "nofail");
392                         automount =
393                                   hasmntopt(me, "comment=systemd.automount") ||
394                                   hasmntopt(me, "x-systemd.automount");
395
396                         if (initrd) {
397                                 post = SPECIAL_INITRD_FS_TARGET;
398                         } else if (mount_in_initrd(me)) {
399                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
400                         } else if (mount_is_network(me)) {
401                                 post = SPECIAL_REMOTE_FS_TARGET;
402                         } else {
403                                 post = SPECIAL_LOCAL_FS_TARGET;
404                         }
405
406                         k = add_mount(what, where, me->mnt_type, me->mnt_opts,
407                                       me->mnt_passno, noauto, nofail, automount,
408                                       post, fstab_path);
409                 }
410
411                 if (k < 0)
412                         r = k;
413         }
414
415         return r;
416 }
417
418 static int parse_new_root_from_proc_cmdline(void) {
419         _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
420         bool noauto, nofail;
421         char *w, *state;
422         size_t l;
423         int r;
424
425         r = proc_cmdline(&line);
426         if (r < 0)
427                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
428         if (r <= 0)
429                 return 0;
430
431         opts = strdup("ro");
432         type = strdup("auto");
433         if (!opts || !type)
434                 return log_oom();
435
436         /* root= and roofstype= may occur more than once, the last instance should take precedence.
437          * In the case of multiple rootflags= the arguments should be concatenated */
438         FOREACH_WORD_QUOTED(w, l, line, state) {
439                 _cleanup_free_ char *word;
440
441                 word = strndup(w, l);
442                 if (!word)
443                         return log_oom();
444
445                 else if (startswith(word, "root=")) {
446                         free(what);
447                         what = fstab_node_to_udev_node(word+5);
448                         if (!what)
449                                 return log_oom();
450
451                 } else if (startswith(word, "rootfstype=")) {
452                         free(type);
453                         type = strdup(word + 11);
454                         if (!type)
455                                 return log_oom();
456
457                 } else if (startswith(word, "rootflags=")) {
458                         char *o;
459
460                         o = strjoin(opts, ",", word + 10, NULL);
461                         if (!o)
462                                 return log_oom();
463
464                         free(opts);
465                         opts = o;
466
467                 } else if (streq(word, "ro") || streq(word, "rw")) {
468                         char *o;
469
470                         o = strjoin(opts, ",", word, NULL);
471                         if (!o)
472                                 return log_oom();
473
474                         free(opts);
475                         opts = o;
476                 }
477         }
478
479         noauto = !!strstr(opts, "noauto");
480         nofail = !!strstr(opts, "nofail");
481
482         if (!what) {
483                 log_debug("Could not find a root= entry on the kernel commandline.");
484                 return 0;
485         }
486
487         if (what[0] != '/') {
488                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
489                 return 0;
490         }
491
492         log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
493         r = add_mount(what, "/sysroot", type, opts, 1, noauto, nofail, false,
494                       SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
495
496         return (r < 0) ? r : 0;
497 }
498
499 static int parse_proc_cmdline(void) {
500         _cleanup_free_ char *line = NULL;
501         char *w, *state;
502         size_t l;
503         int r;
504
505         r = proc_cmdline(&line);
506         if (r < 0)
507                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
508         if (r <= 0)
509                 return 0;
510
511         FOREACH_WORD_QUOTED(w, l, line, state) {
512                 _cleanup_free_ char *word = NULL;
513
514                 word = strndup(w, l);
515                 if (!word)
516                         return log_oom();
517
518                 if (startswith(word, "fstab=")) {
519                         r = parse_boolean(word + 6);
520                         if (r < 0)
521                                 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
522                         else
523                                 arg_enabled = r;
524
525                 } else if (startswith(word, "rd.fstab=")) {
526
527                         if (in_initrd()) {
528                                 r = parse_boolean(word + 9);
529                                 if (r < 0)
530                                         log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
531                                 else
532                                         arg_enabled = r;
533                         }
534
535                 } else if (startswith(word, "fstab.") ||
536                            (in_initrd() && startswith(word, "rd.fstab."))) {
537
538                         log_warning("Unknown kernel switch %s. Ignoring.", word);
539                 }
540         }
541
542         return 0;
543 }
544
545 int main(int argc, char *argv[]) {
546         int r = 0, k, l = 0;
547
548         if (argc > 1 && argc != 4) {
549                 log_error("This program takes three or no arguments.");
550                 return EXIT_FAILURE;
551         }
552
553         if (argc > 1)
554                 arg_dest = argv[1];
555
556         log_set_target(LOG_TARGET_SAFE);
557         log_parse_environment();
558         log_open();
559
560         umask(0022);
561
562         if (parse_proc_cmdline() < 0)
563                 return EXIT_FAILURE;
564
565         if (in_initrd())
566                 r = parse_new_root_from_proc_cmdline();
567
568         if (!arg_enabled)
569                 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
570
571         k = parse_fstab(NULL, false);
572
573         if (in_initrd())
574                 l = parse_fstab("/sysroot", true);
575
576         return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
577 }