diff --git a/Source/Common/RootFSSetup.cpp b/Source/Common/RootFSSetup.cpp index bbee1b98a8..02326809be 100644 --- a/Source/Common/RootFSSetup.cpp +++ b/Source/Common/RootFSSetup.cpp @@ -13,8 +13,10 @@ #include #include #include -#include #include +#include +#include +#include namespace FEX::RootFS { @@ -65,6 +67,83 @@ bool CheckLockExists(std::string const &LockPath) { return false; } +bool SendSocketPipe(std::string const &SocketPath) { + // Open pipes so we can send the daemon one + int fds[2]{}; + if (pipe2(fds, 0) != 0) { + LogMan::Msg::E("Couldn't open pipe"); + return false; + } + + // Setup our msg header + struct msghdr msg{}; + struct iovec iov{}; + char iov_data{}; + + constexpr size_t CMSG_SIZE = CMSG_SPACE(sizeof(int)); + union AncillaryBuffer { + struct cmsghdr Header; + uint8_t Buffer[CMSG_SIZE]; + }; + AncillaryBuffer AncBuf{}; + + // Set up message header + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // We need to send some data in addition to the ancillary data, doesn't matter what + iov.iov_base = &iov_data; + iov.iov_len = sizeof(iov_data); + + // Now link to our ancilllary buffer + msg.msg_control = AncBuf.Buffer; + msg.msg_controllen = CMSG_SIZE; + + // Now we need to setup the ancillary buffer data. We are only sending an FD + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + // We are giving the daemon the write side of the pipe + memcpy(CMSG_DATA(cmsg), &fds[1], sizeof(int)); + + // Time to open up the actual socket and send the FD over to the daemon + // Create the initial unix socket + int socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (socket_fd == -1) { + LogMan::Msg::D("Couldn't open AF_UNIX socket: %d %s", errno, strerror(errno)); + return false; + } + + struct sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, SocketPath.data(), sizeof(addr.sun_path)); + + if (connect(socket_fd, reinterpret_cast(&addr), sizeof(addr)) == -1) { + LogMan::Msg::D("Couldn't connect to AF_UNIX socket: %d %s", errno, strerror(errno)); + close(socket_fd); + return false; + } + + ssize_t ResultSend = sendmsg(socket_fd, &msg, 0); + if (ResultSend == -1) { + LogMan::Msg::D("Couldn't sendmsg"); + close(socket_fd); + return false; + } + + // We've sent the message which means we're done with the socket + close(socket_fd); + + // Close the write side of the pipe, leave read side open + // Daemon now has a copy of the fd + close(fds[1]); + return true; +} + void OpenLock(std::string const LockPath) { SquashFSLock.open(LockPath, std::ios_base::in | std::ios_base::binary); } @@ -106,6 +185,18 @@ std::string GetRootFSLockFile() { return LockPath; } +std::string GetRootFSSocketFile() { + // FEX_ROOTFS needs to be the path to the squashfs, not the mount + FEX_CONFIG_OPT(LDPath, ROOTFS); + struct utsname uts{}; + uname (&uts); + std::string SocketPath = "/tmp/.FEX-"; + SocketPath += std::filesystem::path(LDPath()).filename(); + SocketPath += ".socket."; + SocketPath += uts.nodename; + return SocketPath; +} + bool Setup(char **const envp) { // We need to setup the rootfs here // If the configuration is set to use a folder then there is nothing to do @@ -117,11 +208,14 @@ bool Setup(char **const envp) { // We can do this by checking the lock file if it exists std::string LockPath = GetRootFSLockFile(); + std::string SocketFile = GetRootFSSocketFile(); - if (CheckLockExists(LockPath)) { - // RootFS already exists. Nothing to do + // If the lock file exists and we can send the process a pipe then nothing to do + // Otherwise we need to spin up a new mount daemon + if (CheckLockExists(LockPath) && SendSocketPipe(SocketFile)) { return true; } + pid_t ParentTID = ::getpid(); std::string ParentTIDString = std::to_string(ParentTID); std::string Tmp = "/tmp/.FEXMount" + ParentTIDString + "-XXXXXX"; @@ -155,13 +249,12 @@ bool Setup(char **const envp) { if (pid == 0) { // Child close(fds[0]); // Close read end of pipe - const char *argv[6]; + const char *argv[5]; argv[0] = FEX_INSTALL_PREFIX "/bin/FEXMountDaemon"; argv[1] = LDPath().c_str(); argv[2] = TempFolder; - argv[3] = ParentTIDString.c_str(); - argv[4] = PipeString.c_str(); - argv[5] = nullptr; + argv[3] = PipeString.c_str(); + argv[4] = nullptr; if (execve(argv[0], (char * const*)argv, envp) == -1) { // Let the parent know that we couldn't execute for some reason @@ -212,6 +305,9 @@ bool Setup(char **const envp) { return false; } + // Send the new FEXMountDaemon a pipe to listen to + SendSocketPipe(SocketFile); + // If everything has passed then we can now update the rootfs path FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, TempFolder); return true; diff --git a/Source/Tools/FEXMountDaemon/CMakeLists.txt b/Source/Tools/FEXMountDaemon/CMakeLists.txt index 30b5079b82..5ae4a6f579 100644 --- a/Source/Tools/FEXMountDaemon/CMakeLists.txt +++ b/Source/Tools/FEXMountDaemon/CMakeLists.txt @@ -8,4 +8,4 @@ install(TARGETS ${NAME} DESTINATION bin COMPONENT runtime) -target_link_libraries(${NAME} PRIVATE ${STATIC_PIE_OPTIONS}) +target_link_libraries(${NAME} PRIVATE ${STATIC_PIE_OPTIONS} ${PTHREAD_LIB}) diff --git a/Source/Tools/FEXMountDaemon/Main.cpp b/Source/Tools/FEXMountDaemon/Main.cpp index 405a745ed2..6e77313eb3 100644 --- a/Source/Tools/FEXMountDaemon/Main.cpp +++ b/Source/Tools/FEXMountDaemon/Main.cpp @@ -1,244 +1,390 @@ #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 #include +#include namespace { -static std::atomic ForceShutdown{}; -static std::atomic ParentShuttingDown{}; -static int ParentPIDProcess{}; -static int pipe_wr{}; -void ActionHandler(int sig, siginfo_t *info, void *context) { - if (sig == SIGCHLD) { - if (!ParentShuttingDown.load()) { - // Check the pipe to see if it is closed - - uint64_t c = 0; - if (write(pipe_wr, &c, sizeof(c)) == -1 && - errno == EPIPE) { - // The pipe is closed, which means the parent no longer exists. Otherwise the pipe would have still been open - // No need to throw an error message this time +void SignalShutdown(); +namespace EPollWatcher { + static int epoll_fd{}; + static std::thread EPollThread{}; + std::atomic NumPipesWatched{}; + std::atomic EPollWatcherShutdown {false}; + std::chrono::time_point TimeWhileZeroFDs{}; + // Timeout is ten seconds + constexpr std::chrono::duration TimeoutPeriod = std::chrono::seconds(10); + + uint64_t NumPipesRemaining() { + return NumPipesWatched.load(); + } + + void AddPipeToWatch(int pipe) { + struct epoll_event evt{}; + evt.events = EPOLLERR; // This event will return when the read end of a pipe is closed + evt.data.fd = pipe; // Just return the pipe in the user data + int Result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe, &evt); + if (Result == -1) { + fprintf(stderr, "[FEXMountDaemon] epoll_ctl returned error %d %s\n", errno, strerror(errno)); + } + else { + ++NumPipesWatched; + } + } + + void RemovePipeToWatch(int pipe) { + int Result = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, pipe, nullptr); + if (Result == -1) { + fprintf(stderr, "[FEXMountDaemon] epoll_ctl returned error %d %s\n", errno, strerror(errno)); + } + else { + --NumPipesWatched; + } + + // If the number of pipes we are watching drops to zero then start a timer so we can exit + if (NumPipesRemaining() == 0) { + TimeWhileZeroFDs = std::chrono::system_clock::now(); + } + } + + void EPollWatch() { + constexpr size_t MAX_EVENTS = 16; + struct epoll_event Events[MAX_EVENTS]{}; + while (!EPollWatcherShutdown.load()) { + // Loop every ten seconds + // epoll_pwait2 only available since kernel 5.11... + int Result = epoll_pwait(epoll_fd, Events, MAX_EVENTS, 10 * 1000, nullptr); + if (Result == -1) { + // EINTR is common here } else { - // If our child process shutdown while our parent is still running - // Then the parent loses its rootfs and problems occur - fprintf(stderr, "FEXMountDaemon child process from squashfuse has closed\n"); - fprintf(stderr, "Expect errors!\n"); + for (size_t i = 0; i < Result; ++i) { + auto &Event = Events[i]; + if (Event.events & EPOLLERR) { + // This pipe's read end has closed + // No need to watch it anymore + RemovePipeToWatch(Event.data.fd); + } + } + + // If we are at zero pipes then check our timer if we are past the timeout period to exit + if (NumPipesRemaining() == 0) { + auto Now = std::chrono::system_clock::now(); + auto Dur = Now - TimeWhileZeroFDs; + if (Dur >= TimeoutPeriod) { + // We need to shutdown now + ::SignalShutdown(); + } + } } - ParentShuttingDown = true; } } - else { - // Signal sent directly to process - // Force a shutdown - fprintf(stderr, "We are being told to shutdown with SIGTERM\n"); - fprintf(stderr, "Watch out! You might get dangling mount points!\n"); - ForceShutdown = true; + + void SetupEPoll() { + epoll_fd = epoll_create1(EPOLL_CLOEXEC); + EPollThread = std::thread{EPollWatcher::EPollWatch}; } -} -static int lock_fd {-1}; -static int notify_fd {-1}; -static int watch_fd {-1}; -enum LockFailure { - LOCK_FAIL_FATAL, - LOCK_FAIL_EXISTS, - LOCK_FAIL_CREATION_RACE, - LOCK_FAIL_CREATED, -}; - -constexpr int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO; -LockFailure CreateINotifyLock(std::string LockPath, const char *MountPath) { - lock_fd = open(LockPath.c_str(), O_RDONLY, USER_PERMS); - if (lock_fd != -1) { - // LockFD already existed! - // This will have now refcounted the existing daemon! - close(lock_fd); - return LOCK_FAIL_EXISTS; + void SignalShutdown() { + EPollWatcherShutdown = true; } - lock_fd = open(LockPath.c_str(), O_CREAT | O_RDWR, USER_PERMS); - if (lock_fd == -1) { - // Couldn't open lock file for some reason - // Likely read only file system - return LOCK_FAIL_FATAL; + void ShutdownEPoll() { + SignalShutdown(); + close(epoll_fd); + EPollThread.join(); } +} - LockFailure Failure = LOCK_FAIL_FATAL; +namespace SocketWatcher { + static int socket_fd{}; + static std::thread SocketThread{}; + std::atomic SocketShutdown {false}; + + void SocketFunction() { + while (!SocketShutdown.load()) { + // Wait for data coming in + struct pollfd pfd{}; + pfd.fd = socket_fd; + pfd.events = POLLIN; + + // Wait for ten seconds + struct timespec ts{}; + ts.tv_sec = 10; + + int Result = ppoll(&pfd, 1, &ts, nullptr); + if (Result == -1) { + // EINTR is common here + } + else if (Result > 0) { + // We got data to grab + struct msghdr msg{}; + struct iovec iov{}; + char iov_data{}; + + // Setup the ancillary buffer. This is where we will be getting pipe FDs + // We only need 4 bytes for the FD + constexpr size_t CMSG_SIZE = CMSG_SPACE(sizeof(int)); + union AncillaryBuffer { + struct cmsghdr Header; + uint8_t Buffer[CMSG_SIZE]; + }; + AncillaryBuffer AncBuf{}; + + // Set up message header + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // Setup iov. We won't be receiving any real data here + iov.iov_base = &iov_data; + iov.iov_len = sizeof(iov_data); + + // Now link to our ancilllary buffer + msg.msg_control = AncBuf.Buffer; + msg.msg_controllen = CMSG_SIZE; + + ssize_t DataResult = recvmsg(socket_fd, &msg, 0); + if (DataResult == -1) { + // Sometimes get a spurious read? + } + else { + // Now that we have the data, we can extract the FD from the ancillary buffer + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + // Do some error checking + if (cmsg == nullptr || + cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + fprintf(stderr, "[FEXMountDaemon:SocketWatcher] cmsg data was incorrect\n"); + } + else { + // Now that we know the cmsg is sane, read the FD + int NewFD{}; + memcpy(&NewFD, CMSG_DATA(cmsg), sizeof(NewFD)); - // Set up the write lock to ensure this doesn't race - { - // Attempt to open a write lease on the lock file - // First thing, mask the signal from the least interface - // By default it is SIGIO - { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGIO); - if (sigprocmask(SIG_BLOCK, &set, nullptr) == -1) { - goto err; + // Add to to the epoll watcher + EPollWatcher::AddPipeToWatch(NewFD); + } + } } } + } - // Set the file's lease signal - // Even if we set it to default, this is necessary - { - int Res = fcntl(lock_fd, F_SETSIG, SIGIO); - if (Res == -1) { - // Shouldn't fail - goto err; - } + bool CreateServerSocket(std::string &SocketPath) { + // Unlink the socket file if it exists + // We are being asked to create a daemon, not error check + // We don't care if this failed or not + remove(SocketPath.c_str()); + + // Create the initial unix socket + socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (socket_fd == -1) { + fprintf(stderr, "[FEXMountDaemon:SocketWatcher] Couldn't create AF_UNIX socket: %d %s\n", errno, strerror(errno)); + return false; } - // Now attempt to get a write lock on this file - { - int Res = fcntl(lock_fd, F_SETLEASE, F_WRLCK); - if (Res == -1) { - // Couldn't get a write lock - // This means another FEXMountDaemon is in the process of setting up a rootfs - // Early exit, this will be mounted in another process - Failure = LOCK_FAIL_CREATION_RACE; - goto err; - } + struct sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, SocketPath.data(), sizeof(addr.sun_path)); + + // Bind the socket to the path + int Result = bind(socket_fd, reinterpret_cast(&addr), sizeof(addr)); + if (Result == -1) { + fprintf(stderr, "[FEXMountDaemon:SocketWatcher] Couldn't bind AF_UNIX socket: %d %s\n", errno, strerror(errno)); + return false; } - // Now that we have a lock on the file. - // Write where we are going to be mounting - // Nothing else can currently open the file for reads yet to see this - write(lock_fd, MountPath, strlen(MountPath)); + return true; } - // Now while we own the lock on the file, setup our notification handling - { - notify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + bool SetupSocketWatcher(std::string &SocketPath) { + if (!CreateServerSocket(SocketPath)) { + return false; + } - // Watch for lock file opening and closing - watch_fd = inotify_add_watch(notify_fd, LockPath.c_str(), IN_OPEN | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE); + SocketThread = std::thread{SocketWatcher::SocketFunction}; + return true; } - return LOCK_FAIL_CREATED; + void SignalShutdown() { + SocketShutdown = true; + } -err: + void ShutdownSocketWatcher(std::string &SocketPath) { + SignalShutdown(); + close(socket_fd); + SocketThread.join(); - if (lock_fd != -1) { - close (lock_fd); + remove(SocketPath.c_str()); } - - return Failure; } -void WatchLock() { - // Let go of the file lease file to allow FEX to continue - fcntl(lock_fd, F_SETLEASE, F_UNLCK); - - bool BrokenRefCount{}; - size_t RefCount{}; - while (true) { - constexpr size_t DATA_SIZE = (16 * (sizeof(struct inotify_event) + NAME_MAX + 1)); - char buf[DATA_SIZE]; - struct timeval tv{}; - int Ret{}; - - do { - fd_set Set{}; - FD_ZERO(&Set); - FD_SET(notify_fd, &Set); - - // Fairly latent ten seconds - tv.tv_sec = 10; - tv.tv_usec = 0; - - Ret = select(notify_fd + 1, &Set, nullptr, nullptr, &tv); - if (Ret == 0 && RefCount == 0) { - // We don't have any more users. Clean up - // Try to get a write lock again for the squashfs - Ret = fcntl(lock_fd, F_SETLEASE, F_WRLCK); - if (Ret == 0) { - // Managed to grab the lock. Means there aren't any more users on the lock - return; +namespace INotifyWatcher { + static int lock_fd {-1}; + static std::condition_variable WaitCV{}; + static std::mutex WaitMutex{}; + static std::atomic INotifyShutdown {false}; + enum LockFailure { + LOCK_FAIL_FATAL, + LOCK_FAIL_EXISTS, + LOCK_FAIL_CREATION_RACE, + LOCK_FAIL_CREATED, + }; + + constexpr int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO; + + LockFailure CreateINotifyLock(std::string LockPath, const char *MountPath) { + lock_fd = open(LockPath.c_str(), O_RDONLY, USER_PERMS); + if (lock_fd != -1) { + // LockFD already existed! + // This will have now refcounted the existing daemon! + close(lock_fd); + return LOCK_FAIL_EXISTS; + } + + lock_fd = open(LockPath.c_str(), O_CREAT | O_RDWR, USER_PERMS); + if (lock_fd == -1) { + // Couldn't open lock file for some reason + // Likely read only file system + return LOCK_FAIL_FATAL; + } + + LockFailure Failure = LOCK_FAIL_FATAL; + + // Set up the write lock to ensure this doesn't race + { + // Attempt to open a write lease on the lock file + // First thing, mask the signal from the least interface + // By default it is SIGIO + { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGIO); + if (sigprocmask(SIG_BLOCK, &set, nullptr) == -1) { + goto err; } - else { - // We weren't able to grab the lease on the lock. This means that our Refcounting - // was broken by something and our world view is broken now - BrokenRefCount = true; + } + + // Set the file's lease signal + // Even if we set it to default, this is necessary + { + int Res = fcntl(lock_fd, F_SETSIG, SIGIO); + if (Res == -1) { + // Shouldn't fail + goto err; } } - if ((BrokenRefCount && ParentShuttingDown) || ForceShutdown.load()) { - // If our ref counting was broken and our parent is gone then try and clean up - return; + // Now attempt to get a write lock on this file + { + int Res = fcntl(lock_fd, F_SETLEASE, F_WRLCK); + if (Res == -1) { + // Couldn't get a write lock + // This means another FEXMountDaemon is in the process of setting up a rootfs + // Early exit, this will be mounted in another process + Failure = LOCK_FAIL_CREATION_RACE; + goto err; + } } - } while (Ret == 0 || (Ret == -1 && errno == EINTR)); - if (Ret == -1) { - return; + // Now that we have a lock on the file. + // Write where we are going to be mounting + // Nothing else can currently open the file for reads yet to see this + write(lock_fd, MountPath, strlen(MountPath)); } - int Read{}; - do { - Read = read(notify_fd, buf, DATA_SIZE); - if (Read > 0) { - inotify_event *Event{}; - for (char *ptr = buf; - ptr < (buf + Read); - ptr += (sizeof(struct inotify_event) + Event->len)) { - Event = reinterpret_cast(ptr); - if (Event->mask & IN_OPEN) { - // Application opened the lock file - ++RefCount; - } + return LOCK_FAIL_CREATED; + err: + if (lock_fd != -1) { + close (lock_fd); + } - if (Event->mask & (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)) { - // Application has finally closed the lock - // Either by choice or crashing - --RefCount; - } - } - } + return Failure; + } + + void WatchLock() { + // Let go of the file lease file to allow FEX to continue + fcntl(lock_fd, F_SETLEASE, F_UNLCK); - } while (Read > 0); + // Wait in a mutex to shutdown + std::unique_lock lk (WaitMutex); + WaitCV.wait(lk, [] { return INotifyShutdown.load(); }); } -} -void RemoveLock(std::string LockPath) { - // Remove the lock file itself - unlink(LockPath.c_str()); + void RemoveLock(std::string LockPath) { + // Remove the lock file itself + unlink(LockPath.c_str()); - // Clear the lease on it since we are shutting down - fcntl(lock_fd, F_SETLEASE, F_UNLCK); + // Clear the lease on it since we are shutting down + fcntl(lock_fd, F_SETLEASE, F_UNLCK); - // Now close the watch FD - close(watch_fd); + // Close the lock fd + close(lock_fd); + } - // Close the notify fd - close(notify_fd); + void SignalShutdown() { + INotifyShutdown = true; + WaitCV.notify_all(); + } +} - // Close the lock fd - close(lock_fd); +void ActionHandler(int sig, siginfo_t *info, void *context) { + if (sig == SIGCHLD) { + if (EPollWatcher::NumPipesRemaining() != 0) { + // Check the pipes we are watching to see if any exist + // If our child process shutdown while our parent is still running + // Then the parent loses its rootfs and problems occur + fprintf(stderr, "FEXMountDaemon child process from squashfuse has closed while FEX instances still exist\n"); + fprintf(stderr, "Expect errors!\n"); + } + } + else { + // Signal sent directly to process + // Force a shutdown + fprintf(stderr, "We are being told to shutdown with SIGTERM\n"); + fprintf(stderr, "Watch out! You might get dangling mount points!\n"); + ::SignalShutdown(); + } +} + + +void SignalShutdown() { + INotifyWatcher::SignalShutdown(); + SocketWatcher::SignalShutdown(); + EPollWatcher::SignalShutdown(); } } int main(int argc, char **argv, char **envp) { - if (argc < 5) { - fprintf(stderr, "usage: %s \n", argv[0]); + if (argc < 4) { + fprintf(stderr, "usage: %s \n", argv[0]); return -1; } @@ -252,8 +398,10 @@ int main(int argc, char **argv, char **envp) { const char *SquashFSPath = argv[1]; const char *MountPath = argv[2]; - ParentPIDProcess = std::atoi(argv[3]); - pipe_wr = std::atoi(argv[4]); + int pipe_wr = std::atoi(argv[3]); + + // Start the epoll watcher + EPollWatcher::SetupEPoll(); // Switch this process over to a new session id // Probably not required but allows this to become the process group leader of its session @@ -273,14 +421,25 @@ int main(int argc, char **argv, char **envp) { LockPath += ".lock."; LockPath += uts.nodename; + std::string SocketPath = "/tmp/.FEX-"; + SocketPath += std::filesystem::path(SquashFSPath).filename(); + SocketPath += ".socket."; + SocketPath += uts.nodename; + + if (!SocketWatcher::SetupSocketWatcher(SocketPath)) { + fprintf(stderr, "[FEXMountDaemon] Failed to setup socket watcher\n"); + return -1; + } + // Use lock files to ensure we aren't racing to mount multiple FSes - auto Failure = CreateINotifyLock(LockPath, MountPath); - if (Failure == LOCK_FAIL_FATAL) { + auto Failure = INotifyWatcher::CreateINotifyLock(LockPath, MountPath); + if (Failure == INotifyWatcher::LOCK_FAIL_FATAL) { + fprintf(stderr, "[FEXMountDaemon] Failed to setup inotify lock\n"); return -1; } - if (Failure == LOCK_FAIL_EXISTS || - Failure == LOCK_FAIL_CREATION_RACE) { + if (Failure == INotifyWatcher::LOCK_FAIL_EXISTS || + Failure == INotifyWatcher::LOCK_FAIL_CREATION_RACE) { // If the lock already exists // Then we don't need to spin up the mounts at all // Cleanly exit early and let FEX know it can continue @@ -360,9 +519,9 @@ int main(int argc, char **argv, char **envp) { write(pipe_wr, &c, sizeof(c)); // Watch our lock file now for users - WatchLock(); + INotifyWatcher::WatchLock(); - RemoveLock(LockPath); + INotifyWatcher::RemoveLock(LockPath); // fusermount for unmounting the mountpoint, then the squashfuse will exit automatically pid = fork(); @@ -394,5 +553,8 @@ int main(int argc, char **argv, char **envp) { } } + EPollWatcher::ShutdownEPoll(); + SocketWatcher::ShutdownSocketWatcher(SocketPath); + return 0; }