1270096Strasz/*- 2270096Strasz * Copyright (c) 2014 The FreeBSD Foundation 3270096Strasz * All rights reserved. 4270096Strasz * 5270096Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6270096Strasz * from the FreeBSD Foundation. 7270096Strasz * 8270096Strasz * Redistribution and use in source and binary forms, with or without 9270096Strasz * modification, are permitted provided that the following conditions 10270096Strasz * are met: 11270096Strasz * 1. Redistributions of source code must retain the above copyright 12270096Strasz * notice, this list of conditions and the following disclaimer. 13270096Strasz * 2. Redistributions in binary form must reproduce the above copyright 14270096Strasz * notice, this list of conditions and the following disclaimer in the 15270096Strasz * documentation and/or other materials provided with the distribution. 16270096Strasz * 17270096Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20270096Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27270096Strasz * SUCH DAMAGE. 28270096Strasz * 29270096Strasz */ 30270096Strasz/*- 31270096Strasz * Copyright (c) 1989, 1991, 1993, 1995 32270096Strasz * The Regents of the University of California. All rights reserved. 33270096Strasz * 34270096Strasz * This code is derived from software contributed to Berkeley by 35270096Strasz * Rick Macklem at The University of Guelph. 36270096Strasz * 37270096Strasz * Redistribution and use in source and binary forms, with or without 38270096Strasz * modification, are permitted provided that the following conditions 39270096Strasz * are met: 40270096Strasz * 1. Redistributions of source code must retain the above copyright 41270096Strasz * notice, this list of conditions and the following disclaimer. 42270096Strasz * 2. Redistributions in binary form must reproduce the above copyright 43270096Strasz * notice, this list of conditions and the following disclaimer in the 44270096Strasz * documentation and/or other materials provided with the distribution. 45270096Strasz * 4. Neither the name of the University nor the names of its contributors 46270096Strasz * may be used to endorse or promote products derived from this software 47270096Strasz * without specific prior written permission. 48270096Strasz * 49270096Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52270096Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59270096Strasz * SUCH DAMAGE. 60270096Strasz * 61270096Strasz */ 62270096Strasz 63270897Strasz#include <sys/cdefs.h> 64270897Strasz __FBSDID("$FreeBSD$"); 65270897Strasz 66270096Strasz#include <sys/param.h> 67270096Strasz#include <sys/systm.h> 68270096Strasz#include <sys/buf.h> 69270096Strasz#include <sys/conf.h> 70270096Strasz#include <sys/dirent.h> 71270096Strasz#include <sys/ioccom.h> 72270096Strasz#include <sys/kernel.h> 73270096Strasz#include <sys/module.h> 74270096Strasz#include <sys/mount.h> 75270096Strasz#include <sys/refcount.h> 76270096Strasz#include <sys/sx.h> 77270096Strasz#include <sys/sysctl.h> 78270096Strasz#include <sys/syscallsubr.h> 79270096Strasz#include <sys/vnode.h> 80270096Strasz#include <machine/atomic.h> 81270096Strasz#include <vm/uma.h> 82270096Strasz 83270898Strasz#include <fs/autofs/autofs.h> 84270898Strasz#include <fs/autofs/autofs_ioctl.h> 85270096Strasz 86270096StraszMALLOC_DEFINE(M_AUTOFS, "autofs", "Automounter filesystem"); 87270096Strasz 88270096Straszuma_zone_t autofs_request_zone; 89270096Straszuma_zone_t autofs_node_zone; 90270096Strasz 91270096Straszstatic int autofs_open(struct cdev *dev, int flags, int fmt, 92270096Strasz struct thread *td); 93270096Straszstatic int autofs_close(struct cdev *dev, int flag, int fmt, 94270096Strasz struct thread *td); 95270096Straszstatic int autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, 96270096Strasz int mode, struct thread *td); 97270096Strasz 98270096Straszstatic struct cdevsw autofs_cdevsw = { 99270096Strasz .d_version = D_VERSION, 100270096Strasz .d_open = autofs_open, 101270096Strasz .d_close = autofs_close, 102270096Strasz .d_ioctl = autofs_ioctl, 103270096Strasz .d_name = "autofs", 104270096Strasz}; 105270096Strasz 106270096Strasz/* 107270096Strasz * List of signals that can interrupt an autofs trigger. Might be a good 108270096Strasz * idea to keep it synchronised with list in sys/fs/nfs/nfs_commonkrpc.c. 109270096Strasz */ 110270096Straszint autofs_sig_set[] = { 111270096Strasz SIGINT, 112270096Strasz SIGTERM, 113270096Strasz SIGHUP, 114270096Strasz SIGKILL, 115270096Strasz SIGQUIT 116270096Strasz}; 117270096Strasz 118270900Straszstruct autofs_softc *autofs_softc; 119270096Strasz 120270096StraszSYSCTL_NODE(_vfs, OID_AUTO, autofs, CTLFLAG_RD, 0, "Automounter filesystem"); 121270096Straszint autofs_debug = 1; 122270096StraszTUNABLE_INT("vfs.autofs.debug", &autofs_debug); 123270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, debug, CTLFLAG_RWTUN, 124270096Strasz &autofs_debug, 1, "Enable debug messages"); 125270096Straszint autofs_mount_on_stat = 0; 126270096StraszTUNABLE_INT("vfs.autofs.mount_on_stat", &autofs_mount_on_stat); 127270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, mount_on_stat, CTLFLAG_RWTUN, 128270096Strasz &autofs_mount_on_stat, 0, "Trigger mount on stat(2) on mountpoint"); 129270096Straszint autofs_timeout = 30; 130270096StraszTUNABLE_INT("vfs.autofs.timeout", &autofs_timeout); 131270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, timeout, CTLFLAG_RWTUN, 132270096Strasz &autofs_timeout, 30, "Number of seconds to wait for automountd(8)"); 133270096Straszint autofs_cache = 600; 134270096StraszTUNABLE_INT("vfs.autofs.cache", &autofs_cache); 135270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, cache, CTLFLAG_RWTUN, 136270096Strasz &autofs_cache, 600, "Number of seconds to wait before reinvoking " 137270096Strasz "automountd(8) for any given file or directory"); 138270096Straszint autofs_retry_attempts = 3; 139270096StraszTUNABLE_INT("vfs.autofs.retry_attempts", &autofs_retry_attempts); 140270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, retry_attempts, CTLFLAG_RWTUN, 141270096Strasz &autofs_retry_attempts, 3, "Number of attempts before failing mount"); 142270096Straszint autofs_retry_delay = 1; 143270096StraszTUNABLE_INT("vfs.autofs.retry_delay", &autofs_retry_delay); 144270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, retry_delay, CTLFLAG_RWTUN, 145270096Strasz &autofs_retry_delay, 1, "Number of seconds before retrying"); 146270096Straszint autofs_interruptible = 1; 147270096StraszTUNABLE_INT("vfs.autofs.interruptible", &autofs_interruptible); 148270096StraszSYSCTL_INT(_vfs_autofs, OID_AUTO, interruptible, CTLFLAG_RWTUN, 149270096Strasz &autofs_interruptible, 1, "Allow requests to be interrupted by signal"); 150270096Strasz 151270096Straszint 152270096Straszautofs_init(struct vfsconf *vfsp) 153270096Strasz{ 154270096Strasz int error; 155270096Strasz 156270900Strasz KASSERT(autofs_softc == NULL, 157270900Strasz ("softc %p, should be NULL", autofs_softc)); 158270096Strasz 159270900Strasz autofs_softc = malloc(sizeof(*autofs_softc), M_AUTOFS, 160270900Strasz M_WAITOK | M_ZERO); 161270900Strasz 162270096Strasz autofs_request_zone = uma_zcreate("autofs_request", 163270096Strasz sizeof(struct autofs_request), NULL, NULL, NULL, NULL, 164270096Strasz UMA_ALIGN_PTR, 0); 165270096Strasz autofs_node_zone = uma_zcreate("autofs_node", 166270096Strasz sizeof(struct autofs_node), NULL, NULL, NULL, NULL, 167270096Strasz UMA_ALIGN_PTR, 0); 168270096Strasz 169270900Strasz TAILQ_INIT(&autofs_softc->sc_requests); 170270900Strasz cv_init(&autofs_softc->sc_cv, "autofscv"); 171270900Strasz sx_init(&autofs_softc->sc_lock, "autofslk"); 172270096Strasz 173270900Strasz error = make_dev_p(MAKEDEV_CHECKNAME, &autofs_softc->sc_cdev, 174270900Strasz &autofs_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "autofs"); 175270096Strasz if (error != 0) { 176270096Strasz AUTOFS_WARN("failed to create device node, error %d", error); 177270900Strasz uma_zdestroy(autofs_request_zone); 178270900Strasz uma_zdestroy(autofs_node_zone); 179270900Strasz free(autofs_softc, M_AUTOFS); 180270900Strasz 181270096Strasz return (error); 182270096Strasz } 183270900Strasz autofs_softc->sc_cdev->si_drv1 = autofs_softc; 184270096Strasz 185270096Strasz return (0); 186270096Strasz} 187270096Strasz 188270096Straszint 189270096Straszautofs_uninit(struct vfsconf *vfsp) 190270096Strasz{ 191270096Strasz 192270900Strasz sx_xlock(&autofs_softc->sc_lock); 193270900Strasz if (autofs_softc->sc_dev_opened) { 194270900Strasz sx_xunlock(&autofs_softc->sc_lock); 195270096Strasz return (EBUSY); 196270096Strasz } 197270900Strasz if (autofs_softc->sc_cdev != NULL) 198270900Strasz destroy_dev(autofs_softc->sc_cdev); 199270096Strasz 200270096Strasz uma_zdestroy(autofs_request_zone); 201270096Strasz uma_zdestroy(autofs_node_zone); 202270096Strasz 203270900Strasz sx_xunlock(&autofs_softc->sc_lock); 204270096Strasz /* 205270096Strasz * XXX: Race with open? 206270096Strasz */ 207270900Strasz free(autofs_softc, M_AUTOFS); 208270096Strasz 209270096Strasz return (0); 210270096Strasz} 211270096Strasz 212270096Straszbool 213270096Straszautofs_ignore_thread(const struct thread *td) 214270096Strasz{ 215270096Strasz struct proc *p; 216270096Strasz 217270096Strasz p = td->td_proc; 218270096Strasz 219270900Strasz if (autofs_softc->sc_dev_opened == false) 220270096Strasz return (false); 221270096Strasz 222270096Strasz PROC_LOCK(p); 223270900Strasz if (p->p_session->s_sid == autofs_softc->sc_dev_sid) { 224270096Strasz PROC_UNLOCK(p); 225270096Strasz return (true); 226270096Strasz } 227270096Strasz PROC_UNLOCK(p); 228270096Strasz 229270096Strasz return (false); 230270096Strasz} 231270096Strasz 232270096Straszstatic char * 233270096Straszautofs_path(struct autofs_node *anp) 234270096Strasz{ 235270096Strasz struct autofs_mount *amp; 236270096Strasz char *path, *tmp; 237270096Strasz 238270096Strasz amp = anp->an_mount; 239270096Strasz 240270096Strasz path = strdup("", M_AUTOFS); 241270096Strasz for (; anp->an_parent != NULL; anp = anp->an_parent) { 242270096Strasz tmp = malloc(strlen(anp->an_name) + strlen(path) + 2, 243270096Strasz M_AUTOFS, M_WAITOK); 244270096Strasz strcpy(tmp, anp->an_name); 245270096Strasz strcat(tmp, "/"); 246270096Strasz strcat(tmp, path); 247270096Strasz free(path, M_AUTOFS); 248270096Strasz path = tmp; 249270096Strasz } 250270096Strasz 251270096Strasz tmp = malloc(strlen(amp->am_mountpoint) + strlen(path) + 2, 252270096Strasz M_AUTOFS, M_WAITOK); 253270096Strasz strcpy(tmp, amp->am_mountpoint); 254270096Strasz strcat(tmp, "/"); 255270096Strasz strcat(tmp, path); 256270096Strasz free(path, M_AUTOFS); 257270096Strasz path = tmp; 258270096Strasz 259270096Strasz return (path); 260270096Strasz} 261270096Strasz 262270096Straszstatic void 263270096Straszautofs_callout(void *context) 264270096Strasz{ 265270096Strasz struct autofs_request *ar; 266270096Strasz 267270096Strasz ar = context; 268270096Strasz 269270900Strasz sx_xlock(&autofs_softc->sc_lock); 270270096Strasz AUTOFS_WARN("request %d for %s timed out after %d seconds", 271270096Strasz ar->ar_id, ar->ar_path, autofs_timeout); 272270096Strasz /* 273270096Strasz * XXX: EIO perhaps? 274270096Strasz */ 275270096Strasz ar->ar_error = ETIMEDOUT; 276270096Strasz ar->ar_done = true; 277270096Strasz ar->ar_in_progress = false; 278270900Strasz cv_broadcast(&autofs_softc->sc_cv); 279270900Strasz sx_xunlock(&autofs_softc->sc_lock); 280270096Strasz} 281270096Strasz 282270096Straszbool 283270096Straszautofs_cached(struct autofs_node *anp, const char *component, int componentlen) 284270096Strasz{ 285270096Strasz int error; 286270096Strasz struct autofs_mount *amp; 287270096Strasz 288270096Strasz amp = anp->an_mount; 289270096Strasz 290270096Strasz AUTOFS_ASSERT_UNLOCKED(amp); 291270096Strasz 292270096Strasz /* 293270096Strasz * For top-level nodes we need to request automountd(8) 294270096Strasz * assistance even if the node is marked as cached, 295270096Strasz * but the requested subdirectory does not exist. This 296270096Strasz * is necessary for wildcard indirect map keys to work. 297270096Strasz */ 298270096Strasz if (anp->an_parent == NULL && componentlen != 0) { 299270096Strasz AUTOFS_LOCK(amp); 300270096Strasz error = autofs_node_find(anp, component, componentlen, NULL); 301270096Strasz AUTOFS_UNLOCK(amp); 302270096Strasz if (error != 0) 303270096Strasz return (false); 304270096Strasz } 305270096Strasz 306270096Strasz return (anp->an_cached); 307270096Strasz} 308270096Strasz 309270096Straszstatic void 310270096Straszautofs_cache_callout(void *context) 311270096Strasz{ 312270096Strasz struct autofs_node *anp; 313270096Strasz 314270096Strasz anp = context; 315270096Strasz anp->an_cached = false; 316270096Strasz} 317270096Strasz 318270096Strasz/* 319270096Strasz * The set/restore sigmask functions are used to (temporarily) overwrite 320270096Strasz * the thread td_sigmask during triggering. 321270096Strasz */ 322270096Straszstatic void 323270096Straszautofs_set_sigmask(sigset_t *oldset) 324270096Strasz{ 325270096Strasz sigset_t newset; 326270096Strasz int i; 327270096Strasz 328270096Strasz SIGFILLSET(newset); 329270096Strasz /* Remove the autofs set of signals from newset */ 330270096Strasz PROC_LOCK(curproc); 331270096Strasz mtx_lock(&curproc->p_sigacts->ps_mtx); 332270096Strasz for (i = 0 ; i < sizeof(autofs_sig_set)/sizeof(int) ; i++) { 333270096Strasz /* 334270096Strasz * But make sure we leave the ones already masked 335270096Strasz * by the process, i.e. remove the signal from the 336270096Strasz * temporary signalmask only if it wasn't already 337270096Strasz * in p_sigmask. 338270096Strasz */ 339270096Strasz if (!SIGISMEMBER(curthread->td_sigmask, autofs_sig_set[i]) && 340270096Strasz !SIGISMEMBER(curproc->p_sigacts->ps_sigignore, 341270096Strasz autofs_sig_set[i])) { 342270096Strasz SIGDELSET(newset, autofs_sig_set[i]); 343270096Strasz } 344270096Strasz } 345270096Strasz mtx_unlock(&curproc->p_sigacts->ps_mtx); 346270096Strasz kern_sigprocmask(curthread, SIG_SETMASK, &newset, oldset, 347270096Strasz SIGPROCMASK_PROC_LOCKED); 348270096Strasz PROC_UNLOCK(curproc); 349270096Strasz} 350270096Strasz 351270096Straszstatic void 352270096Straszautofs_restore_sigmask(sigset_t *set) 353270096Strasz{ 354270096Strasz 355270096Strasz kern_sigprocmask(curthread, SIG_SETMASK, set, NULL, 0); 356270096Strasz} 357270096Strasz 358270096Straszstatic int 359270096Straszautofs_trigger_one(struct autofs_node *anp, 360270096Strasz const char *component, int componentlen) 361270096Strasz{ 362270096Strasz sigset_t oldset; 363270096Strasz struct autofs_mount *amp; 364270096Strasz struct autofs_node *firstanp; 365270096Strasz struct autofs_request *ar; 366270096Strasz char *key, *path; 367270096Strasz int error = 0, request_error, last; 368270096Strasz 369270096Strasz amp = VFSTOAUTOFS(anp->an_vnode->v_mount); 370270096Strasz 371270900Strasz sx_assert(&autofs_softc->sc_lock, SA_XLOCKED); 372270096Strasz 373270096Strasz if (anp->an_parent == NULL) { 374270096Strasz key = strndup(component, componentlen, M_AUTOFS); 375270096Strasz } else { 376270096Strasz for (firstanp = anp; firstanp->an_parent->an_parent != NULL; 377270096Strasz firstanp = firstanp->an_parent) 378270096Strasz continue; 379270096Strasz key = strdup(firstanp->an_name, M_AUTOFS); 380270096Strasz } 381270096Strasz 382270096Strasz path = autofs_path(anp); 383270096Strasz 384270900Strasz TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 385270096Strasz if (strcmp(ar->ar_path, path) != 0) 386270096Strasz continue; 387270096Strasz if (strcmp(ar->ar_key, key) != 0) 388270096Strasz continue; 389270096Strasz 390270096Strasz KASSERT(strcmp(ar->ar_from, amp->am_from) == 0, 391270096Strasz ("from changed; %s != %s", ar->ar_from, amp->am_from)); 392270096Strasz KASSERT(strcmp(ar->ar_prefix, amp->am_prefix) == 0, 393270096Strasz ("prefix changed; %s != %s", 394270096Strasz ar->ar_prefix, amp->am_prefix)); 395270096Strasz KASSERT(strcmp(ar->ar_options, amp->am_options) == 0, 396270096Strasz ("options changed; %s != %s", 397270096Strasz ar->ar_options, amp->am_options)); 398270096Strasz 399270096Strasz break; 400270096Strasz } 401270096Strasz 402270096Strasz if (ar != NULL) { 403270096Strasz refcount_acquire(&ar->ar_refcount); 404270096Strasz } else { 405270096Strasz ar = uma_zalloc(autofs_request_zone, M_WAITOK | M_ZERO); 406270096Strasz ar->ar_mount = amp; 407270096Strasz 408270900Strasz ar->ar_id = 409270900Strasz atomic_fetchadd_int(&autofs_softc->sc_last_request_id, 1); 410270096Strasz strlcpy(ar->ar_from, amp->am_from, sizeof(ar->ar_from)); 411270096Strasz strlcpy(ar->ar_path, path, sizeof(ar->ar_path)); 412270096Strasz strlcpy(ar->ar_prefix, amp->am_prefix, sizeof(ar->ar_prefix)); 413270096Strasz strlcpy(ar->ar_key, key, sizeof(ar->ar_key)); 414270096Strasz strlcpy(ar->ar_options, 415270096Strasz amp->am_options, sizeof(ar->ar_options)); 416270096Strasz 417270096Strasz callout_init(&ar->ar_callout, 1); 418270096Strasz callout_reset(&ar->ar_callout, 419270096Strasz autofs_timeout * hz, autofs_callout, ar); 420270096Strasz refcount_init(&ar->ar_refcount, 1); 421270900Strasz TAILQ_INSERT_TAIL(&autofs_softc->sc_requests, ar, ar_next); 422270096Strasz } 423270096Strasz 424270900Strasz cv_broadcast(&autofs_softc->sc_cv); 425270096Strasz while (ar->ar_done == false) { 426270096Strasz if (autofs_interruptible != 0) { 427270096Strasz autofs_set_sigmask(&oldset); 428270900Strasz error = cv_wait_sig(&autofs_softc->sc_cv, 429270900Strasz &autofs_softc->sc_lock); 430270096Strasz autofs_restore_sigmask(&oldset); 431270096Strasz if (error != 0) { 432270096Strasz /* 433270096Strasz * XXX: For some reson this returns -1 434270096Strasz * instead of EINTR, wtf?! 435270096Strasz */ 436270096Strasz error = EINTR; 437270096Strasz AUTOFS_WARN("cv_wait_sig for %s failed " 438270096Strasz "with error %d", ar->ar_path, error); 439270096Strasz break; 440270096Strasz } 441270096Strasz } else { 442270900Strasz cv_wait(&autofs_softc->sc_cv, &autofs_softc->sc_lock); 443270096Strasz } 444270096Strasz } 445270096Strasz 446270096Strasz request_error = ar->ar_error; 447270096Strasz if (request_error != 0) { 448270096Strasz AUTOFS_WARN("request for %s completed with error %d", 449270096Strasz ar->ar_path, request_error); 450270096Strasz } 451270096Strasz 452270096Strasz last = refcount_release(&ar->ar_refcount); 453270096Strasz if (last) { 454270900Strasz TAILQ_REMOVE(&autofs_softc->sc_requests, ar, ar_next); 455270096Strasz /* 456270096Strasz * XXX: Is it safe? 457270096Strasz */ 458270900Strasz sx_xunlock(&autofs_softc->sc_lock); 459270096Strasz callout_drain(&ar->ar_callout); 460270900Strasz sx_xlock(&autofs_softc->sc_lock); 461270096Strasz uma_zfree(autofs_request_zone, ar); 462270096Strasz } 463270096Strasz 464270096Strasz /* 465270096Strasz * Note that we do not do negative caching on purpose. This 466270096Strasz * way the user can retry access at any time, e.g. after fixing 467270096Strasz * the failure reason, without waiting for cache timer to expire. 468270096Strasz */ 469270096Strasz if (error == 0 && request_error == 0 && autofs_cache > 0) { 470270096Strasz anp->an_cached = true; 471270096Strasz callout_reset(&anp->an_callout, autofs_cache * hz, 472270096Strasz autofs_cache_callout, anp); 473270096Strasz } 474270096Strasz 475270096Strasz free(key, M_AUTOFS); 476270096Strasz free(path, M_AUTOFS); 477270096Strasz 478270096Strasz if (error != 0) 479270096Strasz return (error); 480270096Strasz return (request_error); 481270096Strasz} 482270096Strasz 483270096Strasz/* 484270096Strasz * Send request to automountd(8) and wait for completion. 485270096Strasz */ 486270096Straszint 487270096Straszautofs_trigger(struct autofs_node *anp, 488270096Strasz const char *component, int componentlen) 489270096Strasz{ 490270096Strasz int error; 491270096Strasz 492270096Strasz for (;;) { 493270096Strasz error = autofs_trigger_one(anp, component, componentlen); 494270096Strasz if (error == 0) { 495270096Strasz anp->an_retries = 0; 496270096Strasz return (0); 497270096Strasz } 498270096Strasz if (error == EINTR) { 499270096Strasz AUTOFS_DEBUG("trigger interrupted by signal, " 500270096Strasz "not retrying"); 501270096Strasz anp->an_retries = 0; 502270096Strasz return (error); 503270096Strasz } 504270096Strasz anp->an_retries++; 505270096Strasz if (anp->an_retries >= autofs_retry_attempts) { 506270096Strasz AUTOFS_DEBUG("trigger failed %d times; returning " 507270096Strasz "error %d", anp->an_retries, error); 508270096Strasz anp->an_retries = 0; 509270096Strasz return (error); 510270096Strasz 511270096Strasz } 512270096Strasz AUTOFS_DEBUG("trigger failed with error %d; will retry in " 513270096Strasz "%d seconds, %d attempts left", error, autofs_retry_delay, 514270096Strasz autofs_retry_attempts - anp->an_retries); 515270900Strasz sx_xunlock(&autofs_softc->sc_lock); 516270096Strasz pause("autofs_retry", autofs_retry_delay * hz); 517270900Strasz sx_xlock(&autofs_softc->sc_lock); 518270096Strasz } 519270096Strasz} 520270096Strasz 521270096Straszstatic int 522270900Straszautofs_ioctl_request(struct autofs_daemon_request *adr) 523270096Strasz{ 524270096Strasz struct autofs_request *ar; 525270096Strasz int error; 526270096Strasz 527270900Strasz sx_xlock(&autofs_softc->sc_lock); 528270096Strasz for (;;) { 529270900Strasz TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 530270096Strasz if (ar->ar_done) 531270096Strasz continue; 532270096Strasz if (ar->ar_in_progress) 533270096Strasz continue; 534270096Strasz 535270096Strasz break; 536270096Strasz } 537270096Strasz 538270096Strasz if (ar != NULL) 539270096Strasz break; 540270096Strasz 541270900Strasz error = cv_wait_sig(&autofs_softc->sc_cv, 542270900Strasz &autofs_softc->sc_lock); 543270096Strasz if (error != 0) { 544270096Strasz /* 545270096Strasz * XXX: For some reson this returns -1 instead 546270096Strasz * of EINTR, wtf?! 547270096Strasz */ 548270096Strasz error = EINTR; 549270900Strasz sx_xunlock(&autofs_softc->sc_lock); 550270096Strasz AUTOFS_DEBUG("failed with error %d", error); 551270096Strasz return (error); 552270096Strasz } 553270096Strasz } 554270096Strasz 555270096Strasz ar->ar_in_progress = true; 556270900Strasz sx_xunlock(&autofs_softc->sc_lock); 557270096Strasz 558270096Strasz adr->adr_id = ar->ar_id; 559270096Strasz strlcpy(adr->adr_from, ar->ar_from, sizeof(adr->adr_from)); 560270096Strasz strlcpy(adr->adr_path, ar->ar_path, sizeof(adr->adr_path)); 561270096Strasz strlcpy(adr->adr_prefix, ar->ar_prefix, sizeof(adr->adr_prefix)); 562270096Strasz strlcpy(adr->adr_key, ar->ar_key, sizeof(adr->adr_key)); 563270096Strasz strlcpy(adr->adr_options, ar->ar_options, sizeof(adr->adr_options)); 564270096Strasz 565270096Strasz PROC_LOCK(curproc); 566270900Strasz autofs_softc->sc_dev_sid = curproc->p_session->s_sid; 567270096Strasz PROC_UNLOCK(curproc); 568270096Strasz 569270096Strasz return (0); 570270096Strasz} 571270096Strasz 572270096Straszstatic int 573270900Straszautofs_ioctl_done(struct autofs_daemon_done *add) 574270096Strasz{ 575270096Strasz struct autofs_request *ar; 576270096Strasz 577270900Strasz sx_xlock(&autofs_softc->sc_lock); 578270900Strasz TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 579270096Strasz if (ar->ar_id == add->add_id) 580270096Strasz break; 581270096Strasz } 582270096Strasz 583270096Strasz if (ar == NULL) { 584270900Strasz sx_xunlock(&autofs_softc->sc_lock); 585270096Strasz AUTOFS_DEBUG("id %d not found", add->add_id); 586270096Strasz return (ESRCH); 587270096Strasz } 588270096Strasz 589270096Strasz ar->ar_error = add->add_error; 590270096Strasz ar->ar_done = true; 591270096Strasz ar->ar_in_progress = false; 592270900Strasz cv_broadcast(&autofs_softc->sc_cv); 593270096Strasz 594270900Strasz sx_xunlock(&autofs_softc->sc_lock); 595270096Strasz 596270096Strasz return (0); 597270096Strasz} 598270096Strasz 599270096Straszstatic int 600270096Straszautofs_open(struct cdev *dev, int flags, int fmt, struct thread *td) 601270096Strasz{ 602270096Strasz 603270900Strasz sx_xlock(&autofs_softc->sc_lock); 604270899Strasz /* 605270899Strasz * We must never block automountd(8) and its descendants, and we use 606270899Strasz * session ID to determine that: we store session id of the process 607270899Strasz * that opened the device, and then compare it with session ids 608270899Strasz * of triggering processes. This means running a second automountd(8) 609270899Strasz * instance would break the previous one. The check below prevents 610270899Strasz * it from happening. 611270899Strasz */ 612270900Strasz if (autofs_softc->sc_dev_opened) { 613270900Strasz sx_xunlock(&autofs_softc->sc_lock); 614270096Strasz return (EBUSY); 615270096Strasz } 616270096Strasz 617270900Strasz autofs_softc->sc_dev_opened = true; 618270900Strasz sx_xunlock(&autofs_softc->sc_lock); 619270096Strasz 620270096Strasz return (0); 621270096Strasz} 622270096Strasz 623270096Straszstatic int 624270096Straszautofs_close(struct cdev *dev, int flag, int fmt, struct thread *td) 625270096Strasz{ 626270096Strasz 627270900Strasz sx_xlock(&autofs_softc->sc_lock); 628270900Strasz KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); 629270900Strasz autofs_softc->sc_dev_opened = false; 630270900Strasz sx_xunlock(&autofs_softc->sc_lock); 631270096Strasz 632270096Strasz return (0); 633270096Strasz} 634270096Strasz 635270096Straszstatic int 636270096Straszautofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 637270096Strasz struct thread *td) 638270096Strasz{ 639270096Strasz 640270900Strasz KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); 641270096Strasz 642270096Strasz switch (cmd) { 643270096Strasz case AUTOFSREQUEST: 644270900Strasz return (autofs_ioctl_request( 645270096Strasz (struct autofs_daemon_request *)arg)); 646270096Strasz case AUTOFSDONE: 647270900Strasz return (autofs_ioctl_done( 648270096Strasz (struct autofs_daemon_done *)arg)); 649270096Strasz default: 650270096Strasz AUTOFS_DEBUG("invalid cmd %lx", cmd); 651270096Strasz return (EINVAL); 652270096Strasz } 653270096Strasz} 654