/* * Copyright (c) 2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include /* The following constant was stolen from */ #ifndef kDSStdNotifySearchPolicyChanged #define kDSStdNotifySearchPolicyChanged "com.apple.DirectoryService.NotifyTypeStandard:SearchPolicyChanged" #endif static char automount_path[] = "/usr/sbin/automount"; static void sc_callback(SCDynamicStoreRef, CFArrayRef, void *); static void debounce_callback(CFRunLoopTimerRef, void *); static void volume_unmounted_callback(CFMachPortRef, void *, CFIndex, void *); static void cancel_debounce_timer(void); static void setup_mounts(void); static CFRunLoopTimerRef debounce_timer; int main(__unused int argc, __unused char **argv) { CFRunLoopSourceRef rls; SCDynamicStoreRef store; CFMutableArrayRef keys, patterns; CFStringRef key, pattern; uint32_t status; int volume_unmount_token; mach_port_t port; CFMachPortRef mach_port_ref; CFMachPortContext context = { 0, NULL, NULL, NULL, NULL }; /* * If launchd is redirecting these two files they'll be block- * buffered, as they'll be pipes, or some other such non-tty, * sending data to launchd. Probably not what you want. */ setlinebuf(stdout); setlinebuf(stderr); openlog("autofsd", LOG_PID, LOG_DAEMON); (void) setlocale(LC_ALL, ""); (void) umask(0); /* * Register for notifications from the System Configuration framework. * We want to re-evaluate the autofs mounts to be done whenever * we get a network or directory services search policy change, * as that might change the automounter maps and fstab entries we get. */ store = SCDynamicStoreCreate(NULL, CFSTR("autofsd"), sc_callback, NULL); if (!store) { syslog(LOG_ERR, "Couldn't open session with configd: %s", SCErrorString(SCError())); exit(EXIT_FAILURE); } keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); /* * Establish dynamic store keys and patterns for notifications from * network change events. */ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); CFArrayAppendValue(keys, key); CFRelease(key); pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); CFArrayAppendValue(patterns, pattern); CFRelease(pattern); key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); CFArrayAppendValue(keys, key); CFRelease(key); pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); CFArrayAppendValue(patterns, pattern); CFRelease(pattern); /* * Establish and register dynamic store key to watch directory * services search policy changes. */ key = SCDynamicStoreKeyCreate(NULL, CFSTR(kDSStdNotifySearchPolicyChanged)); CFArrayAppendValue(keys, key); CFRelease(key); if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) { syslog(LOG_ERR, "Couldn't register notification keys: %s", SCErrorString(SCError())); CFRelease(store); CFRelease(keys); CFRelease(patterns); exit(EXIT_FAILURE); } CFRelease(keys); CFRelease(patterns); /* add a callback */ rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); if (rls == NULL) { syslog(LOG_ERR, "Couldn't create run loop source: %s", SCErrorString(SCError())); CFRelease(store); exit(EXIT_FAILURE); } CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); /* * Also watch for volume unmounts. If, for example, you * disconnect from a network, and some mount is no longer * accessible, but it's mounted atop an autofs mount, and * that autofs mount should be removed (because we're no * longer getting that fstab or map entry from Directory * Services), it can't be removed at the time we get the * network change notification. However, if the user later * gets a "server not responding" dialog and asks to disconnect * from the server, the mount will be forcibly unmounted, * which might allow us to unmount the autofs mount. */ status = notify_register_mach_port("com.apple.system.kernel.unmount", &port, 0, &volume_unmount_token); if (status != NOTIFY_STATUS_OK) { syslog(LOG_ERR, "Couldn't get Mach port for volume unmount notifications: %u", status); exit(EXIT_FAILURE); } mach_port_ref = CFMachPortCreateWithPort(NULL, port, volume_unmounted_callback, &context, NULL); if (mach_port_ref == NULL) { syslog(LOG_ERR, "Couldn't create CFMachPort for volume unmount notifications"); exit(EXIT_FAILURE); } rls = CFMachPortCreateRunLoopSource(NULL, mach_port_ref, 0); if (rls == NULL) { syslog(LOG_ERR, "Couldn't create CFRunLoopSource for volume unmount notifications"); exit(EXIT_FAILURE); } CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); /* * Set up the initial set of mounts. */ setup_mounts(); /* * Now wait to be told to update the mounts. */ CFRunLoopRun(); syslog(LOG_ERR, "Run loop exited"); return 0; } #define STABLE_DELAY 5.0 // sec to delay after receiving notification /* * Network notifications, in particular, can come thick and fast. * To avoid doing a mount check on each one, we wait STABLE_DELAY * seconds for a stable network status. * Each network notification restarts the debounce timer. * It calls its callback function only if it has been running * uninterrupted for STABLE_DELAY seconds. * * XXX - do we want to debounce DS policy change notifications, or do we * want to respond to those immediately, canceling any running debounce * timer? */ static void sc_callback(__unused SCDynamicStoreRef store, __unused CFArrayRef changedKeys, __unused void *info) { if (debounce_timer) { /* Already running - cancel the timer */ cancel_debounce_timer(); } debounce_timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + STABLE_DELAY, 0.0, 0, 0, debounce_callback, NULL); CFRunLoopAddTimer(CFRunLoopGetCurrent(), debounce_timer, kCFRunLoopDefaultMode); } static void debounce_callback(__unused CFRunLoopTimerRef timer, __unused void *info) { cancel_debounce_timer(); setup_mounts(); } static void volume_unmounted_callback(__unused CFMachPortRef port, __unused void *msg, __unused CFIndex size, __unused void *info) { if (debounce_timer) { /* * The debounce timer is running; wait for it to fire * before running automount. */ return; } /* * Run automount now, to try to get rid of any out-of-date triggers * ASAP. */ setup_mounts(); } static void cancel_debounce_timer(void) { CFRunLoopTimerInvalidate(debounce_timer); CFRelease(debounce_timer); debounce_timer = NULL; } static void setup_mounts(void) { int error; char *args[3]; int i; pid_t child; pid_t pid; int status; extern char **environ; i = 0; args[i++] = automount_path; args[i++] = "-c"; /* tell automountd to clear its caches */ args[i] = NULL; error = posix_spawn(&child, automount_path, NULL, NULL, args, environ); if (error != 0) { syslog(LOG_ERR, "Can't run %s: %s", automount_path, strerror(error)); return; } /* * Wait for the child to complete. */ for (;;) { pid = waitpid(child, &status, 0); if (pid == child) break; if (pid == -1 && errno != EINTR) { syslog(LOG_ERR, "Error %m while waiting for %s", automount_path); return; } } if (WIFSIGNALED(status)) { syslog(LOG_ERR, "%s terminated with signal %s%s", automount_path, strsignal(WTERMSIG(status)), WCOREDUMP(status) ? "- core dumped" : ""); } }