2 * Copyright (c) 2016 The DragonFly Project
3 * Copyright (c) 2014 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/types.h>
33 #include <sys/mount.h>
34 #include <sys/event.h>
46 #define AUTOUNMOUNTD_PIDFILE "/var/run/autounmountd.pid"
48 struct automounted_fs
{
49 TAILQ_ENTRY(automounted_fs
) af_next
;
53 char af_mountpoint
[MNAMELEN
];
56 static TAILQ_HEAD(, automounted_fs
) automounted
;
58 static struct automounted_fs
*
59 automounted_find(fsid_t fsid
)
61 struct automounted_fs
*af
;
63 TAILQ_FOREACH(af
, &automounted
, af_next
) {
64 if (af
->af_fsid
.val
[0] == fsid
.val
[0] &&
65 af
->af_fsid
.val
[1] == fsid
.val
[1])
72 static struct automounted_fs
*
73 automounted_add(fsid_t fsid
, const char *mountpoint
)
75 struct automounted_fs
*af
;
77 af
= calloc(sizeof(*af
), 1);
80 af
->af_mount_time
= time(NULL
);
82 strlcpy(af
->af_mountpoint
, mountpoint
, sizeof(af
->af_mountpoint
));
84 TAILQ_INSERT_TAIL(&automounted
, af
, af_next
);
90 automounted_remove(struct automounted_fs
*af
)
93 TAILQ_REMOVE(&automounted
, af
, af_next
);
98 refresh_automounted(void)
100 struct automounted_fs
*af
, *tmpaf
;
101 struct statfs
*mntbuf
;
104 nitems
= getmntinfo(&mntbuf
, MNT_WAIT
);
106 log_err(1, "getmntinfo");
108 log_debugx("refreshing list of automounted filesystems");
110 TAILQ_FOREACH(af
, &automounted
, af_next
)
113 for (i
= 0; i
< nitems
; i
++) {
114 if (strcmp(mntbuf
[i
].f_fstypename
, "autofs") == 0) {
115 log_debugx("skipping %s, filesystem type is autofs",
116 mntbuf
[i
].f_mntonname
);
120 if ((mntbuf
[i
].f_flags
& MNT_AUTOMOUNTED
) == 0) {
121 log_debugx("skipping %s, not automounted",
122 mntbuf
[i
].f_mntonname
);
126 af
= automounted_find(mntbuf
[i
].f_fsid
);
128 log_debugx("new automounted filesystem found on %s "
129 "(FSID:%d:%d)", mntbuf
[i
].f_mntonname
,
130 mntbuf
[i
].f_fsid
.val
[0], mntbuf
[i
].f_fsid
.val
[1]);
131 af
= automounted_add(mntbuf
[i
].f_fsid
,
132 mntbuf
[i
].f_mntonname
);
134 log_debugx("already known automounted filesystem "
135 "found on %s (FSID:%d:%d)", mntbuf
[i
].f_mntonname
,
136 mntbuf
[i
].f_fsid
.val
[0], mntbuf
[i
].f_fsid
.val
[1]);
141 TAILQ_FOREACH_SAFE(af
, &automounted
, af_next
, tmpaf
) {
144 log_debugx("lost filesystem mounted on %s (FSID:%d:%d)",
145 af
->af_mountpoint
, af
->af_fsid
.val
[0], af
->af_fsid
.val
[1]);
146 automounted_remove(af
);
151 do_unmount(const fsid_t fsid __unused
, const char *mountpoint
)
154 int error
, isbusy
= 0;
156 error
= unmount(mountpoint
, 0);
158 if (errno
== EBUSY
) {
160 log_debugx("cannot unmount %s: %s",
161 mountpoint
, strerror(errno
));
163 log_warn("cannot unmount %s", mountpoint
);
168 * XXX: Workaround for DragonFly kernel bug.
169 * https://bugs.dragonflybsd.org/issues/2908
172 log_debugx("workaround DragonFly kernel bug via stat(2)");
173 if (stat(mountpoint
, &sb
))
174 log_warn("cannot stat %s", mountpoint
);
181 expire_automounted(double expiration_time
)
183 struct automounted_fs
*af
, *tmpaf
;
185 double mounted_for
, mounted_max
= -1.0;
190 log_debugx("expiring automounted filesystems");
192 TAILQ_FOREACH_SAFE(af
, &automounted
, af_next
, tmpaf
) {
193 mounted_for
= difftime(now
, af
->af_mount_time
);
195 if (mounted_for
< expiration_time
) {
196 log_debugx("skipping %s (FSID:%d:%d), mounted "
197 "for %.0f seconds", af
->af_mountpoint
,
198 af
->af_fsid
.val
[0], af
->af_fsid
.val
[1],
201 if (mounted_for
> mounted_max
)
202 mounted_max
= mounted_for
;
207 log_debugx("filesystem mounted on %s (FSID:%d:%d), "
208 "was mounted for %.0f seconds; unmounting",
209 af
->af_mountpoint
, af
->af_fsid
.val
[0], af
->af_fsid
.val
[1],
211 error
= do_unmount(af
->af_fsid
, af
->af_mountpoint
);
213 if (mounted_for
> mounted_max
)
214 mounted_max
= mounted_for
;
218 return (mounted_max
);
222 usage_autounmountd(void)
225 fprintf(stderr
, "usage: autounmountd [-r time][-t time][-dv]\n");
230 do_wait(int kq
, double sleep_time
)
232 struct timespec timeout
;
233 struct kevent unused
;
236 if (sleep_time
!= -1.0) {
237 assert(sleep_time
> 0.0);
238 timeout
.tv_sec
= sleep_time
;
241 log_debugx("waiting for filesystem event for %.0f seconds", sleep_time
);
242 nevents
= kevent(kq
, NULL
, 0, &unused
, 1, &timeout
);
244 log_debugx("waiting for filesystem event");
245 nevents
= kevent(kq
, NULL
, 0, &unused
, 1, NULL
);
248 log_err(1, "kevent");
251 log_debugx("timeout reached");
252 assert(sleep_time
> 0.0);
254 log_debugx("got filesystem event");
259 main_autounmountd(int argc
, char **argv
)
264 const char *pidfile_path
= AUTOUNMOUNTD_PIDFILE
;
265 int ch
, debug
= 0, error
, kq
;
266 double expiration_time
= 600, retry_time
= 600, mounted_max
, sleep_time
;
267 bool dont_daemonize
= false;
269 while ((ch
= getopt(argc
, argv
, "dr:t:v")) != -1) {
272 dont_daemonize
= true;
276 retry_time
= atoi(optarg
);
279 expiration_time
= atoi(optarg
);
286 usage_autounmountd();
291 usage_autounmountd();
294 log_errx(1, "retry time must be greater than zero");
295 if (expiration_time
<= 0)
296 log_errx(1, "expiration time must be greater than zero");
300 pidfh
= pidfile_open(pidfile_path
, 0600, &otherpid
);
302 if (errno
== EEXIST
) {
303 log_errx(1, "daemon already running, pid: %jd.",
306 log_err(1, "cannot open or create pidfile \"%s\"",
310 if (dont_daemonize
== false) {
311 if (daemon(0, 0) == -1) {
312 log_warn("cannot daemonize");
313 pidfile_remove(pidfh
);
318 pidfile_write(pidfh
);
320 TAILQ_INIT(&automounted
);
324 log_err(1, "kqueue");
326 EV_SET(&event
, 0, EVFILT_FS
, EV_ADD
| EV_CLEAR
, 0, 0, NULL
);
327 error
= kevent(kq
, &event
, 1, NULL
, 0, NULL
);
329 log_err(1, "kevent");
332 refresh_automounted();
333 mounted_max
= expire_automounted(expiration_time
);
334 if (mounted_max
== -1.0) {
335 sleep_time
= mounted_max
;
336 log_debugx("no filesystems to expire");
337 } else if (mounted_max
< expiration_time
) {
338 sleep_time
= difftime(expiration_time
, mounted_max
);
339 log_debugx("some filesystems expire in %.0f seconds",
342 sleep_time
= retry_time
;
343 log_debugx("some expired filesystems remain mounted, "
344 "will retry in %.0f seconds", sleep_time
);
347 do_wait(kq
, sleep_time
);