int main(int argc, char **argv) { int32_t arg_id; int fd, clonefd = -1; int i, j, eoff, off, ret; kfs_event_arg_t *kea; struct fsevent_clone_args fca; char buffer[FSEVENT_BUFSIZ]; struct passwd *p; struct group *g; mode_t va_mode; u_int32_t va_type; u_int32_t is_fse_arg_vnode = 0; char fileModeString[11 + 1]; int8_t event_list[] = { // action to take for each event FSE_REPORT, // FSE_CREATE_FILE, FSE_REPORT, // FSE_DELETE, FSE_REPORT, // FSE_STAT_CHANGED, FSE_REPORT, // FSE_RENAME, FSE_REPORT, // FSE_CONTENT_MODIFIED, FSE_REPORT, // FSE_EXCHANGE, FSE_REPORT, // FSE_FINDER_INFO_CHANGED, FSE_REPORT, // FSE_CREATE_DIR, FSE_REPORT, // FSE_CHOWN, FSE_REPORT, // FSE_XATTR_MODIFIED, FSE_REPORT, // FSE_XATTR_REMOVED, }; if (argc != 1) { fprintf(stderr, "%s (%s)\n", PROGNAME, PROGVERS); fprintf(stderr, "File system change logger for Mac OS X. Usage:\n"); fprintf(stderr, "\n\t%s\n\n", PROGNAME); fprintf(stderr, "%s does not take any arguments. " "It must be run as root.\n\n", PROGNAME); printf("Please report bugs using the following contact information:\n" "<URL:http://www.osxbook.com/software/bugs/>\n"); exit(1); exit(1); } if (geteuid() != 0) { fprintf(stderr, "You must be root to run %s. Try again using 'sudo'.\n", PROGNAME); exit(1); } setbuf(stdout, NULL); if ((fd = open(DEV_FSEVENTS, O_RDONLY)) < 0) { perror("open"); exit(1); } fca.event_list = (int8_t *)event_list; fca.num_events = sizeof(event_list)/sizeof(int8_t); fca.event_queue_depth = EVENT_QUEUE_SIZE; fca.fd = &clonefd; if ((ret = ioctl(fd, FSEVENTS_CLONE, (char *)&fca)) < 0) { perror("ioctl"); close(fd); exit(1); } close(fd); if ((ret = ioctl(clonefd, FSEVENTS_WANT_EXTENDED_INFO, NULL)) < 0) { perror("ioctl"); close(clonefd); exit(1); } // char string2[20]="red dwarf"; // char string1[20]=""; // strcpy(string1, string2); char lastpath[300] = "blank"; char currentpath[300]; while (1) { // event processing loop ret = read(clonefd, buffer, FSEVENT_BUFSIZ); off = 0; while (off < ret) { // process one or more events received struct kfs_event *kfse = (struct kfs_event *)((char *)buffer + off); off += sizeof(int32_t) + sizeof(pid_t); // type + pid if (kfse->type == FSE_EVENTS_DROPPED) { // special event fprintf(stderr, " %-14s = %s\n", "type", "EVENTS DROPPED"); exit(1); } int32_t atype = kfse->type & FSE_TYPE_MASK; uint32_t aflags = FSE_GET_FLAGS(kfse->type); if ((atype < FSE_MAX_EVENTS) && (atype >= -1)) { if (aflags & FSE_COMBINED_EVENTS) { fprintf(stderr,"%s", ", combined events"); exit(1); } if (aflags & FSE_CONTAINS_DROPPED_EVENTS) { fprintf(stderr,"%s", ", contains dropped events"); exit(1); } } else { // should never happen printf("This may be a program bug (type = %d).\n", atype); exit(1); } kea = kfse->args; i = 0; while (off < ret) { // process arguments i++; if (kea->type == FSE_ARG_DONE) { // no more arguments // printf(" %s (%#x)\n", "FSE_ARG_DONE", kea->type); off += sizeof(u_int16_t); break; } eoff = sizeof(kea->type) + sizeof(kea->len) + kea->len; off += eoff; arg_id = (kea->type > FSE_MAX_ARGS) ? 0 : kea->type; // printf(" %-16s%4hd ", kfseArgNames[arg_id], kea->len); // switch kfseNames[atype] // printf("%d ", kfse->pid); if (kea->type == FSE_ARG_STRING) { // handle based on argument type strcpy (currentpath,(char *)&(kea->data.str)); // OR atype == FSE_RENAME // strcmp(currentpath, lastpath) != 0 if ( atype == FSE_RENAME || atype == FSE_CREATE_FILE || atype == FSE_CREATE_DIR || strcmp(currentpath, lastpath) != 0) { printf("%d\t%s\t%s\t%s\n", kfse->pid, get_proc_name(kfse->pid), kfseNames[atype], currentpath); } strcpy (lastpath,currentpath); } kea = (kfs_event_arg_t *)((char *)kea + eoff); // next } // for each argument } // for each event } // forever close(clonefd); exit(0); }
int main(int argc, char **argv) { int32_t arg_id; int fd, clonefd = -1; int i, j, eoff, off, ret; FILE* onf; char msg[MAX_SEND]; int c; int mlen = 0; int udp = 0; int det = 0; char fname[MAX_FILENAME] = ""; char raddr[MAX_IP] = "127.0.0.1"; int rport = 12345; kfs_event_arg_t *kea; struct fsevent_clone_args fca; char buffer[FSEVENT_BUFSIZ]; struct passwd *p; struct group *g; mode_t va_mode; u_int32_t va_type; u_int32_t is_fse_arg_vnode = 0; char fileModeString[11 + 1]; int8_t event_list[] = { // action to take for each event FSE_REPORT, // FSE_CREATE_FILE, FSE_REPORT, // FSE_DELETE, FSE_REPORT, // FSE_STAT_CHANGED, FSE_REPORT, // FSE_RENAME, FSE_REPORT, // FSE_CONTENT_MODIFIED, FSE_REPORT, // FSE_EXCHANGE, FSE_REPORT, // FSE_FINDER_INFO_CHANGED, FSE_REPORT, // FSE_CREATE_DIR, FSE_REPORT, // FSE_CHOWN, FSE_REPORT, // FSE_XATTR_MODIFIED, FSE_REPORT, // FSE_XATTR_REMOVED, }; // Print usage if not root if (geteuid() != 0){ usage(); exit(1); } onf = stdout; opterr = 0; while ((c = getopt (argc, argv, "huf:s:p:")) != -1) switch (c){ case 'f': strncpy(fname,optarg,MAX_FILENAME - 1); onf = fopen(fname,"w"); if (onf == NULL){ fprintf(stderr, "Cannot open output file %s.\n\n",optarg); usage(); exit(1); } break; case 'u': udp = 1; break; case 's': strncpy(raddr,optarg,MAX_IP); break; case 'p': rport = atoi( optarg ); break; case 'h': case '?': if (optopt == 'f'){ fprintf(stderr, "Output filename required.\n"); usage(); exit(1); } usage(); exit(1); } setbuf(onf, NULL); //Set UDP Socket set_dest(raddr, rport); set_sock(); if ((fd = open(DEV_FSEVENTS, O_RDONLY)) < 0) { perror("open"); exit(1); } fca.event_list = (int8_t *)event_list; fca.num_events = sizeof(event_list)/sizeof(int8_t); fca.event_queue_depth = EVENT_QUEUE_SIZE; fca.fd = &clonefd; if ((ret = ioctl(fd, FSEVENTS_CLONE, (char *)&fca)) < 0) { perror("ioctl"); close(fd); exit(1); } close(fd); //YAML comments lines start with '#'. Use this for debug and status statements snprintf(msg, MAX_DATA,"#fsevents device cloned (fd %d)\n#fslogger ready\n",clonefd); if (udp){ send_packet(msg, strlen(msg)); } else { fprintf(onf,"%s",msg); // Since we use setbuf this might not be necessary. Let's do it anyway. fflush(onf); } if ((ret = ioctl(clonefd, FSEVENTS_WANT_EXTENDED_INFO, NULL)) < 0) { perror("ioctl"); close(clonefd); exit(1); } while (1) { // event processing loop if ((ret = read(clonefd, buffer, FSEVENT_BUFSIZ)) > 0){ snprintf(msg, MAX_DATA, "# => received %d bytes\n", ret); if (udp){ send_packet(msg, strlen(msg)); } else { fprintf(onf,"%s", msg); fflush(onf); } } off = 0; while (off < ret) { // process one or more events received // Start message over mlen = 0; struct kfs_event *kfse = (struct kfs_event *)((char *)buffer + off); off += sizeof(int32_t) + sizeof(pid_t); // type + pid //Use snprintf for formatting to permit concantenting the message together mlen += snprintf(msg + mlen, MAX_DATA, "---\n"); if (kfse->type == FSE_EVENTS_DROPPED) { // special event mlen += snprintf(msg + mlen, MAX_DATA, "Event:\n"); mlen += snprintf(msg + mlen, MAX_DATA, " %s: %s\n", "type", "EVENTS_DROPPED"); mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "pid", kfse->pid); // Special event with continue. So send data then restart loop if (udp){ send_packet(msg, strlen(msg)); } else { fprintf(onf,"%s", msg); fflush(onf); } off += sizeof(u_int16_t); // FSE_ARG_DONE: sizeof(type) continue; } int32_t atype = kfse->type & FSE_TYPE_MASK; uint32_t aflags = FSE_GET_FLAGS(kfse->type); if ((atype < FSE_MAX_EVENTS) && (atype >= -1)) { mlen += snprintf(msg + mlen, MAX_DATA, "Event:\n"); mlen += snprintf(msg + mlen, MAX_DATA, " %s: %s", "type", kfseNames[atype]); if (aflags & FSE_COMBINED_EVENTS) { mlen += snprintf(msg + mlen, MAX_DATA,"%s", ", combined events"); } if (aflags & FSE_CONTAINS_DROPPED_EVENTS) { mlen += snprintf(msg + mlen, MAX_DATA, "%s", ", contains dropped events"); } mlen += snprintf(msg + mlen,MAX_DATA, "%s","\n"); } else { // should never happen mlen += snprintf(msg + mlen, MAX_DATA, "# This may be a program bug (type = %d).\n", atype); // Special event with exit. So send data if (udp){ send_packet(msg, strlen(msg)); } else { fprintf(onf,"%s", msg); fflush(onf); } exit(1); } mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "pid", kfse->pid); mlen += snprintf(msg + mlen, MAX_DATA, " %s: %s\n", "pname", get_proc_name(kfse->pid)); mlen += snprintf(msg + mlen, MAX_DATA, "%s", "Details:\n"); kea = kfse->args; i = 0; //while ((off < ret) && (i <= FSE_MAX_ARGS)) { // process arguments while (off < ret) { i++; if (kea->type == FSE_ARG_DONE) { // no more arguments mlen += snprintf(msg + mlen, MAX_DATA, " %s:\n", "FSE_ARG_DONE"); // Added Length for FSE_ARG_DONE to be consistent with other values mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "len", 0); // Added Type for FSE_ARG_DONE to be consistent with other values mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "type", kea->type); //This should be the only time to send data for a YAML doc which is a full FSEVENT if (udp){ send_packet(msg, strlen(msg)); } else { fprintf(onf,"%s", msg); fflush(onf); } det = 0; off += sizeof(u_int16_t); break; } eoff = sizeof(kea->type) + sizeof(kea->len) + kea->len; off += eoff; arg_id = (kea->type > FSE_MAX_ARGS) ? 0 : kea->type; // Do no put detail marker on timestamp if (arg_id == 5){ mlen += snprintf(msg + mlen, MAX_DATA, " %s:\n", kfseArgNames[arg_id]); } else { mlen += snprintf(msg + mlen, MAX_DATA, " %s_%d:\n", kfseArgNames[arg_id],det); } mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "len", kea->len); switch (kea->type) { // handle based on argument type case FSE_ARG_VNODE: // a vnode (string) pointer is_fse_arg_vnode = 1; mlen += snprintf(msg + mlen, MAX_DATA, " %s: %s\n", "path", (char *)&(kea->data.vp)); break; case FSE_ARG_STRING: // a string pointer // Added double quotes to protect strings with ":"s // Actually, to handle "\" it needs to be a single quote mlen += snprintf(msg + mlen, MAX_DATA, " %s: \'%s\'\n", "string", (char *)&(kea->data.str)-4); break; case FSE_ARG_INT32: mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "int32", kea->data.int32); break; case FSE_ARG_RAW: // a void pointer mlen += snprintf(msg + mlen, MAX_DATA, " %s: ", "ptr"); for (j = 0; j < kea->len; j++) mlen += snprintf(msg + mlen, MAX_DATA, "%02x ", ((char *)kea->data.ptr)[j]); mlen += snprintf(msg + mlen, MAX_DATA, "%s", "\n"); break; case FSE_ARG_INO: // an inode number mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d\n", "ino", (int)kea->data.ino); break; case FSE_ARG_UID: // a user ID p = getpwuid(kea->data.uid); mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d (%s)\n", "uid", kea->data.uid, (p) ? p->pw_name : "?"); break; case FSE_ARG_DEV: // a file system ID or a device number if (is_fse_arg_vnode) { mlen += snprintf(msg + mlen, MAX_DATA, " %s: %#08x\n", "fsid", kea->data.dev); is_fse_arg_vnode = 0; } else { mlen += snprintf(msg + mlen, MAX_DATA, " %s: %#08x (major %u, minor %u)\n", "dev", kea->data.dev, major(kea->data.dev), minor(kea->data.dev)); } break; case FSE_ARG_MODE: // a combination of file mode and file type va_mode = (kea->data.mode & 0x0000ffff); va_type = (kea->data.mode & 0xfffff000); strmode(va_mode, fileModeString); va_type = iftovt_tab[(va_type & S_IFMT) >> 12]; mlen += snprintf(msg + mlen, MAX_DATA, " %s: %s (%#08x, vnode type %s)", "mode", fileModeString, kea->data.mode, (va_type < VTYPE_MAX) ? vtypeNames[va_type] : "?"); if (kea->data.mode & FSE_MODE_HLINK) { mlen += snprintf(msg + mlen, MAX_DATA, "%s", ", hard link"); } if (kea->data.mode & FSE_MODE_LAST_HLINK) { mlen += snprintf(msg + mlen, MAX_DATA, "%s", ", link count zero now"); } mlen += snprintf(msg + mlen, MAX_DATA, "%s", "\n"); break; case FSE_ARG_GID: // a group ID g = getgrgid(kea->data.gid); mlen += snprintf(msg + mlen, MAX_DATA, " %s: %d (%s)\n", "gid", kea->data.gid, (g) ? g->gr_name : "?"); // This is usually the last value before everything repeats. Inc det det += 1; break; case FSE_ARG_INT64: // timestamp mlen += snprintf(msg + mlen, MAX_DATA, " %s: %llu\n", "tstamp", kea->data.timestamp); break; default: mlen += snprintf(msg + mlen, MAX_DATA, " %s = ?\n", "unknown"); break; } kea = (kfs_event_arg_t *)((char *)kea + eoff); // next } // for each argument } // for each event } // forever close(clonefd); // Only close output file if it is not stdout if (argc == 2) { fclose(onf); } exit(0); }