static int watchman_read_and_handle_errors(struct watchman_connection *conn, struct watchman_error *error) { proto_t obj = watchman_read(conn, error); if (proto_is_null(obj)) { return 1; } if (!proto_is_object(obj)) { char *bogus_text = proto_dumps(obj, 0); watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got non-object result from watchman : %s", bogus_text); free(bogus_text); proto_free(obj); return 1; } proto_t error_node = proto_object_get(obj, "error"); if (!proto_is_null(error_node)) { watchman_err(error, WATCHMAN_ERR_OTHER, "Got error result from watchman : %s", proto_strdup(error_node)); proto_free(obj); return 1; } proto_free(obj); return 0; }
static int watchman_send_simple_command(struct watchman_connection *conn, struct watchman_error *error, ...) { int result = 0; json_t *cmd_array = json_array(); va_list argptr; va_start(argptr, error); char *arg; while ((arg = va_arg(argptr, char *))) { json_array_append_new(cmd_array, json_string(arg)); } if (use_bser_encoding) { result = bser_write_to_file(cmd_array, conn->fp) == 0; } else { result = json_dumpf(cmd_array, conn->fp, JSON_COMPACT); fputc('\n', conn->fp); } if (result) { if (errno == EAGAIN || errno == EWOULDBLOCK) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout sending simple watchman command"); } else { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Failed to send simple watchman command"); } result = 1; } json_decref(cmd_array); return result; }
int watchman_pclose(struct watchman_error *error, struct watchman_popen *popen) { close(popen->fd); int status; int pid = waitpid(popen->pid, &status, 0); if (pid < 0) { watchman_err(error, WATCHMAN_ERR_RUN_WATCHMAN, get_sockname_msg, strerror(errno)); return -1; } switch(WEXITSTATUS(status)) { case 0: return 0; case WATCHMAN_EXEC_FAILED: watchman_err(error, WATCHMAN_ERR_RUN_WATCHMAN, get_sockname_msg, strerror(errno)); return -1; case WATCHMAN_EXEC_INTERNAL_ERROR: watchman_err(error, WATCHMAN_ERR_OTHER, get_sockname_msg, strerror(errno)); return -1; default: watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, get_sockname_msg, strerror(errno)); return -1; } }
struct watchman_connection * watchman_sock_connect(struct watchman_error *error, const char *sockname) { struct sockaddr_un addr = { }; int fd; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { watchman_err(error, "Socket error %s", strerror(errno)); return NULL; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sockname, sizeof(addr.sun_path) - 1); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { close(fd); watchman_err(error, "Connect error %s", strerror(errno)); return NULL; } FILE *sockfp = fdopen(fd, "r+"); if (!sockfp) { close(fd); watchman_err(error, "Failed to connect to watchman socket %s: %s.", sockname, strerror(errno)); return NULL; } setlinebuf(sockfp); struct watchman_connection *conn = malloc(sizeof(*conn)); conn->fp = sockfp; return conn; }
static int watchman_read_and_handle_errors(struct watchman_connection *conn, struct watchman_error *error) { json_t *obj = watchman_read(conn, error); if (!obj) { return 1; } if (!json_is_object(obj)) { char *bogus_json_text = json_dumps(obj, 0); watchman_err(error, "Got non-object result from watchman : %s", bogus_json_text); free(bogus_json_text); json_decref(obj); return 1; } json_t *error_json = json_object_get(obj, "error"); if (error_json) { watchman_err(error, "Got error result from watchman : %s", json_string_value(error_json)); json_decref(obj); return 1; } json_decref(obj); return 0; }
static int watchman_send(struct watchman_connection *conn, json_t *query, struct watchman_error *error) { int result; if (use_bser_encoding) { result = bser_write_to_file(query, conn->fp) == 0; } else { result = json_dumpf(query, conn->fp, JSON_COMPACT); fputc('\n', conn->fp); } if (result) { if (errno == EAGAIN || errno == EWOULDBLOCK) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout sending to watchman"); } else { char *dump = json_dumps(query, 0); watchman_err(error, WATCHMAN_ERR_OTHER, "Failed to send watchman query %s", dump); free(dump); } return 1; } return 0; }
/* * Connect to watchman's socket. Sets a socket send and receive * timeout of `timeout`. Pass a {0} for no-timeout. On error, * returns NULL and, if `error` is non-NULL, fills it in. */ struct watchman_connection * watchman_connect(struct timeval timeout, struct watchman_error *error) { struct watchman_connection *conn = NULL; /* If an environment variable WATCHMAN_SOCK is set, establish a connection to that address. Otherwise, run `watchman get-sockname` to start the daemon and retrieve its address. */ const char *sockname_env = getenv("WATCHMAN_SOCK"); if (sockname_env) { conn = watchman_sock_connect(sockname_env, timeout, error); goto done; } struct watchman_popen *p = watchman_popen_getsockname(error); if (p == NULL) { return NULL; } char buf[WATCHMAN_GET_SOCKNAME_MAX + 1]; proto_t proto = read_with_timeout(p->fd, buf, WATCHMAN_GET_SOCKNAME_MAX, timeout); if (watchman_pclose(error, p)) { goto done; } if (proto_is_null(proto)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad or no JSON/BSER from watchman get-sockname"); goto done; } if (!proto_is_object(proto)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad JSON/BSER from watchman get-sockname: object expected"); goto bad_proto; } proto_t sockname_obj = proto_object_get(proto, "sockname"); if (proto_is_null(sockname_obj)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad JSON/BSER from watchman get-sockname: " "sockname element expected"); goto bad_proto; } if (!proto_is_string(sockname_obj)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad JSON/BSER from watchman get-sockname:" " sockname is not string"); goto bad_proto; } const char *sockname = proto_strdup(sockname_obj); conn = watchman_sock_connect(sockname, timeout, error); bad_proto: proto_free(proto); done: return conn; }
/* * Connect to watchman's socket. Sets a socket send and receive * timeout of `timeout`. Pass a {0} for no-timeout. On error, * returns NULL and, if `error` is non-NULL, fills it in. */ struct watchman_connection * watchman_connect(struct timeval timeout, struct watchman_error *error) { struct watchman_connection *conn = NULL; /* If an environment variable WATCHMAN_SOCK is set, establish a connection to that address. Otherwise, run `watchman get-sockname` to start the daemon and retrieve its address. */ const char *sockname_env = getenv("WATCHMAN_SOCK"); if (sockname_env) { conn = watchman_sock_connect(sockname_env, timeout, error); goto done; } struct watchman_popen *p = watchman_popen_getsockname(error); if (p == NULL) { return NULL; } char buf[WATCHMAN_GET_SOCKNAME_MAX + 1]; json_t *json = read_json_with_timeout(p->fd, buf, WATCHMAN_GET_SOCKNAME_MAX, timeout); if (watchman_pclose(error, p)) { goto done; } if (!json) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad or no JSON from watchman get-sockname"); goto done; } if (!json_is_object(json)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad JSON from watchman get-sockname: object expected"); goto bad_json; } json_t *sockname_obj = json_object_get(json, "sockname"); if (!sockname_obj) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad JSON from watchman get-sockname: " "sockname element expected"); goto bad_json; } if (!json_is_string(sockname_obj)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Got bad JSON from watchman get-sockname:" " sockname is not string"); goto bad_json; } const char *sockname = json_string_value(sockname_obj); conn = watchman_sock_connect(sockname, timeout, error); bad_json: json_decref(json); done: return conn; }
static struct watchman_connection * watchman_sock_connect(const char *sockname, struct timeval timeout, struct watchman_error *error) { struct sockaddr_un addr = { }; int fd; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { watchman_err(error, WATCHMAN_ERR_OTHER, "Socket error %s", strerror(errno)); return NULL; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sockname, sizeof(addr.sun_path) - 1); /* We don't need to worry about connect hanging, because it's a * Unix Domain Socket, and connect never hangs on those */ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { close(fd); watchman_err(error, WATCHMAN_ERR_CONNECT, "Connect error %s", strerror(errno)); return NULL; } if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))) { watchman_err(error, WATCHMAN_ERR_CONNECT, "Failed to set timeout %s", strerror(errno)); return NULL; } if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) { watchman_err(error, WATCHMAN_ERR_CONNECT, "Failed to set timeout %s", strerror(errno)); return NULL; } FILE *sockfp = fdopen(fd, "r+"); if (!sockfp) { close(fd); watchman_err(error, WATCHMAN_ERR_OTHER, "Failed to connect to watchman socket %s: %s.", sockname, strerror(errno)); return NULL; } setlinebuf(sockfp); struct watchman_connection *conn = malloc(sizeof(*conn)); conn->fp = sockfp; return conn; }
static int unix_stream_connect(const char *path, struct watchman_error *error) { struct sockaddr_un sa; struct unix_sockaddr_context ctx; if (unix_sockaddr_init(&sa, path, &ctx) < 0) { return -1; } int fd = unix_stream_socket(); if (fd < 0) { return -1; } if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { watchman_err(error, WATCHMAN_ERR_CONNECT, "Connect error %s", strerror(errno)); if (unix_sockaddr_cleanup(&ctx, error)) { error->code |= WATCHMAN_ERR_CWD; } close(fd); return -1; } if (unix_sockaddr_cleanup(&ctx, error)) { close(fd); return -1; } return fd; }
static json_t * watchman_read(struct watchman_connection *conn, struct watchman_error *error) { json_error_t jerror; int flags = JSON_DISABLE_EOF_CHECK; json_t *result = json_loadf(conn->fp, flags, &jerror); if (!result) { watchman_err(error, "Can't parse result from watchman: %s", jerror.text); return NULL; } if (fgetc(conn->fp) != '\n') { watchman_err(error, "No newline at end of reply"); json_decref(result); return NULL; } return result; }
static struct watchman_connection * watchman_sock_connect(const char *sockname, struct timeval timeout, struct watchman_error *error) { use_bser_encoding = getenv("LIBWATCHMAN_USE_JSON_PROTOCOL") == NULL; if (getenv("LIBWATCHMAN_TRACE_WATCHMAN") != NULL) { fprintf(stderr, "Using bser encoding: %s\n", use_bser_encoding ? "yes" : "no"); } int fd; error->message = NULL; fd = unix_stream_connect(sockname, error); if (fd < 0 && !error->message) { watchman_err(error, WATCHMAN_ERR_CONNECT, "Connect error %s", strerror(errno)); return NULL; } if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))) { watchman_err(error, WATCHMAN_ERR_CONNECT, "Failed to set timeout %s", strerror(errno)); return NULL; } if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) { watchman_err(error, WATCHMAN_ERR_CONNECT, "Failed to set timeout %s", strerror(errno)); return NULL; } FILE *sockfp = fdopen(fd, "r+"); if (!sockfp) { close(fd); watchman_err(error, WATCHMAN_ERR_OTHER, "Failed to connect to watchman socket %s: %s.", sockname, strerror(errno)); return NULL; } setlinebuf(sockfp); struct watchman_connection *conn = malloc(sizeof(*conn)); conn->fp = sockfp; return conn; }
struct watchman_connection * watchman_connect(struct watchman_error *error) { struct watchman_connection *conn = NULL; FILE *fp = popen("watchman get-sockname 2>/dev/null", "r"); if (!fp) { watchman_err(error, "Could not run watchman get-sockname: %s", strerror(errno)); return NULL; } json_error_t jerror; json_t *json = json_loadf(fp, 0, &jerror); pclose(fp); if (!json) { watchman_err(error, "Got bad JSON from watchman get-sockname: %s", jerror.text); goto done; } if (!json_is_object(json)) { watchman_err(error, "Got bad JSON from watchman get-sockname:" " object expected"); goto bad_json; } json_t *sockname_obj = json_object_get(json, "sockname"); if (!sockname_obj) { watchman_err(error, "Got bad JSON from watchman get-sockname:" " sockname element expected"); goto bad_json; } if (!json_is_string(sockname_obj)) { watchman_err(error, "Got bad JSON from watchman get-sockname:" " sockname is not string"); goto bad_json; } const char *sockname = json_string_value(sockname_obj); conn = watchman_sock_connect(error, sockname); bad_json: json_decref(json); done: return conn; }
static proto_t watchman_read_with_timeout(struct watchman_connection *conn, struct timeval *timeout, struct watchman_error *error) { proto_t result; json_error_t jerror; int ret = 1; if (!timeout || timeout->tv_sec || timeout->tv_usec) ret = block_on_read(fileno(conn->fp), timeout); if (ret == -1) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Error encountered blocking on watchman"); return proto_null(); } if (ret != 1) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "timed out waiting for watchman"); return proto_null(); } if (use_bser_encoding) { bser_t* bser = bser_parse_from_file(conn->fp, NULL); result = proto_from_bser(bser); } else { json_t* json = json_loadf(conn->fp, JSON_DISABLE_EOF_CHECK, &jerror); result = proto_from_json(json); if (fgetc(conn->fp) != '\n') { if (errno == EAGAIN || errno == EWOULDBLOCK) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout reading EOL from watchman"); } else { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "No newline at end of reply"); } json_decref(json); return proto_null(); } } if (proto_is_null(result)) { if (errno == EAGAIN) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout:EAGAIN reading from watchman."); } else if (errno == EWOULDBLOCK) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout:EWOULDBLOCK reading from watchman"); } else { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Can't parse result from watchman: %s", jerror.text); } return proto_null(); } return result; }
static int watchman_send(struct watchman_connection *conn, json_t *query, struct watchman_error *error) { int json_result = json_dumpf(query, conn->fp, JSON_COMPACT); if (json_result) { char *dump = json_dumps(query, 0); watchman_err(error, "Failed to send watchman query %s", dump); free(dump); return 1; } fputc('\n', conn->fp); return 0; }
static int unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx, struct watchman_error *error) { int ret = 0; if (!ctx->orig_dir) { return 0; } /* * If we fail, we have moved the cwd of the whole process, which * could confuse calling code. But we don't want to just die, because * libraries shouldn't do that. So we'll return an error but be * sad about it. */ if (chdir(ctx->orig_dir) < 0) { watchman_err(error, WATCHMAN_ERR_CWD, "unable to restore original working directory"); ret = -1; } free(ctx->orig_dir); return ret; }
/* Runs watchman get-sockname and returns a FILE from which the output can be read. */ static struct watchman_popen *watchman_popen_getsockname(struct watchman_error *error) { int pipefd[2]; static struct watchman_popen ret = {0, 0}; if (pipe(pipefd) < 0) { goto fail; } pid_t pid = fork(); if (pid < 0) { goto fail; } else if (pid == 0) { if (dup2(pipefd[1], 1) < 0) { exit(WATCHMAN_EXEC_INTERNAL_ERROR); } int devnull_fh = open("/dev/null", O_RDWR); if (devnull_fh < 0) { exit(WATCHMAN_EXEC_INTERNAL_ERROR); } if (dup2(devnull_fh, 2) < 0) { exit(WATCHMAN_EXEC_INTERNAL_ERROR); } execlp("watchman", "watchman", "get-sockname", (char *) NULL); exit(WATCHMAN_EXEC_FAILED); } else { close(pipefd[1]); ret.fd = pipefd[0]; ret.pid = pid; return &ret; } fail: watchman_err(error, WATCHMAN_ERR_OTHER, get_sockname_msg, strerror(errno)); return NULL; }
static int watchman_send_simple_command(struct watchman_connection *conn, struct watchman_error *error, ...) { int result = 0; json_t *cmd_array = json_array(); va_list argptr; va_start(argptr, error); char *arg; while ((arg = va_arg(argptr, char *))) { json_array_append_new(cmd_array, json_string(arg)); } int json_result = json_dumpf(cmd_array, conn->fp, JSON_COMPACT); if (json_result) { watchman_err(error, "Failed to send simple watchman command"); result = 1; } fputc('\n', conn->fp); json_decref(cmd_array); return result; }
static json_t * watchman_read_with_timeout(struct watchman_connection *conn, struct timeval *timeout, struct watchman_error *error) { json_error_t jerror; int flags = JSON_DISABLE_EOF_CHECK; json_t *result; int ret = block_on_read(fileno(conn->fp), timeout); if (ret == -1) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Error encountered blocking on watchman"); } result = json_loadf(conn->fp, flags, &jerror); if (!result) { if (errno == EAGAIN) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout:EAGAIN reading from watchman."); } else if (errno == EWOULDBLOCK) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout:EWOULDBLOCK reading from watchman"); } else { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "Can't parse result from watchman: %s", jerror.text); } return NULL; } if (fgetc(conn->fp) != '\n') { if (errno == EAGAIN || errno == EWOULDBLOCK) { watchman_err(error, WATCHMAN_ERR_TIMEOUT, "Timeout reading EOL from watchman"); } else { watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, "No newline at end of reply"); } json_decref(result); return NULL; } return result; }
static struct watchman_query_result * watchman_query_json(struct watchman_connection *conn, json_t *query, struct watchman_error *error) { struct watchman_query_result *result = NULL; struct watchman_query_result *res = NULL; if (watchman_send(conn, query, error)) { return NULL; } /* parse the result */ json_t *obj = watchman_read(conn, error); if (!obj) { return NULL; } JSON_ASSERT(json_is_object, obj, "Failed to send watchman query %s"); json_t *jerror = json_object_get(obj, "error"); if (jerror) { watchman_err(error, "Error result from watchman: %s", json_string_value(jerror)); goto done; } res = calloc(1, sizeof(*res)); json_t *files = json_object_get(obj, "files"); JSON_ASSERT(json_is_array, files, "Bad files %s"); int nr = json_array_size(files); res->stats = calloc(nr, sizeof(*res->stats)); int i; for (i = 0; i < nr; ++i) { struct watchman_stat *stat = res->stats + i; json_t *statobj = json_array_get(files, i); if (json_is_string(statobj)) { /* then hopefully we only requested names */ stat->name = strdup(json_string_value(statobj)); res->nr++; continue; } JSON_ASSERT(json_is_object, statobj, "must be object: %s"); json_t *name = json_object_get(statobj, "name"); JSON_ASSERT(json_is_string, name, "name must be string: %s"); stat->name = strdup(json_string_value(name)); WRITE_BOOL_STAT(stat, statobj, exists); WRITE_INT_STAT(stat, statobj, ctime); WRITE_INT_STAT(stat, statobj, ctime_ms); WRITE_INT_STAT(stat, statobj, ctime_us); WRITE_INT_STAT(stat, statobj, ctime_ns); WRITE_INT_STAT(stat, statobj, dev); WRITE_INT_STAT(stat, statobj, gid); WRITE_INT_STAT(stat, statobj, ino); WRITE_INT_STAT(stat, statobj, mode); WRITE_INT_STAT(stat, statobj, mtime); WRITE_INT_STAT(stat, statobj, mtime_ms); WRITE_INT_STAT(stat, statobj, mtime_us); WRITE_INT_STAT(stat, statobj, mtime_ns); WRITE_INT_STAT(stat, statobj, nlink); WRITE_INT_STAT(stat, statobj, size); WRITE_INT_STAT(stat, statobj, uid); WRITE_STR_STAT(stat, statobj, cclock); WRITE_STR_STAT(stat, statobj, oclock); WRITE_FLOAT_STAT(stat, statobj, ctime_f); WRITE_FLOAT_STAT(stat, statobj, mtime_f); /* the one we have to do manually because we don't * want to use the name "new" */ json_t *newer = json_object_get(statobj, "new"); if (newer) { stat->newer = json_is_true(newer); } res->nr++; } json_t *version = json_object_get(obj, "version"); JSON_ASSERT(json_is_string, version, "Bad version %s"); res->version = strdup(json_string_value(version)); json_t *clock = json_object_get(obj, "clock"); JSON_ASSERT(json_is_string, clock, "Bad clock %s"); res->clock = strdup(json_string_value(clock)); json_t *fresh = json_object_get(obj, "is_fresh_instance"); JSON_ASSERT(json_is_boolean, fresh, "Bad is_fresh_instance %s"); res->is_fresh_instance = json_is_true(fresh); result = res; res = NULL; done: if (res) { watchman_free_query_result(res); } json_decref(obj); return result; }
static struct watchman_query_result * watchman_query_json(struct watchman_connection *conn, json_t *query, struct timeval *timeout, struct watchman_error *error) { struct watchman_query_result *result = NULL; struct watchman_query_result *res = NULL; if (watchman_send(conn, query, error)) { return NULL; } /* parse the result */ proto_t obj = watchman_read_with_timeout(conn, timeout, error); if (proto_is_null(obj)) { return NULL; } PROTO_ASSERT(proto_is_object, obj, "Failed to send watchman query %s"); proto_t jerror = proto_object_get(obj, "error"); if (!proto_is_null(jerror)) { watchman_err(error, WATCHMAN_ERR_WATCHMAN_REPORTED, "Error result from watchman: %s", proto_strdup(jerror)); goto done; } res = calloc(1, sizeof(*res)); proto_t files = proto_object_get(obj, "files"); PROTO_ASSERT(proto_is_array, files, "Bad files %s"); int nr = proto_array_size(files); res->stats = calloc(nr, sizeof(*res->stats)); int i; for (i = 0; i < nr; ++i) { struct watchman_stat *stat = res->stats + i; proto_t statobj = proto_array_get(files, i); if (proto_is_string(statobj)) { /* then hopefully we only requested names */ stat->name = proto_strdup(statobj); res->nr++; continue; } PROTO_ASSERT(proto_is_object, statobj, "must be object: %s"); proto_t name = proto_object_get(statobj, "name"); PROTO_ASSERT(proto_is_string, name, "name must be string: %s"); stat->name = proto_strdup(name); WRITE_BOOL_STAT(stat, statobj, exists); WRITE_INT_STAT(stat, statobj, ctime); WRITE_INT_STAT(stat, statobj, ctime_ms); WRITE_INT_STAT(stat, statobj, ctime_us); WRITE_INT_STAT(stat, statobj, ctime_ns); WRITE_INT_STAT(stat, statobj, dev); WRITE_INT_STAT(stat, statobj, gid); WRITE_INT_STAT(stat, statobj, ino); WRITE_INT_STAT(stat, statobj, mode); WRITE_INT_STAT(stat, statobj, mtime); WRITE_INT_STAT(stat, statobj, mtime_ms); WRITE_INT_STAT(stat, statobj, mtime_us); WRITE_INT_STAT(stat, statobj, mtime_ns); WRITE_INT_STAT(stat, statobj, nlink); WRITE_INT_STAT(stat, statobj, size); WRITE_INT_STAT(stat, statobj, uid); WRITE_STR_STAT(stat, statobj, cclock); WRITE_STR_STAT(stat, statobj, oclock); WRITE_FLOAT_STAT(stat, statobj, ctime_f); WRITE_FLOAT_STAT(stat, statobj, mtime_f); /* the one we have to do manually because we don't * want to use the name "new" */ proto_t newer = proto_object_get(statobj, "new"); if (!proto_is_null(newer)) { stat->newer = proto_is_true(newer); } res->nr++; } proto_t version = proto_object_get(obj, "version"); PROTO_ASSERT(proto_is_string, version, "Bad version %s"); res->version = proto_strdup(version); proto_t clock = proto_object_get(obj, "clock"); PROTO_ASSERT(proto_is_string, clock, "Bad clock %s"); res->clock = proto_strdup(clock); proto_t fresh = proto_object_get(obj, "is_fresh_instance"); PROTO_ASSERT(proto_is_boolean, fresh, "Bad is_fresh_instance %s"); res->is_fresh_instance = proto_is_true(fresh); result = res; res = NULL; done: if (res) { watchman_free_query_result(res); } proto_free(obj); return result; }