chiark / gitweb /
fstab-generator: remove redundancy in mount_is_bind
[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_bind(struct mntent *me) {
195         assert(me);
196
197         return hasmntopt(me, "bind");
198 }
199
200 static bool mount_is_network(struct mntent *me) {
201         assert(me);
202
203         return
204                 hasmntopt(me, "_netdev") ||
205                 fstype_is_network(me->mnt_type);
206 }
207
208 static int add_mount(const char *what, const char *where, struct mntent *me) {
209         char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL, *automount_name = NULL, *automount_unit = NULL;
210         FILE *f = NULL;
211         bool noauto, nofail, automount, isbind, isnetwork;
212         int r;
213         const char *post, *pre;
214
215         assert(what);
216         assert(where);
217         assert(me);
218
219         if (streq(me->mnt_type, "autofs"))
220                 return 0;
221
222         if (!is_path(where)) {
223                 log_warning("Mount point %s is not a valid path, ignoring.", where);
224                 return 0;
225         }
226
227         if (mount_point_is_api(where) ||
228             mount_point_ignore(where))
229                 return 0;
230
231         isnetwork = mount_is_network(me);
232         isbind = mount_is_bind(me);
233
234         noauto = !!hasmntopt(me, "noauto");
235         nofail = !!hasmntopt(me, "nofail");
236         automount =
237                 hasmntopt(me, "comment=systemd.automount") ||
238                 hasmntopt(me, "x-systemd.automount");
239
240         if (isnetwork) {
241                 post = SPECIAL_REMOTE_FS_TARGET;
242                 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
243         } else {
244                 post = SPECIAL_LOCAL_FS_TARGET;
245                 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
246         }
247
248         name = unit_name_from_path(where, ".mount");
249         if (!name) {
250                 r = log_oom();
251                 goto finish;
252         }
253
254         unit = strjoin(arg_dest, "/", name, NULL);
255         if (!unit) {
256                 r = log_oom();
257                 goto finish;
258         }
259
260         f = fopen(unit, "wxe");
261         if (!f) {
262                 r = -errno;
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 }