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