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)
264 static char *fstab_node_to_udev_node(char *p
) {
268 /* FIXME: to follow udev's logic 100% we need to leave valid
269 * UTF8 chars unescaped */
271 if (startswith(p
, "LABEL=")) {
273 if (!(t
= xescape(p
+6, "/ ")))
276 r
= asprintf(&dn
, "/dev/disk/by-label/%s", t
);
285 if (startswith(p
, "UUID=")) {
287 if (!(t
= xescape(p
+5, "/ ")))
290 r
= asprintf(&dn
, "/dev/disk/by-uuid/%s", ascii_strlower(t
));
302 static int mount_load_etc_fstab(Manager
*m
, bool set_flags
) {
310 if (!(f
= setmntent("/etc/fstab", "r")))
313 while ((me
= getmntent(f
))) {
316 if (!(what
= fstab_node_to_udev_node(me
->mnt_fsname
))) {
321 if (!(where
= strdup(me
->mnt_dir
))) {
328 path_kill_slashes(what
);
331 path_kill_slashes(where
);
333 r
= mount_add_one(m
, what
, where
, false, set_flags
);
348 static int mount_load_proc_self_mountinfo(Manager
*m
, bool set_flags
) {
353 rewind(m
->proc_self_mountinfo
);
357 char *device
, *path
, *d
, *p
;
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 */
380 if (!(d
= cunescape(device
))) {
387 if (!(p
= cunescape(path
))) {
394 r
= mount_add_one(m
, d
, p
, true, set_flags
);
405 static int mount_enumerate(Manager
*m
) {
407 struct epoll_event ev
;
410 if (!(m
->proc_self_mountinfo
= fopen("/proc/self/mountinfo", "r")))
413 m
->mount_watch
.type
= WATCH_MOUNT
;
414 m
->mount_watch
.fd
= fileno(m
->proc_self_mountinfo
);
417 ev
.events
= EPOLLERR
;
418 ev
.data
.ptr
= &m
->mount_watch
;
420 if (epoll_ctl(m
->epoll_fd
, EPOLL_CTL_ADD
, m
->mount_watch
.fd
, &ev
) < 0)
423 if ((r
= mount_load_etc_fstab(m
, false)) < 0)
426 if ((r
= mount_load_proc_self_mountinfo(m
, false)) < 0)
436 void mount_fd_event(Manager
*m
, int events
) {
441 assert(events
== EPOLLERR
);
443 /* The manager calls this for every fd event happening on the
444 * /proc/self/mountinfo file, which informs us about mounting
447 if ((r
= mount_load_proc_self_mountinfo(m
, true)) < 0) {
448 log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-errno
));
452 manager_dispatch_load_queue(m
);
454 LIST_FOREACH(units_per_type
, meta
, m
->units_per_type
[UNIT_MOUNT
]) {
455 Mount
*mount
= (Mount
*) meta
;
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;
464 /* Clear the flags for later calls */
465 mount
->just_created
= false;
466 mount
->still_exists
= false;
470 const UnitVTable mount_vtable
= {
473 .init
= unit_load_fragment_and_dropin
,
475 .coldplug
= mount_coldplug
,
479 .active_state
= mount_active_state
,
481 .enumerate
= mount_enumerate
,
482 .shutdown
= mount_shutdown