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