static void cmd_shutdown(struct watchman_client *client, json_t *args) { json_t *resp = make_response(); unused_parameter(args); w_log(W_LOG_ERR, "shutdown-server was requested, exiting!\n"); w_request_shutdown(); set_prop(resp, "shutdown-server", json_true()); send_and_dispose_response(client, resp); }
/* watch-list * Returns a list of watched roots */ static void cmd_watch_list(struct watchman_client *client, json_t *args) { json_t *resp; json_t *root_paths; unused_parameter(args); resp = make_response(); root_paths = w_root_watch_list_to_json(); set_prop(resp, "roots", root_paths); send_and_dispose_response(client, resp); }
static bool kqueue_root_consume_notify(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_pending_collection *coll) { struct kqueue_root_state *state = root->watch; int n; int i; struct timespec ts = { 0, 0 }; struct timeval now; unused_parameter(watcher); errno = 0; w_log(W_LOG_DBG, "kqueue(%s)\n", root->root_path->buf); n = kevent(state->kq_fd, NULL, 0, state->keventbuf, sizeof(state->keventbuf) / sizeof(state->keventbuf[0]), &ts); w_log(W_LOG_DBG, "consume_kqueue: %s n=%d err=%s\n", root->root_path->buf, n, strerror(errno)); if (root->cancelled) { return 0; } gettimeofday(&now, NULL); for (i = 0; n > 0 && i < n; i++) { uint32_t fflags = state->keventbuf[i].fflags; if (IS_DIR_BIT_SET(state->keventbuf[i].udata)) { struct watchman_dir *dir = DECODE_DIR(state->keventbuf[i].udata); w_log(W_LOG_DBG, " KQ dir %s [0x%x]\n", dir->path->buf, fflags); if ((fflags & (NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE)) && w_string_equal(dir->path, root->root_path)) { w_log(W_LOG_ERR, "root dir %s has been (re)moved [code 0x%x], canceling watch\n", root->root_path->buf, fflags); w_root_cancel(root); return 0; } w_pending_coll_add(coll, dir->path, false, now, false); } else { // NetBSD defines udata as intptr type, so the cast is necessary struct watchman_file *file = (void *)state->keventbuf[i].udata; w_string_t *path; path = w_string_path_cat(file->parent->path, file->name); w_pending_coll_add(coll, path, true, now, true); w_log(W_LOG_DBG, " KQ file %.*s [0x%x]\n", path->len, path->buf, fflags); w_string_delref(path); } } return n > 0; }
w_query_expr *w_expr_imatch_parser(w_query *query, json_t *term) { #ifdef NO_CASELESS_FNMATCH unused_parameter(term); asprintf(&query->errmsg, "imatch: Your system doesn't support FNM_CASEFOLD"); return NULL; #else return match_parser(query, term, true); #endif }
/* version */ void cmd_version(struct watchman_client *client, json_t *args) { json_t *resp = make_response(); unused_parameter(args); #ifdef WATCHMAN_BUILD_INFO set_prop(resp, "buildinfo", json_string(WATCHMAN_BUILD_INFO)); #endif send_and_dispose_response(client, resp); }
static bool kqueue_root_start_watch_file(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_file *file) { struct kqueue_root_state *state = root->watch; struct kevent k; w_ht_val_t fdval; int fd; w_string_t *full_name; unused_parameter(watcher); full_name = w_string_path_cat(file->parent->path, file->name); pthread_mutex_lock(&state->lock); if (w_ht_lookup(state->name_to_fd, w_ht_ptr_val(full_name), &fdval, false)) { // Already watching it pthread_mutex_unlock(&state->lock); return true; } pthread_mutex_unlock(&state->lock); w_log(W_LOG_DBG, "watch_file(%s)\n", full_name->buf); fd = open(full_name->buf, O_EVTONLY|O_CLOEXEC); if (fd == -1) { w_log(W_LOG_ERR, "failed to open %s O_EVTONLY: %s\n", full_name->buf, strerror(errno)); w_string_delref(full_name); return false; } memset(&k, 0, sizeof(k)); EV_SET(&k, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE|NOTE_DELETE|NOTE_EXTEND|NOTE_RENAME|NOTE_ATTRIB, 0, full_name); pthread_mutex_lock(&state->lock); w_ht_replace(state->name_to_fd, w_ht_ptr_val(full_name), fd); w_ht_replace(state->fd_to_name, fd, w_ht_ptr_val(full_name)); pthread_mutex_unlock(&state->lock); if (kevent(state->kq_fd, &k, 1, NULL, 0, 0)) { w_log(W_LOG_DBG, "kevent EV_ADD file %s failed: %s", full_name->buf, strerror(errno)); close(fd); pthread_mutex_lock(&state->lock); w_ht_del(state->name_to_fd, w_ht_ptr_val(full_name)); w_ht_del(state->fd_to_name, fd); pthread_mutex_unlock(&state->lock); } else { w_log(W_LOG_DBG, "kevent file %s -> %d\n", full_name->buf, fd); } w_string_delref(full_name); return true; }
static void portfs_root_stop_watch_file(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_file *file) { struct portfs_root_state *state = root->watch; unused_parameter(watcher); port_dissociate(state->port_fd, PORT_SOURCE_FILE, (uintptr_t)&file->port_file); if (file->port_file.fo_name) { free(file->port_file.fo_name); file->port_file.fo_name = NULL; } }
static bool eval_size(struct w_query_ctx *ctx, struct watchman_file *file, void *data) { struct w_query_int_compare *comp = data; unused_parameter(ctx); // Removed files never evaluate true if (!file->exists) { return false; } return eval_int_compare(file->stat.size, comp); }
void kqueue_root_dtor(watchman_global_watcher_t watcher, w_root_t *root) { struct kqueue_root_state *state = root->watch; unused_parameter(watcher); if (!state) { return; } close(state->kq_fd); state->kq_fd = -1; free(state); root->watch = NULL; }
static DIR *winwatch_root_start_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { DIR *osdir; unused_parameter(watcher); osdir = opendir(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, strerror(errno)); return NULL; } return osdir; }
static bool kqueue_root_wait_notify(watchman_global_watcher_t watcher, w_root_t *root, int timeoutms) { struct kqueue_root_state *state = root->watch; int n; struct pollfd pfd; unused_parameter(watcher); pfd.fd = state->kq_fd; pfd.events = POLLIN; n = poll(&pfd, 1, timeoutms); return n == 1; }
static void crash_handler(int signo, siginfo_t *si, void *ucontext) { const char *reason = ""; unused_parameter(ucontext); if (si) { switch (si->si_signo) { case SIGILL: switch (si->si_code) { case ILL_ILLOPC: reason = "illegal opcode"; break; case ILL_ILLOPN: reason = "illegal operand"; break; case ILL_ILLADR: reason = "illegal addressing mode"; break; case ILL_ILLTRP: reason = "illegal trap"; break; case ILL_PRVOPC: reason = "privileged opcode"; break; case ILL_PRVREG: reason = "privileged register"; break; case ILL_COPROC: reason = "co-processor error"; break; case ILL_BADSTK: reason = "internal stack error"; break; } break; case SIGFPE: switch (si->si_code) { case FPE_INTDIV: reason = "integer divide by zero"; break; case FPE_INTOVF: reason = "integer overflow"; break; case FPE_FLTDIV: reason = "floating point divide by zero"; break; case FPE_FLTOVF: reason = "floating point overflow"; break; case FPE_FLTUND: reason = "floating point underflow"; break; case FPE_FLTRES: reason = "floating point inexact result"; break; case FPE_FLTINV: reason = "invalid floating point operation"; break; case FPE_FLTSUB: reason = "subscript out of range"; break; } break; case SIGSEGV: switch (si->si_code) { case SEGV_MAPERR: reason = "address not mapped to object"; break; case SEGV_ACCERR: reason = "invalid permissions for mapped object"; break; } break; #ifdef SIGBUS case SIGBUS: switch (si->si_code) { case BUS_ADRALN: reason = "invalid address alignment"; break; case BUS_ADRERR: reason = "non-existent physical address"; break; } break; #endif } } w_log(W_LOG_FATAL, "Terminating due to signal %d %s. %s (%p)\n", signo, strsignal(signo), reason, si ? si->si_value.sival_ptr : NULL); }
static void cmd_shutdown( struct watchman_client *client, json_t *args) { json_t *resp = make_response(); unused_parameter(args); w_log(W_LOG_ERR, "shutdown-server was requested, exiting!\n"); stopping = true; // Knock listener thread out of poll/accept pthread_kill(listener_thread, SIGUSR1); set_prop(resp, "shutdown-server", json_true()); send_and_dispose_response(client, resp); }
static void kqueue_root_stop_watch_file(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_file *file) { struct kqueue_root_state *state = root->watch; struct kevent k; unused_parameter(watcher); if (file->kq_fd == -1) { return; } memset(&k, 0, sizeof(k)); EV_SET(&k, file->kq_fd, EVFILT_VNODE, EV_DELETE, 0, 0, file); kevent(state->kq_fd, &k, 1, NULL, 0, 0); close(file->kq_fd); file->kq_fd = -1; }
void kqueue_root_dtor(watchman_global_watcher_t watcher, w_root_t *root) { struct kqueue_root_state *state = root->watch; unused_parameter(watcher); if (!state) { return; } pthread_mutex_destroy(&state->lock); close(state->kq_fd); state->kq_fd = -1; w_ht_free(state->name_to_fd); w_ht_free(state->fd_to_name); free(state); root->watch = NULL; }
static void *child_reaper(void *arg) { sigset_t sigset; unused_parameter(arg); // Unblock SIGCHLD only in this thread sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); while (!stopping) { usleep(200000); w_reap_children(true); } return 0; }
static bool default_generators( w_query *query, w_root_t *root, struct w_query_ctx *ctx, void *gendata) { bool generated = false; unused_parameter(gendata); // Time based query if (ctx->since.is_timestamp || !ctx->since.clock.is_fresh_instance) { if (!time_generator(query, root, ctx)) { return false; } generated = true; } // Suffix if (query->suffixes) { if (!suffix_generator(query, root, ctx)) { return false; } generated = true; } if (query->npaths) { if (!path_generator(query, root, ctx)) { return false; } generated = true; } // And finally, if there were no other generators, we walk all known // files if (!generated) { if (!all_files_generator(query, root, ctx)) { return false; } } return true; }
static struct watchman_dir_handle *inot_root_start_watch_dir( watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct inot_root_state *state = root->watch; struct watchman_dir_handle *osdir = NULL; int newwd, err; unused_parameter(watcher); // Carry out our very strict opendir first to ensure that we're not // traversing symlinks in the context of this root osdir = w_dir_open(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, NULL); return NULL; } // The directory might be different since the last time we looked at it, so // call inotify_add_watch unconditionally. newwd = inotify_add_watch(state->infd, path, WATCHMAN_INOTIFY_MASK); if (newwd == -1) { err = errno; if (errno == ENOSPC || errno == ENOMEM) { // Limits exceeded, no recovery from our perspective set_poison_state(root, dir->path, now, "inotify-add-watch", errno, inot_strerror(errno)); } else { handle_open_errno(root, dir, now, "inotify_add_watch", errno, inot_strerror(errno)); } w_dir_close(osdir); errno = err; return NULL; } // record mapping pthread_mutex_lock(&state->lock); w_ht_replace(state->wd_to_name, newwd, w_ht_ptr_val(dir->path)); pthread_mutex_unlock(&state->lock); w_log(W_LOG_DBG, "adding %d -> %s mapping\n", newwd, path); return osdir; }
void winwatch_root_dtor(watchman_global_watcher_t watcher, w_root_t *root) { struct winwatch_root_state *state = root->watch; unused_parameter(watcher); if (!state) { return; } // wait for readchanges_thread to quit before we tear down state if (!pthread_equal(state->thread, pthread_self())) { void *ignore; pthread_join(state->thread, &ignore); } CloseHandle(state->ping); CloseHandle(state->olap); CloseHandle(state->dir_handle); free(state); root->watch = NULL; }
static bool eval_dirname(struct w_query_ctx *ctx, struct watchman_file *file, void *ptr) { struct dirname_data *data = ptr; w_string_t *str = w_query_ctx_get_wholename(ctx); json_int_t depth = 0; size_t i; unused_parameter(file); if (str->len <= data->dirname->len) { // Either it doesn't prefix match, or file name is == dirname. // That means that the best case is that the wholename matches. // we only want to match if dirname(wholename) matches, so it // is not possible for us to match unless the length of wholename // is greater than the dirname operand return false; } // Want to make sure that wholename is a child of dirname, so // check for a dir separator. Special case for dirname == '' (the root), // which won't have a slash in position 0. if (data->dirname->len > 0 && str->buf[data->dirname->len] != '/') { // may have a common prefix with, but is not a child of dirname return false; } if (!data->startswith(str, data->dirname)) { return false; } // Now compute the depth of file from dirname. We do this by // counting dir separators, not including the one we saw above. for (i = data->dirname->len + 1; i < str->len; i++) { if (str->buf[i] == '/') { depth++; } } return eval_int_compare(depth, &data->depth); }
static DIR *portfs_root_start_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct portfs_root_state *state = root->watch; DIR *osdir; struct stat st; unused_parameter(watcher); osdir = opendir_nofollow(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, NULL); return NULL; } if (fstat(dirfd(osdir), &st) == -1) { // whaaa? w_log(W_LOG_ERR, "fstat on opened dir %s failed: %s\n", path, strerror(errno)); w_root_schedule_recrawl(root, "fstat failed"); closedir(osdir); return NULL; } dir->port_file.fo_mtime = st.st_atim; dir->port_file.fo_mtime = st.st_mtim; dir->port_file.fo_ctime = st.st_ctim; dir->port_file.fo_name = (char*)dir->path->buf; errno = 0; if (port_associate(state->port_fd, PORT_SOURCE_FILE, (uintptr_t)&dir->port_file, WATCHMAN_PORT_EVENTS, SET_DIR_BIT(dir))) { w_log(W_LOG_ERR, "port_associate %s %s\n", dir->port_file.fo_name, strerror(errno)); } return osdir; }
static bool winwatch_root_wait_notify(watchman_global_watcher_t watcher, w_root_t *root, int timeoutms) { struct winwatch_root_state *state = root->watch; struct timeval now, delta, target; struct timespec ts; unused_parameter(watcher); if (timeoutms == 0 || state->head) { return state->head ? true : false; } // Add timeout to current time, convert to absolute timespec gettimeofday(&now, NULL); delta.tv_sec = timeoutms / 1000; delta.tv_usec = (timeoutms - (delta.tv_sec * 1000)) * 1000; w_timeval_add(now, delta, &target); w_timeval_to_timespec(target, &ts); pthread_mutex_lock(&state->mtx); pthread_cond_timedwait(&state->cond, &state->mtx, &ts); pthread_mutex_unlock(&state->mtx); return state->head ? true : false; }
bool kqueue_root_init(watchman_global_watcher_t watcher, w_root_t *root, char **errmsg) { struct kqueue_root_state *state; unused_parameter(watcher); state = calloc(1, sizeof(*state)); if (!state) { *errmsg = strdup("out of memory"); return false; } root->watch = state; state->kq_fd = kqueue(); if (state->kq_fd == -1) { ignore_result(asprintf(errmsg, "watch(%.*s): kqueue() error: %s", root->root_path->len, root->root_path->buf, strerror(errno))); w_log(W_LOG_ERR, "%s\n", *errmsg); return false; } w_set_cloexec(state->kq_fd); return true; }
static void kqueue_root_stop_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir) { struct kqueue_root_state *state = root->watch; struct kevent k; unused_parameter(watcher); if (dir->wd == -1) { return; } memset(&k, 0, sizeof(k)); EV_SET(&k, dir->wd, EVFILT_VNODE, EV_DELETE, 0, 0, SET_DIR_BIT(dir)); if (kevent(state->kq_fd, &k, 1, NULL, 0, 0)) { w_log(W_LOG_ERR, "rm_watch: %d %.*s %s\n", dir->wd, dir->path->len, dir->path->buf, strerror(errno)); w_root_schedule_recrawl(root, "rm_watch failed"); } close(dir->wd); dir->wd = -1; }
void inot_root_dtor(watchman_global_watcher_t watcher, w_root_t *root) { struct inot_root_state *state = root->watch; unused_parameter(watcher); if (!state) { return; } pthread_mutex_destroy(&state->lock); close(state->infd); state->infd = -1; if (state->wd_to_name) { w_ht_free(state->wd_to_name); state->wd_to_name = NULL; } if (state->move_map) { w_ht_free(state->move_map); state->move_map = NULL; } free(state); root->watch = NULL; }
static void *child_reaper(void *arg) { #ifndef _WIN32 sigset_t sigset; // By default, keep both SIGCHLD and SIGUSR1 blocked sigemptyset(&sigset); sigaddset(&sigset, SIGUSR1); sigaddset(&sigset, SIGCHLD); pthread_sigmask(SIG_BLOCK, &sigset, NULL); // SIGCHLD is ordinarily blocked, so we listen for it only in // sigsuspend, when we're also listening for the SIGUSR1 that tells // us to exit. pthread_sigmask(SIG_BLOCK, NULL, &sigset); sigdelset(&sigset, SIGCHLD); sigdelset(&sigset, SIGUSR1); #endif unused_parameter(arg); w_set_thread_name("child_reaper"); #ifdef _WIN32 while (!stopping) { usleep(200000); w_reap_children(true); } #else while (!stopping) { w_reap_children(false); sigsuspend(&sigset); } #endif return 0; }
// This is just a placeholder. // This catches SIGUSR1 so we don't terminate. // We use this to interrupt blocking syscalls // on the worker threads static void wakeme(int signo) { unused_parameter(signo); }
void kqueue_global_dtor(watchman_global_watcher_t watcher) { unused_parameter(watcher); }
static void kqueue_file_free(watchman_global_watcher_t watcher, struct watchman_file *file) { unused_parameter(watcher); unused_parameter(file); }
static void kqueue_root_signal_threads(watchman_global_watcher_t watcher, w_root_t *root) { unused_parameter(watcher); unused_parameter(root); }