]>
Commit | Line | Data |
---|---|---|
5cb5a6ff LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
a7334b09 LP |
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 | ||
5cb5a6ff | 22 | #include <errno.h> |
b08d03ff LP |
23 | #include <stdio.h> |
24 | #include <mntent.h> | |
ef734fd6 LP |
25 | #include <sys/epoll.h> |
26 | #include <sys/poll.h> | |
5cb5a6ff | 27 | |
87f0e418 | 28 | #include "unit.h" |
5cb5a6ff LP |
29 | #include "mount.h" |
30 | #include "load-fragment.h" | |
5cb5a6ff | 31 | #include "load-dropin.h" |
b08d03ff | 32 | #include "log.h" |
5cb5a6ff | 33 | |
f50e0a01 LP |
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 | }; | |
5cb5a6ff | 41 | |
f50e0a01 LP |
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 | }; | |
5cb5a6ff | 49 | |
87f0e418 | 50 | static void mount_done(Unit *u) { |
ef734fd6 | 51 | Mount *m = MOUNT(u); |
034c6ed7 | 52 | |
ef734fd6 LP |
53 | assert(m); |
54 | free(m->what); | |
55 | free(m->where); | |
034c6ed7 LP |
56 | } |
57 | ||
f50e0a01 LP |
58 | static void mount_set_state(Mount *m, MountState state) { |
59 | MountState old_state; | |
60 | assert(m); | |
61 | ||
ef734fd6 LP |
62 | if (state == m->state) |
63 | return; | |
64 | ||
f50e0a01 LP |
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); | |
5cb5a6ff | 78 | |
f50e0a01 LP |
79 | if (m->from_proc_self_mountinfo) |
80 | mount_set_state(m, MOUNT_MOUNTED); | |
5cb5a6ff | 81 | |
f50e0a01 LP |
82 | return 0; |
83 | } | |
84 | ||
85 | static void mount_dump(Unit *u, FILE *f, const char *prefix) { | |
87f0e418 | 86 | Mount *s = MOUNT(u); |
5cb5a6ff LP |
87 | |
88 | assert(s); | |
89 | ||
90 | fprintf(f, | |
91 | "%sMount State: %s\n" | |
b08d03ff | 92 | "%sWhere: %s\n" |
f50e0a01 LP |
93 | "%sWhat: %s\n" |
94 | "%sFrom /etc/fstab: %s\n" | |
95 | "%sFrom /proc/self/mountinfo: %s\n", | |
96 | prefix, state_string_table[s->state], | |
b08d03ff | 97 | prefix, s->where, |
f50e0a01 LP |
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]; | |
b08d03ff LP |
107 | } |
108 | ||
109 | static void mount_shutdown(Manager *m) { | |
ef734fd6 LP |
110 | assert(m); |
111 | ||
112 | if (m->proc_self_mountinfo) | |
113 | fclose(m->proc_self_mountinfo); | |
b08d03ff LP |
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 | ||
2e478a46 | 128 | if (!(e = unit_name_escape_path(m->what+1, ".device"))) |
b08d03ff LP |
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) { | |
ef734fd6 | 150 | Meta *other; |
b08d03ff LP |
151 | int r; |
152 | ||
153 | /* Adds in link to other mount points, that might lie below or | |
154 | * above us in the hierarchy */ | |
155 | ||
ef734fd6 | 156 | LIST_FOREACH(units_per_type, other, UNIT(m)->meta.manager->units_per_type[UNIT_MOUNT]) { |
b08d03ff LP |
157 | Mount *n; |
158 | ||
ef734fd6 | 159 | n = (Mount*) other; |
b08d03ff | 160 | |
ef734fd6 LP |
161 | if (n == m) |
162 | return 0; | |
b08d03ff LP |
163 | |
164 | if (path_startswith(m->where, n->where)) { | |
165 | ||
ef734fd6 | 166 | if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, (Unit*) other)) < 0) |
b08d03ff LP |
167 | return r; |
168 | ||
ef734fd6 | 169 | if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, (Unit*) other)) < 0) |
b08d03ff LP |
170 | return r; |
171 | ||
172 | } else if (startswith(n->where, m->where)) { | |
173 | ||
ef734fd6 | 174 | if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, (Unit*) other)) < 0) |
b08d03ff LP |
175 | return r; |
176 | ||
ef734fd6 | 177 | if ((r = unit_add_dependency((Unit*) other, UNIT_REQUIRES, UNIT(m))) < 0) |
b08d03ff LP |
178 | return r; |
179 | } | |
180 | } | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
ef734fd6 | 185 | static int mount_add_one(Manager *m, const char *what, const char *where, bool live, bool set_flags) { |
b08d03ff LP |
186 | char *e; |
187 | int r; | |
188 | Unit *u; | |
189 | bool delete; | |
190 | ||
f50e0a01 | 191 | assert(m); |
b08d03ff LP |
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, "/")) | |
2e478a46 | 200 | e = strdup("-.mount"); |
b08d03ff | 201 | else |
2e478a46 | 202 | e = unit_name_escape_path(where+1, ".mount"); |
b08d03ff LP |
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)) || | |
f50e0a01 LP |
222 | !(MOUNT(u)->where = strdup(where))) { |
223 | r = -ENOMEM; | |
224 | goto fail; | |
225 | } | |
226 | ||
227 | if ((r = unit_set_description(u, where)) < 0) | |
b08d03ff | 228 | goto fail; |
ef734fd6 | 229 | |
f94ea366 | 230 | unit_add_to_load_queue(u); |
b08d03ff LP |
231 | } else { |
232 | delete = false; | |
233 | free(e); | |
234 | } | |
235 | ||
ef734fd6 LP |
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; | |
f50e0a01 | 242 | MOUNT(u)->from_proc_self_mountinfo = true; |
ef734fd6 LP |
243 | } else { |
244 | if (set_flags) | |
245 | MOUNT(u)->just_created = !MOUNT(u)->from_etc_fstab; | |
f50e0a01 | 246 | MOUNT(u)->from_etc_fstab = true; |
ef734fd6 | 247 | } |
f50e0a01 | 248 | |
b08d03ff LP |
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 | ||
c1e1601e LP |
255 | unit_add_to_dbus_queue(u); |
256 | ||
b08d03ff LP |
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 | ||
f50e0a01 | 275 | if (!(t = xescape(p+6, "/ "))) |
b08d03ff LP |
276 | return NULL; |
277 | ||
f50e0a01 | 278 | r = asprintf(&dn, "/dev/disk/by-label/%s", t); |
b08d03ff LP |
279 | free(t); |
280 | ||
281 | if (r < 0) | |
282 | return NULL; | |
283 | ||
284 | return dn; | |
285 | } | |
286 | ||
287 | if (startswith(p, "UUID=")) { | |
288 | ||
f50e0a01 | 289 | if (!(t = xescape(p+5, "/ "))) |
b08d03ff LP |
290 | return NULL; |
291 | ||
f50e0a01 | 292 | r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t)); |
b08d03ff LP |
293 | free(t); |
294 | ||
295 | if (r < 0) | |
296 | return NULL; | |
297 | ||
298 | return dn; | |
299 | } | |
300 | ||
301 | return strdup(p); | |
302 | } | |
303 | ||
ef734fd6 | 304 | static int mount_load_etc_fstab(Manager *m, bool set_flags) { |
b08d03ff LP |
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 | ||
ef734fd6 | 335 | r = mount_add_one(m, what, where, false, set_flags); |
b08d03ff LP |
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 | ||
ef734fd6 | 350 | static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { |
b08d03ff LP |
351 | int r; |
352 | ||
353 | assert(m); | |
354 | ||
ef734fd6 | 355 | rewind(m->proc_self_mountinfo); |
b08d03ff LP |
356 | |
357 | for (;;) { | |
358 | int k; | |
359 | char *device, *path, *d, *p; | |
360 | ||
ef734fd6 | 361 | if ((k = fscanf(m->proc_self_mountinfo, |
b08d03ff LP |
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 */ | |
ef734fd6 LP |
371 | "%ms" /* (10) mount source */ |
372 | "%*[^\n]", /* some rubbish at the end */ | |
b08d03ff LP |
373 | &path, |
374 | &device)) != 2) { | |
375 | ||
ef734fd6 LP |
376 | if (k == EOF) |
377 | break; | |
b08d03ff | 378 | |
ef734fd6 | 379 | return -EBADMSG; |
b08d03ff LP |
380 | } |
381 | ||
382 | if (!(d = cunescape(device))) { | |
383 | free(device); | |
384 | free(path); | |
ef734fd6 | 385 | return -ENOMEM; |
b08d03ff LP |
386 | } |
387 | free(device); | |
388 | ||
389 | if (!(p = cunescape(path))) { | |
390 | free(d); | |
391 | free(path); | |
ef734fd6 | 392 | return -ENOMEM; |
b08d03ff LP |
393 | } |
394 | free(path); | |
395 | ||
ef734fd6 | 396 | r = mount_add_one(m, d, p, true, set_flags); |
b08d03ff LP |
397 | free(d); |
398 | free(p); | |
399 | ||
400 | if (r < 0) | |
ef734fd6 | 401 | return r; |
b08d03ff LP |
402 | } |
403 | ||
ef734fd6 | 404 | return 0; |
b08d03ff LP |
405 | } |
406 | ||
407 | static int mount_enumerate(Manager *m) { | |
408 | int r; | |
ef734fd6 | 409 | struct epoll_event ev; |
b08d03ff LP |
410 | assert(m); |
411 | ||
ef734fd6 LP |
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) | |
b08d03ff LP |
426 | goto fail; |
427 | ||
ef734fd6 | 428 | if ((r = mount_load_proc_self_mountinfo(m, false)) < 0) |
b08d03ff LP |
429 | goto fail; |
430 | ||
431 | return 0; | |
432 | ||
433 | fail: | |
434 | mount_shutdown(m); | |
435 | return r; | |
5cb5a6ff LP |
436 | } |
437 | ||
ef734fd6 LP |
438 | void mount_fd_event(Manager *m, int events) { |
439 | Meta *meta; | |
440 | int r; | |
441 | ||
442 | assert(m); | |
f94ea366 | 443 | assert(events == EPOLLERR); |
ef734fd6 LP |
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 | ||
87f0e418 | 472 | const UnitVTable mount_vtable = { |
5cb5a6ff LP |
473 | .suffix = ".mount", |
474 | ||
f50e0a01 | 475 | .init = unit_load_fragment_and_dropin, |
034c6ed7 | 476 | .done = mount_done, |
f50e0a01 LP |
477 | .coldplug = mount_coldplug, |
478 | ||
034c6ed7 | 479 | .dump = mount_dump, |
5cb5a6ff | 480 | |
f50e0a01 | 481 | .active_state = mount_active_state, |
b08d03ff | 482 | |
f50e0a01 LP |
483 | .enumerate = mount_enumerate, |
484 | .shutdown = mount_shutdown | |
5cb5a6ff | 485 | }; |