I am porting a log watching program from Linux, which uses sys/inotify.h, to OS X, which will use kqueues. The idea is that the kqueue will use the file descriptor to watch for changes to the file. Once the file has changed, a different function will be passed a file pointer and scan the changes to the file, looking for specific data. Original file data will be printed to stdout and the data being scanned for to stderr. The kqueues appear to be monitoring the log just fine, returning the correct number of bytes per change. It is the scanning function that doesn't appear to work properly.
The kqueue loop:
#define BUFLEN 2048
int i, kq, off, rval, fplen;
char *path = "file_to_watch";
FILE *fp;
struct kevent ke_mon, ke_data;
struct stat stats;
fp = fopen(path, "r");
fstat(fileno(fp), &stats);
fplen = stats.st_size;
kq = open(path, O_RDONLY);
EV_SET(&ke_mon, kq, EVFILT_VNODE, EV_ADD, NOTE_DELETE|NOTE_RENAME, 0, path);
EV_SET(&ke_mon, kq, EVFILT_READ, EV_ADD, 0, 0, path);
off = 0;
rval = 0;
while(off < BUFLEN) {
memset(&ke, 0x00, sizeof(ke));
i = kevent(kq, NULL, 0, &ke_data, 1, NULL); // wait indefinitely for log to update
if (i < 0 || ke_data.flags & EV_ERROR)
continue;
if (ke_data.flags & EVFILT_READ)
rval |= LOG_MODIFIED;
if (ke_data.flags & EVFILT_VNODE)
// rval |= LOG_DELETED or LOG_RENAMED, depending on ke_data.fflags
off += ke_data.data;
lseek(kq, ke_data.data, SEEK_CUR); // Update the descriptor's location
}
if (rval & LOG_MODIFIED) {
// check for truncation.
if (truncated) {
// do some stuff
} else {
fplen = scan_log(fp);
}
}
if (rval & (LOG_DELETED|LOG_RENAMED)
// log was moved or renamed (rotation) so we would start over with new log
scan_log fucntion:
int scan_log(FILE *fp)
{
char buf[1024];
int ct = 0;
while (fgets(buf, 1024, fp) != NULL {
ct += strlen(buf);
fprintf(stdout, "%s", buf);
for ( some looping construct) {
// Sometimes we don't ever enter this loop. It all depends on if there
// is data we are expecting to see.
// If we are in this loop however, do the following...
// Match data that I'm looking for and print to stderr.
// Print all log data to stdout
}
}
fflush(stdout);
fflush(stderr);
return ct;
}
The scan_log function is not altered form the Linux version of this program, which is verified to work properly, even if the for loop in scan_log is never entered. Sometimes it isn't necessary.
What's happening is that nothing from the log is being printed to stderr or stdout. I'm using calls to ftell before and after scan_log to watch the fp's position. It moves on the first time through, but doesn't ever move after. In either case, no data is printed to stdout/stderr.
EDIT: Updated scan_log() code to include the missing fprintf call.
The problem I was having has to do with the subtle differences with Linux and Unix. A file pointer in Unix sets an EOF flag that must be manually cleared with a call to clearerr(FILE *)
in order for the file pointer to advance once the file has grown beyond its old EOF.