#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <spawn.h>
#include <syslog.h>
#include <err.h>
#include <notify.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#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 };
setlinebuf(stdout);
setlinebuf(stderr);
openlog("autofsd", LOG_PID, LOG_DAEMON);
(void) setlocale(LC_ALL, "");
(void) umask(0);
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);
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);
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);
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);
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);
setup_mounts();
CFRunLoopRun();
syslog(LOG_ERR, "Run loop exited");
return 0;
}
#define STABLE_DELAY 5.0 // sec to delay after receiving notification
static void
sc_callback(__unused SCDynamicStoreRef store, __unused CFArrayRef changedKeys,
__unused void *info)
{
if (debounce_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) {
return;
}
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";
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;
}
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" : "");
}
}