chiark / gitweb /
build-sys: automatically detect SysV init dirs
[elogind.git] / mount.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <mntent.h>
25 #include <sys/epoll.h>
26 #include <sys/poll.h>
27
28 #include "unit.h"
29 #include "mount.h"
30 #include "load-fragment.h"
31 #include "load-dropin.h"
32 #include "log.h"
33
34 static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
35         [MOUNT_DEAD] = UNIT_INACTIVE,
36         [MOUNT_MOUNTING] = UNIT_ACTIVATING,
37         [MOUNT_MOUNTED] = UNIT_ACTIVE,
38         [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
39         [MOUNT_MAINTAINANCE] = UNIT_INACTIVE,
40 };
41
42 static const char* const state_string_table[_MOUNT_STATE_MAX] = {
43         [MOUNT_DEAD] = "dead",
44         [MOUNT_MOUNTING] = "mounting",
45         [MOUNT_MOUNTED] = "mounted",
46         [MOUNT_UNMOUNTING] = "unmounting",
47         [MOUNT_MAINTAINANCE] = "maintainance"
48 };
49
50 static void mount_done(Unit *u) {
51         Mount *m = MOUNT(u);
52
53         assert(m);
54         free(m->what);
55         free(m->where);
56 }
57
58 static void mount_set_state(Mount *m, MountState state) {
59         MountState old_state;
60         assert(m);
61
62         if (state == m->state)
63                 return;
64
65         old_state = m->state;
66         m->state = state;
67
68         log_debug("%s changed %s → %s", unit_id(UNIT(m)), state_string_table[old_state], state_string_table[state]);
69
70         unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state]);
71 }
72
73 static int mount_coldplug(Unit *u) {
74         Mount *m = MOUNT(u);
75
76         assert(m);
77         assert(m->state == MOUNT_DEAD);
78
79         if (m->from_proc_self_mountinfo)
80                 mount_set_state(m, MOUNT_MOUNTED);
81
82         return 0;
83 }
84
85 static void mount_dump(Unit *u, FILE *f, const char *prefix) {
86         Mount *s = MOUNT(u);
87
88         assert(s);
89
90         fprintf(f,
91                 "%sMount State: %s\n"
92                 "%sWhere: %s\n"
93                 "%sWhat: %s\n"
94                 "%sFrom /etc/fstab: %s\n"
95                 "%sFrom /proc/self/mountinfo: %s\n",
96                 prefix, state_string_table[s->state],
97                 prefix, s->where,
98                 prefix, s->what,
99                 prefix, yes_no(s->from_etc_fstab),
100                 prefix, yes_no(s->from_proc_self_mountinfo));
101 }
102
103 static UnitActiveState mount_active_state(Unit *u) {
104         assert(u);
105
106         return state_translation_table[MOUNT(u)->state];
107 }
108
109 static void mount_shutdown(Manager *m) {
110         assert(m);
111
112         if (m->proc_self_mountinfo)
113                 fclose(m->proc_self_mountinfo);
114 }
115
116 static int mount_add_node_links(Mount *m) {
117         Unit *device;
118         char *e;
119         int r;
120
121         assert(m);
122
123         /* Adds in links to the device that this node is based on */
124
125         if (!path_startswith(m->what, "/dev/"))
126                 return 0;
127
128         if (!(e = unit_name_escape_path(m->what+1, ".device")))
129                 return -ENOMEM;
130
131         r = manager_load_unit(UNIT(m)->meta.manager, e, &device);
132         free(e);
133
134         if (r < 0)
135                 return r;
136
137         if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, device)) < 0)
138                 return r;
139
140         if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, device)) < 0)
141                 return r;
142
143         if ((r = unit_add_dependency(device, UNIT_WANTS, UNIT(m))) < 0)
144                 return r;
145
146         return 0;
147 }
148
149 static int mount_add_path_links(Mount *m) {
150         Meta *other;
151         int r;
152
153         /* Adds in link to other mount points, that might lie below or
154          * above us in the hierarchy */
155
156         LIST_FOREACH(units_per_type, other, UNIT(m)->meta.manager->units_per_type[UNIT_MOUNT]) {
157                 Mount *n;
158
159                 n = (Mount*) other;
160
161                 if (n == m)
162                         continue;
163
164                 if (path_startswith(m->where, n->where)) {
165
166                         if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, (Unit*) other)) < 0)
167                                 return r;
168
169                         if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, (Unit*) other)) < 0)
170                                 return r;
171
172                 } else if (startswith(n->where, m->where)) {
173
174                         if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, (Unit*) other)) < 0)
175                                 return r;
176
177                         if ((r = unit_add_dependency((Unit*) other, UNIT_REQUIRES, UNIT(m))) < 0)
178                                 return r;
179                 }
180         }
181
182         return 0;
183 }
184
185 static int mount_add_one(Manager *m, const char *what, const char *where, bool live, bool set_flags) {
186         char *e;
187         int r;
188         Unit *u;
189         bool delete;
190
191         assert(m);
192         assert(what);
193         assert(where);
194
195         /* probably some kind of swap, which we don't cover for now */
196         if (where[0] != '/')
197                 return 0;
198
199         if (streq(where, "/"))
200                 e = strdup("-.mount");
201         else
202                 e = unit_name_escape_path(where+1, ".mount");
203
204         if (!e)
205                 return -ENOMEM;
206
207         if (!(u = manager_get_unit(m, e))) {
208                 delete = true;
209
210                 if (!(u = unit_new(m))) {
211                         free(e);
212                         return -ENOMEM;
213                 }
214
215                 r = unit_add_name(u, e);
216                 free(e);
217
218                 if (r < 0)
219                         goto fail;
220
221                 if (!(MOUNT(u)->what = strdup(what)) ||
222                     !(MOUNT(u)->where = strdup(where))) {
223                             r = -ENOMEM;
224                             goto fail;
225                     }
226
227                 if ((r = unit_set_description(u, where)) < 0)
228                         goto fail;
229
230                 unit_add_to_load_queue(u);
231         } else {
232                 delete = false;
233                 free(e);
234         }
235
236         if (set_flags)
237                 MOUNT(u)->still_exists = true;
238
239         if (live) {
240                 if (set_flags)
241                         MOUNT(u)->just_created = !MOUNT(u)->from_proc_self_mountinfo;
242                 MOUNT(u)->from_proc_self_mountinfo = true;
243         } else {
244                 if (set_flags)
245                         MOUNT(u)->just_created = !MOUNT(u)->from_etc_fstab;
246                 MOUNT(u)->from_etc_fstab = true;
247         }
248
249         if ((r = mount_add_node_links(MOUNT(u))) < 0)
250                 goto fail;
251
252         if ((r = mount_add_path_links(MOUNT(u))) < 0)
253                 goto fail;
254
255         unit_add_to_dbus_queue(u);
256
257         return 0;
258
259 fail:
260         if (delete && u)
261                 unit_free(u);
262
263         return 0;
264 }
265
266 static char *fstab_node_to_udev_node(char *p) {
267         char *dn, *t;
268         int r;
269
270         /* FIXME: to follow udev's logic 100% we need to leave valid
271          * UTF8 chars unescaped */
272
273         if (startswith(p, "LABEL=")) {
274
275                 if (!(t = xescape(p+6, "/ ")))
276                         return NULL;
277
278                 r = asprintf(&dn, "/dev/disk/by-label/%s", t);
279                 free(t);
280
281                 if (r < 0)
282                         return NULL;
283
284                 return dn;
285         }
286
287         if (startswith(p, "UUID=")) {
288
289                 if (!(t = xescape(p+5, "/ ")))
290                         return NULL;
291
292                 r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t));
293                 free(t);
294
295                 if (r < 0)
296                         return NULL;
297
298                 return dn;
299         }
300
301         return strdup(p);
302 }
303
304 static int mount_load_etc_fstab(Manager *m, bool set_flags) {
305         FILE *f;
306         int r;
307         struct mntent* me;
308
309         assert(m);
310
311         errno = 0;
312         if (!(f = setmntent("/etc/fstab", "r")))
313                 return -errno;
314
315         while ((me = getmntent(f))) {
316                 char *where, *what;
317
318                 if (!(what = fstab_node_to_udev_node(me->mnt_fsname))) {
319                         r = -ENOMEM;
320                         goto finish;
321                 }
322
323                 if (!(where = strdup(me->mnt_dir))) {
324                         free(what);
325                         r = -ENOMEM;
326                         goto finish;
327                 }
328
329                 if (what[0] == '/')
330                         path_kill_slashes(what);
331
332                 if (where[0] == '/')
333                         path_kill_slashes(where);
334
335                 r = mount_add_one(m, what, where, false, set_flags);
336                 free(what);
337                 free(where);
338
339                 if (r < 0)
340                         goto finish;
341         }
342
343         r = 0;
344 finish:
345
346         endmntent(f);
347         return r;
348 }
349
350 static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
351         int r;
352
353         assert(m);
354
355         rewind(m->proc_self_mountinfo);
356
357         for (;;) {
358                 int k;
359                 char *device, *path, *d, *p;
360
361                 if ((k = fscanf(m->proc_self_mountinfo,
362                                 "%*s "       /* (1) mount id */
363                                 "%*s "       /* (2) parent id */
364                                 "%*s "       /* (3) major:minor */
365                                 "%*s "       /* (4) root */
366                                 "%ms "       /* (5) mount point */
367                                 "%*s"        /* (6) mount options */
368                                 "%*[^-]"     /* (7) optional fields */
369                                 "- "         /* (8) seperator */
370                                 "%*s "       /* (9) file system type */
371                                 "%ms"        /* (10) mount source */
372                                 "%*[^\n]",   /* some rubbish at the end */
373                                 &path,
374                                 &device)) != 2) {
375
376                         if (k == EOF)
377                                 break;
378
379                         return -EBADMSG;
380                 }
381
382                 if (!(d = cunescape(device))) {
383                         free(device);
384                         free(path);
385                         return -ENOMEM;
386                 }
387                 free(device);
388
389                 if (!(p = cunescape(path))) {
390                         free(d);
391                         free(path);
392                         return -ENOMEM;
393                 }
394                 free(path);
395
396                 r = mount_add_one(m, d, p, true, set_flags);
397                 free(d);
398                 free(p);
399
400                 if (r < 0)
401                         return r;
402         }
403
404         return 0;
405 }
406
407 static int mount_enumerate(Manager *m) {
408         int r;
409         struct epoll_event ev;
410         assert(m);
411
412         if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "r")))
413                 return -errno;
414
415         m->mount_watch.type = WATCH_MOUNT;
416         m->mount_watch.fd = fileno(m->proc_self_mountinfo);
417
418         zero(ev);
419         ev.events = EPOLLERR;
420         ev.data.ptr = &m->mount_watch;
421
422         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
423                 return -errno;
424
425         if ((r = mount_load_etc_fstab(m, false)) < 0)
426                 goto fail;
427
428         if ((r = mount_load_proc_self_mountinfo(m, false)) < 0)
429                 goto fail;
430
431         return 0;
432
433 fail:
434         mount_shutdown(m);
435         return r;
436 }
437
438 void mount_fd_event(Manager *m, int events) {
439         Meta *meta;
440         int r;
441
442         assert(m);
443         assert(events == EPOLLERR);
444
445         /* The manager calls this for every fd event happening on the
446          * /proc/self/mountinfo file, which informs us about mounting
447          * table changes */
448
449         if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) {
450                 log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-errno));
451                 return;
452         }
453
454         manager_dispatch_load_queue(m);
455
456         LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_MOUNT]) {
457                 Mount *mount = (Mount*) meta;
458
459                 if (mount->just_created && mount->state == MOUNT_DEAD)
460                         mount_set_state(mount, MOUNT_MOUNTED);
461                 else if (!mount->still_exists && mount->state == MOUNT_MOUNTED) {
462                         mount_set_state(mount, MOUNT_DEAD);
463                         mount->from_proc_self_mountinfo = false;
464                 }
465
466                 /* Clear the flags for later calls */
467                 mount->just_created = false;
468                 mount->still_exists = false;
469         }
470 }
471
472 const UnitVTable mount_vtable = {
473         .suffix = ".mount",
474
475         .init = unit_load_fragment_and_dropin_optional,
476         .done = mount_done,
477         .coldplug = mount_coldplug,
478
479         .dump = mount_dump,
480
481         .active_state = mount_active_state,
482
483         .enumerate = mount_enumerate,
484         .shutdown = mount_shutdown
485 };