/* * 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; }
/* * 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; }
/* * 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; }
// 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; }
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; }
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; }
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; }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }