]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - share/examples/autofs/driver/autodriver.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / share / examples / autofs / driver / autodriver.c
1 /*
2  * Copyright (c) 2004 Alfred Perlstein <alfred@FreeBSD.org>
3  * All rights reserved.
4  *
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.
13  *
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.
25  *
26  * $Id: autodriver.c,v 1.9 2004/09/08 08:12:21 bright Exp $
27  * $FreeBSD$
28  */
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>
37
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>
44
45 #include <libautofs.h>
46
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. */
60 };
61
62 struct autoentry *entries;
63 const char *mount_prog = "mount";
64 const char *fstype = "autofs";
65
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);
77
78 #define DSTR(s) sizeof(s) - 1, s
79
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") },
85 };
86
87 void *
88 xmalloc(size_t size)
89 {
90         void *ret;
91
92         ret = malloc(size);
93         if (ret == NULL)
94                 err(1, "malloc %d", (int) size);
95         return (ret);
96 }
97
98 void *
99 xcalloc(size_t number, size_t size)
100 {
101         void *ret;
102
103         ret = calloc(number, size);
104         if (ret == NULL)
105                 err(1, "calloc %d %d", (int)number, (int)size);
106         return (ret);
107 }
108
109 void
110 parsetab(void)
111 {
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
121         };
122
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;
131         }
132         if (fp == NULL) {
133                 err(1, "no config file available.");
134         }
135
136         fprintf(stderr, "using config file: %s\n", tab);
137
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;
154                 }
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;
189                         }
190                 }
191                 if (!gotopt) {
192                         warnx("no options specified %s", expecting);
193                         goto bad2;
194                 }
195                 if (ent->ae_direct && ent->ae_indirect) {
196                         warnx("direct and indirect are mutually exclusive");
197                         goto bad2;
198
199                 }
200                 x = asprintf(&ent->ae_fullpath, "%s/%s",
201                     ent->ae_mnt, ent->ae_path);
202                 if (x == -1)
203                         err(1, "asprintf");
204
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;
210                 }
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);
222         }
223         if (ferror(fp))
224                 err(1, "error with file %s", tab);
225 }
226
227 void
228 populate_tab(void)
229 {
230         struct autoentry *ent;
231         char *path, *cmd;
232         int error;
233         autoh_t ah;
234
235         path = cmd = NULL;
236
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;
250                 }
251                 if (autoh_get(ent->ae_mnt, &ah)) {
252                         warn("autoh_get %s", path);
253                         continue;
254                 }
255                 error = autoh_togglepath(ah, AUTO_MOUNTER, getpid(), path);
256                 if (error) {
257                         err(1, "AUTO_MOUNTER %s", path);
258                         continue;
259                 }
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);
265                 }
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);
271                 }
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);
277                 }
278                 autoh_free(ah);
279         }
280         free(path);
281         free(cmd);
282 }
283
284 /*
285  * Process an autofs request, scan the list of entries in the config
286  * looking for our node, if found mount it.
287  */
288 void
289 doreq(autoh_t ah, autoreq_t req)
290 {
291         struct autoentry *ent;
292         int error;
293         int mcmp;
294         int xid;
295         const char *mnt;
296
297         mnt = autoh_mp(ah);
298
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;
313                 }
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;
320         }
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");
327         }
328 }
329
330 int
331 mount_indirect(req, ent)
332         struct autofs_userreq *req;
333         struct autoentry *ent;
334 {
335         struct stat sb;
336         char *path, *cmd;
337         int error, x;
338
339         if (ent->ae_indirect != 1) {
340                 fprintf(stderr, "not indirect.\n");
341                 return (0);
342         }
343         fprintf(stderr, "indirect mount...\n");
344         /*
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.
348          */
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);
358         }
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);
365         }
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);
370         }
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; */
388         }
389         free(path);
390         return (1);
391 }
392
393 int
394 mount_direct(req, ent)
395         struct autofs_userreq *req;
396         struct autoentry *ent;
397 {
398         struct stat sb;
399         char *cmd;
400         int error;
401
402         if (ent->ae_direct != 1) {
403                 fprintf(stderr, "not direct.\n");
404                 return (0);
405         }
406         fprintf(stderr, "direct mount...\n");
407         /*
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.
411          */
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);
419         }
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);
435         }
436         /* XXX: fix ONLIST in kernel */
437         /* req->au_flags = 1; */
438         return (1);
439 }
440
441 int
442 mount_browse(req, ent)
443         struct autofs_userreq *req;
444         struct autoentry *ent;
445 {
446         off_t off;
447
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);
459 }
460
461 /*
462  * Ask the filesystem passed in if it has a pending request.
463  * if so process them.
464  */
465 void
466 dotheneedful(autoh_t ah)
467 {
468         int cnt, i;
469         autoreq_t *reqs;
470
471         if (autoreq_get(ah, &reqs, &cnt))
472                 err(1, "autoreq_get");
473
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]);
478         }
479         free(reqs);
480 }
481
482 int
483 poll_handles(autoh_t *array, int cnt)
484 {
485         int i, saved_errno, x;
486         static struct pollfd *pfd = NULL;
487
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;
495         }
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);
507 }
508
509 void
510 eventloop(void)
511 {
512         autoh_t *array;
513         int cnt, i;
514
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]);
523                 }
524                 autoh_freeall(array);
525         }
526 }
527
528 int
529 main(int argc __unused, char **argv __unused)
530 {
531
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);
538 }