xref: /netbsd-src/external/bsd/am-utils/dist/conf/autofs/autofs_linux.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: autofs_linux.c,v 1.1.1.2 2009/03/20 20:26:51 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1999-2003 Ion Badulescu
5  * Copyright (c) 1997-2009 Erez Zadok
6  * Copyright (c) 1990 Jan-Simon Pendry
7  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
8  * Copyright (c) 1990 The Regents of the University of California.
9  * All rights reserved.
10  *
11  * This code is derived from software contributed to Berkeley by
12  * Jan-Simon Pendry at Imperial College, London.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgment:
24  *      This product includes software developed by the University of
25  *      California, Berkeley and its contributors.
26  * 4. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  *
43  * File: am-utils/conf/autofs/autofs_linux.c
44  *
45  */
46 
47 /*
48  * Automounter filesystem for Linux
49  */
50 
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif /* HAVE_CONFIG_H */
54 #include <am_defs.h>
55 #include <amd.h>
56 
57 #ifdef HAVE_FS_AUTOFS
58 
59 /*
60  * MACROS:
61  */
62 
63 #define AUTOFS_MIN_VERSION 3
64 #define AUTOFS_MAX_VERSION AUTOFS_MAX_PROTO_VERSION
65 
66 
67 /*
68  * STRUCTURES:
69  */
70 
71 /*
72  * VARIABLES:
73  */
74 
75 static int autofs_max_fds;
76 static am_node **hash;
77 static int *list;
78 static int numfds = 0;
79 static int bind_works = 1;
80 
81 
82 static void
83 hash_init(void)
84 {
85   int i;
86   struct rlimit rlim;
87 
88   if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
89     plog(XLOG_ERROR, "getrlimit failed, defaulting to 256 fd's");
90     autofs_max_fds = 256;
91   } else {
92     autofs_max_fds = (rlim.rlim_cur > 1024) ? 1024 : rlim.rlim_cur;
93     plog(XLOG_INFO, "%d fd's available for autofs", autofs_max_fds);
94   }
95 
96   list = malloc(autofs_max_fds * sizeof(*list));
97   hash = malloc(autofs_max_fds * sizeof(*hash));
98 
99   for (i = 0 ; i < autofs_max_fds; i++) {
100     hash[i] = NULL;
101     list[i] = -1;
102   }
103 }
104 
105 
106 static void
107 hash_insert(int fd, am_node *mp)
108 {
109   if (hash[fd] != 0)
110     plog(XLOG_ERROR, "file descriptor %d already in the hash", fd);
111 
112   hash[fd] = mp;
113   list[numfds] = fd;
114   numfds++;
115 }
116 
117 
118 static void
119 hash_delete(int fd)
120 {
121   int i;
122 
123   if (hash[fd] == 0)
124     plog(XLOG_WARNING, "file descriptor %d not in the hash", fd);
125 
126   hash[fd] = NULL;
127   numfds--;
128   for (i = 0; i < numfds; i++)
129     if (list[i] == fd) {
130       list[i] = list[numfds];
131       break;
132     }
133 }
134 
135 
136 int
137 autofs_get_fh(am_node *mp)
138 {
139   int fds[2];
140   autofs_fh_t *fh;
141 
142   plog(XLOG_DEBUG, "autofs_get_fh for %s", mp->am_path);
143   if (pipe(fds) < 0)
144     return errno;
145 
146   /* sanity check */
147   if (fds[0] > autofs_max_fds) {
148     close(fds[0]);
149     close(fds[1]);
150     return EMFILE;
151   }
152 
153   fh = ALLOC(autofs_fh_t);
154   fh->fd = fds[0];
155   fh->kernelfd = fds[1];
156   fh->ioctlfd = -1;
157   fh->pending_mounts = NULL;
158   fh->pending_umounts = NULL;
159 
160   mp->am_autofs_fh = fh;
161 
162   return 0;
163 }
164 
165 
166 void
167 autofs_mounted(am_node *mp)
168 {
169   autofs_fh_t *fh = mp->am_autofs_fh;
170   unsigned long timeout = gopt.am_timeo;
171 
172   close(fh->kernelfd);
173   fh->kernelfd = -1;
174 
175   autofs_get_mp(mp);
176 
177   /* Get autofs protocol version */
178   if (ioctl(fh->ioctlfd, AUTOFS_IOC_PROTOVER, &fh->version) < 0) {
179     plog(XLOG_ERROR, "AUTOFS_IOC_PROTOVER: %s", strerror(errno));
180     fh->version = AUTOFS_MIN_VERSION;
181     plog(XLOG_ERROR, "autofs: assuming protocol version %d", fh->version);
182   } else
183     plog(XLOG_INFO, "autofs: using protocol version %d", fh->version);
184 
185   /* set expiration timeout */
186   if (ioctl(fh->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout) < 0)
187     plog(XLOG_ERROR, "AUTOFS_IOC_SETTIMEOUT: %s", strerror(errno));
188 
189   /* tell the daemon to call us for expirations */
190   mp->am_autofs_ttl = clocktime(NULL) + gopt.am_timeo_w;
191 }
192 
193 
194 void
195 autofs_get_mp(am_node *mp)
196 {
197   autofs_fh_t *fh = mp->am_autofs_fh;
198   dlog("autofs: getting mount point");
199   if (fh->ioctlfd < 0)
200     fh->ioctlfd = open(mp->am_path, O_RDONLY);
201   hash_insert(fh->fd, mp);
202 }
203 
204 
205 void
206 autofs_release_mp(am_node *mp)
207 {
208   autofs_fh_t *fh = mp->am_autofs_fh;
209   dlog("autofs: releasing mount point");
210   if (fh->ioctlfd >= 0) {
211     close(fh->ioctlfd);
212     fh->ioctlfd = -1;
213   }
214   /*
215    * take the kernel fd out of the hash/fdset
216    * so select() doesn't go crazy if the umount succeeds
217    */
218   if (fh->fd >= 0)
219     hash_delete(fh->fd);
220 }
221 
222 
223 void
224 autofs_release_fh(am_node *mp)
225 {
226   autofs_fh_t *fh = mp->am_autofs_fh;
227   struct autofs_pending_mount **pp, *p;
228   struct autofs_pending_umount **upp, *up;
229 
230   dlog("autofs: releasing file handle");
231   if (fh) {
232     /*
233      * if a mount succeeded, the kernel fd was closed on
234      * the amd side, so it might have been reused.
235      * we set it to -1 after closing it, to avoid the problem.
236      */
237     if (fh->kernelfd >= 0)
238       close(fh->kernelfd);
239 
240     if (fh->ioctlfd >= 0)
241       close(fh->ioctlfd);
242 
243     if (fh->fd >= 0)
244       close(fh->fd);
245 
246     pp = &fh->pending_mounts;
247     while (*pp) {
248       p = *pp;
249       XFREE(p->name);
250       *pp = p->next;
251       XFREE(p);
252     }
253 
254     upp = &fh->pending_umounts;
255     while (*upp) {
256       up = *upp;
257       XFREE(up->name);
258       *upp = up->next;
259       XFREE(up);
260     }
261 
262     XFREE(fh);
263     mp->am_autofs_fh = NULL;
264   }
265 }
266 
267 
268 void
269 autofs_add_fdset(fd_set *readfds)
270 {
271   int i;
272   for (i = 0; i < numfds; i++)
273     FD_SET(list[i], readfds);
274 }
275 
276 
277 static int
278 autofs_get_pkt(int fd, char *buf, int bytes)
279 {
280   int i;
281 
282   do {
283     i = read(fd, buf, bytes);
284 
285     if (i <= 0)
286       break;
287 
288     buf += i;
289     bytes -= i;
290   } while (bytes);
291 
292   return bytes;
293 }
294 
295 
296 static void
297 send_fail(int fd, unsigned long token)
298 {
299   if (token == 0)
300     return;
301   if (ioctl(fd, AUTOFS_IOC_FAIL, token) < 0)
302     plog(XLOG_ERROR, "AUTOFS_IOC_FAIL: %s", strerror(errno));
303 }
304 
305 
306 static void
307 send_ready(int fd, unsigned long token)
308 {
309   if (token == 0)
310     return;
311   if (ioctl(fd, AUTOFS_IOC_READY, token) < 0)
312     plog(XLOG_ERROR, "AUTOFS_IOC_READY: %s", strerror(errno));
313 }
314 
315 
316 static void
317 autofs_lookup_failed(am_node *mp, char *name)
318 {
319   autofs_fh_t *fh = mp->am_autofs_fh;
320   struct autofs_pending_mount **pp, *p;
321 
322   pp = &fh->pending_mounts;
323   while (*pp && !STREQ((*pp)->name, name))
324     pp = &(*pp)->next;
325 
326   /* sanity check */
327   if (*pp == NULL)
328     return;
329 
330   p = *pp;
331   plog(XLOG_INFO, "autofs: lookup of %s failed", name);
332   send_fail(fh->ioctlfd, p->wait_queue_token);
333 
334   XFREE(p->name);
335   *pp = p->next;
336   XFREE(p);
337 }
338 
339 
340 static void
341 autofs_expire_one(am_node *mp, char *name, unsigned long token)
342 {
343   autofs_fh_t *fh;
344   am_node *ap;
345   struct autofs_pending_umount *p;
346   char *ap_path;
347 
348   fh = mp->am_autofs_fh;
349 
350   ap_path = str3cat(NULL, mp->am_path, "/", name);
351   if (amuDebug(D_TRACE))
352     plog(XLOG_DEBUG, "\tumount(%s)", ap_path);
353 
354   p = fh->pending_umounts;
355   while (p && p->wait_queue_token != token)
356     p = p->next;
357 
358   if (p) {
359     /* already pending */
360     dlog("Umounting of %s already pending", ap_path);
361     amd_stats.d_drops++;
362     goto out;
363   }
364 
365   ap = find_ap(ap_path);
366   if (ap == NULL) {
367     /* not found??? not sure what to do here... */
368     send_fail(fh->ioctlfd, token);
369     goto out;
370   }
371 
372   p = ALLOC(struct autofs_pending_umount);
373   p->wait_queue_token = token;
374   p->name = strdup(name);
375   p->next = fh->pending_umounts;
376   fh->pending_umounts = p;
377 
378   unmount_mp(ap);
379 
380 out:
381   XFREE(ap_path);
382 }
383 
384 
385 static void
386 autofs_handle_expire(am_node *mp, struct autofs_packet_expire *pkt)
387 {
388   autofs_expire_one(mp, pkt->name, 0);
389 }
390 
391 
392 #if AUTOFS_MAX_VERSION >= 4
393 static void
394 autofs_handle_expire_multi(am_node *mp, struct autofs_packet_expire_multi *pkt)
395 {
396   autofs_expire_one(mp, pkt->name, pkt->wait_queue_token);
397 }
398 #endif /* AUTOFS_MAX_VERSION >= 4 */
399 
400 
401 static void
402 autofs_handle_missing(am_node *mp, struct autofs_packet_missing *pkt)
403 {
404   autofs_fh_t *fh;
405   mntfs *mf;
406   am_node *ap;
407   struct autofs_pending_mount *p;
408   int error;
409 
410   mf = mp->am_mnt;
411   fh = mp->am_autofs_fh;
412 
413   p = fh->pending_mounts;
414   while (p && p->wait_queue_token != pkt->wait_queue_token)
415     p = p->next;
416 
417   if (p) {
418     /* already pending */
419     dlog("Mounting of %s/%s already pending",
420 	 mp->am_path, pkt->name);
421     amd_stats.d_drops++;
422     return;
423   }
424 
425   p = ALLOC(struct autofs_pending_mount);
426   p->wait_queue_token = pkt->wait_queue_token;
427   p->name = strdup(pkt->name);
428   p->next = fh->pending_mounts;
429   fh->pending_mounts = p;
430 
431   if (amuDebug(D_TRACE))
432     plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, pkt->name);
433   ap = mf->mf_ops->lookup_child(mp, pkt->name, &error, VLOOK_CREATE);
434   if (ap && error < 0)
435     ap = mf->mf_ops->mount_child(ap, &error);
436 
437   /* some of the rest can be done in amfs_auto_cont */
438 
439   if (ap == 0) {
440     if (error < 0) {
441       dlog("Mount still pending, not sending autofs reply yet");
442       return;
443     }
444     autofs_lookup_failed(mp, pkt->name);
445   }
446   mp->am_stats.s_lookup++;
447 }
448 
449 
450 int
451 autofs_handle_fdset(fd_set *readfds, int nsel)
452 {
453   int i;
454   union autofs_packet_union pkt;
455   autofs_fh_t *fh;
456   am_node *mp;
457 
458   for (i = 0; nsel && i < numfds; i++) {
459     if (!FD_ISSET(list[i], readfds))
460       continue;
461 
462     nsel--;
463     FD_CLR(list[i], readfds);
464     mp = hash[list[i]];
465     fh = mp->am_autofs_fh;
466 
467     if (autofs_get_pkt(fh->fd, (char *) &pkt,
468 		       sizeof(union autofs_packet_union)))
469       continue;
470 
471     switch (pkt.hdr.type) {
472     case autofs_ptype_missing:
473       autofs_handle_missing(mp, &pkt.missing);
474       break;
475     case autofs_ptype_expire:
476       autofs_handle_expire(mp, &pkt.expire);
477       break;
478 #if AUTOFS_MAX_VERSION >= 4
479     case autofs_ptype_expire_multi:
480       autofs_handle_expire_multi(mp, &pkt.expire_multi);
481       break;
482 #endif /* AUTOFS_MAX_VERSION >= 4 */
483     default:
484       plog(XLOG_ERROR, "Unknown autofs packet type %d",
485 	   pkt.hdr.type);
486     }
487   }
488   return nsel;
489 }
490 
491 
492 int
493 create_autofs_service(void)
494 {
495   hash_init();
496 
497   /* not the best place, but... */
498   if (linux_version_code() < KERNEL_VERSION(2,4,0))
499     bind_works = 0;
500 
501   return 0;
502 }
503 
504 
505 int
506 destroy_autofs_service(void)
507 {
508   /* Nothing to do */
509   return 0;
510 }
511 
512 
513 static int
514 autofs_bind_umount(char *mountpoint)
515 {
516   int err = 1;
517 #ifdef MNT2_GEN_OPT_BIND
518   if (bind_works && gopt.flags & CFM_AUTOFS_USE_LOFS) {
519     struct stat buf;
520 
521     if ((err = lstat(mountpoint, &buf)))
522       return errno;
523     if (S_ISLNK(buf.st_mode))
524       goto use_symlink;
525 
526     plog(XLOG_INFO, "autofs: un-bind-mounting %s", mountpoint);
527     err = umount_fs(mountpoint, mnttab_file_name, 1);
528     if (err)
529       plog(XLOG_INFO, "autofs: unmounting %s failed: %m", mountpoint);
530     else
531       err = rmdir(mountpoint);
532     goto out;
533   }
534 #endif /* MNT2_GEN_OPT_BIND */
535  use_symlink:
536   plog(XLOG_INFO, "autofs: deleting symlink %s", mountpoint);
537   err = unlink(mountpoint);
538 
539  out:
540   if (err)
541     return errno;
542   return 0;
543 }
544 
545 
546 int
547 autofs_mount_fs(am_node *mp, mntfs *mf)
548 {
549   char *target, *target2 = NULL;
550   int err = 0;
551 
552   if (mf->mf_flags & MFF_ON_AUTOFS) {
553     if ((err = mkdir(mp->am_path, 0555)))
554       return errno;
555   }
556 
557   /*
558    * For sublinks, we could end up here with an already mounted f/s.
559    * Don't do anything in that case.
560    */
561   if (!(mf->mf_flags & MFF_MOUNTED))
562     err = mf->mf_ops->mount_fs(mp, mf);
563 
564   if (err) {
565     if (mf->mf_flags & MFF_ON_AUTOFS)
566       rmdir(mp->am_path);
567     return err;
568   }
569 
570   if (mf->mf_flags & MFF_ON_AUTOFS)
571     /* Nothing else to do */
572     return 0;
573 
574   if (mp->am_link)
575     target = mp->am_link;
576   else
577     target = mf->mf_fo->opt_fs;
578 
579 #ifdef MNT2_GEN_OPT_BIND
580   if (bind_works && gopt.flags & CFM_AUTOFS_USE_LOFS) {
581     struct stat buf;
582 
583     /*
584      * HACK ALERT!
585      *
586      * Since the bind mount mechanism doesn't allow mountpoint crossing,
587      * we _must_ use symlinks for the host mount case. Otherwise we end up
588      * with a bunch of empty mountpoints...
589      */
590     if (mf->mf_ops == &amfs_host_ops)
591       goto use_symlink;
592 
593     if (target[0] != '/')
594       target2 = str3cat(NULL, mp->am_parent->am_path, "/", target);
595     else
596       target2 = strdup(target);
597 
598     /*
599      * We need to stat() the destination, because the bind mount does not
600      * follow symlinks and/or allow for non-existent destinations.
601      * We fall back to symlinks if there are problems.
602      *
603      * We also need to temporarily change pgrp, otherwise our stat() won't
604      * trigger whatever cascading mounts are needed.
605      *
606      * WARNING: we will deadlock if this function is called from the master
607      * amd process and it happens to trigger another auto mount. Therefore,
608      * this function should be called only from a child amd process, or
609      * at the very least it should not be called from the parent unless we
610      * know for sure that it won't cause a recursive mount. We refuse to
611      * cause the recursive mount anyway if called from the parent amd.
612      */
613     if (!foreground) {
614       pid_t pgrp = getpgrp();
615       setpgrp();
616       err = stat(target2, &buf);
617       if ((err = setpgid(0, pgrp))) {
618 	plog(XLOG_ERROR, "autofs: cannot restore pgrp: %s", strerror(errno));
619 	plog(XLOG_ERROR, "autofs: aborting the mount");
620 	goto out;
621       }
622       if (err)
623 	goto use_symlink;
624     }
625     if ((err = lstat(target2, &buf)))
626       goto use_symlink;
627     if (S_ISLNK(buf.st_mode))
628       goto use_symlink;
629 
630     plog(XLOG_INFO, "autofs: bind-mounting %s -> %s", mp->am_path, target2);
631     mkdir(mp->am_path, 0555);
632     err = mount_lofs(mp->am_path, target2, mf->mf_mopts, 1);
633     if (err) {
634       rmdir(mp->am_path);
635       plog(XLOG_INFO, "autofs: bind-mounting %s -> %s failed", mp->am_path, target2);
636       goto use_symlink;
637     }
638     goto out;
639   }
640 #endif /* MNT2_GEN_OPT_BIND */
641  use_symlink:
642   plog(XLOG_INFO, "autofs: symlinking %s -> %s", mp->am_path, target);
643   err = symlink(target, mp->am_path);
644 
645  out:
646   if (target2)
647     XFREE(target2);
648 
649   if (err)
650     return errno;
651   return 0;
652 }
653 
654 
655 int
656 autofs_umount_fs(am_node *mp, mntfs *mf)
657 {
658   int err = 0;
659   if (!(mf->mf_flags & MFF_ON_AUTOFS)) {
660     err = autofs_bind_umount(mp->am_path);
661     if (err)
662       return err;
663   }
664 
665   /*
666    * Multiple sublinks could reference this f/s.
667    * Don't actually unmount it unless we're holding the last reference.
668    */
669   if (mf->mf_refc == 1) {
670     err = mf->mf_ops->umount_fs(mp, mf);
671     if (err)
672       return err;
673     if (mf->mf_flags & MFF_ON_AUTOFS)
674       rmdir(mp->am_path);
675   }
676   return 0;
677 }
678 
679 
680 int
681 autofs_umount_succeeded(am_node *mp)
682 {
683   autofs_fh_t *fh = mp->am_parent->am_autofs_fh;
684   struct autofs_pending_umount **pp, *p;
685 
686   pp = &fh->pending_umounts;
687   while (*pp && !STREQ((*pp)->name, mp->am_name))
688     pp = &(*pp)->next;
689 
690   /* sanity check */
691   if (*pp == NULL)
692     return -1;
693 
694   p = *pp;
695   plog(XLOG_INFO, "autofs: unmounting %s succeeded", mp->am_path);
696   send_ready(fh->ioctlfd, p->wait_queue_token);
697 
698   XFREE(p->name);
699   *pp = p->next;
700   XFREE(p);
701   return 0;
702 }
703 
704 
705 int
706 autofs_umount_failed(am_node *mp)
707 {
708   autofs_fh_t *fh = mp->am_parent->am_autofs_fh;
709   struct autofs_pending_umount **pp, *p;
710 
711   pp = &fh->pending_umounts;
712   while (*pp && !STREQ((*pp)->name, mp->am_name))
713     pp = &(*pp)->next;
714 
715   /* sanity check */
716   if (*pp == NULL)
717     return -1;
718 
719   p = *pp;
720   plog(XLOG_INFO, "autofs: unmounting %s failed", mp->am_path);
721   send_fail(fh->ioctlfd, p->wait_queue_token);
722 
723   XFREE(p->name);
724   *pp = p->next;
725   XFREE(p);
726   return 0;
727 }
728 
729 
730 void
731 autofs_mount_succeeded(am_node *mp)
732 {
733   autofs_fh_t *fh = mp->am_parent->am_autofs_fh;
734   struct autofs_pending_mount **pp, *p;
735 
736   /*
737    * don't expire the entries -- the kernel will do it for us.
738    *
739    * but it won't do autofs filesystems, so we expire them the old
740    * fashioned way instead.
741    */
742   if (!(mp->am_mnt->mf_flags & MFF_IS_AUTOFS))
743     mp->am_flags |= AMF_NOTIMEOUT;
744 
745   pp = &fh->pending_mounts;
746   while (*pp && !STREQ((*pp)->name, mp->am_name))
747     pp = &(*pp)->next;
748 
749   /* sanity check */
750   if (*pp == NULL)
751     return;
752 
753   p = *pp;
754   plog(XLOG_INFO, "autofs: mounting %s succeeded", mp->am_path);
755   send_ready(fh->ioctlfd, p->wait_queue_token);
756 
757   XFREE(p->name);
758   *pp = p->next;
759   XFREE(p);
760 }
761 
762 
763 void
764 autofs_mount_failed(am_node *mp)
765 {
766   autofs_fh_t *fh = mp->am_parent->am_autofs_fh;
767   struct autofs_pending_mount **pp, *p;
768 
769   pp = &fh->pending_mounts;
770   while (*pp && !STREQ((*pp)->name, mp->am_name))
771     pp = &(*pp)->next;
772 
773   /* sanity check */
774   if (*pp == NULL)
775     return;
776 
777   p = *pp;
778   plog(XLOG_INFO, "autofs: mounting %s failed", mp->am_path);
779   send_fail(fh->ioctlfd, p->wait_queue_token);
780 
781   XFREE(p->name);
782   *pp = p->next;
783   XFREE(p);
784 }
785 
786 
787 void
788 autofs_get_opts(char *opts, size_t l, autofs_fh_t *fh)
789 {
790   xsnprintf(opts, l, "fd=%d,minproto=%d,maxproto=%d",
791 	    fh->kernelfd, AUTOFS_MIN_VERSION, AUTOFS_MAX_VERSION);
792 }
793 
794 
795 int
796 autofs_compute_mount_flags(mntent_t *mnt)
797 {
798   return 0;
799 }
800 
801 
802 #if AUTOFS_MAX_VERSION >= 4
803 static int autofs_timeout_mp_task(void *arg)
804 {
805   am_node *mp = (am_node *)arg;
806   autofs_fh_t *fh = mp->am_autofs_fh;
807   int now = 0;
808 
809   while (ioctl(fh->ioctlfd, AUTOFS_IOC_EXPIRE_MULTI, &now) == 0);
810   return 0;
811 }
812 #endif /* AUTOFS_MAX_VERSION >= 4 */
813 
814 
815 void autofs_timeout_mp(am_node *mp)
816 {
817   autofs_fh_t *fh = mp->am_autofs_fh;
818   time_t now = clocktime(NULL);
819 
820   /* update the ttl */
821   mp->am_autofs_ttl = now + gopt.am_timeo_w;
822 
823   if (fh->version < 4) {
824     struct autofs_packet_expire pkt;
825     while (ioctl(fh->ioctlfd, AUTOFS_IOC_EXPIRE, &pkt) == 0)
826       autofs_handle_expire(mp, &pkt);
827     return;
828   }
829 
830 #if AUTOFS_MAX_VERSION >= 4
831   run_task(autofs_timeout_mp_task, mp, NULL, NULL);
832 #endif /* AUTOFS_MAX_VERSION >= 4 */
833 }
834 
835 #endif /* HAVE_FS_AUTOFS */
836