Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DerekSelander/fbd4fba1a00c99b138250d7868f959b0 to your computer and use it in GitHub Desktop.
Save DerekSelander/fbd4fba1a00c99b138250d7868f959b0 to your computer and use it in GitHub Desktop.
j's filemon
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h> // for _IOW, a macro required by FSEVENTS_CLONE
#include <sys/types.h> // for uint32_t and friends, on which fsevents.h relies
#include <unistd.h>
#include <string.h> // memset
//#include <sys/_types.h> // for uint32_t and friends, on which fsevents.h relies
#include <sys/stat.h> // for mkdir
#include <libgen.h> // for basename
#include <sys/sysctl.h> // for sysctl, KERN_PROC, etc.
#include <errno.h>
/*
* Copyright (c) 2000-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_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. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* 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_OSREFERENCE_LICENSE_HEADER_END@
*/
#ifndef FSEVENT_H
#define FSEVENT_H 1
// Event types that you can ask to listen for
#define FSE_INVALID -1
#define FSE_CREATE_FILE 0
#define FSE_DELETE 1
#define FSE_STAT_CHANGED 2
#define FSE_RENAME 3
#define FSE_CONTENT_MODIFIED 4
#define FSE_EXCHANGE 5
#define FSE_FINDER_INFO_CHANGED 6
#define FSE_CREATE_DIR 7
#define FSE_CHOWN 8
#define FSE_XATTR_MODIFIED 9
#define FSE_XATTR_REMOVED 10
#define FSE_DOCID_CREATED 11
#define FSE_DOCID_CHANGED 12
#define FSE_UNMOUNT_PENDING 13 // iOS-only: client must respond via FSEVENTS_UNMOUNT_PENDING_ACK
#define FSE_CLONE 14
#define FSE_MAX_EVENTS 15
#define FSE_ALL_EVENTS 998
#define FSE_EVENTS_DROPPED 999
//
// These defines only apply if you've asked for extended
// type info. In that case the type field's low 12-bits
// contain the basic types defined above and the top 4
// bits contain flags that indicate if an event had other
// events combined with it or if it represents a directory
// that has dropped events below it.
//
#define FSE_TYPE_MASK 0x0fff
#define FSE_FLAG_MASK 0xf000
#define FSE_FLAG_SHIFT 12
#define FSE_GET_FLAGS(type) (((type) >> 12) & 0x000f)
#define FSE_COMBINED_EVENTS 0x0001
#define FSE_CONTAINS_DROPPED_EVENTS 0x0002
// Actions for each event type
#define FSE_IGNORE 0
#define FSE_REPORT 1
#define FSE_ASK 2 // Not implemented yet
// The types of each of the arguments for an event
// Each type is followed by the size and then the
// data. FSE_ARG_VNODE is just a path string
#define FSE_ARG_VNODE 0x0001 // next arg is a vnode pointer
#define FSE_ARG_STRING 0x0002 // next arg is length followed by string ptr
#define FSE_ARG_PATH 0x0003 // next arg is a full path
#define FSE_ARG_INT32 0x0004 // next arg is a 32-bit int
#define FSE_ARG_INT64 0x0005 // next arg is a 64-bit int
#define FSE_ARG_RAW 0x0006 // next arg is a length followed by a void ptr
#define FSE_ARG_INO 0x0007 // next arg is the inode number (ino_t)
#define FSE_ARG_UID 0x0008 // next arg is the file's uid (uid_t)
#define FSE_ARG_DEV 0x0009 // next arg is the file's dev_t
#define FSE_ARG_MODE 0x000a // next arg is the file's mode (as an int32, file type only)
#define FSE_ARG_GID 0x000b // next arg is the file's gid (gid_t)
#define FSE_ARG_FINFO 0x000c // next arg is a packed finfo (dev, ino, mode, uid, gid)
#define FSE_ARG_DONE 0xb33f // no more arguments
#define FSE_MAX_ARGS 12
//
// These are special bits that be set in the 32-bit mode
// field that /dev/fsevents provides.
//
#define FSE_MODE_HLINK (1U << 31) // notification is for a hard-link
#define FSE_MODE_LAST_HLINK (1U << 30) // link count == 0 on a hard-link delete
#define FSE_REMOTE_DIR_EVENT (1U << 29) // this is a remotely generated directory-level granularity event
#define FSE_TRUNCATED_PATH (1U << 28) // the path for this item had to be truncated
#define FSE_MODE_CLONE (1U << 27) // notification is for a clone
// ioctl's on /dev/fsevents
typedef struct fsevent_clone_args {
int8_t *event_list;
int32_t num_events;
int32_t event_queue_depth;
int32_t *fd;
} fsevent_clone_args;
#define FSEVENTS_CLONE _IOW('s', 1, fsevent_clone_args)
// ioctl's on the cloned fd
#pragma pack(push, 4)
typedef struct fsevent_dev_filter_args {
uint32_t num_devices;
dev_t *devices;
} fsevent_dev_filter_args;
#pragma pack(pop)
#define FSEVENTS_DEVICE_FILTER _IOW('s', 100, fsevent_dev_filter_args)
#define FSEVENTS_WANT_COMPACT_EVENTS _IO('s', 101)
#define FSEVENTS_WANT_EXTENDED_INFO _IO('s', 102)
#define FSEVENTS_GET_CURRENT_ID _IOR('s', 103, uint64_t)
#define FSEVENTS_UNMOUNT_PENDING_ACK _IOW('s', 104, dev_t)
#ifdef BSD_KERNEL_PRIVATE
void fsevents_init(void);
void fsevent_unmount(struct mount *mp, vfs_context_t ctx);
struct vnode_attr;
void create_fsevent_from_kevent(vnode_t vp, uint32_t kevents, struct vnode_attr *vap);
// misc utility functions for fsevent info and pathbuffers...
typedef struct fse_info {
ino64_t ino;
dev_t dev;
int32_t mode;// note: this is not a mode_t (it's 32-bits, not 16)
uid_t uid;
gid_t gid;
uint64_t nlink;// only filled in if the vnode is marked as a hardlink
} fse_info;
int get_fse_info(struct vnode *vp, fse_info *fse, vfs_context_t ctx);
int vnode_get_fse_info_from_vap(vnode_t vp, fse_info *fse, struct vnode_attr *vap);
char *get_pathbuff(void);
void release_pathbuff(char *path);
#endif /* BSD_KERNEL_PRIVATE */
#ifdef KERNEL_PRIVATE
int need_fsevent(int type, vnode_t vp);
int add_fsevent(int type, vfs_context_t, ...);
#endif /* KERNEL_PRIVATE */
#endif /* FSEVENT_H */
//-- Begin: copied from "bsd/vfs/vfs_events.c"
#pragma pack(1)
typedef struct kfs_event_a {
uint16_t type;
uint16_t refcount;
pid_t pid;
} kfs_event_a;
typedef struct kfs_event_arg {
uint16_t type;
uint16_t pathlen;
char data[0];
} kfs_event_arg;
#pragma pack()
/*
* Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_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. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* 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_OSREFERENCE_LICENSE_HEADER_END@
*/
#ifndef FSEVENT_H
#define FSEVENT_H 1
// Event types that you can ask to listen for
#define FSE_INVALID -1
#define FSE_CREATE_FILE 0
#define FSE_DELETE 1
#define FSE_STAT_CHANGED 2
#define FSE_RENAME 3
#define FSE_CONTENT_MODIFIED 4
#define FSE_EXCHANGE 5
#define FSE_FINDER_INFO_CHANGED 6
#define FSE_CREATE_DIR 7
#define FSE_CHOWN 8
#define FSE_XATTR_MODIFIED 9
#define FSE_XATTR_REMOVED 10
#define FSE_MAX_EVENTS 11
#define FSE_ALL_EVENTS 998
#define FSE_EVENTS_DROPPED 999
//
// These defines only apply if you've asked for extended
// type info. In that case the type field's low 12-bits
// contain the basic types defined above and the top 4
// bits contain flags that indicate if an event had other
// events combined with it or if it represents a directory
// that has dropped events below it.
//
#define FSE_TYPE_MASK 0x0fff
#define FSE_FLAG_MASK 0xf000
#define FSE_FLAG_SHIFT 12
#define FSE_GET_FLAGS(type) (((type) >> 12) & 0x000f)
#define FSE_COMBINED_EVENTS 0x0001
#define FSE_CONTAINS_DROPPED_EVENTS 0x0002
// Actions for each event type
#define FSE_IGNORE 0
#define FSE_REPORT 1
#define FSE_ASK 2 // Not implemented yet
// The types of each of the arguments for an event
// Each type is followed by the size and then the
// data. FSE_ARG_VNODE is just a path string
#define FSE_ARG_VNODE 0x0001 // next arg is a vnode pointer
#define FSE_ARG_STRING 0x0002 // next arg is length followed by string ptr
#define FSE_ARG_PATH 0x0003 // next arg is a full path
#define FSE_ARG_INT32 0x0004 // next arg is a 32-bit int
#define FSE_ARG_INT64 0x0005 // next arg is a 64-bit int
#define FSE_ARG_RAW 0x0006 // next arg is a length followed by a void ptr
#define FSE_ARG_INO 0x0007 // next arg is the inode number (ino_t)
#define FSE_ARG_UID 0x0008 // next arg is the file's uid (uid_t)
#define FSE_ARG_DEV 0x0009 // next arg is the file's dev_t
#define FSE_ARG_MODE 0x000a // next arg is the file's mode (as an int32, file type only)
#define FSE_ARG_GID 0x000b // next arg is the file's gid (gid_t)
#define FSE_ARG_FINFO 0x000c // next arg is a packed finfo (dev, ino, mode, uid, gid)
#define FSE_ARG_DONE 0xb33f // no more arguments
#define FSE_MAX_ARGS 12
//
// These are special bits that be set in the 32-bit mode
// field that /dev/fsevents provides.
//
#define FSE_MODE_HLINK (1 << 31) // notification is for a hard-link
#define FSE_MODE_LAST_HLINK (1 << 30) // link count == 0 on a hard-link delete
#define FSE_REMOTE_DIR_EVENT (1 << 29) // this is a remotely generated directory-level granularity event
#define FSE_TRUNCATED_PATH (1 << 28) // the path for this item had to be truncated
// ioctl's on /dev/fsevents
#if __LP64__
typedef struct fsevent_clone_args {
int8_t *event_list;
int32_t num_events;
int32_t event_queue_depth;
int32_t *fd;
} fsevent_clone_args;
#else
typedef struct fsevent_clone_args {
int8_t *event_list;
int32_t pad1;
int32_t num_events;
int32_t event_queue_depth;
int32_t *fd;
int32_t pad2;
} fsevent_clone_args;
#endif
#define FSEVENTS_CLONE _IOW('s', 1, fsevent_clone_args)
// ioctl's on the cloned fd
#if __LP64__
#pragma pack(push, 4)
typedef struct fsevent_dev_filter_args {
uint32_t num_devices;
dev_t *devices;
} fsevent_dev_filter_args;
#pragma pack(pop)
#else
typedef struct fsevent_dev_filter_args {
uint32_t num_devices;
dev_t *devices;
int32_t pad1;
} fsevent_dev_filter_args;
#endif
#define FSEVENTS_DEVICE_FILTER _IOW('s', 100, fsevent_dev_filter_args)
#define FSEVENTS_WANT_COMPACT_EVENTS _IO('s', 101)
#define FSEVENTS_WANT_EXTENDED_INFO _IO('s', 102)
#define FSEVENTS_GET_CURRENT_ID _IOR('s', 103, uint64_t)
#ifdef KERNEL
void fsevents_init(void);
int need_fsevent(int type, vnode_t vp);
int add_fsevent(int type, vfs_context_t, ...);
void fsevent_unmount(struct mount *mp);
struct vnode_attr;
void create_fsevent_from_kevent(vnode_t vp, uint32_t kevents, struct vnode_attr *vap);
// misc utility functions for fsevent info and pathbuffers...
typedef struct fse_info {
ino64_t ino;
dev_t dev;
int32_t mode; // note: this is not a mode_t (it's 32-bits, not 16)
uid_t uid;
gid_t gid;
uint64_t nlink; // only filled in if the vnode is marked as a hardlink
} fse_info;
int get_fse_info(struct vnode *vp, fse_info *fse, vfs_context_t ctx);
int vnode_get_fse_info_from_vap(vnode_t vp, fse_info *fse, struct vnode_attr *vap);
char *get_pathbuff(void);
void release_pathbuff(char *path);
#endif /* KERNEL */
#endif /* FSEVENT_H */
//#include <sys/fsevents.h>
//#include "fsevents.h"
/* $NetBSD: filemon.h,v 1.2 2020/01/22 22:10:36 sjg Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Taylor R. Campbell.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FILEMON_H_
#define FILEMON_H_
#include <sys/types.h>
struct filemon;
const char *
filemon_path(void);
struct filemon *
filemon_open(void);
int filemon_close(struct filemon *);
int filemon_setfd(struct filemon *, int);
void filemon_setpid_parent(struct filemon *, pid_t);
int filemon_setpid_child(const struct filemon *, pid_t);
int filemon_readfd(const struct filemon *);
int filemon_process(struct filemon *);
#endif /* FILEMON_H_ */
//#include "colors.h"
#define BOLD "\033[1;1m"
#define RED "\033[0;31m"
#define M0 "\e[0;30m"
#define CYAN "\e[0;36m"
#define M1 "\e[0;31m"
#define GREY "\e[0;37m"
#define M8 "\e[0;38m"
#define M9 "\e[0;39m"
#define GREEN "\e[0;32m"
#define YELLOW "\e[0;33m"
#define BLUE "\e[0;34m"
#define PINK "\e[0;35m"
#define NORMAL "\e[0;0m"
#include <signal.h> // for kill
/**
*
* Filemon: A simple but useful fsevents client utilty for OS X and iOS
*
* Author: Jonathan Levin, TWTR: @Morpheus______ http://NewOSXBook.com/
*
* For details, q.v. MOXiI 1st Ed, chapter 3 (pp 74-78), or MOXiI II, Volume I, Chapter 5
*
* Source is wide open, so you are free to use and abuse, but leaving credit to the original
* author and the book website (http://NewOSXBook.com/) would be appreciated.
*
* New in 2.0:
*
* - Supports filters: Process IDs, names or events
* - Supports auto-stop and auto-link
* - Color
*
* 2.0.1 - Fixed linking and filter self out
* - Can link from elsewhere in the path
*
*/
int g_dumpArgs =0;
#define BUFSIZE 1024 *1024
#define COLOR_OP YELLOW
#define COLOR_PROC BLUE
#define COLOR_PATH CYAN
// Utility functions
static char *
typeToString (uint32_t Type)
{
switch (Type)
{
case FSE_CREATE_FILE: return ("Created ");
case FSE_DELETE: return ("Deleted ");
case FSE_STAT_CHANGED: return ("Changed stat ");
case FSE_RENAME: return ("Renamed ");
case FSE_CONTENT_MODIFIED: return ("Modified ");
case FSE_CREATE_DIR: return ("Created dir ");
case FSE_CHOWN: return ("Chowned ");
case FSE_EXCHANGE: return ("Exchanged "); /* 5 */
case FSE_FINDER_INFO_CHANGED: return ("Finder Info "); /* 6 */
case FSE_XATTR_MODIFIED: return ("Changed xattr "); /* 9 */
case FSE_XATTR_REMOVED: return ("Removed xattr "); /* 10 */
case FSE_DOCID_CREATED: return ("DocID created "); // 11
case FSE_DOCID_CHANGED: return ("DocID changed "); // 11
default : return ("?! ");
}
}
int lastPID = 0;
static char *
getProcName(long pid)
{
static char procName[4096];
size_t len = 1000;
int rc;
int mib[4];
// minor optimization
if (pid != lastPID)
{
memset(procName, '\0', 4096);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
if ((rc = sysctl(mib, 4, procName, &len, NULL,0)) < 0)
{
perror("trace facility failure, KERN_PROC_PID\n");
exit(1);
}
//printf ("GOT PID: %d and rc: %d - %s\n", mib[3], rc, ((struct kinfo_proc *)procName)->kp_proc.p_comm);
lastPID = pid;
}
return (((struct kinfo_proc *)procName)->kp_proc.p_comm);
}
int
doArg(char *arg, int Print)
{
// Dump an arg value
unsigned short *argType = (unsigned short *) arg;
unsigned short *argLen = (unsigned short *) (arg + 2);
uint32_t *argVal = (uint32_t *) (arg+4);
uint64_t *argVal64 = (uint64_t *) (arg+4);
dev_t *dev;
char *str;
switch (*argType)
{
case FSE_ARG_INT64: // This is a timestamp field on the FSEvent
if (g_dumpArgs) printf ("Arg64: %lld ", *argVal64);
break;
case FSE_ARG_STRING:
str = (char *)argVal;
if (Print) printf("%s ", str);
break;
case FSE_ARG_DEV:
dev = (dev_t *) argVal;
if (g_dumpArgs) printf ("DEV: %d,%d ", major(*dev), minor(*dev)); break;
case FSE_ARG_MODE:
if (g_dumpArgs) printf("MODE: %x ", *argVal); break;
case FSE_ARG_PATH:
printf ("PATH: " ); break;
case FSE_ARG_INO:
if (g_dumpArgs) printf ("INODE: %d ", *argVal); break;
case FSE_ARG_UID:
if (g_dumpArgs) printf ("UID: %d ", *argVal); break;
case FSE_ARG_GID:
if (g_dumpArgs) printf ("GID: %d ", *argVal); break;
#define FSE_ARG_FINFO 0x000c // next arg is a packed finfo (dev, ino, mode, uid, gid)
case FSE_ARG_FINFO:
printf ("FINFO\n"); break;
case FSE_ARG_DONE: if (Print)printf("\n");return 2;
default:
printf ("(ARG of type %hd, len %hd)\n", *argType, *argLen);
exit(0);
}
return (4 + *argLen);
}
#define COLOR_OPTION "-c"
#define COLOR_LONG_OPTION "--color"
#define FILTER_PROC_OPTION "-p"
#define FILTER_PROC_LONG_OPTION "--proc"
#define FILTER_FILE_OPTION "-f"
#define FILTER_FILE_LONG_OPTION "--file"
#define FILTER_EVENT_OPTION "-e"
#define FILTER_EVENT_LONG_OPTION "--event"
#define STOP_OPTION "-s"
#define STOP_LONG_OPTION "--stop"
#define LINK_OPTION "-l"
#define LINK_LONG_OPTION "--link"
void
usage()
{
fprintf(stderr,"Usage: %s [options]\n", getprogname());
fprintf(stderr,"Where [options] are optional, and may be any of:\n");
fprintf(stderr,"\t" FILTER_PROC_OPTION "|" FILTER_PROC_LONG_OPTION " pid/procname: filter only this process or PID\n");
fprintf(stderr,"\t" FILTER_FILE_OPTION "|" FILTER_FILE_LONG_OPTION " string[,string]: filter only paths containing this string (/ will catch everything)\n");
fprintf(stderr,"\t" FILTER_EVENT_OPTION "|" FILTER_EVENT_LONG_OPTION " event[,event]: filter only these events\n");
fprintf(stderr,"\t" STOP_OPTION "|" STOP_LONG_OPTION ": auto-stop the process generating event\n");
fprintf(stderr,"\t" LINK_OPTION "|" LINK_LONG_OPTION ": auto-create a hard link to file (prevents deletion by program :-)\n");
fprintf(stderr,"\t" COLOR_OPTION "|" COLOR_LONG_OPTION " (or set JCOLOR=1 first)\n");
fprintf(stderr,"\tThis is J's filemon, compiled on " __DATE__ "\n");
}
// And.. Ze Main
#define MAX_FILTERS 10
int
interesting_process (int pid, char *Filters[], int NumFilters)
{
// filter myself out
if (pid == getpid()) return 0;
// Otherwise, if no user defined filters, get all
if (!NumFilters) return 1;
return 0;
}
int
interesting_file (char *FileName, char *Filters[], int NumFilters)
{
// if no filters - everything is interesting
if (!NumFilters) return 1;
while (NumFilters > 0)
{
// fprintf(stderr,"Checking %s vs %s\n", FileName, Filters[NumFilters-1]);
if (strstr(FileName, Filters[NumFilters-1])) return 1;
NumFilters--;
}
return 0;
}
int
main (int argc, char **argv)
{
#ifdef ARM
// It's smarter to do with statfs()
char *filemonDir = "/private/var/tmp/filemon";
mkdir (filemonDir, 0777);
#endif
int fsed, cloned_fsed;
int i;
int rc;
fsevent_clone_args clone_args;
unsigned short *arg_type;
char *buf = malloc(BUFSIZE);
int autostop = 0;
int autolink = 0;
char *fileFilters[MAX_FILTERS]= { 0 };
char *procFilters[MAX_FILTERS]= { 0 };
int numFileFilters = 0;
int numProcFilters = 0;
int color = 0;
if (getenv("JCOLOR")) color++;
//if (argc > 1) { if (strcmp(argv[1],"-v") == 0) g_dumpArgs++; }
int arg = 1;
for (arg = 1; arg < argc; arg++)
{
if (strcmp(argv[arg], "-h") == 0) { usage(); exit(1);}
if ((strcmp(argv[arg], FILTER_PROC_OPTION) == 0) ||
(strcmp(argv[arg], FILTER_PROC_LONG_OPTION) == 0))
{
if (arg == argc -1)
{
fprintf(stderr, "%s: Option requires an argument\n",
argv[arg]);
exit(2);
}
numProcFilters++;
arg++; continue;
}
if ((strcmp(argv[arg], FILTER_EVENT_OPTION) == 0) ||
(strcmp(argv[arg], FILTER_EVENT_LONG_OPTION) == 0))
{
if (arg == argc -1)
{
fprintf(stderr, "%s: Option requires an argument\n",
argv[arg]);
exit(2);
}
arg++; continue;
}
if ((strcmp(argv[arg], FILTER_FILE_OPTION) == 0) ||
(strcmp(argv[arg], FILTER_FILE_LONG_OPTION) == 0))
{
if (arg == argc -1)
{
fprintf(stderr, "%s: Option requires an argument\n",
argv[arg]);
exit(2);
}
// Got it - add filters, separate by ","
char *begin = argv[arg+1];
char *sep = strchr (begin, ',');
while (sep)
{
*sep = '\0';
fprintf(stderr,"Adding File filter %d: %s\n", numFileFilters, begin);
fileFilters[numFileFilters++] = strdup(begin);
begin = sep + 1;
sep = strchr (begin, ',');
}
fprintf(stderr,"Adding File filter %d: %s\n", numFileFilters, begin);
fileFilters[numFileFilters++] = strdup(begin);
arg++; continue;
}
if ((strcmp(argv[arg], COLOR_OPTION) == 0) || (strcmp(argv[arg], COLOR_LONG_OPTION) == 0))
{
color++;
continue;
}
if ((strcmp(argv[arg], STOP_OPTION) == 0) || (strcmp(argv[arg], STOP_LONG_OPTION) == 0))
{
autostop++;
continue;
}
if ((strcmp(argv[arg], LINK_OPTION) == 0) || (strcmp(argv[arg], LINK_LONG_OPTION) == 0))
{
autolink++;
continue;
}
fprintf(stderr, "%s: Unknown option\n", argv[arg]); exit(3);
}
// This is for your own good: If autostop is allowed on everything, there is a chance a kill -STOP will be
// sent to your own terminal app or SSH.
if (autostop && (!numFileFilters && !numProcFilters))
{
fprintf(stderr,"Error: Cannot allow auto-stopping of processes without either a file or process filter.\nIf you are sure you want to do this, set a null filter\n"); exit(4);
}
// Open the device
fsed = open ("/dev/fsevents", O_RDONLY);
int8_t events[FSE_MAX_EVENTS];
if (geteuid())
{
fprintf(stderr,"Opening /dev/fsevents requires root permissions\n");
}
if (fsed < 0)
{
perror ("open");
exit(1);
}
// Prepare event mask list. In our simple example, we want everything
// (i.e. all events, so we say "FSE_REPORT" all). Otherwise, we
// would have to specifically toggle FSE_IGNORE for each:
//
// e.g.
// events[FSE_XATTR_MODIFIED] = FSE_IGNORE;
// events[FSE_XATTR_REMOVED] = FSE_IGNORE;
// etc..
for (i = 0; i < FSE_MAX_EVENTS; i++)
{
events[i] = FSE_REPORT;
}
// But in v2.0, we allow user to specify events
// Get ready to clone the descriptor:
memset(&clone_args, '\0', sizeof(clone_args));
clone_args.fd = &cloned_fsed; // This is the descriptor we get back
clone_args.event_queue_depth = 100; // Makes all the difference
clone_args.event_list = events;
clone_args.num_events = FSE_MAX_EVENTS;
// Do it.
rc = ioctl (fsed, FSEVENTS_CLONE, &clone_args);
if (rc < 0) { perror ("ioctl"); exit(2);}
// We no longer need original..
close (fsed);
// And now we simply read, ad infinitum (aut nauseam)
while ((rc = read (cloned_fsed, buf, BUFSIZE)) > 0)
{
// rc returns the count of bytes for one or more events:
int offInBuf = 0;
while (offInBuf < rc) {
// printf("----%d/%d bytes\n",offInBuf,rc);
struct kfs_event_a *fse = (struct kfs_event_a *)(buf + offInBuf);
struct kfs_event_arg *fse_arg;
// if (offInBuf) { printf ("Next event: %d\n", offInBuf);};
if (fse->type == FSE_EVENTS_DROPPED)
{
printf("Some events dropped\n");
break;
}
if (!fse->pid){ printf ("%x %x\n", fse->type, fse->refcount); }
int print = 0;
char *procName = getProcName(fse->pid);
offInBuf+= sizeof(struct kfs_event_a);
fse_arg = (struct kfs_event_arg *) &buf[offInBuf];
if (interesting_process(fse->pid, procFilters, numProcFilters)
&& interesting_file (fse_arg->data, fileFilters, numFileFilters))
{
printf ("%5d %s%s%s\t%s%s%s ", fse->pid,
color ? COLOR_PROC: "" , procName, color ? NORMAL : "",
color ? COLOR_OP : "", typeToString(fse->type), color ? NORMAL : "" );
// The path name is null terminated, so that's cool
printf ("%s%s%s\t",
color ? COLOR_PATH : "" , fse_arg->data, color ? NORMAL :"");
// Fix: Don't autolink own files
if (fse->type == FSE_CREATE_FILE && autolink && (fse->pid != getpid()) &&
!strstr(fse_arg->data, "filemon"))
{
int fileLen = strlen(fse_arg->data);
char *linkName = malloc (fileLen + 20);
#ifndef ARM
strcpy(linkName, fse_arg->data);
#else
if (strncmp(fse_arg->data, "/private/var",12) == 0)
{
// Might be in some subdir which will go away
strcpy(linkName, filemonDir);
strcat(linkName,"/");
strcat (linkName, basename(fse_arg->data));
}
#endif
// Don't need to worry about buffer boundaries here..
snprintf(linkName + strlen(linkName), fileLen + 20, ".filemon.%d", autolink);
int rc = link (fse_arg->data, linkName);
if (rc) { fprintf(stderr,"%sWarning: Unable to autolink %s%s - file must have been deleted already\n",
color ? RED : "",
fse_arg->data,
color ? NORMAL : "");}
else { fprintf(stderr,"%sAuto-linked %s to %s%s\n",
color ? GREEN : "",
fse_arg->data, linkName,
color ? NORMAL :"");
autolink++;
}
free (linkName);
}
// Autostop only if this is a file creation, and interesting
if (autostop && fse->type == FSE_CREATE_FILE ) {
fprintf(stderr, "%sAuto-stopping process %s (%d) on file operation%s\n",
color ? RED : "",
procName,
fse->pid,
color ? NORMAL : "");
kill (fse->pid, SIGSTOP);
}
print = 1;
}
offInBuf += sizeof(kfs_event_arg) + fse_arg->pathlen ;
int arg_len = doArg(buf + offInBuf,print);
offInBuf += arg_len;
while (arg_len >2 && offInBuf < rc)
{
arg_len = doArg(buf + offInBuf, print);
offInBuf += arg_len;
}
}
memset (buf,'\0', BUFSIZE);
if (rc > offInBuf) { printf ("*** Warning: Some events may be lost\n"); }
}
} // end main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment