├── FSMonitor.m ├── LICENSE ├── Makefile ├── NSDistributedNotificationCenter.h ├── README.md ├── fsmonitord ├── Makefile ├── fsevents.h ├── main.m └── theos ├── layout ├── DEBIAN │ ├── control │ ├── extrainst_ │ └── prerm ├── Library │ └── LaunchDaemons │ │ └── com.eswick.fsmonitord.plist └── usr │ └── include │ └── fsmonitor.h └── theos /FSMonitor.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "NSDistributedNotificationCenter.h" 3 | 4 | #define FSE_INVALID -1 5 | #define FSE_CREATE_FILE 0 6 | #define FSE_DELETE 1 7 | #define FSE_STAT_CHANGED 2 8 | #define FSE_RENAME 3 9 | #define FSE_CONTENT_MODIFIED 4 10 | #define FSE_EXCHANGE 5 11 | #define FSE_FINDER_INFO_CHANGED 6 12 | #define FSE_CREATE_DIR 7 13 | #define FSE_CHOWN 8 14 | #define FSE_XATTR_MODIFIED 9 15 | #define FSE_XATTR_REMOVED 10 16 | 17 | #define URL_STD(url) [[url path] stringByStandardizingPath] 18 | 19 | @implementation FSMonitor 20 | 21 | - (id)init{ 22 | if(![super init]) 23 | return nil; 24 | 25 | NSDistributedNotificationCenter* notificationCenter; 26 | notificationCenter = [NSDistributedNotificationCenter defaultCenter]; 27 | [notificationCenter addObserver:self selector:@selector(daemonCallback:) name:@"FSMONITORD_CALLBACK" object:nil]; 28 | 29 | self.typeFilter = FSMonitorEventTypeAll; 30 | 31 | self.directoryFilter = [NSMutableArray new]; 32 | 33 | [self.directoryFilter release]; 34 | return self; 35 | } 36 | 37 | - (void)daemonCallback:(NSNotification*)notification{ 38 | if(![[self.delegate class] conformsToProtocol:@protocol(FSMonitorDelegate)]) 39 | return; 40 | 41 | NSDictionary *eventInfo = [notification userInfo]; 42 | NSMutableDictionary *delegateEventInfo = [eventInfo mutableCopy]; 43 | 44 | int type = [[delegateEventInfo objectForKey:@"TYPE"] intValue]; 45 | 46 | if([self typeFilterAllowsEventType:type]){ 47 | [delegateEventInfo removeObjectForKey:@"FILE"]; 48 | if([NSURL URLWithString:[[eventInfo objectForKey:@"FILE"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]) 49 | [delegateEventInfo setObject:[NSURL URLWithString:[[eventInfo objectForKey:@"FILE"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] forKey:@"FILE"]; 50 | else{ 51 | NSLog(@"URL is nil. Path: %@ truncated?", [[eventInfo objectForKey:@"FILE"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]); 52 | return; 53 | } 54 | 55 | if([delegateEventInfo objectForKey:@"DEST_FILE"]){ 56 | [delegateEventInfo removeObjectForKey:@"DEST_FILE"]; 57 | 58 | if([NSURL URLWithString:[[eventInfo objectForKey:@"DEST_FILE"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]) 59 | [delegateEventInfo setObject:[NSURL URLWithString:[[eventInfo objectForKey:@"DEST_FILE"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] forKey:@"DEST_FILE"]; 60 | else{ 61 | NSLog(@"Dest URL is nil. Path: %@ truncated?", [[eventInfo objectForKey:@"DEST_FILE"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]); 62 | return; 63 | } 64 | } 65 | 66 | [delegateEventInfo removeObjectForKey:@"TYPE"]; 67 | [delegateEventInfo setObject:@([self convertEventType:[[eventInfo objectForKey:@"TYPE"] intValue]]) forKey:@"TYPE"]; 68 | 69 | if([self checkFilterWithEventInfo:delegateEventInfo]) 70 | [[self delegate] monitor:self recievedEventInfo:delegateEventInfo]; 71 | } 72 | 73 | [delegateEventInfo release]; 74 | } 75 | 76 | - (void)addDirectoryFilter:(NSURL*)url recursive:(BOOL)recursive{ 77 | for(NSDictionary *dictionary in self.directoryFilter){ 78 | if([[dictionary objectForKey:@"URL"] isEqual:url]) 79 | return; 80 | } 81 | 82 | [self.directoryFilter addObject:[NSDictionary dictionaryWithObjectsAndKeys:url, @"URL", @(recursive), @"RECURSIVE", nil]]; 83 | } 84 | 85 | - (void)removeDirectoryFilter:(NSURL*)url{ 86 | for(NSDictionary *dictionary in self.directoryFilter){ 87 | if([[dictionary objectForKey:@"URL"] isEqual:url]) 88 | [self.directoryFilter removeObject:dictionary]; 89 | } 90 | } 91 | 92 | - (BOOL)checkFilterWithEventInfo:(NSDictionary*)eventInfo{ 93 | BOOL directoryMatch = false; 94 | 95 | for(NSDictionary *dictionary in self.directoryFilter){ 96 | if([URL_STD([eventInfo objectForKey:@"FILE"]) isEqualToString:URL_STD([dictionary objectForKey:@"URL"])])//Directory itself 97 | directoryMatch = true; 98 | if([URL_STD([[eventInfo objectForKey:@"FILE"] URLByDeletingLastPathComponent]) isEqualToString:URL_STD([dictionary objectForKey:@"URL"])])//Top level files/dirs 99 | directoryMatch = true; 100 | if([[dictionary objectForKey:@"RECURSIVE"] boolValue] == true){//Deep 101 | if([URL_STD([eventInfo objectForKey:@"FILE"]) hasPrefix:URL_STD([dictionary objectForKey:@"URL"])]) 102 | directoryMatch = true; 103 | } 104 | } 105 | 106 | return directoryMatch; 107 | } 108 | 109 | - (FSMonitorEventType)convertEventType:(int)eventType{ 110 | switch(eventType){ 111 | case FSE_CREATE_FILE: 112 | return FSMonitorEventTypeFileCreation; 113 | case FSE_DELETE: 114 | return FSMonitorEventTypeDeletion; 115 | case FSE_RENAME: 116 | return FSMonitorEventTypeRename; 117 | case FSE_CONTENT_MODIFIED: 118 | return FSMonitorEventTypeModification; 119 | case FSE_CREATE_DIR: 120 | return FSMonitorEventTypeDirectoryCreation; 121 | case FSE_CHOWN: 122 | return FSMonitorEventTypeOwnershipChange; 123 | default: 124 | return FSMonitorEventTypeNone; 125 | } 126 | } 127 | 128 | - (BOOL)typeFilterAllowsEventType:(int)eventType{ 129 | switch(eventType){ 130 | case FSE_CREATE_FILE: 131 | return (self.typeFilter & FSMonitorEventTypeFileCreation); 132 | case FSE_DELETE: 133 | return (self.typeFilter & FSMonitorEventTypeDeletion); 134 | case FSE_RENAME: 135 | return (self.typeFilter & FSMonitorEventTypeRename); 136 | case FSE_CONTENT_MODIFIED: 137 | return (self.typeFilter & FSMonitorEventTypeModification); 138 | case FSE_CREATE_DIR: 139 | return (self.typeFilter & FSMonitorEventTypeDirectoryCreation); 140 | case FSE_CHOWN: 141 | return (self.typeFilter & FSMonitorEventTypeOwnershipChange); 142 | default: 143 | return false; 144 | } 145 | } 146 | 147 | - (void)dealloc{ 148 | [self.directoryFilter release]; 149 | [super dealloc]; 150 | } 151 | 152 | @end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Evan Swick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export ARCHS=armv7 arm64 2 | 3 | include theos/makefiles/common.mk 4 | 5 | LIBRARY_NAME = libfsmonitor 6 | libfsmonitor_FILES = FSMonitor.m 7 | libfsmonitor_PRIVATE_FRAMEWORKS = AppSupport 8 | CFLAGS = -Ilayout/usr/include 9 | 10 | include $(THEOS_MAKE_PATH)/library.mk 11 | SUBPROJECTS += fsmonitord 12 | include $(THEOS_MAKE_PATH)/aggregate.mk 13 | -------------------------------------------------------------------------------- /NSDistributedNotificationCenter.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @interface NSDistributedNotificationCenter : NSNotificationCenter 5 | 6 | + (id)defaultCenter; 7 | 8 | - (void)addObserver:(id)arg1 selector:(SEL)arg2 name:(id)arg3 object:(id)arg4; 9 | - (void)postNotificationName:(id)arg1 object:(id)arg2 userInfo:(id)arg3; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libfsmonitor # 2 | 3 | ##Overview## 4 | 5 | libfsmonitor is an Objective-C library allows processes to be notified upon various filesystem events, such as files being created, modified, or deleted. The daemon (fsmonitord) trails /dev/fsevents and parses the data into an NSDictionary, which is then sent via CPDistributedNotificationCenter to any interested clients. Included is a small wrapper around CPDistributedNotificationCenter which handles listening and directory filtering. 6 | 7 | ## Usage ## 8 | Classes wishing to receive filesystem notification events must conform to the **FSMonitorDelegate** protocol. 9 | 10 | #### Initialization #### 11 | 12 | FSMonitor *filesystemMonitor = [FSMonitor new]; 13 | filesystemMonitor.delegate = self; 14 | 15 | [filesystemMonitor addDirectoryFilter:[NSURL URLWithString:@"/path/to/monitor/"]]; 16 | 17 | #### Delegate Method #### 18 | - (void)monitor:(FSMonitor*)monitor recievedEventInfo:(NSDictionary*)info{ 19 | FSMonitorEventType type = [[info objectForKey:@"TYPE"] intValue]; 20 | switch(type){ 21 | case FSMonitorEventTypeRename: 22 | { 23 | NSString *originalName = [[info objectForKey:@"FILE"] path]; 24 | NSString *newName = [[info objectForKey:@"DEST_FILE"] path]; 25 | break; 26 | } 27 | case FSMonitorEventTypeDeletion: 28 | ... 29 | #### Event Info #### 30 | The info dictionary contains information about the event. All events include the following keys and their corresponding values: 31 | 32 | * FILE 33 | * The path of the file or directory involved with the event. 34 | * DEVICE_MAJOR 35 | * The major value of the physical drive FILE is located on. 36 | * DEVICE_MINOR 37 | * The minor value of the physical drive FILE is located on. 38 | * INODE 39 | * Whatever the hell an inode is. 40 | * MODE 41 | * The read/write/execute protections on FILE. 42 | * UID 43 | * The UID of the process that caused the event. 44 | * GID 45 | * The GID of the process that caused the event. 46 | 47 | The only exception is FSMonitorEventTypeRename, which contains one extra value: 48 | 49 | * DEST_FILE 50 | * The path of the new file. 51 | -------------------------------------------------------------------------------- /fsmonitord/Makefile: -------------------------------------------------------------------------------- 1 | include theos/makefiles/common.mk 2 | 3 | TOOL_NAME = fsmonitord 4 | fsmonitord_FILES = main.m 5 | fsmonitord_PRIVATE_FRAMEWORKS = AppSupport 6 | fsmonitord_INSTALL_PATH = /usr/libexec 7 | 8 | include $(THEOS_MAKE_PATH)/tool.mk 9 | -------------------------------------------------------------------------------- /fsmonitord/fsevents.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. 3 | * 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. The rights granted to you under the License 10 | * may not be used to create, or enable the creation or redistribution of, 11 | * unlawful or unlicensed copies of an Apple operating system, or to 12 | * circumvent, violate, or enable the circumvention or violation of, any 13 | * terms of an Apple operating system software license agreement. 14 | * 15 | * Please obtain a copy of the License at 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 | * 18 | * The Original Code and all software distributed under the License are 19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 | * Please see the License for the specific language governing rights and 24 | * limitations under the License. 25 | * 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 | */ 28 | #ifndef FSEVENT_H 29 | #define FSEVENT_H 1 30 | 31 | // Event types that you can ask to listen for 32 | #define FSE_INVALID -1 33 | #define FSE_CREATE_FILE 0 34 | #define FSE_DELETE 1 35 | #define FSE_STAT_CHANGED 2 36 | #define FSE_RENAME 3 37 | #define FSE_CONTENT_MODIFIED 4 38 | #define FSE_EXCHANGE 5 39 | #define FSE_FINDER_INFO_CHANGED 6 40 | #define FSE_CREATE_DIR 7 41 | #define FSE_CHOWN 8 42 | #define FSE_XATTR_MODIFIED 9 43 | #define FSE_XATTR_REMOVED 10 44 | 45 | #define FSE_MAX_EVENTS 11 46 | #define FSE_ALL_EVENTS 998 47 | 48 | #define FSE_EVENTS_DROPPED 999 49 | 50 | // 51 | // These defines only apply if you've asked for extended 52 | // type info. In that case the type field's low 12-bits 53 | // contain the basic types defined above and the top 4 54 | // bits contain flags that indicate if an event had other 55 | // events combined with it or if it represents a directory 56 | // that has dropped events below it. 57 | // 58 | #define FSE_TYPE_MASK 0x0fff 59 | #define FSE_FLAG_MASK 0xf000 60 | #define FSE_FLAG_SHIFT 12 61 | #define FSE_GET_FLAGS(type) (((type) >> 12) & 0x000f) 62 | 63 | #define FSE_COMBINED_EVENTS 0x0001 64 | #define FSE_CONTAINS_DROPPED_EVENTS 0x0002 65 | 66 | 67 | // Actions for each event type 68 | #define FSE_IGNORE 0 69 | #define FSE_REPORT 1 70 | #define FSE_ASK 2 // Not implemented yet 71 | 72 | // The types of each of the arguments for an event 73 | // Each type is followed by the size and then the 74 | // data. FSE_ARG_VNODE is just a path string 75 | #define FSE_ARG_VNODE 0x0001 // next arg is a vnode pointer 76 | #define FSE_ARG_STRING 0x0002 // next arg is length followed by string ptr 77 | #define FSE_ARG_PATH 0x0003 // next arg is a full path 78 | #define FSE_ARG_INT32 0x0004 // next arg is a 32-bit int 79 | #define FSE_ARG_INT64 0x0005 // next arg is a 64-bit int 80 | #define FSE_ARG_RAW 0x0006 // next arg is a length followed by a void ptr 81 | #define FSE_ARG_INO 0x0007 // next arg is the inode number (ino_t) 82 | #define FSE_ARG_UID 0x0008 // next arg is the file's uid (uid_t) 83 | #define FSE_ARG_DEV 0x0009 // next arg is the file's dev_t 84 | #define FSE_ARG_MODE 0x000a // next arg is the file's mode (as an int32, file type only) 85 | #define FSE_ARG_GID 0x000b // next arg is the file's gid (gid_t) 86 | #define FSE_ARG_FINFO 0x000c // next arg is a packed finfo (dev, ino, mode, uid, gid) 87 | #define FSE_ARG_DONE 0xb33f // no more arguments 88 | 89 | #define FSE_MAX_ARGS 12 90 | 91 | // 92 | // These are special bits that be set in the 32-bit mode 93 | // field that /dev/fsevents provides. 94 | // 95 | #define FSE_MODE_HLINK (1 << 31) // notification is for a hard-link 96 | #define FSE_MODE_LAST_HLINK (1 << 30) // link count == 0 on a hard-link delete 97 | #define FSE_REMOTE_DIR_EVENT (1 << 29) // this is a remotely generated directory-level granularity event 98 | #define FSE_TRUNCATED_PATH (1 << 28) // the path for this item had to be truncated 99 | 100 | // ioctl's on /dev/fsevents 101 | #if __LP64__ 102 | typedef struct fsevent_clone_args { 103 | int8_t *event_list; 104 | int32_t num_events; 105 | int32_t event_queue_depth; 106 | int32_t *fd; 107 | } fsevent_clone_args; 108 | #else 109 | typedef struct fsevent_clone_args { 110 | int8_t *event_list; 111 | int32_t pad1; 112 | int32_t num_events; 113 | int32_t event_queue_depth; 114 | int32_t *fd; 115 | int32_t pad2; 116 | } fsevent_clone_args; 117 | #endif 118 | 119 | #define FSEVENTS_CLONE _IOW('s', 1, fsevent_clone_args) 120 | 121 | 122 | // ioctl's on the cloned fd 123 | #if __LP64__ 124 | #pragma pack(push, 4) 125 | typedef struct fsevent_dev_filter_args { 126 | uint32_t num_devices; 127 | dev_t *devices; 128 | } fsevent_dev_filter_args; 129 | #pragma pack(pop) 130 | #else 131 | typedef struct fsevent_dev_filter_args { 132 | uint32_t num_devices; 133 | dev_t *devices; 134 | int32_t pad1; 135 | } fsevent_dev_filter_args; 136 | #endif 137 | 138 | #define FSEVENTS_DEVICE_FILTER _IOW('s', 100, fsevent_dev_filter_args) 139 | #define FSEVENTS_WANT_COMPACT_EVENTS _IO('s', 101) 140 | #define FSEVENTS_WANT_EXTENDED_INFO _IO('s', 102) 141 | #define FSEVENTS_GET_CURRENT_ID _IOR('s', 103, uint64_t) 142 | 143 | 144 | #ifdef KERNEL 145 | 146 | void fsevents_init(void); 147 | int need_fsevent(int type, vnode_t vp); 148 | int add_fsevent(int type, vfs_context_t, ...); 149 | void fsevent_unmount(struct mount *mp); 150 | struct vnode_attr; 151 | void create_fsevent_from_kevent(vnode_t vp, uint32_t kevents, struct vnode_attr *vap); 152 | 153 | // misc utility functions for fsevent info and pathbuffers... 154 | typedef struct fse_info { 155 | ino64_t ino; 156 | dev_t dev; 157 | int32_t mode; // note: this is not a mode_t (it's 32-bits, not 16) 158 | uid_t uid; 159 | gid_t gid; 160 | uint64_t nlink; // only filled in if the vnode is marked as a hardlink 161 | } fse_info; 162 | 163 | int get_fse_info(struct vnode *vp, fse_info *fse, vfs_context_t ctx); 164 | int vnode_get_fse_info_from_vap(vnode_t vp, fse_info *fse, struct vnode_attr *vap); 165 | 166 | char *get_pathbuff(void); 167 | void release_pathbuff(char *path); 168 | 169 | #endif /* KERNEL */ 170 | 171 | #endif /* FSEVENT_H */ 172 | -------------------------------------------------------------------------------- /fsmonitord/main.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #import "fsevents.h" 13 | #import "../NSDistributedNotificationCenter.h" 14 | 15 | #define DEV_FSEVENTS "/dev/fsevents" // the fsevents pseudo-device 16 | #define FSEVENT_BUFSIZ 131072 // buffer for reading from the device 17 | #define EVENT_QUEUE_SIZE 4096 // limited by MAX_KFS_EVENTS 18 | 19 | void handleEvent(pid_t pid, int32_t type, NSArray *arguments); 20 | 21 | NSDistributedNotificationCenter *notifier; 22 | 23 | typedef struct kfs_event_arg { 24 | u_int16_t type; // argument type 25 | u_int16_t len; // size of argument data that follows this field 26 | } kfs_event_arg_t; 27 | 28 | 29 | #define KFS_NUM_ARGS FSE_MAX_ARGS 30 | 31 | // an event 32 | typedef struct kfs_event { 33 | int32_t type; // event type 34 | pid_t pid; // pid of the process that performed the operation 35 | kfs_event_arg_t args[KFS_NUM_ARGS]; // event arguments 36 | } kfs_event; 37 | 38 | int main(int argc, char **argv){ 39 | int32_t arg_id; 40 | int fd, clonefd = -1; 41 | int i, eoff, off, ret; 42 | 43 | kfs_event_arg_t *kea; 44 | struct fsevent_clone_args fca; 45 | char buffer[FSEVENT_BUFSIZ]; 46 | u_int32_t is_fse_arg_vnode = 0; 47 | int8_t event_list[] = { // action to take for each event 48 | FSE_REPORT, // FSE_CREATE_FILE, 49 | FSE_REPORT, // FSE_DELETE, 50 | FSE_REPORT, // FSE_STAT_CHANGED, 51 | FSE_REPORT, // FSE_RENAME, 52 | FSE_REPORT, // FSE_CONTENT_MODIFIED, 53 | FSE_REPORT, // FSE_EXCHANGE, 54 | FSE_REPORT, // FSE_FINDER_INFO_CHANGED, 55 | FSE_REPORT, // FSE_CREATE_DIR, 56 | FSE_REPORT, // FSE_CHOWN, 57 | FSE_REPORT, // FSE_XATTR_MODIFIED, 58 | FSE_REPORT, // FSE_XATTR_REMOVED, 59 | }; 60 | 61 | notifier = [NSDistributedNotificationCenter defaultCenter]; 62 | 63 | if (geteuid() != 0) { 64 | NSLog(@"Error: %s must be run as root.", argv[0]); 65 | exit(1); 66 | } 67 | 68 | setbuf(stdout, NULL); 69 | 70 | if ((fd = open(DEV_FSEVENTS, O_RDONLY)) < 0) { 71 | perror("open"); 72 | exit(1); 73 | } 74 | 75 | fca.event_list = (int8_t *)event_list; 76 | fca.num_events = sizeof(event_list)/sizeof(int8_t); 77 | fca.event_queue_depth = EVENT_QUEUE_SIZE; 78 | fca.fd = &clonefd; 79 | if ((ret = ioctl(fd, FSEVENTS_CLONE, (char *)&fca)) < 0) { 80 | perror("ioctl"); 81 | close(fd); 82 | exit(1); 83 | } 84 | 85 | close(fd); 86 | 87 | if ((ret = ioctl(clonefd, FSEVENTS_WANT_EXTENDED_INFO, NULL)) < 0) { 88 | perror("ioctl"); 89 | close(clonefd); 90 | exit(1); 91 | } 92 | 93 | while (1) { // event processing loop 94 | 95 | ret = read(clonefd, buffer, FSEVENT_BUFSIZ); 96 | 97 | off = 0; 98 | 99 | while (off < ret) { // process one or more events received 100 | 101 | struct kfs_event *kfse = (struct kfs_event *)((char *)buffer + off); 102 | 103 | off += sizeof(int32_t) + sizeof(pid_t); // type + pid 104 | 105 | if (kfse->type == FSE_EVENTS_DROPPED) { // special event 106 | NSLog(@"Process %d dropped events.", kfse->pid); 107 | off += sizeof(u_int16_t); // FSE_ARG_DONE: sizeof(type) 108 | continue; 109 | } 110 | 111 | int32_t atype = kfse->type & FSE_TYPE_MASK; 112 | uint32_t aflags = FSE_GET_FLAGS(kfse->type); 113 | 114 | if ((atype < FSE_MAX_EVENTS) && (atype >= -1)) { //atype = Event Type 115 | if (aflags & FSE_COMBINED_EVENTS) { 116 | //Combined events 117 | } 118 | if (aflags & FSE_CONTAINS_DROPPED_EVENTS) { 119 | //Contains dropped events 120 | } 121 | } else { // should never happen 122 | NSLog(@"This may be a program bug (type = %d).", atype); 123 | exit(1); 124 | } 125 | 126 | NSMutableArray *arguments = [[NSMutableArray alloc] init]; 127 | 128 | kea = kfse->args; 129 | i = 0; 130 | 131 | while (off < ret) {// process arguments 132 | i++; 133 | 134 | if (kea->type == FSE_ARG_DONE) { // no more arguments 135 | off += sizeof(u_int16_t); 136 | break; 137 | } 138 | 139 | eoff = sizeof(kea->type) + sizeof(kea->len) + kea->len; 140 | off += eoff; 141 | 142 | arg_id = (kea->type > FSE_MAX_ARGS) ? 0 : kea->type; 143 | 144 | void *value_offset = (char *)kea + sizeof(kfs_event_arg_t); 145 | 146 | switch (kea->type) { // handle based on argument type 147 | 148 | case FSE_ARG_VNODE: // a vnode (string) pointer 149 | is_fse_arg_vnode = 1; 150 | [arguments addObject:[NSString stringWithUTF8String:(char*)value_offset]]; 151 | break; 152 | 153 | case FSE_ARG_STRING: // a string pointer 154 | [arguments addObject:[NSString stringWithUTF8String:(char*)value_offset]]; 155 | break; 156 | 157 | case FSE_ARG_INT32: 158 | [arguments addObject:@(*((uint32_t*)(value_offset)))]; 159 | break; 160 | 161 | case FSE_ARG_RAW: // a void pointer 162 | //Not implemented 163 | break; 164 | 165 | case FSE_ARG_INO: // an inode number 166 | [arguments addObject:@(*((uint32_t*)(value_offset)))]; 167 | break; 168 | 169 | case FSE_ARG_UID: // a user ID 170 | [arguments addObject:@(*((uid_t*)(value_offset)))]; 171 | break; 172 | 173 | case FSE_ARG_DEV: // a file system ID or a device number 174 | [arguments addObject:@(*((dev_t*)(value_offset)))]; 175 | break; 176 | 177 | case FSE_ARG_MODE: // a combination of file mode and file type 178 | [arguments addObject:@(*((int32_t*)(value_offset)))]; 179 | break; 180 | 181 | case FSE_ARG_GID: // a group ID 182 | [arguments addObject:@(*((gid_t*)(value_offset)))]; 183 | break; 184 | 185 | case FSE_ARG_INT64: // timestamp 186 | [arguments addObject:@(*((time_t*)(value_offset)))]; 187 | break; 188 | 189 | default: 190 | NSLog(@"such argument. so unknown. wow."); 191 | NSLog(@"(Unknown event argument)"); 192 | [arguments addObject:@"unknown"]; 193 | break; 194 | } 195 | 196 | kea = (kfs_event_arg_t *)((char *)kea + eoff); // next 197 | } // for each argument 198 | handleEvent(kfse->pid, kfse->type, arguments); 199 | [arguments release]; 200 | } // for each event 201 | } // forever 202 | 203 | close(clonefd); 204 | 205 | exit(0); 206 | } 207 | 208 | 209 | void handleEvent(pid_t pid, int32_t type, NSArray *arguments){ 210 | 211 | NSMutableDictionary *event = [NSMutableDictionary new]; 212 | 213 | [event setObject:@(type) forKey:@"TYPE"]; 214 | [event setObject:@(pid) forKey:@"PID"]; 215 | [event setObject:[arguments objectAtIndex:[arguments count] - 1] forKey:@"TIMESTAMP"]; 216 | 217 | switch(type){ 218 | case FSE_XATTR_REMOVED: 219 | case FSE_FINDER_INFO_CHANGED: 220 | case FSE_EXCHANGE: 221 | NSLog(@"Event type %i not implemented with arguments: %@", type, arguments); 222 | [event release]; 223 | return; 224 | case FSE_RENAME: 225 | [event setObject:[arguments objectAtIndex:6] forKey:@"DEST_FILE"]; 226 | default: 227 | [event setObject:[arguments objectAtIndex:0] forKey:@"FILE"]; 228 | [event setObject:@(major([[arguments objectAtIndex:1] intValue])) forKey:@"DEVICE_MAJOR"]; 229 | [event setObject:@(minor([[arguments objectAtIndex:1] intValue])) forKey:@"DEVICE_MINOR"]; 230 | [event setObject:[arguments objectAtIndex:2] forKey:@"INODE"]; 231 | [event setObject:[arguments objectAtIndex:3] forKey:@"MODE"]; 232 | [event setObject:[arguments objectAtIndex:4] forKey:@"UID"]; 233 | [event setObject:[arguments objectAtIndex:5] forKey:@"GID"]; 234 | break; 235 | } 236 | 237 | [notifier postNotificationName:@"FSMONITORD_CALLBACK" object:nil userInfo:event]; 238 | 239 | [event release]; 240 | } 241 | 242 | -------------------------------------------------------------------------------- /fsmonitord/theos: -------------------------------------------------------------------------------- 1 | /opt/theos -------------------------------------------------------------------------------- /layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.eswick.libfsmonitor 2 | Name: libfsmonitor 3 | Version: 1.0.0 4 | Architecture: iphoneos-arm 5 | Description: Open source Objective-C library for monitoring filesystem events. 6 | Maintainer: Evan Swick 7 | Author: Evan Swick 8 | Section: Development 9 | Tag: role::developer 10 | -------------------------------------------------------------------------------- /layout/DEBIAN/extrainst_: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | chown root /Library/LaunchDaemons/com.eswick.fsmonitord.plist 3 | chmod 644 /Library/LaunchDaemons/com.eswick.fsmonitord.plist 4 | 5 | if [[ $1 == upgrade ]]; then 6 | launchctl unload /Library/LaunchDaemons/com.eswick.fsmonitord.plist 7 | fi 8 | 9 | if [[ $1 == install || $1 == upgrade ]]; then 10 | launchctl load /Library/LaunchDaemons/com.eswick.fsmonitord.plist 11 | fi 12 | 13 | exit 0 -------------------------------------------------------------------------------- /layout/DEBIAN/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | chown root /Library/LaunchDaemons/com.eswick.fsmonitord.plist 3 | chmod 644 /Library/LaunchDaemons/com.eswick.fsmonitord.plist 4 | 5 | if [[ $1 == remove || $1 == purge ]]; then 6 | launchctl unload /Library/LaunchDaemons/com.eswick.fsmonitord.plist 7 | fi 8 | 9 | exit 0 -------------------------------------------------------------------------------- /layout/Library/LaunchDaemons/com.eswick.fsmonitord.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eswick/libfsmonitor/62d98adcbd0689ac455ad4eec7e977bca1c68cd6/layout/Library/LaunchDaemons/com.eswick.fsmonitord.plist -------------------------------------------------------------------------------- /layout/usr/include/fsmonitor.h: -------------------------------------------------------------------------------- 1 | enum{ 2 | FSMonitorEventTypeNone = 0, 3 | FSMonitorEventTypeFileCreation = 1 << 0, 4 | FSMonitorEventTypeDeletion = 1 << 1, 5 | FSMonitorEventTypeRename = 1 << 2, 6 | FSMonitorEventTypeModification = 1 << 3, 7 | FSMonitorEventTypeDirectoryCreation = 1 << 4, 8 | FSMonitorEventTypeOwnershipChange = 1 << 5, 9 | FSMonitorEventTypeAll = 0xFF 10 | }; 11 | typedef NSUInteger FSMonitorEventType; 12 | 13 | @class FSMonitor; 14 | 15 | @protocol FSMonitorDelegate 16 | @required 17 | - (void)monitor:(FSMonitor*)monitor recievedEventInfo:(NSDictionary*)info; 18 | @end 19 | 20 | @interface FSMonitor : NSObject 21 | 22 | @property (assign) id delegate; 23 | @property (assign) FSMonitorEventType typeFilter; 24 | @property (retain) NSMutableArray *directoryFilter; 25 | 26 | - (void)addDirectoryFilter:(NSURL*)url recursive:(BOOL)recursive; 27 | - (void)removeDirectoryFilter:(NSURL*)url; 28 | 29 | @end -------------------------------------------------------------------------------- /theos: -------------------------------------------------------------------------------- 1 | /opt/theos --------------------------------------------------------------------------------