/* * Copyright (c) 2000-2004 Apple Computer, 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 "webdavd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" #include "webdav_authcache.h" #include "webdav_network.h" #include "webdav_requestqueue.h" #include "webdav_cache.h" /*****************************************************************************/ /* * shared globals */ unsigned int gtimeout_val; /* the pulse_thread runs at double this rate */ char *gtimeout_string; /* the length of time LOCKs are held on on the server */ int gWebdavfsDebug = FALSE; /* TRUE if the WEBDAVFS_DEBUG environment variable is set */ uid_t gProcessUID = -1; /* the daemon's UID */ int gSuppressAllUI = FALSE; /* if TRUE, the mount requested that all UI be supressed */ char gWebdavCachePath[MAXPATHLEN + 1] = ""; /* the current path to the cache directory */ int gSecureConnection = FALSE; /* if TRUE, the connection is secure */ /* * mount_webdav.c file globals */ static int wakeupFDs[2] = { -1, -1 }; /* used by webdav_kill() to communicate with main select loop */ static char mountPoint[MAXPATHLEN]; /* path to our mount point */ static char mntfromname[MNAMELEN]; /* the mntfromname */ /*****************************************************************************/ void webdav_debug_assert(const char *componentNameString, const char *assertionString, const char *exceptionLabelString, const char *errorString, const char *fileName, long lineNumber, int errorCode) { #pragma unused(componentNameString) if ( (assertionString != NULL) && (*assertionString != '\0') ) { if ( errorCode != 0 ) { syslog(WEBDAV_LOG_LEVEL, "(%s) failed with %d%s%s%s%s; file: %s; line: %ld", assertionString, errorCode, (errorString != NULL) ? "; " : "", (errorString != NULL) ? errorString : "", (exceptionLabelString != NULL) ? "; going to " : "", (exceptionLabelString != NULL) ? exceptionLabelString : "", fileName, lineNumber); } else { syslog(WEBDAV_LOG_LEVEL, "(%s) failed%s%s%s%s; file: %s; line: %ld", assertionString, (errorString != NULL) ? "; " : "", (errorString != NULL) ? errorString : "", (exceptionLabelString != NULL) ? "; going to " : "", (exceptionLabelString != NULL) ? exceptionLabelString : "", fileName, lineNumber); } } else { syslog(WEBDAV_LOG_LEVEL, "%s; file: %s; line: %ld", errorString, fileName, lineNumber); } } /*****************************************************************************/ /* * webdav_kill lets the select loop know to call webdav_force_unmount by feeding * the wakeupFDs pipe. This is the signal handler for signals that should * terminate us. */ void webdav_kill(int message) { /* if there's a read end of the pipe*/ if (wakeupFDs[0] != -1) { /* if there's a write end of the pipe */ if (wakeupFDs[1] != -1) { /* write the message */ verify(write(wakeupFDs[1], &message, sizeof(int)) == sizeof(int)); } /* else we are already in the process of force unmounting */ } else { /* there's no read end so just exit */ exit(EXIT_FAILURE); } } /*****************************************************************************/ /* * webdav_force_unmount * * webdav_force_unmount is called from our select loop when the mount_webdav * process receives a signal, or hits some unrecoverable condition which * requires a force unmount. */ static void webdav_force_unmount(char *mntpt) { int pid, terminated_pid; int result = -1; union wait status; pid = fork(); if (pid == 0) { result = execl(PRIVATE_UNMOUNT_COMMAND, PRIVATE_UNMOUNT_COMMAND, PRIVATE_UNMOUNT_FLAGS, mntpt, NULL); /* We can only get here if the exec failed */ goto Return; } require(pid != -1, Return); /* wait for completion here */ while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 ) { /* retry if EINTR, else break out with error */ if ( errno != EINTR ) { break; } } Return: /* execution will not reach this point unless umount fails */ check_noerr_string(errno, strerror(errno)); _exit(EXIT_FAILURE); } /*****************************************************************************/ /* * The attempt_webdav_load function forks and executes the load_webdav command * which in turn loads the webdavfs kext. */ static int attempt_webdav_load(void) { int pid, terminated_pid; int result = -1; union wait status; pid = fork(); if (pid == 0) { result = execl(PRIVATE_LOAD_COMMAND, PRIVATE_LOAD_COMMAND, NULL); /* We can only get here if the exec failed */ goto Return; } require_action(pid != -1, Return, result = errno); /* Success! */ while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 ) { /* retry if EINTR, else break out with error */ if ( errno != EINTR ) { break; } } if ( (terminated_pid == pid) && (WIFEXITED(status)) ) { result = WEXITSTATUS(status); } else { result = -1; } Return: check_noerr_string(result, strerror(errno)); return result; } /*****************************************************************************/ /* called with child normally terminates the parent */ static void parentexit(int x) { #pragma unused(x) exit(EXIT_SUCCESS); } /*****************************************************************************/ /* start up a new thread to call webdav_force_unmount() */ static void create_unmount_thread(void) { int error; pthread_t unmount_thread; pthread_attr_t unmount_thread_attr; error = pthread_attr_init(&unmount_thread_attr); require_noerr(error, pthread_attr_init); error = pthread_attr_setdetachstate(&unmount_thread_attr, PTHREAD_CREATE_DETACHED); require_noerr(error, pthread_attr_setdetachstate); error = pthread_create(&unmount_thread, &unmount_thread_attr, (void *)webdav_force_unmount, (void *)mntfromname); require_noerr(error, pthread_create); return; pthread_create: pthread_attr_setdetachstate: pthread_attr_init: exit(error); } /*****************************************************************************/ /* start up a new thread to call network_update_proxy() */ static void create_change_thread(void) { int error; pthread_t change_thread; pthread_attr_t change_thread_attr; error = pthread_attr_init(&change_thread_attr); require_noerr(error, pthread_attr_init); error = pthread_attr_setdetachstate(&change_thread_attr, PTHREAD_CREATE_DETACHED); require_noerr(error, pthread_attr_setdetachstate); error = pthread_create(&change_thread, &change_thread_attr, (void *)network_update_proxy, (void *)NULL); require_noerr(error, pthread_create); return; pthread_create: pthread_attr_setdetachstate: pthread_attr_init: exit(error); } /*****************************************************************************/ static void usage(void) { (void)fprintf(stderr, "usage: mount_webdav [-S] [-a] [-o options] [-v ]\n"); (void)fprintf(stderr, "\t node\n"); } /*****************************************************************************/ static char *GetMountURI(char *arguri, int *isHTTPS) { int hasScheme; int hasTrailingSlash; size_t argURILength; char *uri; argURILength = strlen(arguri); *isHTTPS = (strncmp(arguri, "https://", strlen("https://")) == 0); /* if there's no scheme, we'll have to add "http://" */ hasScheme = ((strncmp(arguri, "http://", strlen("http://")) == 0) || *isHTTPS); /* if there's no trailing slash, we'll have to add one */ hasTrailingSlash = arguri[argURILength - 1] == '/'; /* allocate space for url */ uri = malloc(argURILength + (hasScheme ? 0 : strlen("http://")) + (hasTrailingSlash ? 0 : 1) + 1); require(uri != NULL, malloc_uri); /* copy arguri adding scheme and trailing slash if needed */ if ( !hasScheme ) { strcpy(uri, "http://"); } else { *uri = '\0'; } strcat(uri, arguri); if ( !hasTrailingSlash ) { strcat(uri, "/"); } malloc_uri: return ( uri ); } /*****************************************************************************/ #define TMP_WEBDAV_UDS _PATH_TMP ".webdavUDS.XXXXXX" /* Scratch socket name */ /* maximum length of username and password */ #define WEBDAV_MAX_USERNAME_LEN 256 #define WEBDAV_MAX_PASSWORD_LEN 256 #define WEBDAV_MAX_DOMAIN_LEN 256 /*****************************************************************************/ int main(int argc, char *argv[]) { struct webdav_args args; struct sockaddr_un un; int mntflags; int servermntflags; struct vfsconf vfc; mode_t mode_mask; int pid, terminated_pid; union wait status; int return_code; int listen_socket; int store_notify_fd; int lowdisk_notify_fd; int out_token; int error; int ch; int i; struct rlimit rlp; struct node_entry *root_node; char volumeName[NAME_MAX + 1] = ""; char *uri; int mirrored_mount; int isMounted = FALSE; /* TRUE if we make it past mount(2) */ char user[WEBDAV_MAX_USERNAME_LEN]; char pass[WEBDAV_MAX_PASSWORD_LEN]; char domain[WEBDAV_MAX_DOMAIN_LEN]; error = 0; /* store our UID */ gProcessUID = getuid(); user[0] = '\0'; pass[0] = '\0'; domain[0] = '\0'; mntflags = 0; /* * Crack command line args */ while ((ch = getopt(argc, argv, "Sa:o:v:")) != -1) { switch (ch) { case 'a': /* get the username and password from URLMount */ { int fd = atoi(optarg), /* fd from URLMount */ zero = 0, len1 = 0, len2 = 0, len3 = 0; /* read the username length, the username, the password length, and the password */ if (fd >= 0 && lseek(fd, 0LL, SEEK_SET) != -1) { if (read(fd, &len1, sizeof(int)) > 0 && len1 > 0 && len1 < WEBDAV_MAX_USERNAME_LEN) { if (read(fd, user, (size_t)len1) > 0) { user[len1] = '\0'; if (read(fd, &len2, sizeof(int)) > 0 && len2 > 0 && len2 < WEBDAV_MAX_PASSWORD_LEN) { if (read(fd, pass, (size_t)len2) > 0) { pass[len2] = '\0'; if (read(fd, &len3, sizeof(int)) > 0 && len3 > 0 && len3 < WEBDAV_MAX_DOMAIN_LEN) { if (read(fd, domain, (size_t)len3) > 0) { domain[len3] = '\0'; } } } } } } /* zero contents of file and close it if * fd is not STDIN_FILENO, STDOUT_FILENO or STDERR_FILENO */ if ( (fd != STDIN_FILENO) && (fd != STDOUT_FILENO) && (fd != STDERR_FILENO) ) { (void)lseek(fd, 0LL, SEEK_SET); for (i = 0; i < (((len1 + len2 + len3) / (int)sizeof(int)) + 3); i++) { if (write(fd, (char *) & zero, sizeof(int)) < 0) { break; } } (void)fsync(fd); (void)close(fd); } } break; } case 'S': /* Suppress ALL dialogs and notifications */ gSuppressAllUI = 1; break; case 'o': /* Get the mount options */ { const struct mntopt mopts[] = { MOPT_STDOPTS, { NULL, 0, 0, 0 } }; error = getmntopts(optarg, mopts, &mntflags, 0); } break; case 'v': /* Use argument as volume name instead of parsing * the mount point path for the volume name */ if ( strlen(optarg) <= NAME_MAX ) { strcpy(volumeName, optarg); } else { error = 1; } break; default: error = 1; break; } } if (!error) { if (optind != (argc - 2) || strlen(argv[optind]) > MAXPATHLEN) { error = 1; } } require_noerr_action_quiet(error, error_exit, error = EINVAL; usage()); /* does this look like a mirrored mount (UI suppressed and not browseable) */ mirrored_mount = gSuppressAllUI && (mntflags & MNT_DONTBROWSE); /* get the mount point */ require_action(realpath(argv[optind + 1], mountPoint) != NULL, error_exit, error = ENOENT); /* derive the volume name from the mount point if needed */ if ( *volumeName == '\0' ) { /* the volume name wasn't passed on the command line so use the * last path segment of mountPoint */ strcpy(volumeName, strrchr(mountPoint, '/') + 1); } /* Get uri (fix it up if needed) */ uri = GetMountURI(argv[optind], &gSecureConnection); require_action_quiet(uri != NULL, error_exit, error = EINVAL); /* Create a mntfromname from the uri. Make sure the string is no longer than MNAMELEN */ strncpy(mntfromname, uri , MNAMELEN); mntfromname[MNAMELEN] = '\0'; /* if this is going to be a volume on the desktop (the MNT_DONTBROWSE is not set) * then check to see if this mntfromname is already used by a mount point by the * current user. Sure, someone could mount using the DNS name one time and * the IP address the next, or they could munge the path with escaped characters, * but this check will catch the obvious duplicates. */ if ( !(mntflags & MNT_DONTBROWSE) ) { struct statfs * buffer; int count = getmntinfo(&buffer, MNT_NOWAIT); unsigned int mntfromnameLength; mntfromnameLength = strlen(mntfromname); for (i = 0; i < count; i++) { /* Is mntfromname already being used as a mntfromname for a webdav mount * owned by this user? */ if ( (buffer[i].f_owner == gProcessUID) && (strcmp("webdav", buffer[i].f_fstypename) == 0) && (strlen(buffer[i].f_mntfromname) == mntfromnameLength) && (strncasecmp(buffer[i].f_mntfromname, mntfromname, mntfromnameLength) == 0) ) { /* Yes, this mntfromname is in use - return EBUSY * (the same error that you'd get if you tried mount a disk device twice). */ syslog(LOG_ERR, "%s is already mounted: %s", mntfromname, strerror(EBUSY)); error = EBUSY; goto error_exit; } } } /* is WEBDAVFS_DEBUG environment variable set? */ gWebdavfsDebug = (getenv("WEBDAVFS_DEBUG") != NULL); /* if the parent is terminated, it will exit with EXIT_SUCCESS */ signal(SIGTERM, parentexit); pid = fork(); require_action(pid >= 0, error_exit, error = errno); if (pid > 0) { /* Parent waits for child's signal or for child's completion here */ while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 ) { /* retry if EINTR, else break out with error */ if ( errno != EINTR ) { break; } } /* we'll get here only if the child completed before killing us */ if ( (terminated_pid == pid) && WIFEXITED(status) ) { error = WEXITSTATUS(status); } else { error = ECHILD; } goto error_exit; } /* We're the child */ /* detach from controlling tty and start a new process group */ if ( setsid() < 0 ) { debug_string("setsid() failed"); } (void)chdir("/"); if ( !gWebdavfsDebug ) { /* redirect standard input, standard output, and standard error to /dev/null if not debugging */ int fd; fd = open(_PATH_DEVNULL, O_RDWR, 0); if (fd != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) { (void)close(fd); } } } /* Workaround for daemon/mach ports problem... */ CFRunLoopGetCurrent(); /* * Set the default timeout value */ gtimeout_string = WEBDAV_PULSE_TIMEOUT; gtimeout_val = atoi(gtimeout_string); /* get the current maximum number of open files for this process */ error = getrlimit(RLIMIT_NOFILE, &rlp); require_noerr_action(error, error_exit, error = EINVAL); /* Close any open file descriptors we may have inherited from our * parent caller. This excludes the first three. We don't close * STDIN_FILENO, STDOUT_FILENO or STDERR_FILENO. Note, this has to * be done before we open any other file descriptors, but after we * check for a file containing authentication in /tmp. */ closelog(); for (i = 0; i < rlp.rlim_cur; ++i) { switch (i) { case STDIN_FILENO: case STDOUT_FILENO: case STDERR_FILENO: break; default: (void)close(i); } } /* Start logging (and change name) */ openlog("webdavd", LOG_CONS | LOG_PID, LOG_DAEMON); /* raise the maximum number of open files for this process if needed */ if ( rlp.rlim_cur < WEBDAV_RLIMIT_NOFILE ) { rlp.rlim_cur = WEBDAV_RLIMIT_NOFILE; error = setrlimit(RLIMIT_NOFILE, &rlp); require_noerr_action(error, error_exit, error = EINVAL); } /* Set global signal handling to protect us from SIGPIPE */ signal(SIGPIPE, SIG_IGN); /* Make sure our kext and filesystem are loaded */ vfc.vfc_typenum = -1; error = getvfsbyname("webdav", &vfc); if (error) { error = attempt_webdav_load(); require_noerr_action_quiet(error, error_exit, error = EINVAL); error = getvfsbyname("webdav", &vfc); } require_noerr_action(error, error_exit, error = EINVAL); error = nodecache_init(strlen(uri), uri, &root_node); require_noerr_action_quiet(error, error_exit, error = EINVAL); error = authcache_init(user, pass, domain); require_noerr_action_quiet(error, error_exit, error = EINVAL); bzero(user, sizeof(user)); bzero(pass, sizeof(pass)); bzero(domain, sizeof(domain)); error = network_init(uri, strlen(uri), &store_notify_fd, mirrored_mount); require_noerr_action_quiet(error, error_exit, error = EINVAL); error = filesystem_init(vfc.vfc_typenum); require_noerr_action_quiet(error, error_exit, error = EINVAL); error = requestqueue_init(); require_noerr_action_quiet(error, error_exit, error = EINVAL); /* * Check out the server and get the mount flags */ servermntflags = 0; error = filesystem_mount(&servermntflags); require_noerr_quiet(error, error_exit); /* * OR in the mnt flags forced on us by the server */ mntflags |= servermntflags; /* * Construct the listening socket */ listen_socket = socket(PF_LOCAL, SOCK_STREAM, 0); require_action(listen_socket >= 0, error_exit, error = EINVAL); bzero(&un, sizeof(un)); un.sun_len = sizeof(un); un.sun_family = AF_LOCAL; require_action(sizeof(TMP_WEBDAV_UDS) < sizeof(un.sun_path), error_exit, error = EINVAL); strcpy(un.sun_path, TMP_WEBDAV_UDS); require_action(mktemp(un.sun_path) != NULL, error_exit, error = EINVAL); /* bind socket with write-only access which is all that's needed for the kext to connect */ mode_mask = umask(0555); require_action(bind(listen_socket, (struct sockaddr *)&un, sizeof(un)) >= 0, error_exit, error = EINVAL); (void)umask(mode_mask); /* make it hard for anyone to unlink the name */ require_action(chflags(un.sun_path, UF_IMMUTABLE) == 0, error_exit, error = EINVAL); /* listen with plenty of backlog */ require_noerr_action(listen(listen_socket, WEBDAV_MAX_KEXT_CONNECTIONS), error_exit, error = EINVAL); /* * Ok, we are about to set up the mount point so set the signal handlers * so that we know if someone is trying to kill us. */ /* open the wakeupFDs pipe */ require_action(pipe(wakeupFDs) == 0, error_exit, wakeupFDs[0] = wakeupFDs[1] = -1; error = EINVAL); /* set the signal handler to webdav_kill for the signals that aren't ignored by default */ signal(SIGHUP, webdav_kill); signal(SIGINT, webdav_kill); signal(SIGQUIT, webdav_kill); signal(SIGILL, webdav_kill); signal(SIGTRAP, webdav_kill); signal(SIGABRT, webdav_kill); signal(SIGEMT, webdav_kill); signal(SIGFPE, webdav_kill); signal(SIGBUS, webdav_kill); signal(SIGSEGV, webdav_kill); signal(SIGSYS, webdav_kill); signal(SIGALRM, webdav_kill); signal(SIGTSTP, webdav_kill); signal(SIGTTIN, webdav_kill); signal(SIGTTOU, webdav_kill); signal(SIGXCPU, webdav_kill); signal(SIGXFSZ, webdav_kill); signal(SIGVTALRM, webdav_kill); signal(SIGPROF, webdav_kill); signal(SIGUSR1, webdav_kill); signal(SIGUSR2, webdav_kill); /* prepare mount args */ args.pa_mntfromname = mntfromname; args.pa_version = 1; args.pa_socket_namelen = sizeof(un); args.pa_socket_name = (struct sockaddr *)&un; args.pa_vol_name = volumeName; args.pa_flags = 0; if ( gSuppressAllUI ) { args.pa_flags |= WEBDAV_SUPPRESSALLUI; } if ( gSecureConnection ) { args.pa_flags |= WEBDAV_SECURECONNECTION; } args.pa_root_obj_ref = (object_ref)root_node; args.pa_root_fileid = WEBDAV_ROOTFILEID; args.pa_dir_size = WEBDAV_DIR_SIZE; /* pathconf values: >=0 to return value; -1 if not supported */ args.pa_link_max = 1; /* 1 for file systems that do not support link counts */ args.pa_name_max = NAME_MAX; /* The maximum number of bytes in a file name */ args.pa_path_max = PATH_MAX; /* The maximum number of bytes in a pathname */ args.pa_pipe_buf = -1; /* no support for pipes */ args.pa_chown_restricted = _POSIX_CHOWN_RESTRICTED; /* appropriate privileges are required for the chown(2) */ args.pa_no_trunc = _POSIX_NO_TRUNC; /* file names longer than KERN_NAME_MAX are truncated */ /* mount the volume */ return_code = mount(vfc.vfc_name, mountPoint, mntflags, &args); require_noerr_action(return_code, error_exit, error = errno); /* we're mounted so kill our parent so it will call parentexit and exit with success */ kill(getppid(), SIGTERM); isMounted = TRUE; /* and set SIGTERM to webdav_kill */ signal(SIGTERM, webdav_kill); syslog(LOG_INFO, "%s mounted", mountPoint); /* * This code is needed so that the network reachability code doesn't have to * be scheduled for every synchronous CFNetwork transaction. This is accomplished * by setting a dummy callback on the main thread's CFRunLoop. */ { struct sockaddr_in socket_address; SCNetworkReachabilityRef reachabilityRef; bzero(&socket_address, sizeof(socket_address)); socket_address.sin_len = sizeof(socket_address); socket_address.sin_family = AF_INET; socket_address.sin_port = 0; socket_address.sin_addr.s_addr = INADDR_LOOPBACK; reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr*)&socket_address); if (reachabilityRef != NULL) { SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), CFSTR("_nonexistent_")); } } /* register for lowdiskspace notifications on the system (boot) volume */ verify(notify_register_file_descriptor("com.apple.system.lowdiskspace.system", &lowdisk_notify_fd, 0, &out_token) == 0); /* * Just loop waiting for new connections and activating them */ while ( TRUE ) { int accept_socket; fd_set readfds; int max_select_fd; /* * Accept a new connection * Will get EINTR if a signal has arrived, so just * ignore that error code */ FD_ZERO(&readfds); FD_SET(listen_socket, &readfds); FD_SET(store_notify_fd, &readfds); max_select_fd = MAX(listen_socket, store_notify_fd); if ( lowdisk_notify_fd != -1 ) { /* This allows low disk notifications to wake up the select() */ FD_SET(lowdisk_notify_fd, &readfds); max_select_fd = MAX(lowdisk_notify_fd, max_select_fd); } if (wakeupFDs[0] != -1) { /* This allows writing to wakeupFDs[1] to wake up the select() */ FD_SET(wakeupFDs[0], &readfds); max_select_fd = MAX(wakeupFDs[0], max_select_fd); } ++max_select_fd; return_code = select(max_select_fd, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0); if (return_code <= 0) { /* if select isn't working, then exit here */ error = errno; require(error == EINTR, error_exit); /* EINTR is OK */ continue; } /* was a signal received? */ if ( (wakeupFDs[0] != -1) && FD_ISSET(wakeupFDs[0], &readfds) ) { int message; /* read the message number out of the pipe */ message = -1; (void)read(wakeupFDs[0], &message, sizeof(int)); /* do nothing with SIGHUP -- we might want to use it to start some debug logging in the future */ if (message == SIGHUP) { } else { /* time to force an unmount */ wakeupFDs[0] = -1; /* don't look for anything else from the pipe */ if ( message >= 0 ) { /* positive messages are signal numbers */ syslog(LOG_ERR, "mount_webdav received signal: %d. Unmounting %s", message, mountPoint); } else { if ( message == -2 ) { /* this is the normal way out of the select loop */ break; } else { syslog(LOG_ERR, "force unmounting %s", mountPoint); } } /* start a new thread to unmount */ create_unmount_thread(); } } /* was a message from the webdav kext received? */ if ( FD_ISSET(listen_socket, &readfds) ) { struct sockaddr_un addr; int addrlen; addrlen = sizeof(addr); accept_socket = accept(listen_socket, (struct sockaddr *)&addr, &addrlen); if (accept_socket < 0) { error = errno; require(error == EINTR, error_exit); continue; } /* * Now put a new element on the thread queue so that a thread * will handle this. */ error = requestqueue_enqueue_request(accept_socket); require_noerr_quiet(error, error_exit); } /* was a message from the dynamic store received? */ if ( FD_ISSET(store_notify_fd, &readfds) ) { ssize_t bytes_read; int32_t buf; /* read the identifier off the fd and do nothing with it */ bytes_read = read(store_notify_fd, &buf, sizeof(buf)); /* pass the change notification off to another thread to handle */ create_change_thread(); } /* was a low disk notification received? */ if ( FD_ISSET(lowdisk_notify_fd, &readfds) ) { ssize_t bytes_read; /* read the token off the fd and do nothing with it */ bytes_read = read(lowdisk_notify_fd, &out_token, sizeof(out_token)); /* try to help the system out by purging any closed cache files */ (void) requestqueue_purge_cache_files(); } } syslog(LOG_INFO, "%s unmounted", mountPoint); /* attempt to delete the cache directory (if any) and the bound socket name */ if (*gWebdavCachePath != '\0') { (void) rmdir(gWebdavCachePath); } /* clear the immutable flag and unlink the name from the listening socket */ (void) chflags(un.sun_path, 0); (void) unlink(un.sun_path); exit(EXIT_SUCCESS); error_exit: /* is we aren't mounted, return the set of error codes things expect mounts to return */ if ( !isMounted ) { switch (error) { /* The server directory could not be mounted by mount_webdav because the node path is invalid. */ case ENOENT: break; /* Could not connect to the server because the name or password is not correct and the user canceled */ case ECANCELED: break; /* You are already connected to this server volume */ case EBUSY: break; /* You cannot connect to this server because it cannot be found on the network. Try again later or try a different URL. */ case ENODEV: break; /* Map everything else to a generic unexpected error */ default: error = EINVAL; break; } } exit(error); } /*****************************************************************************/