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