Esempio n. 1
0
/*
 * Write to a file in directory watch mode
 */
int watch_fd_exec_08() {
	int kq = kqueue();
	static char *argv[] = { "prog", "arg1", "arg2", NULL };

	postpone_opt = 1;
	dirwatch_opt = 1;
	strlcpy(files[0]->fn, "src", sizeof(files[0]->fn));
	files[0]->is_dir = 1;
	watch_file(kq, files[0]);
	strlcpy(files[1]->fn, "main.py", sizeof(files[1]->fn));
	watch_file(kq, files[1]);

	ctx.event.nlist = 2;
	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[0]);
	EV_SET(&ctx.event.List[1], files[1]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[1]);

	watch_loop(kq, argv);

	ok(ctx.event.nset == 2);
	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(ctx.exit.count == 1);
	ok(strcmp(leading_edge->fn, "main.py") == 0);

	return 0;
}
Esempio n. 2
0
/*
 * In restart mode the first action should be to start the server
 */
int watch_fd_restart_01() {
	int kq = kqueue();
	char *argv[] = { "ruby", "main.rb", NULL };

	restart_opt = 1;
	strlcpy(files[0]->fn, "main.rb", sizeof(files[0]->fn));
	watch_file(kq, files[0]);

	ctx.event.nlist = 0;
	watch_loop(kq, argv);

	ok(strcmp(leading_edge->fn, "main.rb") == 0);
	ok(ctx.event.nset == 1);
	ok(ctx.event.Set[0].ident);
	ok(ctx.event.Set[0].filter == EVFILT_VNODE);
	ok(ctx.event.Set[0].flags == (EV_CLEAR|EV_ADD)); /* open */
	ok(ctx.event.Set[0].fflags == (NOTE_ALL));
	ok(ctx.event.Set[0].udata == files[0]);

	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "ruby") == 0);
	ok(strcmp(ctx.exec.argv[0], "ruby") == 0);
	ok(strcmp(ctx.exec.argv[1], "main.rb") == 0);
	return 0;
}
Esempio n. 3
0
/*
 * Change a file attribute
 */
int watch_fd_exec_02() {
	int kq = kqueue();
	static char *argv[] = { "prog", "arg1", "arg2", NULL };

	postpone_opt = 1;
	strlcpy(files[0]->fn, "main.py", sizeof(files[0]->fn));
	watch_file(kq, files[0]);

	ctx.event.nlist = 1;
	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_ATTRIB, 0, files[0]);

	watch_loop(kq, argv);

	ok(ctx.event.nset == 1);
	ok(ctx.event.Set[0].ident);
	ok(ctx.event.Set[0].filter == EVFILT_VNODE);
	ok(ctx.event.Set[0].flags == (EV_CLEAR|EV_ADD));
	ok(ctx.event.Set[0].fflags == (NOTE_ALL));
	ok(ctx.event.Set[0].udata == files[0]);

	ok(ctx.exec.count == 0);
	ok(ctx.exec.file == 0);
	ok(ctx.exit.count == 0);
	return 0;
}
Esempio n. 4
0
// Load IANA OUI descriptions from the specified file, and watch it for updates
int init_iana_naming(const char *fn){
	ouitrie *path,*p;
	wchar_t *w;

	if(((p = make_oui(NULL)) == NULL)){
		return -1;
	}
	if((path = make_oui(L"RFC 2464 IPv6 multicast")) == NULL){
		free_ouitries(&p);
		return -1;
	}
	if((w = wcsdup(L"RFC 4862 IPv6 link-local solicitation")) == NULL){
		free_ouitries(&p);
		free_ouitries(&path);
		return -1;

	}
	free(path->next[0xff]);
	path->next[0xff] = w;
	trie[0x33] = p;
	p->next[0x33] = path;
	if(watch_file(fn,parse_file)){
		free_ouitries(trie);
		return -1;
	}
	return 0;
}
Esempio n. 5
0
input_source::input_source(device_manager* manager, device_plugin plugin, void* plug_data) 
      : manager(manager), plugin(plugin), plug_data(plug_data), uniq(plugin.uniq), phys(plugin.phys) {

  for (auto ev : manager->get_events())
    register_event(ev);
  std::vector<option_info> prof_opts;
  manager->mapprofile->list_options(prof_opts);
  for (auto opt : prof_opts)
    options[opt.name] = opt;

  epfd = epoll_create(1);
  if (epfd < 1) perror("epoll create");

  int internal[2];
  pipe(internal);
  watch_file(internal[0], this);

  priv_pipe = internal[1];
  internalpipe = internal[0];
  
  if (plugin.init)
    plugin.init(plug_data, this);

  ff_ids[0] = -1;
}
Esempio n. 6
0
int main(int argc, char **argv)
{
	const char *filename;
	long lines;
	int ch;
	struct stat st;
	off_t size = 0;

	static const struct option longopts[] = {
		{ "lines",   required_argument, 0, 'n' },
		{ "version", no_argument,	0, 'V' },
		{ "help",    no_argument,	0, 'h' },
		{ NULL,      0, 0, 0 }
	};

	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	atexit(close_stdout);

	lines = old_style_option(&argc, argv);
	if (lines < 0)
		lines = DEFAULT_LINES;

	while ((ch = getopt_long(argc, argv, "n:N:Vh", longopts, NULL)) != -1)
		switch((char)ch) {
		case 'n':
		case 'N':
			lines = strtol_or_err(optarg,
					_("failed to parse number of lines"));
			break;
		case 'V':
			printf(_("%s from %s\n"), program_invocation_short_name,
						  PACKAGE_STRING);
			exit(EXIT_SUCCESS);
		case 'h':
			usage(stdout);
		default:
			usage(stderr);
		}

	if (argc == optind)
		errx(EXIT_FAILURE, _("no input file specified"));

	filename = argv[optind];

	if (stat(filename, &st) != 0)
		err(EXIT_FAILURE, _("stat failed %s"), filename);

	size = st.st_size;;
	tailf(filename, lines);

#ifdef HAVE_INOTIFY_INIT
	if (!watch_file_inotify(filename, &size))
#endif
		watch_file(filename, &size);

	return EXIT_SUCCESS;
}
Esempio n. 7
0
int main(int argc, char **argv)
{
	const char *filename;
	size_t lines;
	int ch;
	struct stat st;

	static const struct option longopts[] = {
		{ "lines",   required_argument, 0, 'n' },
		{ "version", no_argument,	0, 'V' },
		{ "help",    no_argument,	0, 'h' },
		{ NULL,      0, 0, 0 }
	};

	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	atexit(close_stdout);

	if (!old_style_option(&argc, argv, &lines))
		lines = DEFAULT_LINES;

	while ((ch = getopt_long(argc, argv, "n:N:Vh", longopts, NULL)) != -1)
		switch ((char)ch) {
		case 'n':
		case 'N':
			lines = strtoul_or_err(optarg,
					_("failed to parse number of lines"));
			break;
		case 'V':
			printf(UTIL_LINUX_VERSION);
			exit(EXIT_SUCCESS);
		case 'h':
			usage(stdout);
		default:
			usage(stderr);
		}

	if (argc == optind)
		errx(EXIT_FAILURE, _("no input file specified"));

	filename = argv[optind];

	if (stat(filename, &st) != 0)
		err(EXIT_FAILURE, _("stat of %s failed"), filename);
	if (!S_ISREG(st.st_mode))
		errx(EXIT_FAILURE, _("%s: is not a file"), filename);
	if (st.st_size)
		tailf(filename, lines, &st);

#ifdef HAVE_INOTIFY_INIT
	if (!watch_file_inotify(filename, &st))
#endif
		watch_file(filename, &st);

	return EXIT_SUCCESS;
}
Esempio n. 8
0
/*
 * Add a file to a directory and write to a file
 */
int watch_fd_exec_07() {
	int kq = kqueue();
	static char *argv[] = { "prog", "arg1", "arg2", NULL };

	postpone_opt = 1;
	strlcpy(files[0]->fn, ".", sizeof(files[0]->fn));
	files[0]->is_dir = 1;
	strlcpy(files[1]->fn, "run.sh", sizeof(files[0]->fn));
	watch_file(kq, files[0]);
	watch_file(kq, files[1]);

	dirwatch_opt = 1;
	ctx.event.nlist = 2;
	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[0]);
	EV_SET(&ctx.event.List[1], files[1]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[1]);

	watch_loop(kq, argv);

	ok(ctx.event.nset == 2);
	ok(ctx.event.Set[0].ident);
	ok(ctx.event.Set[0].filter == EVFILT_VNODE);
	ok(ctx.event.Set[0].flags == (EV_CLEAR|EV_ADD)); /* open */
	ok(ctx.event.Set[0].fflags == (NOTE_ALL));
	ok(ctx.event.Set[0].udata == files[0]->fn);

	ok(ctx.event.Set[1].ident);
	ok(ctx.event.Set[1].filter == EVFILT_VNODE);
	ok(ctx.event.Set[1].flags == (EV_CLEAR|EV_ADD)); /* open */
	ok(ctx.event.Set[1].fflags == (NOTE_ALL));
	ok(ctx.event.Set[1].udata == files[1]->fn);

	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "prog") == 0);
	ok(strcmp(ctx.exec.argv[0], "prog") == 0);
	ok(strcmp(ctx.exec.argv[1], "arg1") == 0);
	ok(strcmp(ctx.exec.argv[2], "arg2") == 0);
	ok(ctx.exit.count == 1);
	return 0;
}
Esempio n. 9
0
/*
 * Write to three files at once
 */
int watch_fd_exec_03() {
	int kq = kqueue();
	static char *argv[] = { "prog", "arg1", "arg2", NULL };

	aggressive_opt = 0;
	postpone_opt = 1;
	strlcpy(files[0]->fn, "main.py", sizeof(files[0]->fn));
	watch_file(kq, files[0]);
	strlcpy(files[1]->fn, "util.py", sizeof(files[1]->fn));
	watch_file(kq, files[1]);
	strlcpy(files[2]->fn, "app.py", sizeof(files[2]->fn));
	watch_file(kq, files[2]);

	ctx.event.nlist = 3;
	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[0]);
	EV_SET(&ctx.event.List[1], files[1]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[1]);
	EV_SET(&ctx.event.List[2], files[1]->fd, EVFILT_VNODE, 0, NOTE_WRITE, 0, files[2]);

	watch_loop(kq, argv);

	ok(strcmp(leading_edge->fn, "main.py") == 0);
	ok(ctx.event.nset == 3);
	ok(ctx.event.Set[0].ident);
	ok(ctx.event.Set[0].filter == EVFILT_VNODE);
	ok(ctx.event.Set[0].flags == (EV_CLEAR|EV_ADD)); /* open */
	ok(ctx.event.Set[0].fflags == (NOTE_ALL));
	ok(ctx.event.Set[0].data == 0);
	ok(ctx.event.Set[0].udata == files[0]->fn);

	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "prog") == 0);
	ok(strcmp(ctx.exec.argv[0], "prog") == 0);
	ok(strcmp(ctx.exec.argv[1], "arg1") == 0);
	ok(strcmp(ctx.exec.argv[2], "arg2") == 0);
	ok(ctx.exit.count == 0);
	return 0;
}
Esempio n. 10
0
/*
 * Remove a file
 */
int watch_fd_exec_01() {
	int kq = kqueue();
	static char *argv[] = { "prog", "arg1", "arg2", NULL };

	postpone_opt = 1;
	strlcpy(files[0]->fn, "arg1", sizeof(files[0]->fn));
	watch_file(kq, files[0]);

	/* event 1/1: 4 (-4) 0x21 0x1 0 0x84d5e800 */
	ctx.event.nlist = 1;
	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_DELETE, 0, files[0]);

	watch_loop(kq, argv);

	ok(ctx.event.nset == 3);
	ok(ctx.event.Set[0].ident);
	ok(ctx.event.Set[0].filter == EVFILT_VNODE);
	ok(ctx.event.Set[0].flags == (EV_CLEAR|EV_ADD)); /* open */
	ok(ctx.event.Set[0].fflags == (NOTE_ALL));
	ok(ctx.event.Set[0].udata == files[0]);

	ok(ctx.event.Set[1].ident);
	ok(ctx.event.Set[1].filter == EVFILT_VNODE);
	ok(ctx.event.Set[1].flags == EV_DELETE); /* remove */
	ok(ctx.event.Set[1].fflags == (NOTE_ALL));
	ok(ctx.event.Set[1].udata == files[0]);

	ok(ctx.event.Set[2].ident);
	ok(ctx.event.Set[2].filter == EVFILT_VNODE);
	ok(ctx.event.Set[2].flags == (EV_CLEAR|EV_ADD)); /* reopen */
	ok(ctx.event.Set[2].fflags == (NOTE_ALL));
	ok(ctx.event.Set[2].udata == files[0]);

	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "prog") == 0);
	ok(strcmp(ctx.exec.argv[0], "prog") == 0);
	ok(strcmp(ctx.exec.argv[1], "arg1") == 0);
	ok(strcmp(ctx.exec.argv[2], "arg2") == 0);
	ok(ctx.exit.count == 0);
	return 0;
}
Esempio n. 11
0
/*
 * Extending a file while in restart mode should result in start-kill-restart
 */
int watch_fd_restart_02() {
	int kq = kqueue();
	char *argv[] = { "ruby", "main.rb", NULL };

	restart_opt = 1;
	strlcpy(files[0]->fn, "main.rb", sizeof(files[0]->fn));
	watch_file(kq, files[0]);
	child_pid = 222;

	ctx.event.nlist = 0;
	watch_loop(kq, argv);

	ok(ctx.event.nset == 1);
	ok(ctx.event.Set[0].ident);
	ok(ctx.event.Set[0].filter == EVFILT_VNODE);
	ok(ctx.event.Set[0].flags == (EV_CLEAR|EV_ADD)); /* open */
	ok(ctx.event.Set[0].fflags == (NOTE_ALL));
	ok(ctx.event.Set[0].udata == files[0]);

	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "ruby") == 0);
	ok(strcmp(ctx.exec.argv[0], "ruby") == 0);
	ok(strcmp(ctx.exec.argv[1], "main.rb") == 0);

	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_EXTEND, 0, files[0]);
	ctx.event.nlist = 0;
	watch_loop(kq, argv);

	ok(ctx.signal.count == 1);
	ok(ctx.signal.pid == 222);
	ok(ctx.signal.sig == 15);

	ok(ctx.exec.count == 2);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "ruby") == 0);
	ok(strcmp(ctx.exec.argv[0], "ruby") == 0);
	ok(strcmp(ctx.exec.argv[1], "main.rb") == 0);

	return 0;
}
Esempio n. 12
0
/*
 * Make a file executable
 */
int watch_fd_exec_09() {
	int kq = kqueue();
	static char *argv[] = { "prog", "arg1", "arg2", NULL };

	postpone_opt = 1;
	strlcpy(files[0]->fn, "main.py", sizeof(files[0]->fn));
	watch_file(kq, files[0]);
	files[0]->mode = S_IFREG | S_IRUSR | S_IXUSR;

	ctx.event.nlist = 1;
	EV_SET(&ctx.event.List[0], files[0]->fd, EVFILT_VNODE, 0, NOTE_ATTRIB, 0, files[0]);

	watch_loop(kq, argv);

	ok(ctx.exec.count == 1);
	ok(ctx.exec.file != 0);
	ok(strcmp(ctx.exec.file, "prog") == 0);
	ok(strcmp(ctx.exec.argv[0], "prog") == 0);
	ok(strcmp(ctx.exec.argv[1], "arg1") == 0);
	ok(strcmp(ctx.exec.argv[2], "arg2") == 0);
	ok(ctx.exit.count == 0);
	return 0;
}
Esempio n. 13
0
/*
 * The Event Notify Test Runner
 * run arbitrary commands when files change
 */
int
main(int argc, char *argv[]) {
	struct rlimit rl;
	int kq;
	struct sigaction act;
	int ttyfd;
	short argv_index;
	int n_files;
	int i;

	if ((*test_runner_main))
		return(test_runner_main(argc, argv));

	/* set up pointers to real functions */
	xstat = stat;
	xkevent = kevent;
	xkillpg = killpg;
	xexecvp = execvp;
	xwaitpid = waitpid;
	xfork = fork;
	xopen = open;
	xrealpath = realpath;
	xfree = free;
	xwarnx = warnx;
	xerrx = errx;
	xlist_dir = list_dir;

	/* call usage() if no command is supplied */
	if (argc < 2) usage();
	argv_index = set_options(argv);

	/* normally a user will exit this utility by do_execting Ctrl-C */
	act.sa_flags = 0;
	act.sa_flags = SA_RESETHAND;
	act.sa_handler = handle_exit;
	if (sigemptyset(&act.sa_mask) & (sigaction(SIGINT, &act, NULL) != 0))
		err(1, "Failed to set SIGINT handler");
	if (sigemptyset(&act.sa_mask) & (sigaction(SIGTERM, &act, NULL) != 0))
		err(1, "Failed to set TERM handler");

	/* raise soft limit */
	getrlimit(RLIMIT_NOFILE, &rl);
	rl.rlim_cur = min((rlim_t)sysconf(_SC_OPEN_MAX), rl.rlim_max);
	if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
		err(1, "setrlimit cannot set rlim_cur to %d", (int)rl.rlim_cur);

	/* prevent interactive utilities from paging output */
	setenv("PAGER", "/bin/cat", 0);

	/* sequential scan may depend on a 0 at the end */
	files = calloc(rl.rlim_cur+1, sizeof(WatchFile *));

	if ((kq = kqueue()) == -1)
		err(1, "cannot create kqueue");

	/* read input and populate watch list, skipping non-regular files */
	n_files = process_input(stdin, files, rl.rlim_cur);
	if (n_files == 0)
		errx(1, "No regular files to watch");
	if (n_files == -1)
		errx(1, "Too many files listed; the hard limit for your login"
		    " class is %d. Please consult"
		    " http://entrproject.org/limits.html", (int)rl.rlim_cur);
	for (i=0; i<n_files; i++)
		watch_file(kq, files[i]);

	/* Attempt to open a tty so that editors don't complain */
	ttyfd = xopen(_PATH_TTY, O_RDONLY);
	if (ttyfd > STDIN_FILENO) {
		if (dup2(ttyfd, STDIN_FILENO) != 0)
			xwarnx("can't dup2 to stdin");
		close(ttyfd);
	}

	watch_loop(kq, argv+argv_index);
	return 1;
}
Esempio n. 14
0
/*
 * Wait for events to and execute a command. Four major concerns are in play:
 *   leading_edge: Global reference to the first file to have changed
 *   reopen_only : Unlink or rename events which require us to spin while
 *                 waiting for the file to reappear. These must always be
 *                 processed
 *   collate_only: Changes that indicate that more events are likely to occur.
 *                 Watch for more events using a short timeout
 *   do_exec     : Delay execution until all events have been processed. Allow
 *                 the user to edit files while the utility is running without
 *                 any visible side-effects
 *   dir_modified: The number of files changed for a directory under watch
 */
void
watch_loop(int kq, char *argv[]) {
	struct kevent evSet;
	struct kevent evList[32];
	int nev;
	WatchFile *file;
	int i;
	struct timespec evTimeout = { 0, 1000000 };
	int reopen_only = 0;
	int collate_only = 0;
	int do_exec = 0;
	int dir_modified = 0;
	int leading_edge_set = 0;
	struct stat sb;

	leading_edge = files[0]; /* default */
	if (postpone_opt == 0)
		run_utility(argv);

main:
	if ((reopen_only == 1) || (collate_only == 1))
		nev = xkevent(kq, NULL, 0, evList, 32, &evTimeout);
	else {
		nev = xkevent(kq, NULL, 0, evList, 32, NULL);
		dir_modified = 0;
	}
	/* escape for test runner */
	if ((nev == -2) && (collate_only == 0))
		return;

	for (i=0; i<nev; i++) {
		if (evList[i].filter != EVFILT_VNODE)
			continue;
		file = (WatchFile *)evList[i].udata;
		if (file->is_dir == 1)
			dir_modified += compare_dir_contents(file);
		else if (leading_edge_set == 0)
			if ((reopen_only == 0) && (collate_only == 0)) {
				leading_edge = file;
				leading_edge_set = 1;
			}
	}

	collate_only = 0;
	for (i=0; i<nev; i++) {
		file = (WatchFile *)evList[i].udata;
		if (evList[i].fflags & NOTE_DELETE ||
		    evList[i].fflags & NOTE_RENAME) {
			EV_SET(&evSet, file->fd, EVFILT_VNODE, EV_DELETE,
			    NOTE_ALL, 0, file);
			if (xkevent(kq, &evSet, 1, NULL, 0, NULL) == -1)
				err(1, "failed to remove VNODE event");
			if ((file->fd != -1) && (close(file->fd) == -1))
				err(1, "unable to close file");
			watch_file(kq, file);
			collate_only = 1;
		}
	}
	if (reopen_only == 1) {
		reopen_only = 0;
		goto main;
	}

	for (i=0; i<nev && reopen_only == 0; i++) {
		file = (WatchFile *)evList[i].udata;
		if ((file->is_dir == 1) && (dir_modified == 0))
			continue;
		if (evList[i].fflags & NOTE_DELETE ||
		    evList[i].fflags & NOTE_WRITE  ||
		    evList[i].fflags & NOTE_RENAME ||
		    evList[i].fflags & NOTE_TRUNCATE) {
			if ((dir_modified > 0) && (restart_opt == 1))
				continue;
			do_exec = 1;
		}
		if (evList[i].fflags & NOTE_ATTRIB &&
		    S_ISREG(file->mode) != 0 &&
		    xstat(file->fn, &sb) == 0 &&
		    file->mode != sb.st_mode) {
			do_exec = 1;
			file->mode = sb.st_mode;
		}
	}

	if (collate_only == 1)
		goto main;
	if (do_exec == 1) {
		do_exec = 0;
		run_utility(argv);
		reopen_only = 1;
		leading_edge_set = 0;
	}
	if (dir_modified > 0) {
		terminate_utility();
		xerrx(2, "directory altered");
	}

	goto main;
}
Esempio n. 15
0
/*
 * Wait for events to and execute a command or write filename to a FIFO.
 * Four major concerns are in play here:
 *   leading_edge: Global reference to the first file to have changed
 *   reopen_only : Unlink or rename events which require us to spin while
 *                 waiting for the file to reappear. These must always be
 *                 processed
 *   collate_only: Changes that indicate that more events are likely to occur.
 *                 Watch for more events using a short timeout
 *   do_exec     : Delay execution until all events have been processed. Allow
 *                 the user to edit files while the utility is running without
 *                 any visible side-effects
 *   dir_modified: The number of files changed for a directory under watch
 */
void
watch_loop(int kq, char *argv[]) {
	struct kevent evSet;
	struct kevent evList[32];
	int nev;
	WatchFile *file;
	int i;
	struct timespec evTimeout = { 0, 1000000 };
	int reopen_only = 0;
	int collate_only = 0;
	int do_exec = 0;
	int dir_modified = 0;

	leading_edge = files[0]; /* default */
	if (restart_opt)
		run_utility(argv);

main:
	if ((reopen_only == 1) || (collate_only == 1))
		nev = xkevent(kq, NULL, 0, evList, 32, &evTimeout);
	else {
		nev = xkevent(kq, NULL, 0, evList, 32, NULL);
		dir_modified = 0;
	}
	/* escape for test runner */
	if ((nev == -2) && (collate_only == 0))
		return;

	for (i=0; i<nev; i++) {
		#ifdef DEBUG
		fprintf(stderr, "event %d/%d: ident %d filter %d flags 0x%x "
		    "fflags 0x%x udata %d udata %p\n", i+1,
		    nev,
		    evList[i].ident,
		    evList[i].filter,
		    evList[i].flags,
		    evList[i].fflags,
		    evList[i].data,
		    evList[i].udata);
		#endif
		if (evList[i].filter != EVFILT_VNODE)
			continue;
		file = (WatchFile *)evList[i].udata;
		if (file->is_dir == 1)
			dir_modified += compare_dir_contents(file);
		if ((i == 0) && (reopen_only == 0) && (collate_only == 0))
			leading_edge = file;
	}

	collate_only = 0;
	for (i=0; i<nev; i++) {
		file = (WatchFile *)evList[i].udata;
		if (evList[i].fflags & NOTE_DELETE ||
		    evList[i].fflags & NOTE_RENAME) {
			EV_SET(&evSet, file->fd, EVFILT_VNODE, EV_DELETE,
			    NOTE_ALL, 0, file);
			if (xkevent(kq, &evSet, 1, NULL, 0, NULL) == -1)
				err(1, "failed to remove VNODE event");
			if ((file->fd != -1) && (close(file->fd) == -1))
				err(1, "unable to close file");
			watch_file(kq, file);
			collate_only = 1;
		}
	}
	if (reopen_only == 1) {
		reopen_only = 0;
		goto main;
	}

	for (i=0; i<nev && reopen_only == 0; i++) {
		file = (WatchFile *)evList[i].udata;
		if ((file->is_dir == 1) && (dir_modified == 0))
			continue;
		if (evList[i].fflags & NOTE_DELETE ||
		    evList[i].fflags & NOTE_WRITE  ||
		    evList[i].fflags & NOTE_RENAME ||
		    evList[i].fflags & NOTE_TRUNCATE) {
			if (fifo.fd == 0) {
				if ((dir_modified > 0) && (restart_opt == 1))
					continue;
				do_exec = 1;
			}
			else {
				write(fifo.fd, file->fn, strlen(file->fn));
				write(fifo.fd, "\n", 1);
				fsync(fifo.fd);
				reopen_only = 1;
			}
		}
	}

	if (collate_only == 1)
		goto main;
	if (do_exec == 1) {
		do_exec = 0;
		run_utility(argv);
		reopen_only = 1;
	}
	if (dir_modified > 0)
		xerrx(2, "directory altered");

	goto main;
}