chiark / gitweb /
execute: drop now unnecessary GCC pragma again
[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("node-", 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                         return 0;
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("rootfs.mount");
201         else
202                 e = unit_name_escape_path("fs-", 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         return 0;
256
257 fail:
258         if (delete && u)
259                 unit_free(u);
260
261         return 0;
262 }
263
264 static char *fstab_node_to_udev_node(char *p) {
265         char *dn, *t;
266         int r;
267
268         /* FIXME: to follow udev's logic 100% we need to leave valid
269          * UTF8 chars unescaped */
270
271         if (startswith(p, "LABEL=")) {
272
273                 if (!(t = xescape(p+6, "/ ")))
274                         return NULL;
275
276                 r = asprintf(&dn, "/dev/disk/by-label/%s", t);
277                 free(t);
278
279                 if (r < 0)
280                         return NULL;
281
282                 return dn;
283         }
284
285         if (startswith(p, "UUID=")) {
286
287                 if (!(t = xescape(p+5, "/ ")))
288                         return NULL;
289
290                 r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t));
291                 free(t);
292
293                 if (r < 0)
294                         return NULL;
295
296                 return dn;
297         }
298
299         return strdup(p);
300 }
301
302 static int mount_load_etc_fstab(Manager *m, bool set_flags) {
303         FILE *f;
304         int r;
305         struct mntent* me;
306
307         assert(m);
308
309         errno = 0;
310         if (!(f = setmntent("/etc/fstab", "r")))
311                 return -errno;
312
313         while ((me = getmntent(f))) {
314                 char *where, *what;
315
316                 if (!(what = fstab_node_to_udev_node(me->mnt_fsname))) {
317                         r = -ENOMEM;
318                         goto finish;
319                 }
320
321                 if (!(where = strdup(me->mnt_dir))) {
322                         free(what);
323                         r = -ENOMEM;
324                         goto finish;
325                 }
326
327                 if (what[0] == '/')
328                         path_kill_slashes(what);
329
330                 if (where[0] == '/')
331                         path_kill_slashes(where);
332
333                 r = mount_add_one(m, what, where, false, set_flags);
334                 free(what);
335                 free(where);
336
337                 if (r < 0)
338                         goto finish;
339         }
340
341         r = 0;
342 finish:
343
344         endmntent(f);
345         return r;
346 }
347
348 static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
349         int r;
350
351         assert(m);
352
353         rewind(m->proc_self_mountinfo);
354
355         for (;;) {
356                 int k;
357                 char *device, *path, *d, *p;
358
359                 if ((k = fscanf(m->proc_self_mountinfo,
360                                 "%*s "       /* (1) mount id */
361                                 "%*s "       /* (2) parent id */
362                                 "%*s "       /* (3) major:minor */
363                                 "%*s "       /* (4) root */
364                                 "%ms "       /* (5) mount point */
365                                 "%*s"        /* (6) mount options */
366                                 "%*[^-]"     /* (7) optional fields */
367                                 "- "         /* (8) seperator */
368                                 "%*s "       /* (9) file system type */
369                                 "%ms"        /* (10) mount source */
370                                 "%*[^\n]",   /* some rubbish at the end */
371                                 &path,
372                                 &device)) != 2) {
373
374                         if (k == EOF)
375                                 break;
376
377                         return -EBADMSG;
378                 }
379
380                 if (!(d = cunescape(device))) {
381                         free(device);
382                         free(path);
383                         return -ENOMEM;
384                 }
385                 free(device);
386
387                 if (!(p = cunescape(path))) {
388                         free(d);
389                         free(path);
390                         return -ENOMEM;
391                 }
392                 free(path);
393
394                 r = mount_add_one(m, d, p, true, set_flags);
395                 free(d);
396                 free(p);
397
398                 if (r < 0)
399                         return r;
400         }
401
402         return 0;
403 }
404
405 static int mount_enumerate(Manager *m) {
406         int r;
407         struct epoll_event ev;
408         assert(m);
409
410         if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "r")))
411                 return -errno;
412
413         m->mount_watch.type = WATCH_MOUNT;
414         m->mount_watch.fd = fileno(m->proc_self_mountinfo);
415
416         zero(ev);
417         ev.events = EPOLLERR;
418         ev.data.ptr = &m->mount_watch;
419
420         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
421                 return -errno;
422
423         if ((r = mount_load_etc_fstab(m, false)) < 0)
424                 goto fail;
425
426         if ((r = mount_load_proc_self_mountinfo(m, false)) < 0)
427                 goto fail;
428
429         return 0;
430
431 fail:
432         mount_shutdown(m);
433         return r;
434 }
435
436 void mount_fd_event(Manager *m, int events) {
437         Meta *meta;
438         int r;
439
440         assert(m);
441         assert(events == EPOLLERR);
442
443         /* The manager calls this for every fd event happening on the
444          * /proc/self/mountinfo file, which informs us about mounting
445          * table changes */
446
447         if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) {
448                 log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-errno));
449                 return;
450         }
451
452         manager_dispatch_load_queue(m);
453
454         LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_MOUNT]) {
455                 Mount *mount = (Mount*) meta;
456
457                 if (mount->just_created && mount->state == MOUNT_DEAD)
458                         mount_set_state(mount, MOUNT_MOUNTED);
459                 else if (!mount->still_exists && mount->state == MOUNT_MOUNTED) {
460                         mount_set_state(mount, MOUNT_DEAD);
461                         mount->from_proc_self_mountinfo = false;
462                 }
463
464                 /* Clear the flags for later calls */
465                 mount->just_created = false;
466                 mount->still_exists = false;
467         }
468 }
469
470 const UnitVTable mount_vtable = {
471         .suffix = ".mount",
472
473         .init = unit_load_fragment_and_dropin,
474         .done = mount_done,
475         .coldplug = mount_coldplug,
476
477         .dump = mount_dump,
478
479         .active_state = mount_active_state,
480
481         .enumerate = mount_enumerate,
482         .shutdown = mount_shutdown
483 };