forked from facebook/watchman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cfg.cpp
399 lines (334 loc) · 10.6 KB
/
cfg.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/* Copyright 2012-present Facebook, Inc.
* Licensed under the Apache License, Version 2.0 */
#include "watchman.h"
#include "watchman_synchronized.h"
namespace {
struct config_state {
json_ref global_cfg;
w_string global_config_file_path;
json_ref arg_cfg;
};
watchman::Synchronized<config_state> configState;
}
/* Called during shutdown to free things so that we run cleanly
* under valgrind */
void cfg_shutdown(void)
{
auto state = configState.wlock();
state->global_cfg.reset();
state->arg_cfg.reset();
}
w_string cfg_get_global_config_file_path(void) {
return configState.rlock()->global_config_file_path;
}
void cfg_load_global_config_file(void)
{
json_error_t err;
const char *cfg_file = getenv("WATCHMAN_CONFIG_FILE");
#ifdef WATCHMAN_CONFIG_FILE
if (!cfg_file) {
cfg_file = WATCHMAN_CONFIG_FILE;
}
#endif
if (!cfg_file || cfg_file[0] == '\0') {
return;
}
if (!w_path_exists(cfg_file)) {
return;
}
auto config = json_load_file(cfg_file, 0, &err);
if (!config) {
w_log(W_LOG_ERR, "failed to parse json from %s: %s\n",
cfg_file, err.text);
return;
}
auto lockedState = configState.wlock();
lockedState->global_cfg = config;
lockedState->global_config_file_path = cfg_file;
}
void cfg_set_arg(const char* name, const json_ref& val) {
auto state = configState.wlock();
if (!state->arg_cfg) {
state->arg_cfg = json_object();
}
state->arg_cfg.set(name, json_ref(val));
}
void cfg_set_global(const char* name, const json_ref& val) {
auto state = configState.wlock();
if (!state->global_cfg) {
state->global_cfg = json_object();
}
state->global_cfg.set(name, json_ref(val));
}
static json_ref cfg_get_raw(const char* name, const json_ref* optr) {
json_ref val;
if (*optr) {
val = optr->get_default(name);
}
return val;
}
json_ref cfg_get_json(const char* name) {
json_ref val;
auto state = configState.rlock();
// Highest precedence: command line arguments
val = cfg_get_raw(name, &state->arg_cfg);
// then: global config options
if (!val) {
val = cfg_get_raw(name, &state->global_cfg);
}
return val;
}
const char* cfg_get_string(const char* name, const char* defval) {
auto val = cfg_get_json(name);
if (val) {
if (!json_is_string(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be a string\n", name);
}
return json_string_value(val);
}
return defval;
}
// Return true if the json ref is an array of string values
static bool is_array_of_strings(const json_ref& ref) {
uint32_t i;
if (!json_is_array(ref)) {
return false;
}
for (i = 0; i < json_array_size(ref); i++) {
if (!json_is_string(json_array_get(ref, i))) {
return false;
}
}
return true;
}
// Given an array of string values, if that array does not contain
// a ".watchmanconfig" entry, prepend it
static void prepend_watchmanconfig_to_array(json_ref& ref) {
const char *val;
if (json_array_size(ref) == 0) {
// json_array_insert_new at index can fail when the array is empty,
// so just append in this case.
json_array_append_new(ref, typed_string_to_json(".watchmanconfig",
W_STRING_UNICODE));
return;
}
val = json_string_value(json_array_get(ref, 0));
if (!strcmp(val, ".watchmanconfig")) {
return;
}
json_array_insert_new(ref, 0, typed_string_to_json(".watchmanconfig",
W_STRING_UNICODE));
}
// Compute the effective value of the root_files configuration and
// return a json reference. The caller must decref the ref when done
// (we may synthesize this value). Sets enforcing to indicate whether
// we will only allow watches on the root_files.
// The array returned by this function (if not NULL) is guaranteed to
// list .watchmanconfig as its zeroth element.
json_ref cfg_compute_root_files(bool* enforcing) {
*enforcing = false;
json_ref ref = cfg_get_json("enforce_root_files");
if (ref) {
if (!json_is_boolean(ref)) {
w_log(W_LOG_FATAL,
"Expected config value enforce_root_files to be boolean\n");
}
*enforcing = json_is_true(ref);
}
ref = cfg_get_json("root_files");
if (ref) {
if (!is_array_of_strings(ref)) {
w_log(W_LOG_FATAL,
"global config root_files must be an array of strings\n");
*enforcing = false;
return nullptr;
}
prepend_watchmanconfig_to_array(ref);
return ref;
}
// Try legacy root_restrict_files configuration
ref = cfg_get_json("root_restrict_files");
if (ref) {
if (!is_array_of_strings(ref)) {
w_log(W_LOG_FATAL, "deprecated global config root_restrict_files "
"must be an array of strings\n");
*enforcing = false;
return nullptr;
}
prepend_watchmanconfig_to_array(ref);
*enforcing = true;
return ref;
}
// Synthesize our conservative default value.
// .watchmanconfig MUST be first
return json_array({typed_string_to_json(".watchmanconfig"),
typed_string_to_json(".hg"),
typed_string_to_json(".git"),
typed_string_to_json(".svn")});
}
// Produces a string like: "`foo`, `bar`, and `baz`"
std::string cfg_pretty_print_root_files(const json_ref& root_files) {
std::string result;
for (unsigned int i = 0; i < root_files.array().size(); ++i) {
const auto& r = root_files.array()[i];
if (i > 1 && i == root_files.array().size() - 1) {
// We are last in a list of multiple items
result.append(", and ");
} else if (i > 0) {
result.append(", ");
}
result.append("`");
result.append(json_string_value(r));
result.append("`");
}
return result;
}
json_int_t cfg_get_int(const char* name, json_int_t defval) {
auto val = cfg_get_json(name);
if (val) {
if (!json_is_integer(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be an integer\n", name);
}
return json_integer_value(val);
}
return defval;
}
bool cfg_get_bool(const char* name, bool defval) {
auto val = cfg_get_json(name);
if (val) {
if (!json_is_boolean(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be a boolean\n", name);
}
return json_is_true(val);
}
return defval;
}
double cfg_get_double(const char* name, double defval) {
auto val = cfg_get_json(name);
if (val) {
if (!json_is_number(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be a number\n", name);
}
return json_real_value(val);
}
return defval;
}
#ifndef _WIN32
#define MAKE_GET_PERM(PROP, SUFFIX) \
static mode_t get_##PROP##_perm( \
const char* name, \
const json_ref& val, \
bool write_bits, \
bool execute_bits) { \
mode_t ret = 0; \
auto perm = val.get_default(#PROP); \
if (perm) { \
if (!json_is_boolean(perm)) { \
w_log( \
W_LOG_FATAL, \
"Expected config value %s." #PROP " to be a boolean\n", \
name); \
} \
if (json_is_true(perm)) { \
ret |= S_IR##SUFFIX; \
if (write_bits) { \
ret |= S_IW##SUFFIX; \
} \
if (execute_bits) { \
ret |= S_IX##SUFFIX; \
} \
} \
} \
return ret; \
}
MAKE_GET_PERM(group, GRP)
MAKE_GET_PERM(others, OTH)
/**
* This function expects the config to be an object containing the keys 'group'
* and 'others', each a bool.
*/
mode_t cfg_get_perms(const char* name, bool write_bits, bool execute_bits) {
auto val = cfg_get_json(name);
mode_t ret = S_IRUSR | S_IWUSR;
if (execute_bits) {
ret |= S_IXUSR;
}
if (val) {
if (!json_is_object(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be an object\n", name);
}
ret |= get_group_perm(name, val, write_bits, execute_bits);
ret |= get_others_perm(name, val, write_bits, execute_bits);
}
return ret;
}
#endif
const char *cfg_get_trouble_url(void) {
return cfg_get_string(
"troubleshooting_url",
"https://facebook.github.io/watchman/docs/troubleshooting.html");
}
Configuration::Configuration(const json_ref& local) : local_(local) {}
json_ref Configuration::get(const char* name) const {
// Highest precedence: options set locally
json_ref val;
if (local_) {
val = local_.get_default(name);
if (val) {
return val;
}
}
auto state = configState.rlock();
// then: command line arguments
if (!val) {
val = cfg_get_raw(name, &state->arg_cfg);
}
// then: global config options
if (!val) {
val = cfg_get_raw(name, &state->global_cfg);
}
return val;
}
const char* Configuration::getString(const char* name, const char* defval)
const {
auto val = get(name);
if (val) {
if (!json_is_string(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be a string\n", name);
}
return json_string_value(val);
}
return defval;
}
json_int_t Configuration::getInt(const char* name, json_int_t defval) const {
auto val = get(name);
if (val) {
if (!json_is_integer(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be an integer\n", name);
}
return json_integer_value(val);
}
return defval;
}
bool Configuration::getBool(const char* name, bool defval) const {
auto val = get(name);
if (val) {
if (!json_is_boolean(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be a boolean\n", name);
}
return json_is_true(val);
}
return defval;
}
double Configuration::getDouble(const char* name, double defval) const {
auto val = get(name);
if (val) {
if (!json_is_number(val)) {
w_log(W_LOG_FATAL, "Expected config value %s to be a number\n", name);
}
return json_real_value(val);
}
return defval;
}
/* vim:ts=2:sw=2:et:
*/