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