chiark / gitweb /
07467249831c6971be722ac769fa10af3332d0d7
[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
36 static const char *arg_dest = "/tmp";
37
38 static int device_name(const char *path, char **unit) {
39         char *p;
40
41         assert(path);
42
43         if (!is_device_path(path))
44                 return 0;
45
46         p = unit_name_from_path(path, ".device");
47         if (!p)
48                 return -ENOMEM;
49
50         *unit = p;
51         return 1;
52 }
53
54 static int mount_find_pri(struct mntent *me, int *ret) {
55         char *end, *pri;
56         unsigned long r;
57
58         assert(me);
59         assert(ret);
60
61         pri = hasmntopt(me, "pri");
62         if (!pri)
63                 return 0;
64
65         pri += 4;
66
67         errno = 0;
68         r = strtoul(pri, &end, 10);
69         if (errno != 0)
70                 return -errno;
71
72         if (end == pri || (*end != ',' && *end != 0))
73                 return -EINVAL;
74
75         *ret = (int) r;
76         return 1;
77 }
78
79 static int add_swap(const char *what, struct mntent *me) {
80         char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
81         FILE *f = NULL;
82         bool noauto, nofail;
83         int r, pri = -1;
84
85         assert(what);
86         assert(me);
87
88         r = mount_find_pri(me, &pri);
89         if (r < 0) {
90                 log_error("Failed to parse priority");
91                 return pri;
92         }
93
94         noauto = !!hasmntopt(me, "noauto");
95         nofail = !!hasmntopt(me, "nofail");
96
97         name = unit_name_from_path(what, ".swap");
98         if (!name) {
99                 log_error("Out of memory");
100                 r = -ENOMEM;
101                 goto finish;
102         }
103
104         unit = join(arg_dest, "/", name, NULL);
105         if (!unit) {
106                 log_error("Out of memory");
107                 r = -ENOMEM;
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 = join(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
148                 if (!lnk) {
149                         log_error("Out of memory");
150                         r = -ENOMEM;
151                         goto finish;
152                 }
153
154                 mkdir_parents_label(lnk, 0755);
155                 if (symlink(unit, lnk) < 0) {
156                         log_error("Failed to create symlink: %m");
157                         r = -errno;
158                         goto finish;
159                 }
160
161                 r = device_name(what, &device);
162                 if (r < 0) {
163                         log_error("Out of memory");
164                         r = -ENOMEM;
165                         goto finish;
166                 }
167
168                 if (r > 0) {
169                         free(lnk);
170                         lnk = join(arg_dest, "/", device, ".wants/", name, NULL);
171                         if (!lnk) {
172                                 log_error("Out of memory");
173                                 r = -ENOMEM;
174                                 goto finish;
175                         }
176
177                         mkdir_parents_label(lnk, 0755);
178                         if (symlink(unit, lnk) < 0) {
179                                 log_error("Failed to create symlink: %m");
180                                 r = -errno;
181                                 goto finish;
182                         }
183                 }
184         }
185
186         r = 0;
187 finish:
188         if (f)
189                 fclose(f);
190
191         free(unit);
192         free(lnk);
193         free(name);
194         free(device);
195
196         return r;
197 }
198
199 static bool mount_is_bind(struct mntent *me) {
200         assert(me);
201
202         return
203                 hasmntopt(me, "bind") ||
204                 streq(me->mnt_opts, "bind");
205 }
206
207 static bool mount_is_network(struct mntent *me) {
208         assert(me);
209
210         return
211                 hasmntopt(me, "_netdev") ||
212                 fstype_is_network(me->mnt_type);
213 }
214
215 static int add_mount(const char *what, const char *where, struct mntent *me) {
216         char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL, *automount_name = NULL, *automount_unit = NULL;
217         FILE *f = NULL;
218         bool noauto, nofail, automount, isbind, isnetwork;
219         int r;
220         const char *post, *pre;
221
222         assert(what);
223         assert(where);
224         assert(me);
225
226         if (streq(me->mnt_type, "autofs"))
227                 return 0;
228
229         if (!is_path(where)) {
230                 log_warning("Mount point %s is not a valid path, ignoring.", where);
231                 return 0;
232         }
233
234         if (mount_point_is_api(where) ||
235             mount_point_ignore(where))
236                 return 0;
237
238         isnetwork = mount_is_network(me);
239         isbind = mount_is_bind(me);
240
241         noauto = !!hasmntopt(me, "noauto");
242         nofail = !!hasmntopt(me, "nofail");
243         automount =
244                 hasmntopt(me, "comment=systemd.automount") ||
245                 hasmntopt(me, "x-systemd.automount");
246
247         if (isnetwork) {
248                 post = SPECIAL_REMOTE_FS_TARGET;
249                 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
250         } else {
251                 post = SPECIAL_LOCAL_FS_TARGET;
252                 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
253         }
254
255         name = unit_name_from_path(where, ".mount");
256         if (!name)  {
257                 log_error("Out of memory");
258                 r = -ENOMEM;
259                 goto finish;
260         }
261
262         unit = join(arg_dest, "/", name, NULL);
263         if (!unit) {
264                 log_error("Out of memory");
265                 r = -ENOMEM;
266                 goto finish;
267         }
268
269         f = fopen(unit, "wxe");
270         if (!f) {
271                 r = -errno;
272                 log_error("Failed to create unit file: %m");
273                 goto finish;
274         }
275
276         fputs("# Automatically generated by systemd-fstab-generator\n\n"
277               "[Unit]\n"
278               "SourcePath=/etc/fstab\n"
279               "DefaultDependencies=no\n", f);
280
281         if (!path_equal(where, "/"))
282                 fprintf(f,
283                         "After=%s\n"
284                         "Wants=%s\n"
285                         "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
286                         "Before=" SPECIAL_UMOUNT_TARGET "\n",
287                         pre,
288                         pre);
289
290
291         if (!noauto && !nofail && !automount)
292                 fprintf(f,
293                         "Before=%s\n",
294                         post);
295
296         fprintf(f,
297                 "\n"
298                 "[Mount]\n"
299                 "What=%s\n"
300                 "Where=%s\n"
301                 "Type=%s\n"
302                 "FsckPassNo=%i\n",
303                 what,
304                 where,
305                 me->mnt_type,
306                 me->mnt_passno);
307
308         if (!isempty(me->mnt_opts) &&
309             !streq(me->mnt_opts, "defaults"))
310                 fprintf(f,
311                         "Options=%s\n",
312                         me->mnt_opts);
313
314         fflush(f);
315         if (ferror(f)) {
316                 log_error("Failed to write unit file: %m");
317                 r = -errno;
318                 goto finish;
319         }
320
321         if (!noauto) {
322                 lnk = join(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
323                 if (!lnk) {
324                         log_error("Out of memory");
325                         r = -ENOMEM;
326                         goto finish;
327                 }
328
329                 mkdir_parents_label(lnk, 0755);
330                 if (symlink(unit, lnk) < 0) {
331                         log_error("Failed to create symlink: %m");
332                         r = -errno;
333                         goto finish;
334                 }
335
336                 if (!isbind &&
337                     !path_equal(where, "/")) {
338
339                         r = device_name(what, &device);
340                         if (r < 0) {
341                                 log_error("Out of memory");
342                                 r = -ENOMEM;
343                                 goto finish;
344                         }
345
346                         if (r > 0) {
347                                 free(lnk);
348                                 lnk = join(arg_dest, "/", device, ".wants/", name, NULL);
349                                 if (!lnk) {
350                                         log_error("Out of memory");
351                                         r = -ENOMEM;
352                                         goto finish;
353                                 }
354
355                                 mkdir_parents_label(lnk, 0755);
356                                 if (symlink(unit, lnk) < 0) {
357                                         log_error("Failed to creat symlink: %m");
358                                         r = -errno;
359                                         goto finish;
360                                 }
361                         }
362                 }
363         }
364
365         if (automount && !path_equal(where, "/")) {
366                 automount_name = unit_name_from_path(where, ".automount");
367                 if (!name) {
368                         log_error("Out of memory");
369                         r = -ENOMEM;
370                         goto finish;
371                 }
372
373                 automount_unit = join(arg_dest, "/", automount_name, NULL);
374                 if (!automount_unit) {
375                         log_error("Out of memory");
376                         r = -ENOMEM;
377                         goto finish;
378                 }
379
380                 fclose(f);
381                 f = fopen(automount_unit, "wxe");
382                 if (!f) {
383                         r = -errno;
384                         log_error("Failed to create unit file: %m");
385                         goto finish;
386                 }
387
388                 fprintf(f,
389                         "# Automatically generated by systemd-fstab-generator\n\n"
390                         "[Unit]\n"
391                         "SourcePath=/etc/fstab\n"
392                         "DefaultDependencies=no\n"
393                         "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
394                         "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
395                         "\n"
396                         "[Automount]\n"
397                         "Where=%s\n",
398                         post,
399                         where);
400
401                 fflush(f);
402                 if (ferror(f)) {
403                         log_error("Failed to write unit file: %m");
404                         r = -errno;
405                         goto finish;
406                 }
407
408                 free(lnk);
409                 lnk = join(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
410                 if (!lnk) {
411                         log_error("Out of memory");
412                         r = -ENOMEM;
413                         goto finish;
414                 }
415
416                 mkdir_parents_label(lnk, 0755);
417                 if (symlink(automount_unit, lnk) < 0) {
418                         log_error("Failed to create symlink: %m");
419                         r = -errno;
420                         goto finish;
421                 }
422         }
423
424         r = 0;
425 finish:
426         if (f)
427                 fclose(f);
428
429         free(unit);
430         free(lnk);
431         free(name);
432         free(device);
433         free(automount_name);
434         free(automount_unit);
435
436         return r;
437 }
438
439 static int parse_fstab(void) {
440         FILE *f;
441         int r = 0;
442         struct mntent *me;
443
444         errno = 0;
445         f = setmntent("/etc/fstab", "r");
446         if (!f) {
447                 if (errno == ENOENT)
448                         return 0;
449
450                 log_error("Failed to open /etc/fstab: %m");
451                 return -errno;
452         }
453
454         while ((me = getmntent(f))) {
455                 char *where, *what;
456                 int k;
457
458                 what = fstab_node_to_udev_node(me->mnt_fsname);
459                 if (!what) {
460                         log_error("Out of memory");
461                         r = -ENOMEM;
462                         goto finish;
463                 }
464
465                 where = strdup(me->mnt_dir);
466                 if (!where) {
467                         log_error("Out of memory");
468                         free(what);
469                         r = -ENOMEM;
470                         goto finish;
471                 }
472
473                 if (path_is_absolute(what))
474                         path_kill_slashes(what);
475
476                 if (path_is_absolute(where))
477                         path_kill_slashes(where);
478
479                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
480
481                 if (streq(me->mnt_type, "swap"))
482                         k = add_swap(what, me);
483                 else
484                         k = add_mount(what, where, me);
485
486                 free(what);
487                 free(where);
488
489                 if (k < 0)
490                         r = k;
491         }
492
493 finish:
494         endmntent(f);
495         return r;
496 }
497
498 int main(int argc, char *argv[]) {
499         int r;
500
501         if (argc > 1 && argc != 4) {
502                 log_error("This program takes three or no arguments.");
503                 return EXIT_FAILURE;
504         }
505
506         if (argc > 1)
507                 arg_dest = argv[1];
508
509         log_set_target(LOG_TARGET_SAFE);
510         log_parse_environment();
511         log_open();
512
513         umask(0022);
514
515         r = parse_fstab();
516
517         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
518 }