1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include <sys/epoll.h>
30 #include "load-fragment.h"
31 #include "load-dropin.h"
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
,
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"
50 static void mount_done(Unit
*u
) {
58 static void mount_set_state(Mount
*m
, MountState state
) {
62 if (state
== m
->state
)
68 log_debug("%s changed %s → %s", unit_id(UNIT(m
)), state_string_table
[old_state
], state_string_table
[state
]);
70 unit_notify(UNIT(m
), state_translation_table
[old_state
], state_translation_table
[state
]);
73 static int mount_coldplug(Unit
*u
) {
77 assert(m
->state
== MOUNT_DEAD
);
79 if (m
->from_proc_self_mountinfo
)
80 mount_set_state(m
, MOUNT_MOUNTED
);
85 static void mount_dump(Unit
*u
, FILE *f
, const char *prefix
) {
94 "%sFrom /etc/fstab: %s\n"
95 "%sFrom /proc/self/mountinfo: %s\n",
96 prefix
, state_string_table
[s
->state
],
99 prefix
, yes_no(s
->from_etc_fstab
),
100 prefix
, yes_no(s
->from_proc_self_mountinfo
));
103 static UnitActiveState
mount_active_state(Unit
*u
) {
106 return state_translation_table
[MOUNT(u
)->state
];
109 static void mount_shutdown(Manager
*m
) {
112 if (m
->proc_self_mountinfo
)
113 fclose(m
->proc_self_mountinfo
);
116 static int mount_add_node_links(Mount
*m
) {
123 /* Adds in links to the device that this node is based on */
125 if (!path_startswith(m
->what
, "/dev/"))
128 if (!(e
= unit_name_escape_path("node-", m
->what
+1, ".device")))
131 r
= manager_load_unit(UNIT(m
)->meta
.manager
, e
, &device
);
137 if ((r
= unit_add_dependency(UNIT(m
), UNIT_AFTER
, device
)) < 0)
140 if ((r
= unit_add_dependency(UNIT(m
), UNIT_REQUIRES
, device
)) < 0)
143 if ((r
= unit_add_dependency(device
, UNIT_WANTS
, UNIT(m
))) < 0)
149 static int mount_add_path_links(Mount
*m
) {
153 /* Adds in link to other mount points, that might lie below or
154 * above us in the hierarchy */
156 LIST_FOREACH(units_per_type
, other
, UNIT(m
)->meta
.manager
->units_per_type
[UNIT_MOUNT
]) {
164 if (path_startswith(m
->where
, n
->where
)) {
166 if ((r
= unit_add_dependency(UNIT(m
), UNIT_AFTER
, (Unit
*) other
)) < 0)
169 if ((r
= unit_add_dependency(UNIT(m
), UNIT_REQUIRES
, (Unit
*) other
)) < 0)
172 } else if (startswith(n
->where
, m
->where
)) {
174 if ((r
= unit_add_dependency(UNIT(m
), UNIT_BEFORE
, (Unit
*) other
)) < 0)
177 if ((r
= unit_add_dependency((Unit
*) other
, UNIT_REQUIRES
, UNIT(m
))) < 0)
185 static int mount_add_one(Manager
*m
, const char *what
, const char *where
, bool live
, bool set_flags
) {
195 /* probably some kind of swap, which we don't cover for now */
199 if (streq(where
, "/"))
200 e
= strdup("rootfs.mount");
202 e
= unit_name_escape_path("fs-", where
+1, ".mount");
207 if (!(u
= manager_get_unit(m
, e
))) {
210 if (!(u
= unit_new(m
))) {
215 r
= unit_add_name(u
, e
);
221 if (!(MOUNT(u
)->what
= strdup(what
)) ||
222 !(MOUNT(u
)->where
= strdup(where
))) {
227 if ((r
= unit_set_description(u
, where
)) < 0)
230 unit_add_to_load_queue(u
);
237 MOUNT(u
)->still_exists
= true;
241 MOUNT(u
)->just_created
= !MOUNT(u
)->from_proc_self_mountinfo
;
242 MOUNT(u
)->from_proc_self_mountinfo
= true;
245 MOUNT(u
)->just_created
= !MOUNT(u
)->from_etc_fstab
;
246 MOUNT(u
)->from_etc_fstab
= true;
249 if ((r
= mount_add_node_links(MOUNT(u
))) < 0)
252 if ((r
= mount_add_path_links(MOUNT(u
))) < 0)
255 unit_add_to_dbus_queue(u
);
266 static char *fstab_node_to_udev_node(char *p
) {
270 /* FIXME: to follow udev's logic 100% we need to leave valid
271 * UTF8 chars unescaped */
273 if (startswith(p
, "LABEL=")) {
275 if (!(t
= xescape(p
+6, "/ ")))
278 r
= asprintf(&dn
, "/dev/disk/by-label/%s", t
);
287 if (startswith(p
, "UUID=")) {
289 if (!(t
= xescape(p
+5, "/ ")))
292 r
= asprintf(&dn
, "/dev/disk/by-uuid/%s", ascii_strlower(t
));
304 static int mount_load_etc_fstab(Manager
*m
, bool set_flags
) {
312 if (!(f
= setmntent("/etc/fstab", "r")))
315 while ((me
= getmntent(f
))) {
318 if (!(what
= fstab_node_to_udev_node(me
->mnt_fsname
))) {
323 if (!(where
= strdup(me
->mnt_dir
))) {
330 path_kill_slashes(what
);
333 path_kill_slashes(where
);
335 r
= mount_add_one(m
, what
, where
, false, set_flags
);
350 static int mount_load_proc_self_mountinfo(Manager
*m
, bool set_flags
) {
355 rewind(m
->proc_self_mountinfo
);
359 char *device
, *path
, *d
, *p
;
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 */
382 if (!(d
= cunescape(device
))) {
389 if (!(p
= cunescape(path
))) {
396 r
= mount_add_one(m
, d
, p
, true, set_flags
);
407 static int mount_enumerate(Manager
*m
) {
409 struct epoll_event ev
;
412 if (!(m
->proc_self_mountinfo
= fopen("/proc/self/mountinfo", "r")))
415 m
->mount_watch
.type
= WATCH_MOUNT
;
416 m
->mount_watch
.fd
= fileno(m
->proc_self_mountinfo
);
419 ev
.events
= EPOLLERR
;
420 ev
.data
.ptr
= &m
->mount_watch
;
422 if (epoll_ctl(m
->epoll_fd
, EPOLL_CTL_ADD
, m
->mount_watch
.fd
, &ev
) < 0)
425 if ((r
= mount_load_etc_fstab(m
, false)) < 0)
428 if ((r
= mount_load_proc_self_mountinfo(m
, false)) < 0)
438 void mount_fd_event(Manager
*m
, int events
) {
443 assert(events
== EPOLLERR
);
445 /* The manager calls this for every fd event happening on the
446 * /proc/self/mountinfo file, which informs us about mounting
449 if ((r
= mount_load_proc_self_mountinfo(m
, true)) < 0) {
450 log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-errno
));
454 manager_dispatch_load_queue(m
);
456 LIST_FOREACH(units_per_type
, meta
, m
->units_per_type
[UNIT_MOUNT
]) {
457 Mount
*mount
= (Mount
*) meta
;
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;
466 /* Clear the flags for later calls */
467 mount
->just_created
= false;
468 mount
->still_exists
= false;
472 const UnitVTable mount_vtable
= {
475 .init
= unit_load_fragment_and_dropin
,
477 .coldplug
= mount_coldplug
,
481 .active_state
= mount_active_state
,
483 .enumerate
= mount_enumerate
,
484 .shutdown
= mount_shutdown