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