1
0

watchman.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. // Copyright 2014 Wincent Colaiuta. All rights reserved.
  2. //
  3. // Redistribution and use in source and binary forms, with or without
  4. // modification, are permitted provided that the following conditions are met:
  5. //
  6. // 1. Redistributions of source code must retain the above copyright notice,
  7. // this list of conditions and the following disclaimer.
  8. // 2. Redistributions in binary form must reproduce the above copyright notice,
  9. // this list of conditions and the following disclaimer in the documentation
  10. // and/or other materials provided with the distribution.
  11. //
  12. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  13. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  14. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  15. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
  16. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  17. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  18. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  19. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  20. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  21. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  22. // POSSIBILITY OF SUCH DAMAGE.
  23. #include "watchman.h"
  24. #ifdef WATCHMAN_BUILD
  25. #if defined(HAVE_RUBY_ST_H)
  26. #include <ruby/st.h>
  27. #elif defined(HAVE_ST_H)
  28. #include <st.h>
  29. #else
  30. #error no st.h header found
  31. #endif
  32. #include <fcntl.h> /* for fcntl() */
  33. #include <sys/errno.h> /* for errno */
  34. #include <sys/socket.h> /* for recv(), MSG_PEEK */
  35. typedef struct {
  36. uint8_t *data; // payload
  37. size_t cap; // total capacity
  38. size_t len; // current length
  39. } watchman_t;
  40. // Forward declarations:
  41. VALUE watchman_load(char **ptr, char *end);
  42. void watchman_dump(watchman_t *w, VALUE serializable);
  43. /**
  44. * Inspect a ruby object (debugging aid)
  45. */
  46. #define ruby_inspect(obj) rb_funcall(rb_mKernel, rb_intern("p"), 1, obj)
  47. /**
  48. * Print `count` bytes of memory at `address` (debugging aid)
  49. */
  50. #define dump(address, count) \
  51. do { \
  52. for (int i = 0; i < count; i++) { \
  53. printf("%02x ", ((unsigned char *)address)[i]); printf("\n"); \
  54. } \
  55. } while(0)
  56. #define WATCHMAN_DEFAULT_STORAGE 4096
  57. #define WATCHMAN_BINARY_MARKER "\x00\x01"
  58. #define WATCHMAN_ARRAY_MARKER 0x00
  59. #define WATCHMAN_HASH_MARKER 0x01
  60. #define WATCHMAN_STRING_MARKER 0x02
  61. #define WATCHMAN_INT8_MARKER 0x03
  62. #define WATCHMAN_INT16_MARKER 0x04
  63. #define WATCHMAN_INT32_MARKER 0x05
  64. #define WATCHMAN_INT64_MARKER 0x06
  65. #define WATCHMAN_FLOAT_MARKER 0x07
  66. #define WATCHMAN_TRUE 0x08
  67. #define WATCHMAN_FALSE 0x09
  68. #define WATCHMAN_NIL 0x0a
  69. #define WATCHMAN_TEMPLATE_MARKER 0x0b
  70. #define WATCHMAN_SKIP_MARKER 0x0c
  71. #define WATCHMAN_HEADER \
  72. WATCHMAN_BINARY_MARKER \
  73. "\x06" \
  74. "\x00\x00\x00\x00\x00\x00\x00\x00"
  75. static const char watchman_array_marker = WATCHMAN_ARRAY_MARKER;
  76. static const char watchman_hash_marker = WATCHMAN_HASH_MARKER;
  77. static const char watchman_string_marker = WATCHMAN_STRING_MARKER;
  78. static const char watchman_true = WATCHMAN_TRUE;
  79. static const char watchman_false = WATCHMAN_FALSE;
  80. static const char watchman_nil = WATCHMAN_NIL;
  81. /**
  82. * Appends `len` bytes, starting at `data`, to the watchman_t struct `w`
  83. *
  84. * Will attempt to reallocate the underlying storage if it is not sufficient.
  85. */
  86. void watchman_append(watchman_t *w, const char *data, size_t len) {
  87. if (w->len + len > w->cap) {
  88. w->cap += w->len + WATCHMAN_DEFAULT_STORAGE;
  89. REALLOC_N(w->data, uint8_t, w->cap);
  90. }
  91. memcpy(w->data + w->len, data, len);
  92. w->len += len;
  93. }
  94. /**
  95. * Allocate a new watchman_t struct
  96. *
  97. * The struct has a small amount of extra capacity preallocated, and a blank
  98. * header that can be filled in later to describe the PDU.
  99. */
  100. watchman_t *watchman_init() {
  101. watchman_t *w = ALLOC(watchman_t);
  102. w->cap = WATCHMAN_DEFAULT_STORAGE;
  103. w->len = 0;
  104. w->data = ALLOC_N(uint8_t, WATCHMAN_DEFAULT_STORAGE);
  105. watchman_append(w, WATCHMAN_HEADER, sizeof(WATCHMAN_HEADER) - 1);
  106. return w;
  107. }
  108. /**
  109. * Free a watchman_t struct `w` that was previously allocated with
  110. * `watchman_init`
  111. */
  112. void watchman_free(watchman_t *w) {
  113. xfree(w->data);
  114. xfree(w);
  115. }
  116. /**
  117. * Encodes and appends the integer `num` to `w`
  118. */
  119. void watchman_dump_int(watchman_t *w, int64_t num) {
  120. char encoded[1 + sizeof(int64_t)];
  121. if (num == (int8_t)num) {
  122. encoded[0] = WATCHMAN_INT8_MARKER;
  123. encoded[1] = (int8_t)num;
  124. watchman_append(w, encoded, 1 + sizeof(int8_t));
  125. } else if (num == (int16_t)num) {
  126. encoded[0] = WATCHMAN_INT16_MARKER;
  127. *(int16_t *)(encoded + 1) = (int16_t)num;
  128. watchman_append(w, encoded, 1 + sizeof(int16_t));
  129. } else if (num == (int32_t)num) {
  130. encoded[0] = WATCHMAN_INT32_MARKER;
  131. *(int32_t *)(encoded + 1) = (int32_t)num;
  132. watchman_append(w, encoded, 1 + sizeof(int32_t));
  133. } else {
  134. encoded[0] = WATCHMAN_INT64_MARKER;
  135. *(int64_t *)(encoded + 1) = (int64_t)num;
  136. watchman_append(w, encoded, 1 + sizeof(int64_t));
  137. }
  138. }
  139. /**
  140. * Encodes and appends the string `string` to `w`
  141. */
  142. void watchman_dump_string(watchman_t *w, VALUE string) {
  143. watchman_append(w, &watchman_string_marker, sizeof(watchman_string_marker));
  144. watchman_dump_int(w, RSTRING_LEN(string));
  145. watchman_append(w, RSTRING_PTR(string), RSTRING_LEN(string));
  146. }
  147. /**
  148. * Encodes and appends the double `num` to `w`
  149. */
  150. void watchman_dump_double(watchman_t *w, double num) {
  151. char encoded[1 + sizeof(double)];
  152. encoded[0] = WATCHMAN_FLOAT_MARKER;
  153. *(double *)(encoded + 1) = num;
  154. watchman_append(w, encoded, sizeof(encoded));
  155. }
  156. /**
  157. * Encodes and appends the array `array` to `w`
  158. */
  159. void watchman_dump_array(watchman_t *w, VALUE array) {
  160. long i;
  161. watchman_append(w, &watchman_array_marker, sizeof(watchman_array_marker));
  162. watchman_dump_int(w, RARRAY_LEN(array));
  163. for (i = 0; i < RARRAY_LEN(array); i++) {
  164. watchman_dump(w, rb_ary_entry(array, i));
  165. }
  166. }
  167. /**
  168. * Helper method that encodes and appends a key/value pair (`key`, `value`) from
  169. * a hash to the watchman_t struct passed in via `data`
  170. */
  171. int watchman_dump_hash_iterator(VALUE key, VALUE value, VALUE data) {
  172. watchman_t *w = (watchman_t *)data;
  173. watchman_dump_string(w, StringValue(key));
  174. watchman_dump(w, value);
  175. return ST_CONTINUE;
  176. }
  177. /**
  178. * Encodes and appends the hash `hash` to `w`
  179. */
  180. void watchman_dump_hash(watchman_t *w, VALUE hash) {
  181. watchman_append(w, &watchman_hash_marker, sizeof(watchman_hash_marker));
  182. watchman_dump_int(w, RHASH_SIZE(hash));
  183. rb_hash_foreach(hash, watchman_dump_hash_iterator, (VALUE)w);
  184. }
  185. /**
  186. * Encodes and appends the serialized Ruby object `serializable` to `w`
  187. *
  188. * Examples of serializable objects include arrays, hashes, strings, numbers
  189. * (integers, floats), booleans, and nil.
  190. */
  191. void watchman_dump(watchman_t *w, VALUE serializable) {
  192. switch (TYPE(serializable)) {
  193. case T_ARRAY:
  194. return watchman_dump_array(w, serializable);
  195. case T_HASH:
  196. return watchman_dump_hash(w, serializable);
  197. case T_STRING:
  198. return watchman_dump_string(w, serializable);
  199. case T_FIXNUM: // up to 63 bits
  200. return watchman_dump_int(w, FIX2LONG(serializable));
  201. case T_BIGNUM:
  202. return watchman_dump_int(w, NUM2LL(serializable));
  203. case T_FLOAT:
  204. return watchman_dump_double(w, NUM2DBL(serializable));
  205. case T_TRUE:
  206. return watchman_append(w, &watchman_true, sizeof(watchman_true));
  207. case T_FALSE:
  208. return watchman_append(w, &watchman_false, sizeof(watchman_false));
  209. case T_NIL:
  210. return watchman_append(w, &watchman_nil, sizeof(watchman_nil));
  211. default:
  212. rb_raise(rb_eTypeError, "unsupported type");
  213. }
  214. }
  215. /**
  216. * Extract and return the int encoded at `ptr`
  217. *
  218. * Moves `ptr` past the extracted int.
  219. *
  220. * Will raise an ArgumentError if extracting the int would take us beyond the
  221. * end of the buffer indicated by `end`, or if there is no int encoded at `ptr`.
  222. *
  223. * @returns The extracted int
  224. */
  225. int64_t watchman_load_int(char **ptr, char *end) {
  226. char *val_ptr = *ptr + sizeof(int8_t);
  227. int64_t val = 0;
  228. if (val_ptr >= end) {
  229. rb_raise(rb_eArgError, "insufficient int storage");
  230. }
  231. switch (*ptr[0]) {
  232. case WATCHMAN_INT8_MARKER:
  233. if (val_ptr + sizeof(int8_t) > end) {
  234. rb_raise(rb_eArgError, "overrun extracting int8_t");
  235. }
  236. val = *(int8_t *)val_ptr;
  237. *ptr = val_ptr + sizeof(int8_t);
  238. break;
  239. case WATCHMAN_INT16_MARKER:
  240. if (val_ptr + sizeof(int16_t) > end) {
  241. rb_raise(rb_eArgError, "overrun extracting int16_t");
  242. }
  243. val = *(int16_t *)val_ptr;
  244. *ptr = val_ptr + sizeof(int16_t);
  245. break;
  246. case WATCHMAN_INT32_MARKER:
  247. if (val_ptr + sizeof(int32_t) > end) {
  248. rb_raise(rb_eArgError, "overrun extracting int32_t");
  249. }
  250. val = *(int32_t *)val_ptr;
  251. *ptr = val_ptr + sizeof(int32_t);
  252. break;
  253. case WATCHMAN_INT64_MARKER:
  254. if (val_ptr + sizeof(int64_t) > end) {
  255. rb_raise(rb_eArgError, "overrun extracting int64_t");
  256. }
  257. val = *(int64_t *)val_ptr;
  258. *ptr = val_ptr + sizeof(int64_t);
  259. break;
  260. default:
  261. rb_raise(rb_eArgError, "bad integer marker 0x%02x", (unsigned int)*ptr[0]);
  262. break;
  263. }
  264. return val;
  265. }
  266. /**
  267. * Reads and returns a string encoded in the Watchman binary protocol format,
  268. * starting at `ptr` and finishing at or before `end`
  269. */
  270. VALUE watchman_load_string(char **ptr, char *end) {
  271. if (*ptr >= end) {
  272. rb_raise(rb_eArgError, "unexpected end of input");
  273. }
  274. if (*ptr[0] != WATCHMAN_STRING_MARKER) {
  275. rb_raise(rb_eArgError, "not a number");
  276. }
  277. *ptr += sizeof(int8_t);
  278. if (*ptr >= end) {
  279. rb_raise(rb_eArgError, "invalid string header");
  280. }
  281. int64_t len = watchman_load_int(ptr, end);
  282. if (len == 0) { // special case for zero-length strings
  283. return rb_str_new2("");
  284. } else if (*ptr + len > end) {
  285. rb_raise(rb_eArgError, "insufficient string storage");
  286. }
  287. VALUE string = rb_str_new(*ptr, len);
  288. *ptr += len;
  289. return string;
  290. }
  291. /**
  292. * Reads and returns a double encoded in the Watchman binary protocol format,
  293. * starting at `ptr` and finishing at or before `end`
  294. */
  295. double watchman_load_double(char **ptr, char *end) {
  296. *ptr += sizeof(int8_t); // caller has already verified the marker
  297. if (*ptr + sizeof(double) > end) {
  298. rb_raise(rb_eArgError, "insufficient double storage");
  299. }
  300. double val = *(double *)*ptr;
  301. *ptr += sizeof(double);
  302. return val;
  303. }
  304. /**
  305. * Helper method which returns length of the array encoded in the Watchman
  306. * binary protocol format, starting at `ptr` and finishing at or before `end`
  307. */
  308. int64_t watchman_load_array_header(char **ptr, char *end) {
  309. if (*ptr >= end) {
  310. rb_raise(rb_eArgError, "unexpected end of input");
  311. }
  312. // verify and consume marker
  313. if (*ptr[0] != WATCHMAN_ARRAY_MARKER) {
  314. rb_raise(rb_eArgError, "not an array");
  315. }
  316. *ptr += sizeof(int8_t);
  317. // expect a count
  318. if (*ptr + sizeof(int8_t) * 2 > end) {
  319. rb_raise(rb_eArgError, "incomplete array header");
  320. }
  321. return watchman_load_int(ptr, end);
  322. }
  323. /**
  324. * Reads and returns an array encoded in the Watchman binary protocol format,
  325. * starting at `ptr` and finishing at or before `end`
  326. */
  327. VALUE watchman_load_array(char **ptr, char *end) {
  328. int64_t count, i;
  329. VALUE array;
  330. count = watchman_load_array_header(ptr, end);
  331. array = rb_ary_new2(count);
  332. for (i = 0; i < count; i++) {
  333. rb_ary_push(array, watchman_load(ptr, end));
  334. }
  335. return array;
  336. }
  337. /**
  338. * Reads and returns a hash encoded in the Watchman binary protocol format,
  339. * starting at `ptr` and finishing at or before `end`
  340. */
  341. VALUE watchman_load_hash(char **ptr, char *end) {
  342. int64_t count, i;
  343. VALUE hash, key, value;
  344. *ptr += sizeof(int8_t); // caller has already verified the marker
  345. // expect a count
  346. if (*ptr + sizeof(int8_t) * 2 > end) {
  347. rb_raise(rb_eArgError, "incomplete hash header");
  348. }
  349. count = watchman_load_int(ptr, end);
  350. hash = rb_hash_new();
  351. for (i = 0; i < count; i++) {
  352. key = watchman_load_string(ptr, end);
  353. value = watchman_load(ptr, end);
  354. rb_hash_aset(hash, key, value);
  355. }
  356. return hash;
  357. }
  358. /**
  359. * Reads and returns a templated array encoded in the Watchman binary protocol
  360. * format, starting at `ptr` and finishing at or before `end`
  361. *
  362. * Templated arrays are arrays of hashes which have repetitive key information
  363. * pulled out into a separate "headers" prefix.
  364. *
  365. * @see https://github.com/facebook/watchman/blob/master/BSER.markdown
  366. */
  367. VALUE watchman_load_template(char **ptr, char *end) {
  368. int64_t header_items_count, i, row_count;
  369. VALUE array, hash, header, key, value;
  370. *ptr += sizeof(int8_t); // caller has already verified the marker
  371. // process template header array
  372. header_items_count = watchman_load_array_header(ptr, end);
  373. header = rb_ary_new2(header_items_count);
  374. for (i = 0; i < header_items_count; i++) {
  375. rb_ary_push(header, watchman_load_string(ptr, end));
  376. }
  377. // process row items
  378. row_count = watchman_load_int(ptr, end);
  379. array = rb_ary_new2(header_items_count);
  380. while (row_count--) {
  381. hash = rb_hash_new();
  382. for (i = 0; i < header_items_count; i++) {
  383. if (*ptr >= end) {
  384. rb_raise(rb_eArgError, "unexpected end of input");
  385. }
  386. if (*ptr[0] == WATCHMAN_SKIP_MARKER) {
  387. *ptr += sizeof(uint8_t);
  388. } else {
  389. value = watchman_load(ptr, end);
  390. key = rb_ary_entry(header, i);
  391. rb_hash_aset(hash, key, value);
  392. }
  393. }
  394. rb_ary_push(array, hash);
  395. }
  396. return array;
  397. }
  398. /**
  399. * Reads and returns an object encoded in the Watchman binary protocol format,
  400. * starting at `ptr` and finishing at or before `end`
  401. */
  402. VALUE watchman_load(char **ptr, char *end) {
  403. if (*ptr >= end) {
  404. rb_raise(rb_eArgError, "unexpected end of input");
  405. }
  406. switch (*ptr[0]) {
  407. case WATCHMAN_ARRAY_MARKER:
  408. return watchman_load_array(ptr, end);
  409. case WATCHMAN_HASH_MARKER:
  410. return watchman_load_hash(ptr, end);
  411. case WATCHMAN_STRING_MARKER:
  412. return watchman_load_string(ptr, end);
  413. case WATCHMAN_INT8_MARKER:
  414. case WATCHMAN_INT16_MARKER:
  415. case WATCHMAN_INT32_MARKER:
  416. case WATCHMAN_INT64_MARKER:
  417. return LL2NUM(watchman_load_int(ptr, end));
  418. case WATCHMAN_FLOAT_MARKER:
  419. return rb_float_new(watchman_load_double(ptr, end));
  420. case WATCHMAN_TRUE:
  421. *ptr += 1;
  422. return Qtrue;
  423. case WATCHMAN_FALSE:
  424. *ptr += 1;
  425. return Qfalse;
  426. case WATCHMAN_NIL:
  427. *ptr += 1;
  428. return Qnil;
  429. case WATCHMAN_TEMPLATE_MARKER:
  430. return watchman_load_template(ptr, end);
  431. default:
  432. rb_raise(rb_eTypeError, "unsupported type");
  433. }
  434. return Qnil; // keep the compiler happy
  435. }
  436. /**
  437. * CommandT::Watchman::Utils.load(serialized)
  438. *
  439. * Converts the binary object, `serialized`, from the Watchman binary protocol
  440. * format into a normal Ruby object.
  441. */
  442. VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) {
  443. serialized = StringValue(serialized);
  444. long len = RSTRING_LEN(serialized);
  445. char *ptr = RSTRING_PTR(serialized);
  446. char *end = ptr + len;
  447. // expect at least the binary marker and a int8_t length counter
  448. if ((size_t)len < sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) * 2) {
  449. rb_raise(rb_eArgError, "undersized header");
  450. }
  451. if (memcmp(ptr, WATCHMAN_BINARY_MARKER, sizeof(WATCHMAN_BINARY_MARKER) - 1)) {
  452. rb_raise(rb_eArgError, "missing binary marker");
  453. }
  454. // get size marker
  455. ptr += sizeof(WATCHMAN_BINARY_MARKER) - 1;
  456. uint64_t payload_size = watchman_load_int(&ptr, end);
  457. if (!payload_size) {
  458. rb_raise(rb_eArgError, "empty payload");
  459. }
  460. // sanity check length
  461. if (ptr + payload_size != end) {
  462. rb_raise(rb_eArgError, "payload size mismatch (%lu)", end - (ptr + payload_size));
  463. }
  464. VALUE loaded = watchman_load(&ptr, end);
  465. // one more sanity check
  466. if (ptr != end) {
  467. rb_raise(rb_eArgError, "payload termination mismatch (%lu)", end - ptr);
  468. }
  469. return loaded;
  470. }
  471. /**
  472. * CommandT::Watchman::Utils.dump(serializable)
  473. *
  474. * Converts the Ruby object, `serializable`, into a binary string in the
  475. * Watchman binary protocol format.
  476. *
  477. * Examples of serializable objects include arrays, hashes, strings, numbers
  478. * (integers, floats), booleans, and nil.
  479. */
  480. VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable) {
  481. watchman_t *w = watchman_init();
  482. watchman_dump(w, serializable);
  483. // update header with final length information
  484. uint64_t *len = (uint64_t *)(w->data + sizeof(WATCHMAN_HEADER) - sizeof(uint64_t) - 1);
  485. *len = w->len - sizeof(WATCHMAN_HEADER) + 1;
  486. // prepare final return value
  487. VALUE serialized = rb_str_buf_new(w->len);
  488. rb_str_buf_cat(serialized, (const char*)w->data, w->len);
  489. watchman_free(w);
  490. return serialized;
  491. }
  492. /**
  493. * Helper method for raising a SystemCallError wrapping a lower-level error code
  494. * coming from the `errno` global variable.
  495. */
  496. void watchman_raise_system_call_error(int number) {
  497. VALUE error = INT2FIX(number);
  498. rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
  499. }
  500. // How far we have to look to figure out the size of the PDU header
  501. #define WATCHMAN_SNIFF_BUFFER_SIZE sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t)
  502. // How far we have to peek, at most, to figure out the size of the PDU itself
  503. #define WATCHMAN_PEEK_BUFFER_SIZE \
  504. sizeof(WATCHMAN_BINARY_MARKER) - 1 + \
  505. sizeof(WATCHMAN_INT64_MARKER) + \
  506. sizeof(int64_t)
  507. /**
  508. * CommandT::Watchman::Utils.query(query, socket)
  509. *
  510. * Converts `query`, a Watchman query comprising Ruby objects, into the Watchman
  511. * binary protocol format, transmits it over socket, and unserializes and
  512. * returns the result.
  513. */
  514. VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) {
  515. int fileno = NUM2INT(rb_funcall(socket, rb_intern("fileno"), 0));
  516. // do blocking I/O to simplify the following logic
  517. int flags = fcntl(fileno, F_GETFL);
  518. if (fcntl(fileno, F_SETFL, flags & ~O_NONBLOCK) == -1) {
  519. rb_raise(rb_eRuntimeError, "unable to clear O_NONBLOCK flag");
  520. }
  521. // send the message
  522. VALUE serialized = CommandTWatchmanUtils_dump(self, query);
  523. long query_len = RSTRING_LEN(serialized);
  524. ssize_t sent = send(fileno, RSTRING_PTR(serialized), query_len, 0);
  525. if (sent == -1) {
  526. watchman_raise_system_call_error(errno);
  527. } else if (sent != query_len) {
  528. rb_raise(rb_eRuntimeError, "expected to send %ld bytes but sent %ld",
  529. query_len, sent);
  530. }
  531. // sniff to see how large the header is
  532. int8_t peek[WATCHMAN_PEEK_BUFFER_SIZE];
  533. ssize_t received = recv(fileno, peek, WATCHMAN_SNIFF_BUFFER_SIZE, MSG_PEEK | MSG_WAITALL);
  534. if (received == -1) {
  535. watchman_raise_system_call_error(errno);
  536. } else if (received != WATCHMAN_SNIFF_BUFFER_SIZE) {
  537. rb_raise(rb_eRuntimeError, "failed to sniff PDU header");
  538. }
  539. // peek at size of PDU
  540. int8_t sizes[] = { 0, 0, 0, 1, 2, 4, 8 };
  541. ssize_t peek_size = sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) +
  542. sizes[peek[sizeof(WATCHMAN_BINARY_MARKER) - 1]];
  543. received = recv(fileno, peek, peek_size, MSG_PEEK);
  544. if (received == -1) {
  545. watchman_raise_system_call_error(errno);
  546. } else if (received != peek_size) {
  547. rb_raise(rb_eRuntimeError, "failed to peek at PDU header");
  548. }
  549. int8_t *pdu_size_ptr = peek + sizeof(WATCHMAN_BINARY_MARKER) - sizeof(int8_t);
  550. int64_t payload_size =
  551. peek_size +
  552. watchman_load_int((char **)&pdu_size_ptr, (char *)peek + peek_size);
  553. // actually read the PDU
  554. void *buffer = xmalloc(payload_size);
  555. if (!buffer) {
  556. rb_raise(rb_eNoMemError, "failed to allocate %lld bytes", payload_size);
  557. }
  558. received = recv(fileno, buffer, payload_size, MSG_WAITALL);
  559. if (received == -1) {
  560. watchman_raise_system_call_error(errno);
  561. } else if (received != payload_size) {
  562. rb_raise(rb_eRuntimeError, "failed to load PDU");
  563. }
  564. char *payload = buffer + peek_size;
  565. VALUE loaded = watchman_load(&payload, payload + payload_size);
  566. free(buffer);
  567. return loaded;
  568. }
  569. #else /* don't build Watchman utils; supply stubs only*/
  570. VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) {
  571. rb_raise(rb_eRuntimeError, "unsupported operation");
  572. }
  573. VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable) {
  574. rb_raise(rb_eRuntimeError, "unsupported operation");
  575. }
  576. VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) {
  577. rb_raise(rb_eRuntimeError, "unsupported operation");
  578. }
  579. #endif