diff --git a/CMakeLists.txt b/CMakeLists.txt
index 63203a2ba..77d85bef9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,6 +79,7 @@ option(DEBIAN_PACKAGING "Packaging for Debian" OFF)
option(ENABLE_TESTS "Install in-prefix unit tests" OFF)
FindDsymutil()
+find_package(Setcap REQUIRED)
# Missing CMakeLists.txt must trigger an error
cmake_policy(SET CMP0014 NEW)
diff --git a/src/external/lkm b/src/external/lkm
index d2e68bab6..cd9430c6b 160000
--- a/src/external/lkm
+++ b/src/external/lkm
@@ -1 +1 @@
-Subproject commit d2e68bab6efa2ba827824aba48fb6e8c43e32126
+Subproject commit cd9430c6bd246b78a1a8201294335fcd858a4a9a
diff --git a/src/frameworks/CoreServices/src/FSEvents/CMakeLists.txt b/src/frameworks/CoreServices/src/FSEvents/CMakeLists.txt
index caf4596c4..913f0b7d4 100644
--- a/src/frameworks/CoreServices/src/FSEvents/CMakeLists.txt
+++ b/src/frameworks/CoreServices/src/FSEvents/CMakeLists.txt
@@ -13,3 +13,9 @@ add_framework(FSEvents
Foundation
system
)
+
+add_darling_executable(fseventsd fseventsd.m)
+target_link_libraries(fseventsd system Foundation CarbonCore)
+
+install(TARGETS fseventsd DESTINATION libexec/darling/usr/sbin)
+install(CODE "execute_process(COMMAND ${SETCAP_EXECUTABLE} \"cap_sys_admin+ep\" \"${CMAKE_INSTALL_PREFIX}/libexec/darling/usr/sbin/fseventsd\")")
diff --git a/src/frameworks/CoreServices/src/FSEvents/fseventsd.h b/src/frameworks/CoreServices/src/FSEvents/fseventsd.h
new file mode 100644
index 000000000..71cec1b8c
--- /dev/null
+++ b/src/frameworks/CoreServices/src/FSEvents/fseventsd.h
@@ -0,0 +1,38 @@
+/*
+ This file is part of Darling.
+
+ Copyright (C) 2020 Lubos Dolezel
+
+ Darling is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Darling is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Darling. If not, see .
+*/
+
+#ifndef FSEVENTSD_H_
+#define FSEVENTSD_H_
+
+#define FSEVENTSD_SOCKET_PATH "/var/run/fseventsd.sock"
+
+typedef struct fseventsd_monitor {
+ int id;
+ char path[4096];
+ int flags;
+} fseventsd_monitor_t;
+
+typedef struct fseventsd_event {
+ int id;
+ char path[4096];
+ int flags;
+} fseventsd_event_t;
+
+#endif
+
diff --git a/src/frameworks/CoreServices/src/FSEvents/fseventsd.m b/src/frameworks/CoreServices/src/FSEvents/fseventsd.m
new file mode 100644
index 000000000..ba1740bd5
--- /dev/null
+++ b/src/frameworks/CoreServices/src/FSEvents/fseventsd.m
@@ -0,0 +1,194 @@
+/*
+ This file is part of Darling.
+
+ Copyright (C) 2020 Lubos Dolezel
+
+ Darling is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Darling is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Darling. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#import
+#import
+#include "fseventsd.h"
+
+int g_listenSocket, g_fanotify;
+
+void setupListenSocket(void);
+void handleNewConnection(int fd);
+void setupFANotify(void);
+
+int main()
+{
+ setupListenSocket();
+ setupFANotify();
+
+ dispatch_main();
+ return 0;
+}
+
+void setupListenSocket(void)
+{
+ struct sockaddr_un sa;
+ sa.sun_family = AF_UNIX;
+
+ strcpy(sa.sun_path, FSEVENTSD_SOCKET_PATH);
+ unlink(FSEVENTSD_SOCKET_PATH);
+
+ g_listenSocket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (g_listenSocket == -1)
+ {
+ perror("bind");
+ exit(EXIT_FAILURE);
+ }
+
+ int rv = bind(g_listenSocket, (const struct sockaddr *) &sa, sizeof(sa));
+ if (rv == -1)
+ {
+ perror("bind");
+ exit(EXIT_FAILURE);
+ }
+
+ rv = listen(g_listenSocket, 5);
+ if (rv == -1)
+ {
+ perror("listen");
+ exit(EXIT_FAILURE);
+ }
+
+ dispatch_source_t listenerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, g_listenSocket, 0, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(listenerSource, ^{
+ struct sockaddr_un sa;
+ sa.sun_family = AF_UNIX;
+
+ socklen_t len = sizeof(sa);
+
+ int rv = accept(g_listenSocket, (struct sockaddr*) &sa, &len);
+ if (rv >= 0)
+ handleNewConnection(rv);
+ });
+ dispatch_resume(listenerSource);
+}
+
+void handleClientCommand(int fd, const fseventsd_monitor_t* cmd)
+{
+
+}
+
+void handleNewConnection(int fd)
+{
+ dispatch_source_t socketSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
+
+ dispatch_source_set_event_handler(socketSource, ^{
+ fseventsd_monitor_t cmd;
+ struct msghdr msg;
+ struct iovec iov = {
+ .iov_base = &cmd,
+ .iov_len = sizeof(cmd)
+ };
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ recvmsg(fd, &msg, 0);
+ int rv = read(fd, &cmd, sizeof(cmd));
+
+ if (rv == sizeof(cmd))
+ handleClientCommand(fd, &cmd);
+ });
+
+ dispatch_resume(socketSource);
+}
+
+void fileHandleToFSRef(const struct file_handle* fh, FSRef* ref)
+{
+ static FSRef rootRef;
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ FSPathMakeRef((const UInt8*) "/", &rootRef, NULL);
+ });
+
+ RefData* refData = (RefData*) ref;
+ refData->mount_id = ((RefData*)&rootRef)->mount_id;
+
+ memcpy(&refData->fh, fh, sizeof(FSRef) - sizeof(refData->mount_id));
+}
+
+void setupFANotify(void)
+{
+ // FAN_REPORT_FID = provide file handles
+ // Linux file handle = FSRef in Darling
+ g_fanotify = fanotify_init(FAN_REPORT_FID, 0);
+ if (g_fanotify == -1)
+ {
+ perror("fanotify_init");
+ exit(EXIT_FAILURE);
+ }
+
+ int rv = fanotify_mark(g_fanotify, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
+ FAN_CREATE | FAN_DELETE | FAN_MOVED_FROM | FAN_MOVED_TO | FAN_MODIFY | FAN_ONDIR, AT_FDCWD, "/");
+ if (rv == -1)
+ {
+ perror("fanotify_mark");
+ exit(EXIT_FAILURE);
+ }
+
+ dispatch_source_t faSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, g_fanotify, 0, dispatch_get_main_queue());
+
+ dispatch_source_set_event_handler(faSource, ^{
+ char events_buf[4096];
+ int len = read(g_fanotify, events_buf, sizeof(events_buf));
+ printf("Read %d bytes of event data\n", len);
+
+ for (struct fanotify_event_metadata* metadata = (struct fanotify_event_metadata *) events_buf;
+ FAN_EVENT_OK(metadata, len);
+ metadata = FAN_EVENT_NEXT(metadata, len))
+ {
+ printf("Event mask: 0x%x\n", metadata->mask);
+ struct fanotify_event_info_fid* fid = (struct fanotify_event_info_fid *) (metadata + 1);
+ if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_FID)
+ {
+ struct file_handle* fh = (struct file_handle *) fid->handle;
+ FSRef fsref;
+
+ printf("fileHandleToFSRef: bytes: %d, type: %d\n", fh->handle_bytes, fh->handle_type);
+ fileHandleToFSRef(fh, &fsref);
+
+ char pathbuf[1024];
+ printf("FSRefMakePath\n");
+ if (FSRefMakePath(&fsref, (UInt8*)pathbuf, sizeof(pathbuf)) != 0)
+ puts("FSRefMakePath failed");
+ else
+ printf("Event on %s\n", pathbuf);
+ }
+ else
+ {
+ puts("Not a FID");
+ }
+ printf("---\n");
+ }
+ });
+
+ dispatch_resume(faSource);
+}