1 /* Copyright 2012-present Facebook, Inc. 2 * Licensed under the Apache License, Version 2.0 */ 3 4 #include "watchman.h" 5 #include "make_unique.h" try_client_mode_command(const json_ref & cmd,bool pretty)6 7 static int proc_pid; 8 static uint64_t proc_start_time; 9 10 void ClockSpec::init() { 11 struct timeval tv; 12 13 proc_pid = (int)getpid(); 14 if (gettimeofday(&tv, NULL) == -1) { 15 w_log(W_LOG_FATAL, "gettimeofday failed: %s\n", strerror(errno)); 16 } 17 proc_start_time = (uint64_t)tv.tv_sec; 18 } 19 20 ClockSpec::ClockSpec(const json_ref& value) { 21 auto parseClockString = [=](const char* str) { 22 uint64_t start_time; 23 int pid; 24 uint32_t root_number; 25 uint32_t ticks; 26 // Parse a >= 2.8.2 version clock string 27 if (sscanf( 28 str, 29 "c:%" PRIu64 ":%d:%" PRIu32 ":%" PRIu32, 30 &start_time, 31 &pid, 32 &root_number, 33 &ticks) == 4) { 34 tag = w_cs_clock; 35 clock.start_time = start_time; 36 clock.pid = pid; 37 clock.position.rootNumber = root_number; 38 clock.position.ticks = ticks; 39 return true; 40 } 41 42 if (sscanf(str, "c:%d:%" PRIu32, &pid, &ticks) == 2) { 43 // old-style clock value (<= 2.8.2) -- by setting clock time and root 44 // number to 0 we guarantee that this is treated as a fresh instance 45 tag = w_cs_clock; 46 clock.start_time = 0; 47 clock.pid = pid; 48 clock.position.rootNumber = root_number; 49 clock.position.ticks = ticks; 50 return true; 51 } 52 53 return false; 54 }; 55 56 switch (json_typeof(value)) { 57 case JSON_INTEGER: 58 tag = w_cs_timestamp; 59 timestamp = (time_t)json_integer_value(value); 60 return; 61 62 case JSON_OBJECT: { 63 auto clockStr = value.get_default("clock"); 64 if (clockStr) { 65 if (!parseClockString(json_string_value(clockStr))) { 66 throw std::domain_error("invalid clockspec"); 67 } 68 } else { 69 tag = w_cs_clock; 70 clock.start_time = 0; 71 clock.pid = 0; 72 clock.position.rootNumber = 0; 73 clock.position.ticks = 0; 74 } 75 76 auto scm = value.get_default("scm"); 77 if (scm) { 78 scmMergeBase = json_to_w_string( 79 scm.get_default("mergebase", w_string_to_json(""))); 80 scmMergeBaseWith = json_to_w_string(scm.get("mergebase-with")); 81 } 82 83 return; 84 } 85 86 case JSON_STRING: { 87 auto str = json_string_value(value); 88 89 if (str[0] == 'n' && str[1] == ':') { 90 tag = w_cs_named_cursor; 91 named_cursor.cursor = json_to_w_string(value); 92 return; 93 } 94 95 if (parseClockString(str)) { 96 return; 97 } 98 99 /* fall through to default case and throw error. 100 * The redundant looking comment below is a hint to 101 * gcc that it is ok to fall through. */ 102 } 103 /* fall through */ 104 105 default: 106 throw std::domain_error("invalid clockspec"); 107 } 108 } 109 110 std::unique_ptr<ClockSpec> ClockSpec::parseOptionalClockSpec( 111 const json_ref& value) { 112 if (json_is_null(value)) { 113 return nullptr; 114 } 115 return watchman::make_unique<ClockSpec>(value); 116 } 117 118 ClockSpec::ClockSpec() : tag(w_cs_timestamp), timestamp(0) {} 119 120 ClockSpec::ClockSpec(const ClockPosition& position) 121 : tag(w_cs_clock), clock{proc_start_time, proc_pid, position} {} 122 123 w_query_since ClockSpec::evaluate( 124 const ClockPosition& position, 125 const uint32_t lastAgeOutTick, 126 watchman::Synchronized<std::unordered_map<w_string, uint32_t>>* cursorMap) 127 const { 128 w_query_since since; 129 130 switch (tag) { 131 case w_cs_timestamp: 132 // just copy the values over 133 since.is_timestamp = true; 134 since.timestamp = timestamp; 135 return since; 136 137 case w_cs_named_cursor: { 138 if (!cursorMap) { 139 // This is checked for and handled at parse time in SinceExpr::parse, 140 // so this should be impossible to hit. 141 throw std::runtime_error( 142 "illegal to use a named cursor in this context"); 143 } 144 145 { 146 auto wlock = cursorMap->wlock(); 147 auto& cursors = *wlock; 148 auto it = cursors.find(named_cursor.cursor); 149 150 if (it == cursors.end()) { 151 since.clock.is_fresh_instance = true; 152 since.clock.ticks = 0; 153 } else { 154 since.clock.ticks = it->second; 155 since.clock.is_fresh_instance = since.clock.ticks < lastAgeOutTick; 156 } 157 158 // record the current tick value against the cursor so that we use that 159 // as the basis for a subsequent query. 160 cursors[named_cursor.cursor] = position.ticks; 161 } 162 163 watchman::log( 164 watchman::DBG, 165 "resolved cursor ", 166 named_cursor.cursor, 167 " -> ", 168 since.clock.ticks, 169 "\n"); 170 171 return since; 172 } 173 174 case w_cs_clock: { 175 if (clock.start_time == proc_start_time && clock.pid == proc_pid && 176 clock.position.rootNumber == position.rootNumber) { 177 since.clock.is_fresh_instance = clock.position.ticks < lastAgeOutTick; 178 if (since.clock.is_fresh_instance) { 179 since.clock.ticks = 0; 180 } else { 181 since.clock.ticks = clock.position.ticks; 182 } 183 } else { 184 // If the pid, start time or root number don't match, they asked a 185 // different incarnation of the server or a different instance of this 186 // root, so we treat them as having never spoken to us before. 187 since.clock.is_fresh_instance = true; 188 since.clock.ticks = 0; 189 } 190 return since; 191 } 192 193 default: 194 throw std::runtime_error("impossible case in ClockSpec::evaluate"); 195 } 196 } 197 198 bool clock_id_string(uint32_t root_number, uint32_t ticks, char *buf, 199 size_t bufsize) { 200 int res = snprintf(buf, bufsize, "c:%" PRIu64 ":%d:%u:%" PRIu32, 201 proc_start_time, proc_pid, root_number, ticks); 202 203 if (res == -1) { 204 return false; 205 } 206 return (size_t)res < bufsize; 207 } 208 209 w_string ClockPosition::toClockString() const { 210 char clockbuf[128]; 211 if (!clock_id_string(rootNumber, ticks, clockbuf, sizeof(clockbuf))) { 212 throw std::runtime_error("clock is too big for clockbuf"); 213 } 214 return w_string(clockbuf, W_STRING_UNICODE); 215 } 216 217 /* Add the current clock value to the response */ 218 void annotate_with_clock( 219 const std::shared_ptr<w_root_t>& root, 220 json_ref& resp) { 221 resp.set("clock", w_string_to_json(root->view()->getCurrentClockString())); 222 } 223 224 json_ref ClockSpec::toJson() const { 225 if (hasScmParams()) { 226 return json_object( 227 {{"clock", w_string_to_json(position().toClockString())}, 228 {"scm", 229 json_object( 230 {{"mergebase", w_string_to_json(scmMergeBase)}, 231 {"mergebase-with", w_string_to_json(scmMergeBaseWith)}})}}); 232 } 233 return w_string_to_json(position().toClockString()); 234 } 235 236 bool ClockSpec::hasScmParams() const { 237 return scmMergeBase; 238 } 239 240 /* vim:ts=2:sw=2:et: 241 */ 242