turns printfs back on
[freebsd-src/fkvm-freebsd.git] / share / examples / autofs / driver / autodriver.c
blob69af0c557eff86bee8fb011fe0568bf4554161cb
1 /*
2 * Copyright (c) 2004 Alfred Perlstein <alfred@FreeBSD.org>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 * $Id: autodriver.c,v 1.9 2004/09/08 08:12:21 bright Exp $
27 * $FreeBSD$
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <fcntl.h>
38 #include <sys/dirent.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/mount.h>
42 #include <sys/poll.h>
43 #include <sys/stat.h>
45 #include <libautofs.h>
47 struct autoentry {
48 char *ae_mnt; /* autofs mountpoint. */
49 char *ae_path; /* path under mount. */
50 char *ae_type; /* fs to be mounted type. */
51 char *ae_opts; /* options passed to mount. */
52 char *ae_rpath; /* remote path */
53 char *ae_free; /* freeme! */
54 char *ae_fullpath; /* full path to mount */
55 int ae_line; /* line it came from in the conf. */
56 int ae_indirect; /* is this an indirect mount? */
57 int ae_direct; /* is this a direct mount? */
58 int ae_browse; /* browseable? */
59 struct autoentry *ae_next; /* next. */
62 struct autoentry *entries;
63 const char *mount_prog = "mount";
64 const char *fstype = "autofs";
66 void *xmalloc(size_t);
67 void *xcalloc(size_t number, size_t size);
68 void parsetab(void);
69 void populate_tab(void);
70 void doreq(autoh_t, autoreq_t);
71 void dotheneedful(autoh_t);
72 void eventloop(void);
73 int poll_handles(autoh_t *array, int cnt);
74 int mount_indirect(struct autofs_userreq *req, struct autoentry *ent);
75 int mount_direct(struct autofs_userreq *req, struct autoentry *ent);
76 int mount_browse(struct autofs_userreq *req, struct autoentry *ent);
78 #define DSTR(s) sizeof(s) - 1, s
80 struct dirent dumbents[] = {
81 {50, sizeof(struct dirent), DT_DIR, DSTR("one") },
82 {51, sizeof(struct dirent), DT_DIR, DSTR(".") },
83 {52, sizeof(struct dirent), DT_DIR, DSTR("..") },
84 {50, sizeof(struct dirent), DT_DIR, DSTR("two") },
87 void *
88 xmalloc(size_t size)
90 void *ret;
92 ret = malloc(size);
93 if (ret == NULL)
94 err(1, "malloc %d", (int) size);
95 return (ret);
98 void *
99 xcalloc(size_t number, size_t size)
101 void *ret;
103 ret = calloc(number, size);
104 if (ret == NULL)
105 err(1, "calloc %d %d", (int)number, (int)size);
106 return (ret);
109 void
110 parsetab(void)
112 FILE *fp;
113 const char *tab;
114 char *cp, *p, *line, *opt;
115 size_t len;
116 struct autoentry *ent;
117 int i, lineno, x, gotopt;
118 const char *expecting = "expecting 'direct', 'indirect' or 'browse'";
119 const char *tabfiles[] = {
120 "/etc/autotab", "/usr/local/etc/autotab", "./autotab", NULL
123 lineno = 0;
124 for (i = 0; (tab = tabfiles[i]) != NULL; i++) {
125 tab = tabfiles[i];
126 fp = fopen(tab, "r");
127 if (fp == NULL)
128 warn("fopen %s", tab);
129 if (fp != NULL)
130 break;
132 if (fp == NULL) {
133 err(1, "no config file available.");
136 fprintf(stderr, "using config file: %s\n", tab);
138 while ((cp = fgetln(fp, &len)) != NULL) {
139 lineno++;
140 while (len > 0 && isspace(cp[len - 1]))
141 len--;
142 line = xmalloc(len + 1);
143 bcopy(cp, line, len);
144 line[len] = '\0';
145 cp = line;
146 if ((cp = strchr(line, '#')) != NULL)
147 *cp = '\0';
148 cp = line;
149 while (isspace(*cp))
150 cp++;
151 if (*cp == '\0') {
152 free(line);
153 continue;
155 ent = xcalloc(1, sizeof(*ent));
156 if ((p = strsep(&cp, " \t")) == NULL)
157 goto bad;
158 ent->ae_mnt = p;
159 if ((p = strsep(&cp, " \t")) == NULL)
160 goto bad;
161 ent->ae_path = p;
162 if ((p = strsep(&cp, " \t")) == NULL)
163 goto bad;
164 ent->ae_type = p;
165 if ((p = strsep(&cp, " \t")) == NULL)
166 goto bad;
167 ent->ae_opts = p;
168 if ((p = strsep(&cp, " \t")) == NULL)
169 goto bad;
170 ent->ae_rpath = p;
171 if ((p = strsep(&cp, " \t")) == NULL)
172 goto bad;
173 gotopt = 0;
174 opt = p;
175 while ((p = strsep(&opt, ",")) != NULL) {
176 if (strcmp(p, "indirect") == 0) {
177 ent->ae_indirect = 1;
178 gotopt = 1;
179 } else if (strcmp(p, "direct") == 0) {
180 ent->ae_direct = 1;
181 gotopt = 1;
182 } else if (strcmp(p, "browse") == 0) {
183 ent->ae_browse = 1;
184 gotopt = 1;
185 } else {
186 warnx("unreconized option '%s', %s",
187 p, expecting);
188 goto bad2;
191 if (!gotopt) {
192 warnx("no options specified %s", expecting);
193 goto bad2;
195 if (ent->ae_direct && ent->ae_indirect) {
196 warnx("direct and indirect are mutually exclusive");
197 goto bad2;
200 x = asprintf(&ent->ae_fullpath, "%s/%s",
201 ent->ae_mnt, ent->ae_path);
202 if (x == -1)
203 err(1, "asprintf");
205 if (strlen(ent->ae_fullpath) + 1 > PATH_MAX) {
206 warnx("Error in file %s, line %d, "
207 "mountpath (%s) exceeds PATH_MAX (%d)",
208 tab, lineno, ent->ae_fullpath, PATH_MAX);
209 goto bad2;
211 ent->ae_line = lineno;
212 ent->ae_free = line;
213 ent->ae_next = entries;
214 entries = ent;
215 continue;
216 bad:
217 warnx("Parse error in file %s, line %d", tab, lineno);
218 bad2:
219 free(ent->ae_fullpath);
220 free(line);
221 free(ent);
223 if (ferror(fp))
224 err(1, "error with file %s", tab);
227 void
228 populate_tab(void)
230 struct autoentry *ent;
231 char *path, *cmd;
232 int error;
233 autoh_t ah;
235 path = cmd = NULL;
237 for (ent = entries; ent != NULL; ent = ent->ae_next) {
238 free(path);
239 free(cmd);
240 error = asprintf(&path, "%s/%s", ent->ae_mnt, ent->ae_path);
241 if (error == -1)
242 err(1, "asprintf");
243 error = asprintf(&cmd, "mkdir -p %s", path);
244 if (error == -1)
245 err(1, "asprintf");
246 error = system(cmd);
247 if (error) {
248 warn("system: %s", cmd);
249 continue;
251 if (autoh_get(ent->ae_mnt, &ah)) {
252 warn("autoh_get %s", path);
253 continue;
255 error = autoh_togglepath(ah, AUTO_MOUNTER, getpid(), path);
256 if (error) {
257 err(1, "AUTO_MOUNTER %s", path);
258 continue;
260 if (ent->ae_browse) {
261 error = autoh_togglepath(ah, AUTO_BROWSE, getpid(),
262 path);
263 if (error)
264 err(1, "AUTO_BROWSE %s", path);
266 if (ent->ae_direct) {
267 error = autoh_togglepath(ah, AUTO_DIRECT, getpid(),
268 path);
269 if (error)
270 err(1, "AUTO_DIRECT %s", path);
272 if (ent->ae_indirect) {
273 error = autoh_togglepath(ah, AUTO_INDIRECT, getpid(),
274 path);
275 if (error)
276 err(1, "AUTO_INDIRECT %s", path);
278 autoh_free(ah);
280 free(path);
281 free(cmd);
285 * Process an autofs request, scan the list of entries in the config
286 * looking for our node, if found mount it.
288 void
289 doreq(autoh_t ah, autoreq_t req)
291 struct autoentry *ent;
292 int error;
293 int mcmp;
294 int xid;
295 const char *mnt;
297 mnt = autoh_mp(ah);
299 autoreq_seterrno(req, 0);
300 for (ent = entries; ent != NULL; ent = ent->ae_next) {
301 fprintf(stderr, "comparing {%s,%s} to {%s,%s}\n",
302 mnt, ent->ae_mnt, autoreq_getpath(req), ent->ae_path);
303 fprintf(stderr, "comparing {%d,%d} to {%d,%d}\n",
304 (int)strlen(mnt),
305 (int)strlen(ent->ae_mnt),
306 (int)strlen(autoreq_getpath(req)),
307 (int)strlen(ent->ae_path));
308 autoreq_getxid(req, &xid);
309 fprintf(stderr, "req xid %d\n", xid);
310 if ((mcmp = strcmp(mnt, ent->ae_mnt)) != 0) {
311 fprintf(stderr, "mcmp = %d\n", mcmp);
312 continue;
314 if (mount_direct(req, ent))
315 goto serve;
316 if (mount_indirect(req, ent))
317 goto serve;
318 if (mount_browse(req, ent))
319 goto serve;
321 fprintf(stderr, "no entry found...\n");
322 autoreq_seterrno(req, ENOENT);
323 serve:
324 error = autoreq_serv(ah, req);
325 if (error == -1) {
326 warn("AUTOFS_CTL_SERVREQ");
331 mount_indirect(req, ent)
332 struct autofs_userreq *req;
333 struct autoentry *ent;
335 struct stat sb;
336 char *path, *cmd;
337 int error, x;
339 if (ent->ae_indirect != 1) {
340 fprintf(stderr, "not indirect.\n");
341 return (0);
343 fprintf(stderr, "indirect mount...\n");
345 * handle lookups, fake all stat(2) requests... this is bad,
346 * but we're a driver so we don't care...
347 * If we don't care about the type of request, then just return.
349 switch (autoreq_getop(req)) {
350 case AUTOREQ_OP_LOOKUP:
351 break;
352 case AUTOREQ_OP_STAT:
353 fprintf(stderr, "stat\n");
354 return (1);
355 default:
356 fprintf(stderr, "unknown\n");
357 return (0);
359 if (stat(ent->ae_fullpath, &sb))
360 return (0);
361 if (sb.st_ino != autoreq_getdirino(req)) {
362 fprintf(stderr, "st_ino %d != dirino %d\n",
363 (int)sb.st_ino, (int)autoreq_getdirino(req));
364 return (0);
366 x = asprintf(&path, "%s/%s", ent->ae_fullpath, autoreq_getpath(req));
367 if (x > PATH_MAX) {
368 autoreq_seterrno(req, ENAMETOOLONG);
369 return (1);
371 if (mkdir(path, 0555) == -1)
372 warn("mkdir %s", path);
373 error = asprintf(&cmd, "%s -t %s -o %s %s/%s %s", mount_prog,
374 ent->ae_type, ent->ae_opts, ent->ae_rpath, autoreq_getpath(req), path);
375 fprintf(stderr, "running:\n\t%s\n", cmd);
376 error = system(cmd);
377 fprintf(stderr, "error = %d\n", error);
378 free(cmd);
379 if (error) {
380 if (rmdir(path) == -1)
381 warn("rmdir %s", path);
382 autoreq_seterrno(req, ENOENT);
383 } else {
384 if (stat(path, &sb) != -1)
385 autoreq_setino(req, sb.st_ino);
386 /* XXX !!! */
387 /* req->au_flags = 1; */
389 free(path);
390 return (1);
394 mount_direct(req, ent)
395 struct autofs_userreq *req;
396 struct autoentry *ent;
398 struct stat sb;
399 char *cmd;
400 int error;
402 if (ent->ae_direct != 1) {
403 fprintf(stderr, "not direct.\n");
404 return (0);
406 fprintf(stderr, "direct mount...\n");
408 * handle lookups, fake all stat(2) requests... this is bad,
409 * but we're a driver so we don't care...
410 * If we don't care about the type of request, then just return.
412 switch (autoreq_getop(req)) {
413 case AUTOREQ_OP_LOOKUP:
414 break;
415 case AUTOREQ_OP_STAT:
416 return (1);
417 default:
418 return (0);
420 if (stat(ent->ae_fullpath, &sb))
421 return (0);
422 if (sb.st_ino != autoreq_getino(req))
423 return (0);
424 error = asprintf(&cmd, "%s -t %s -o %s %s %s", mount_prog,
425 ent->ae_type, ent->ae_opts, ent->ae_rpath, ent->ae_fullpath);
426 if (error == -1)
427 err(1, "asprintf");
428 fprintf(stderr, "running:\n\t%s\n", cmd);
429 error = system(cmd);
430 fprintf(stderr, "error = %d\n", error);
431 free(cmd);
432 if (error) {
433 autoreq_seterrno(req, ENOENT);
434 return (1);
436 /* XXX: fix ONLIST in kernel */
437 /* req->au_flags = 1; */
438 return (1);
442 mount_browse(req, ent)
443 struct autofs_userreq *req;
444 struct autoentry *ent;
446 off_t off;
448 if (ent->ae_browse != 1)
449 return (0);
450 if (autoreq_getop(req) != AUTOREQ_OP_READDIR)
451 return (0);
452 autoreq_getoffset(req, &off);
453 if (off < sizeof(dumbents))
454 autoreq_setaux(req, dumbents, sizeof(dumbents));
455 fprintf(stderr, "mount_browse: offset %d, size %d\n",
456 (int)off, (int)sizeof(dumbents));
457 autoreq_seteof(req, 1);
458 return (1);
462 * Ask the filesystem passed in if it has a pending request.
463 * if so process them.
465 void
466 dotheneedful(autoh_t ah)
468 int cnt, i;
469 autoreq_t *reqs;
471 if (autoreq_get(ah, &reqs, &cnt))
472 err(1, "autoreq_get");
474 for (i = 0; i < cnt; i++) {
475 fprintf(stderr, "processing request for '%s' '%s'\n",
476 autoh_mp(ah), autoreq_getpath(reqs[i]));
477 doreq(ah, reqs[i]);
479 free(reqs);
483 poll_handles(autoh_t *array, int cnt)
485 int i, saved_errno, x;
486 static struct pollfd *pfd = NULL;
488 pfd = reallocf(pfd, cnt * sizeof(*pfd));
489 if (pfd == NULL)
490 return (-1);
491 for (i = 0; i < cnt; i++) {
492 pfd[i].fd = autoh_fd(array[i]);
493 pfd[i].events = POLLPRI;
494 pfd[i].revents = 0;
496 fprintf(stderr, "start polling...\n");
497 x = poll(pfd, cnt, 10000);
498 saved_errno = errno;
499 fprintf(stderr, "done polling...\n");
500 errno = saved_errno;
501 if (x == -1)
502 return (-1);
503 /* at least one fs is ready... */
504 if (x > 0)
505 return (0);
506 return (0);
509 void
510 eventloop(void)
512 autoh_t *array;
513 int cnt, i;
515 fprintf(stderr, "starting event loop...\n");
516 for ( ;; ) {
517 if (autoh_getall(&array, &cnt))
518 err(1, "autoh_getall");
519 if (poll_handles(array, cnt))
520 err(1, "poll_handles");
521 for (i = 0; i < cnt; i++) {
522 dotheneedful(array[i]);
524 autoh_freeall(array);
529 main(int argc __unused, char **argv __unused)
532 if (getuid() != 0)
533 errx(1, "autodriver needs to be run as root to work.");
534 parsetab();
535 populate_tab();
536 eventloop();
537 return (0);