Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/autofs/autounmountd.c
105241 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2014 The FreeBSD Foundation
5
*
6
* This software was developed by Edward Tomasz Napierala under sponsorship
7
* from the FreeBSD Foundation.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
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.
17
*
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
28
* SUCH DAMAGE.
29
*
30
*/
31
32
#include <sys/types.h>
33
#include <sys/event.h>
34
#include <sys/mount.h>
35
#include <sys/time.h>
36
#include <assert.h>
37
#include <errno.h>
38
#include <libutil.h>
39
#include <stdbool.h>
40
#include <stdint.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
#include "common.h"
47
48
#define AUTOUNMOUNTD_PIDFILE "/var/run/autounmountd.pid"
49
50
struct automounted_fs {
51
TAILQ_ENTRY(automounted_fs) af_next;
52
time_t af_mount_time;
53
bool af_mark;
54
fsid_t af_fsid;
55
char af_mountpoint[MNAMELEN];
56
};
57
58
static TAILQ_HEAD(, automounted_fs) automounted;
59
60
static struct automounted_fs *
61
automounted_find(fsid_t fsid)
62
{
63
struct automounted_fs *af;
64
65
TAILQ_FOREACH(af, &automounted, af_next) {
66
if (fsidcmp(&af->af_fsid, &fsid) == 0)
67
return (af);
68
}
69
70
return (NULL);
71
}
72
73
static struct automounted_fs *
74
automounted_add(fsid_t fsid, const char *mountpoint)
75
{
76
struct automounted_fs *af;
77
78
af = calloc(1, sizeof(*af));
79
if (af == NULL)
80
log_err(1, "calloc");
81
af->af_mount_time = time(NULL);
82
af->af_fsid = fsid;
83
strlcpy(af->af_mountpoint, mountpoint, sizeof(af->af_mountpoint));
84
85
TAILQ_INSERT_TAIL(&automounted, af, af_next);
86
87
return (af);
88
}
89
90
static void
91
automounted_remove(struct automounted_fs *af)
92
{
93
94
TAILQ_REMOVE(&automounted, af, af_next);
95
free(af);
96
}
97
98
static void
99
refresh_automounted(void)
100
{
101
struct automounted_fs *af, *tmpaf;
102
struct statfs *mntbuf;
103
int i, nitems;
104
105
nitems = getmntinfo(&mntbuf, MNT_WAIT);
106
if (nitems <= 0)
107
log_err(1, "getmntinfo");
108
109
log_debugx("refreshing list of automounted filesystems");
110
111
TAILQ_FOREACH(af, &automounted, af_next)
112
af->af_mark = false;
113
114
for (i = 0; i < nitems; i++) {
115
if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
116
log_debugx("skipping %s, filesystem type is autofs",
117
mntbuf[i].f_mntonname);
118
continue;
119
}
120
121
if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) {
122
log_debugx("skipping %s, not automounted",
123
mntbuf[i].f_mntonname);
124
continue;
125
}
126
127
af = automounted_find(mntbuf[i].f_fsid);
128
if (af == NULL) {
129
log_debugx("new automounted filesystem found on %s "
130
"(FSID:%d:%d)", mntbuf[i].f_mntonname,
131
mntbuf[i].f_fsid.val[0], mntbuf[i].f_fsid.val[1]);
132
af = automounted_add(mntbuf[i].f_fsid,
133
mntbuf[i].f_mntonname);
134
} else {
135
log_debugx("already known automounted filesystem "
136
"found on %s (FSID:%d:%d)", mntbuf[i].f_mntonname,
137
mntbuf[i].f_fsid.val[0], mntbuf[i].f_fsid.val[1]);
138
}
139
af->af_mark = true;
140
}
141
142
TAILQ_FOREACH_SAFE(af, &automounted, af_next, tmpaf) {
143
if (af->af_mark)
144
continue;
145
log_debugx("lost filesystem mounted on %s (FSID:%d:%d)",
146
af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1]);
147
automounted_remove(af);
148
}
149
}
150
151
static int
152
unmount_by_fsid(const fsid_t fsid, const char *mountpoint)
153
{
154
char *fsid_str;
155
int error, ret;
156
157
ret = asprintf(&fsid_str, "FSID:%d:%d", fsid.val[0], fsid.val[1]);
158
if (ret < 0)
159
log_err(1, "asprintf");
160
161
error = unmount(fsid_str, MNT_NONBUSY | MNT_BYFSID);
162
if (error != 0) {
163
if (errno == EBUSY) {
164
log_debugx("cannot unmount %s (%s): %s",
165
mountpoint, fsid_str, strerror(errno));
166
} else {
167
log_warn("cannot unmount %s (%s)",
168
mountpoint, fsid_str);
169
}
170
} else
171
rpc_umntall();
172
173
free(fsid_str);
174
175
return (error);
176
}
177
178
static time_t
179
expire_automounted(time_t expiration_time)
180
{
181
struct automounted_fs *af, *tmpaf;
182
time_t now;
183
time_t mounted_for, mounted_max = -1;
184
int error;
185
186
now = time(NULL);
187
188
log_debugx("expiring automounted filesystems");
189
190
TAILQ_FOREACH_SAFE(af, &automounted, af_next, tmpaf) {
191
mounted_for = difftime(now, af->af_mount_time);
192
193
if (mounted_for < expiration_time) {
194
log_debugx("skipping %s (FSID:%d:%d), mounted "
195
"for %jd seconds", af->af_mountpoint,
196
af->af_fsid.val[0], af->af_fsid.val[1],
197
(intmax_t)mounted_for);
198
199
if (mounted_for > mounted_max)
200
mounted_max = mounted_for;
201
202
continue;
203
}
204
205
log_debugx("filesystem mounted on %s (FSID:%d:%d), "
206
"was mounted for %ld seconds; unmounting",
207
af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1],
208
(long)mounted_for);
209
error = unmount_by_fsid(af->af_fsid, af->af_mountpoint);
210
if (error != 0) {
211
if (mounted_for > mounted_max)
212
mounted_max = mounted_for;
213
}
214
}
215
216
return (mounted_max);
217
}
218
219
static void
220
usage_autounmountd(void)
221
{
222
223
fprintf(stderr, "usage: autounmountd [-r time][-t time][-dv]\n");
224
exit(1);
225
}
226
227
static void
228
do_wait(int kq, time_t sleep_time)
229
{
230
struct timespec timeout;
231
struct kevent unused;
232
int nevents;
233
234
if (sleep_time != -1) {
235
assert(sleep_time > 0);
236
timeout.tv_sec = sleep_time;
237
timeout.tv_nsec = 0;
238
239
log_debugx("waiting for filesystem event for %ld seconds",
240
(long)sleep_time);
241
nevents = kevent(kq, NULL, 0, &unused, 1, &timeout);
242
} else {
243
log_debugx("waiting for filesystem event");
244
nevents = kevent(kq, NULL, 0, &unused, 1, NULL);
245
}
246
if (nevents < 0) {
247
if (errno == EINTR)
248
return;
249
log_err(1, "kevent");
250
}
251
252
if (nevents == 0) {
253
log_debugx("timeout reached");
254
assert(sleep_time > 0);
255
} else {
256
log_debugx("got filesystem event");
257
}
258
}
259
260
int
261
main_autounmountd(int argc, char **argv)
262
{
263
struct kevent event;
264
struct pidfh *pidfh;
265
pid_t otherpid;
266
const char *pidfile_path = AUTOUNMOUNTD_PIDFILE;
267
int ch, debug = 0, error, kq;
268
time_t expiration_time = 600, retry_time = 600, mounted_max, sleep_time;
269
bool dont_daemonize = false;
270
271
while ((ch = getopt(argc, argv, "dr:t:v")) != -1) {
272
switch (ch) {
273
case 'd':
274
dont_daemonize = true;
275
debug++;
276
break;
277
case 'r':
278
retry_time = atoi(optarg);
279
break;
280
case 't':
281
expiration_time = atoi(optarg);
282
break;
283
case 'v':
284
debug++;
285
break;
286
case '?':
287
default:
288
usage_autounmountd();
289
}
290
}
291
argc -= optind;
292
if (argc != 0)
293
usage_autounmountd();
294
295
if (retry_time <= 0)
296
log_errx(1, "retry time must be greater than zero");
297
if (expiration_time <= 0)
298
log_errx(1, "expiration time must be greater than zero");
299
300
log_init(debug);
301
302
pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
303
if (pidfh == NULL) {
304
if (errno == EEXIST) {
305
log_errx(1, "daemon already running, pid: %jd.",
306
(intmax_t)otherpid);
307
}
308
log_err(1, "cannot open or create pidfile \"%s\"",
309
pidfile_path);
310
}
311
312
if (dont_daemonize == false) {
313
if (daemon(0, 0) == -1) {
314
log_warn("cannot daemonize");
315
pidfile_remove(pidfh);
316
exit(1);
317
}
318
}
319
320
pidfile_write(pidfh);
321
322
TAILQ_INIT(&automounted);
323
324
kq = kqueue();
325
if (kq < 0)
326
log_err(1, "kqueue");
327
328
EV_SET(&event, 0, EVFILT_FS, EV_ADD | EV_CLEAR, VQ_MOUNT | VQ_UNMOUNT, 0, NULL);
329
error = kevent(kq, &event, 1, NULL, 0, NULL);
330
if (error < 0)
331
log_err(1, "kevent");
332
333
for (;;) {
334
refresh_automounted();
335
mounted_max = expire_automounted(expiration_time);
336
if (mounted_max == -1) {
337
sleep_time = mounted_max;
338
log_debugx("no filesystems to expire");
339
} else if (mounted_max < expiration_time) {
340
sleep_time = difftime(expiration_time, mounted_max);
341
log_debugx("some filesystems expire in %ld seconds",
342
(long)sleep_time);
343
} else {
344
sleep_time = retry_time;
345
log_debugx("some expired filesystems remain mounted, "
346
"will retry in %ld seconds", (long)sleep_time);
347
}
348
349
do_wait(kq, sleep_time);
350
}
351
352
return (0);
353
}
354
355