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