1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2010-2020 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "internal.h"
19 #include "bucketconfig/clconfig.h"
20 #include "contrib/lcb-jsoncpp/lcb-jsoncpp.h"
21 #include <lcbio/iotable.h>
22 #include <mcserver/negotiate.h>
23 #include <lcbio/ssl.h>
24 #include "n1ql/query_utils.hh"
25 
26 #define LOGARGS(instance, lvl) instance->settings, "cntl", LCB_LOG_##lvl, __FILE__, __LINE__
27 
28 #define CNTL__MODE_SETSTRING 0x1000
29 
30 /* Basic definition/declaration for handlers */
31 #define HANDLER(name) static lcb_STATUS name(int mode, lcb_INSTANCE *instance, int cmd, void *arg)
32 
33 /* For handlers which only retrieve values */
34 #define RETURN_GET_ONLY(T, acc)                                                                                        \
35     if (mode != LCB_CNTL_GET) {                                                                                        \
36         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;                                                                       \
37     }                                                                                                                  \
38     *reinterpret_cast<T *>(arg) = (T)acc;                                                                              \
39     return LCB_SUCCESS;                                                                                                \
40     (void)cmd;
41 
42 #define RETURN_SET_ONLY(T, acc)                                                                                        \
43     if (mode != LCB_CNTL_SET) {                                                                                        \
44         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;                                                                       \
45     }                                                                                                                  \
46     acc = *reinterpret_cast<T *>(arg);                                                                                 \
47     return LCB_SUCCESS;
48 
49 #define RETURN_GET_SET(T, acc)                                                                                         \
50     if (mode == LCB_CNTL_GET) {                                                                                        \
51         RETURN_GET_ONLY(T, acc);                                                                                       \
52     } else if (mode == LCB_CNTL_SET) {                                                                                 \
53         RETURN_SET_ONLY(T, acc);                                                                                       \
54     } else {                                                                                                           \
55         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;                                                                       \
56     }
57 
58 typedef lcb_STATUS (*ctl_handler)(int, lcb_INSTANCE *, int, void *);
59 typedef struct {
60     const char *s;
61     std::uint32_t u32;
62 } STR_u32MAP;
u32_from_map(const char *s, const STR_u32MAP *lookup)63 static const STR_u32MAP *u32_from_map(const char *s, const STR_u32MAP *lookup)
64 {
65     const STR_u32MAP *ret;
66     for (ret = lookup; ret->s; ret++) {
67         std::size_t maxlen = strlen(ret->s);
68         if (!strncmp(ret->s, s, maxlen)) {
69             return ret;
70         }
71     }
72     return nullptr;
73 }
74 #define DO_CONVERT_STR2NUM(s, lookup, v)                                                                               \
75     {                                                                                                                  \
76         const STR_u32MAP *str__rv = u32_from_map(s, lookup);                                                           \
77         if (str__rv) {                                                                                                 \
78             v = str__rv->u32;                                                                                          \
79         } else {                                                                                                       \
80             return LCB_ERR_CONTROL_INVALID_ARGUMENT;                                                                   \
81         }                                                                                                              \
82     }
83 
get_timeout_field(lcb_INSTANCE *instance, int cmd)84 static std::uint32_t *get_timeout_field(lcb_INSTANCE *instance, int cmd)
85 {
86     lcb_settings *settings = instance->settings;
87     switch (cmd) {
88         case LCB_CNTL_OP_TIMEOUT:
89             return &settings->operation_timeout;
90         case LCB_CNTL_VIEW_TIMEOUT:
91             return &settings->views_timeout;
92         case LCB_CNTL_QUERY_TIMEOUT:
93             return &settings->n1ql_timeout;
94         case LCB_CNTL_QUERY_GRACE_PERIOD:
95             return &settings->n1ql_grace_period;
96         case LCB_CNTL_ANALYTICS_TIMEOUT:
97             return &settings->analytics_timeout;
98         case LCB_CNTL_SEARCH_TIMEOUT:
99             return &settings->search_timeout;
100         case LCB_CNTL_DURABILITY_INTERVAL:
101             return &settings->durability_interval;
102         case LCB_CNTL_DURABILITY_TIMEOUT:
103             return &settings->durability_timeout;
104         case LCB_CNTL_HTTP_TIMEOUT:
105             return &settings->http_timeout;
106         case LCB_CNTL_CONFIGURATION_TIMEOUT:
107             return &settings->config_timeout;
108         case LCB_CNTL_CONFDELAY_THRESH:
109             return &settings->weird_things_delay;
110         case LCB_CNTL_CONFIG_NODE_TIMEOUT:
111             return &settings->config_node_timeout;
112         case LCB_CNTL_HTCONFIG_IDLE_TIMEOUT:
113             return &settings->bc_http_stream_time;
114         case LCB_CNTL_RETRY_INTERVAL:
115             return &settings->retry_interval;
116         case LCB_CNTL_RETRY_NMV_INTERVAL:
117             return &settings->retry_nmv_interval;
118         case LCB_CNTL_CONFIG_POLL_INTERVAL:
119             return &settings->config_poll_interval;
120         case LCB_CNTL_TRACING_ORPHANED_QUEUE_FLUSH_INTERVAL:
121             return &settings->tracer_orphaned_queue_flush_interval;
122         case LCB_CNTL_TRACING_THRESHOLD_QUEUE_FLUSH_INTERVAL:
123             return &settings->tracer_threshold_queue_flush_interval;
124         case LCB_CNTL_TRACING_THRESHOLD_KV:
125             return &settings->tracer_threshold[LCBTRACE_THRESHOLD_KV];
126         case LCB_CNTL_TRACING_THRESHOLD_QUERY:
127             return &settings->tracer_threshold[LCBTRACE_THRESHOLD_QUERY];
128         case LCB_CNTL_TRACING_THRESHOLD_VIEW:
129             return &settings->tracer_threshold[LCBTRACE_THRESHOLD_VIEW];
130         case LCB_CNTL_TRACING_THRESHOLD_SEARCH:
131             return &settings->tracer_threshold[LCBTRACE_THRESHOLD_SEARCH];
132         case LCB_CNTL_TRACING_THRESHOLD_ANALYTICS:
133             return &settings->tracer_threshold[LCBTRACE_THRESHOLD_ANALYTICS];
134         case LCB_CNTL_PERSISTENCE_TIMEOUT_FLOOR:
135             return &settings->persistence_timeout_floor;
136         case LCB_CNTL_OP_METRICS_FLUSH_INTERVAL:
137             return &settings->op_metrics_flush_interval;
138         default:
139             return nullptr;
140     }
141 }
142 
HANDLER(timeout_common)143 HANDLER(timeout_common)
144 {
145     std::uint32_t *ptr;
146     auto *user = reinterpret_cast<std::uint32_t *>(arg);
147 
148     ptr = get_timeout_field(instance, cmd);
149     if (!ptr) {
150         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
151     }
152     if (mode == LCB_CNTL_GET) {
153         *user = *ptr;
154     } else {
155         if (cmd == LCB_CNTL_PERSISTENCE_TIMEOUT_FLOOR && *user < LCB_DEFAULT_PERSISTENCE_TIMEOUT_FLOOR) {
156             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
157         }
158         *ptr = *user;
159     }
160     return LCB_SUCCESS;
161 }
162 
HANDLER(noop_handler)163 HANDLER(noop_handler)
164 {
165     (void)mode;
166     (void)instance;
167     (void)cmd;
168     (void)arg;
169     return LCB_SUCCESS;
170 }
171 
HANDLER(get_vbconfig)172 HANDLER(get_vbconfig){RETURN_GET_ONLY(lcbvb_CONFIG *, LCBT_VBCONFIG(instance))}
173 
HANDLER(get_htype)174 HANDLER(get_htype){RETURN_GET_ONLY(lcb_INSTANCE_TYPE, static_cast<lcb_INSTANCE_TYPE>(instance->settings->conntype))}
175 
HANDLER(get_iops)176 HANDLER(get_iops){RETURN_GET_ONLY(lcb_io_opt_t, instance->iotable->p)}
177 
HANDLER(ippolicy)178 HANDLER(ippolicy){RETURN_GET_SET(lcb_ipv6_t, instance->settings->ipv6)}
179 
HANDLER(confthresh)180 HANDLER(confthresh){RETURN_GET_SET(std::size_t, instance->settings->weird_things_threshold)}
181 
HANDLER(randomize_bootstrap_hosts_handler)182 HANDLER(randomize_bootstrap_hosts_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, randomize_bootstrap_nodes))}
183 
HANDLER(get_changeset)184 HANDLER(get_changeset)
185 {
186     (void)instance;
187     RETURN_GET_ONLY(char *, LCB_VERSION_CHANGESET)
188 }
189 
HANDLER(ssl_mode_handler)190 HANDLER(ssl_mode_handler){RETURN_GET_ONLY(int, LCBT_SETTING(instance, sslopts))}
191 
HANDLER(ssl_truststorepath_handler)192 HANDLER(ssl_truststorepath_handler){RETURN_GET_ONLY(char *, LCBT_SETTING(instance, truststorepath))}
193 
HANDLER(ssl_certpath_handler)194 HANDLER(ssl_certpath_handler){RETURN_GET_ONLY(char *, LCBT_SETTING(instance, certpath))}
195 
HANDLER(ssl_keypath_handler)196 HANDLER(ssl_keypath_handler){RETURN_GET_ONLY(char *, LCBT_SETTING(instance, keypath))}
197 
HANDLER(htconfig_urltype_handler)198 HANDLER(htconfig_urltype_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, bc_http_urltype))}
199 
HANDLER(syncdtor_handler)200 HANDLER(syncdtor_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, syncdtor))}
201 
HANDLER(detailed_errcode_handler)202 HANDLER(detailed_errcode_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, detailed_neterr))}
203 
HANDLER(http_poolsz_handler)204 HANDLER(http_poolsz_handler){RETURN_GET_SET(std::size_t, instance->http_sockpool->get_options().maxidle)}
205 
HANDLER(http_pooltmo_handler)206 HANDLER(http_pooltmo_handler){RETURN_GET_SET(uint32_t, instance->http_sockpool->get_options().tmoidle)}
207 
HANDLER(http_refresh_config_handler)208 HANDLER(http_refresh_config_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, refresh_on_hterr))}
209 
HANDLER(compmode_handler)210 HANDLER(compmode_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, compressopts))}
211 
HANDLER(bucketname_handler)212 HANDLER(bucketname_handler){RETURN_GET_ONLY(const char *, LCBT_SETTING(instance, bucket))}
213 
HANDLER(buckettype_handler)214 HANDLER(buckettype_handler){RETURN_GET_ONLY(lcb_BTYPE, static_cast<lcb_BTYPE>(instance->btype))}
215 
HANDLER(schedflush_handler)216 HANDLER(schedflush_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, sched_implicit_flush))}
217 
HANDLER(vbguess_handler)218 HANDLER(vbguess_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, keep_guess_vbs))}
219 
HANDLER(vb_noremap_handler)220 HANDLER(vb_noremap_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, vb_noremap))}
221 
HANDLER(wait_for_config_handler)222 HANDLER(wait_for_config_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, wait_for_config))}
223 
HANDLER(fetch_mutation_tokens_handler)224 HANDLER(fetch_mutation_tokens_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, fetch_mutation_tokens))}
225 
HANDLER(nmv_imm_retry_handler)226 HANDLER(nmv_imm_retry_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, nmv_retry_imm))}
227 
HANDLER(tcp_nodelay_handler)228 HANDLER(tcp_nodelay_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, tcp_nodelay))}
229 
HANDLER(tcp_keepalive_handler)230 HANDLER(tcp_keepalive_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, tcp_keepalive))}
231 
HANDLER(readj_ts_wait_handler)232 HANDLER(readj_ts_wait_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, readj_ts_wait))}
233 
HANDLER(kv_hg_handler)234 HANDLER(kv_hg_handler){RETURN_GET_ONLY(lcb_HISTOGRAM *, instance->kv_timings)}
235 
HANDLER(read_chunk_size_handler)236 HANDLER(read_chunk_size_handler){RETURN_GET_SET(std::uint32_t, LCBT_SETTING(instance, read_chunk_size))}
237 
HANDLER(select_bucket_handler)238 HANDLER(select_bucket_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, select_bucket))}
239 
HANDLER(log_redaction_handler)240 HANDLER(log_redaction_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, log_redaction))}
241 
HANDLER(enable_tracing_handler)242 HANDLER(enable_tracing_handler)
243 {
244     if (mode == LCB_CNTL_GET) {
245         RETURN_GET_ONLY(int, instance->settings->use_tracing)
246     } else if (mode == LCB_CNTL_SET) {
247         if (arg == nullptr) {
248             return LCB_ERR_INVALID_ARGUMENT;
249         }
250         int enabled = *(static_cast<int *>(arg));
251         if (enabled) {
252             if (instance->settings->use_tracing) {
253                 /* do nothing */
254                 return LCB_SUCCESS;
255             }
256             instance->settings->tracer = lcbtrace_new(instance, LCBTRACE_F_THRESHOLD);
257             instance->settings->use_tracing = true;
258             return LCB_SUCCESS;
259         } else {
260             if (instance->settings->use_tracing) {
261                 lcbtrace_destroy(instance->settings->tracer);
262                 instance->settings->tracer = nullptr;
263                 instance->settings->use_tracing = false;
264             }
265             return LCB_SUCCESS;
266         }
267     } else {
268         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
269     }
270 }
271 
HANDLER(enable_errmap_handler)272 HANDLER(enable_errmap_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, use_errmap))}
273 
HANDLER(enable_op_metrics_handler)274 HANDLER(enable_op_metrics_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, op_metrics_enabled))}
275 
HANDLER(tracing_orphaned_queue_size_handler)276 HANDLER(tracing_orphaned_queue_size_handler){
277     RETURN_GET_SET(std::uint32_t, LCBT_SETTING(instance, tracer_orphaned_queue_size))}
278 
HANDLER(tracing_threshold_queue_size_handler)279 HANDLER(tracing_threshold_queue_size_handler){
280     RETURN_GET_SET(std::uint32_t, LCBT_SETTING(instance, tracer_threshold_queue_size))}
281 
HANDLER(config_poll_interval_handler)282 HANDLER(config_poll_interval_handler)
283 {
284     auto *user = reinterpret_cast<std::uint32_t *>(arg);
285     if (mode == LCB_CNTL_SET && *user > 0 && *user < LCB_CONFIG_POLL_INTERVAL_FLOOR) {
286         lcb_log(LOGARGS(instance, ERROR), "Interval for background poll is too low: %dus (min: %dus)", *user,
287                 LCB_CONFIG_POLL_INTERVAL_FLOOR);
288         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
289     }
290     lcb_STATUS rv = timeout_common(mode, instance, cmd, arg);
291     if (rv == LCB_SUCCESS && (mode == LCB_CNTL_SET || mode == CNTL__MODE_SETSTRING) &&
292         // Note: This might be nullptr during creation!
293         instance->bs_state) {
294         instance->bs_state->check_bgpoll();
295     }
296     return rv;
297 }
298 
HANDLER(get_kvb)299 HANDLER(get_kvb)
300 {
301     auto *vbi = reinterpret_cast<lcb_cntl_vbinfo_st *>(arg);
302 
303     if (mode != LCB_CNTL_GET) {
304         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
305     }
306     if (!LCBT_VBCONFIG(instance)) {
307         return LCB_ERR_NO_CONFIGURATION;
308     }
309     if (vbi->version != 0) {
310         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
311     }
312 
313     lcbvb_map_key(LCBT_VBCONFIG(instance), vbi->v.v0.key, vbi->v.v0.nkey, &vbi->v.v0.vbucket, &vbi->v.v0.server_index);
314     (void)cmd;
315     return LCB_SUCCESS;
316 }
317 
HANDLER(conninfo)318 HANDLER(conninfo)
319 {
320     const lcbio_SOCKET *sock;
321     auto *si = reinterpret_cast<lcb_cntl_server_st *>(arg);
322     const lcb_host_t *host;
323 
324     if (mode != LCB_CNTL_GET) {
325         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
326     }
327     if (si->version < 0 || si->version > 1) {
328         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
329     }
330 
331     if (cmd == LCB_CNTL_MEMDNODE_INFO) {
332         lcb::Server *server;
333         int ix = si->v.v0.index;
334 
335         if (ix < 0 || ix > (int)LCBT_NSERVERS(instance)) {
336             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
337         }
338         server = instance->get_server(ix);
339         if (!server) {
340             return LCB_ERR_NETWORK;
341         }
342         sock = server->connctx->sock;
343         if (si->version == 1 && sock) {
344             lcb::SessionInfo *info = lcb::SessionInfo::get(server->connctx->sock);
345             if (info) {
346                 si->v.v1.sasl_mech = info->get_mech().c_str();
347             }
348         }
349     } else if (cmd == LCB_CNTL_CONFIGNODE_INFO) {
350         sock = lcb::clconfig::http_get_conn(instance->confmon);
351     } else {
352         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
353     }
354 
355     if (!sock) {
356         return LCB_SUCCESS;
357     }
358     host = lcbio_get_host(sock);
359     si->v.v0.connected = 1;
360     si->v.v0.host = host->host;
361     si->v.v0.port = host->port;
362     if (instance->iotable->model == LCB_IOMODEL_EVENT) {
363         si->v.v0.sock.sockfd = sock->u.fd;
364     } else {
365         si->v.v0.sock.sockptr = sock->u.sd;
366     }
367     return LCB_SUCCESS;
368 }
369 
HANDLER(config_cache_loaded_handler)370 HANDLER(config_cache_loaded_handler)
371 {
372     if (mode != LCB_CNTL_GET) {
373         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
374     }
375     *(int *)arg = instance->cur_configinfo && instance->cur_configinfo->get_origin() == lcb::clconfig::CLCONFIG_FILE;
376     (void)cmd;
377     return LCB_SUCCESS;
378 }
379 
HANDLER(force_sasl_mech_handler)380 HANDLER(force_sasl_mech_handler)
381 {
382     if (mode == LCB_CNTL_SET) {
383         free(instance->settings->sasl_mech_force);
384         if (arg) {
385             const char *s = reinterpret_cast<const char *>(arg);
386             instance->settings->sasl_mech_force = lcb_strdup(s);
387             for (char *p = instance->settings->sasl_mech_force; *p != '\0'; p++) {
388                 if (*p == ',') {
389                     *p = ' ';
390                 }
391             }
392         }
393     } else {
394         *(char **)arg = instance->settings->sasl_mech_force;
395     }
396     (void)cmd;
397     return LCB_SUCCESS;
398 }
399 
HANDLER(max_redirects)400 HANDLER(max_redirects)
401 {
402     if (mode == LCB_CNTL_SET && *(int *)arg < -1) {
403         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
404     }
405     RETURN_GET_SET(int, LCBT_SETTING(instance, max_redir))
406 }
407 
HANDLER(logprocs_handler)408 HANDLER(logprocs_handler)
409 {
410     if (mode == LCB_CNTL_GET) {
411         *(const lcb_LOGGER **)arg = LCBT_SETTING(instance, logger);
412     } else if (mode == LCB_CNTL_SET) {
413         LCBT_SETTING(instance, logger) = (const lcb_LOGGER *)arg;
414     }
415     (void)cmd;
416     return LCB_SUCCESS;
417 }
418 
HANDLER(config_transport)419 HANDLER(config_transport)
420 {
421     auto *val = reinterpret_cast<lcb_BOOTSTRAP_TRANSPORT *>(arg);
422     if (mode == LCB_CNTL_SET) {
423         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
424     }
425     if (!instance->cur_configinfo) {
426         return LCB_ERR_NO_CONFIGURATION;
427     }
428 
429     switch (instance->cur_configinfo->get_origin()) {
430         case lcb::clconfig::CLCONFIG_HTTP:
431             *val = LCB_CONFIG_TRANSPORT_HTTP;
432             break;
433         case lcb::clconfig::CLCONFIG_CCCP:
434             *val = LCB_CONFIG_TRANSPORT_CCCP;
435             break;
436         default:
437             return LCB_ERR_NO_CONFIGURATION;
438     }
439     (void)cmd;
440     return LCB_SUCCESS;
441 }
442 
HANDLER(config_nodes)443 HANDLER(config_nodes)
444 {
445     const char *node_strs = reinterpret_cast<const char *>(arg);
446     lcb::clconfig::Provider *target;
447     lcb::Hostlist hostlist;
448     lcb_STATUS err;
449 
450     if (mode != LCB_CNTL_SET) {
451         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
452     }
453 
454     err = hostlist.add(node_strs, -1, cmd == LCB_CNTL_CONFIG_HTTP_NODES ? LCB_CONFIG_HTTP_PORT : LCB_CONFIG_MCD_PORT);
455 
456     if (err != LCB_SUCCESS) {
457         return err;
458     }
459 
460     if (cmd == LCB_CNTL_CONFIG_HTTP_NODES) {
461         target = instance->confmon->get_provider(lcb::clconfig::CLCONFIG_HTTP);
462     } else {
463         target = instance->confmon->get_provider(lcb::clconfig::CLCONFIG_CCCP);
464     }
465 
466     target->configure_nodes(hostlist);
467 
468     return LCB_SUCCESS;
469 }
470 
HANDLER(config_cache_handler)471 HANDLER(config_cache_handler)
472 {
473     using namespace lcb::clconfig;
474     Provider *provider;
475 
476     provider = instance->confmon->get_provider(lcb::clconfig::CLCONFIG_FILE);
477     if (mode == LCB_CNTL_SET) {
478         bool rv = file_set_filename(provider, reinterpret_cast<const char *>(arg), cmd == LCB_CNTL_CONFIGCACHE_RO);
479 
480         if (rv) {
481             instance->settings->bc_http_stream_time = LCB_MS2US(10000);
482             return LCB_SUCCESS;
483         }
484         return LCB_ERR_INVALID_ARGUMENT;
485     } else {
486         *(const char **)arg = file_get_filename(provider);
487         return LCB_SUCCESS;
488     }
489 }
490 
HANDLER(retrymode_handler)491 HANDLER(retrymode_handler)
492 {
493     auto *val = reinterpret_cast<std::uint32_t *>(arg);
494     std::uint32_t rmode = LCB_RETRYOPT_GETMODE(*val);
495 
496     if (rmode >= LCB_RETRY_ON_MAX) {
497         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
498     }
499     uint8_t *p = &(LCBT_SETTING(instance, retry)[rmode]);
500     if (mode == LCB_CNTL_SET) {
501         *p = LCB_RETRYOPT_GETPOLICY(*val);
502     } else {
503         *val = LCB_RETRYOPT_CREATE(rmode, *p);
504     }
505     (void)cmd;
506     return LCB_SUCCESS;
507 }
508 
HANDLER(allocfactory_handler)509 HANDLER(allocfactory_handler)
510 {
511     auto *cbw = reinterpret_cast<lcb_cntl_rdballocfactory *>(arg);
512     if (mode == LCB_CNTL_SET) {
513         LCBT_SETTING(instance, allocator_factory) = cbw->factory;
514     } else {
515         cbw->factory = LCBT_SETTING(instance, allocator_factory);
516     }
517     (void)cmd;
518     return LCB_SUCCESS;
519 }
520 
HANDLER(console_log_handler)521 HANDLER(console_log_handler)
522 {
523     std::uint32_t level;
524     struct lcb_CONSOLELOGGER *logger;
525     const lcb_LOGGER *procs;
526 
527     level = *(std::uint32_t *)arg;
528     if (mode != LCB_CNTL_SET) {
529         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
530     }
531 
532     procs = LCBT_SETTING(instance, logger);
533     if (!procs) {
534         procs = lcb_init_console_logger();
535     }
536     if (procs) {
537         /* don't override previous config */
538         return LCB_SUCCESS;
539     }
540 
541     logger = (struct lcb_CONSOLELOGGER *)lcb_console_logger;
542     level = LCB_LOG_ERROR - level;
543     logger->minlevel = (lcb_LOG_SEVERITY)level;
544     LCBT_SETTING(instance, logger) = &logger->base;
545     (void)cmd;
546     return LCB_SUCCESS;
547 }
548 
HANDLER(console_fp_handler)549 HANDLER(console_fp_handler)
550 {
551     auto *logger = (struct lcb_CONSOLELOGGER *)lcb_console_logger;
552     if (mode == LCB_CNTL_GET) {
553         *(FILE **)arg = logger->fp;
554     } else if (mode == LCB_CNTL_SET) {
555         logger->fp = *(FILE **)arg;
556     } else if (mode == CNTL__MODE_SETSTRING) {
557         FILE *fp = fopen(reinterpret_cast<const char *>(arg), "w");
558         if (!fp) {
559             return LCB_ERR_INVALID_ARGUMENT;
560         } else {
561             logger->fp = fp;
562         }
563     }
564     (void)cmd;
565     (void)instance;
566     return LCB_SUCCESS;
567 }
568 
HANDLER(reinit_spec_handler)569 HANDLER(reinit_spec_handler)
570 {
571     if (mode == LCB_CNTL_GET) {
572         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
573     }
574     (void)cmd;
575     return lcb_reinit(instance, reinterpret_cast<const char *>(arg));
576 }
577 
HANDLER(client_string_handler)578 HANDLER(client_string_handler)
579 {
580     if (mode == LCB_CNTL_SET) {
581         const char *val = reinterpret_cast<const char *>(arg);
582         free(LCBT_SETTING(instance, client_string));
583         LCBT_SETTING(instance, client_string) = nullptr;
584         if (val) {
585             char *p, *buf = lcb_strdup(val);
586             for (p = buf; *p != '\0'; p++) {
587                 switch (*p) {
588                     case '\n':
589                     case '\r':
590                         *p = ' ';
591                         break;
592                     default:
593                         break;
594                 }
595             }
596             LCBT_SETTING(instance, client_string) = buf;
597         }
598     } else {
599         *(const char **)arg = LCBT_SETTING(instance, client_string);
600     }
601     (void)cmd;
602     return LCB_SUCCESS;
603 }
604 
HANDLER(unsafe_optimize)605 HANDLER(unsafe_optimize)
606 {
607     lcb_STATUS rc;
608     int val = *(int *)arg;
609     if (mode != LCB_CNTL_SET) {
610         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
611     }
612     if (!val) {
613         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
614     }
615 
616 /* Simpler to just input strings here. */
617 #define APPLY_UNSAFE(k, v)                                                                                             \
618     if ((rc = lcb_cntl_string(instance, k, v)) != LCB_SUCCESS) {                                                       \
619         return rc;                                                                                                     \
620     }
621 
622     APPLY_UNSAFE("vbguess_persist", "1")
623     APPLY_UNSAFE("retry_policy", "topochange:none")
624     APPLY_UNSAFE("retry_policy", "sockerr:none")
625     APPLY_UNSAFE("retry_policy", "maperr:none")
626     APPLY_UNSAFE("retry_policy", "missingnode:none")
627     (void)cmd;
628     return LCB_SUCCESS;
629 }
630 
HANDLER(mutation_tokens_supported_handler)631 HANDLER(mutation_tokens_supported_handler)
632 {
633     size_t ii;
634     if (mode != LCB_CNTL_GET) {
635         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
636     }
637 
638     *(int *)arg = 0;
639 
640     for (ii = 0; ii < LCBT_NSERVERS(instance); ii++) {
641         if (instance->get_server(ii)->supports_mutation_tokens()) {
642             *(int *)arg = 1;
643             break;
644         }
645     }
646     (void)cmd;
647     return LCB_SUCCESS;
648 }
649 
HANDLER(n1ql_cache_clear_handler)650 HANDLER(n1ql_cache_clear_handler)
651 {
652     if (mode != LCB_CNTL_SET) {
653         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
654     }
655 
656     lcb_n1qlcache_clear(instance->n1ql_cache);
657 
658     (void)cmd;
659     (void)arg;
660     return LCB_SUCCESS;
661 }
662 
HANDLER(bucket_auth_handler)663 HANDLER(bucket_auth_handler)
664 {
665     const lcb_BUCKETCRED *cred;
666     (void)cmd;
667     if (mode == LCB_CNTL_SET) {
668         if (LCBT_SETTING(instance, keypath)) {
669             return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
670         }
671         /* Parse the bucket string... */
672         cred = (const lcb_BUCKETCRED *)arg;
673         return lcbauth_add_pass(instance->settings->auth, (*cred)[0], (*cred)[1], LCBAUTH_F_BUCKET);
674     } else if (mode == CNTL__MODE_SETSTRING) {
675         const char *ss = reinterpret_cast<const char *>(arg);
676         size_t sslen = strlen(ss);
677         Json::Value root;
678         if (!Json::Reader().parse(ss, ss + sslen, root)) {
679             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
680         }
681         if (!root.isArray() || root.size() != 2) {
682             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
683         }
684         return lcbauth_add_pass(instance->settings->auth, root[0].asString().c_str(), root[1].asString().c_str(),
685                                 LCBAUTH_F_BUCKET);
686     } else {
687         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
688     }
689 }
690 
HANDLER(metrics_handler)691 HANDLER(metrics_handler)
692 {
693     (void)cmd;
694     if (mode == LCB_CNTL_SET) {
695         int val = *(int *)arg;
696         if (!val) {
697             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
698         }
699         if (!instance->settings->metrics) {
700             instance->settings->metrics = lcb_metrics_new();
701         }
702         return LCB_SUCCESS;
703     } else if (mode == LCB_CNTL_GET) {
704         *(lcb_METRICS **)arg = instance->settings->metrics;
705         return LCB_SUCCESS;
706     } else {
707         return LCB_ERR_CONTROL_UNSUPPORTED_MODE;
708     }
709 }
710 
HANDLER(collections_handler)711 HANDLER(collections_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, use_collections))}
712 
HANDLER(allow_static_config_handler)713 HANDLER(allow_static_config_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, allow_static_config))}
714 
HANDLER(comp_min_size_handler)715 HANDLER(comp_min_size_handler)
716 {
717     if (mode == LCB_CNTL_SET && *reinterpret_cast<std::uint32_t *>(arg) < LCB_DEFAULT_COMPRESS_MIN_SIZE) {
718         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
719     }
720     RETURN_GET_SET(std::uint32_t, LCBT_SETTING(instance, compress_min_size))
721 }
722 
HANDLER(comp_min_ratio_handler)723 HANDLER(comp_min_ratio_handler)
724 {
725     if (mode == LCB_CNTL_SET) {
726         float val = *reinterpret_cast<float *>(arg);
727         if (val > 1 || val < 0) {
728             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
729         }
730     }
731     RETURN_GET_SET(float, LCBT_SETTING(instance, compress_min_ratio))
732 }
733 
HANDLER(network_handler)734 HANDLER(network_handler)
735 {
736     if (mode == LCB_CNTL_SET) {
737         const char *val = reinterpret_cast<const char *>(arg);
738         free(LCBT_SETTING(instance, network));
739         LCBT_SETTING(instance, network) = nullptr;
740         if (val) {
741             LCBT_SETTING(instance, network) = lcb_strdup(val);
742         }
743     } else {
744         *(const char **)arg = LCBT_SETTING(instance, network);
745     }
746     (void)cmd;
747     return LCB_SUCCESS;
748 }
749 
HANDLER(durable_write_handler)750 HANDLER(durable_write_handler){RETURN_GET_SET(int, LCBT_SETTING(instance, enable_durable_write))}
751 
HANDLER(unordered_execution_handler)752 HANDLER(unordered_execution_handler)
753 {
754     RETURN_GET_SET(int, LCBT_SETTING(instance, enable_unordered_execution))
755 }
756 
757 /* clang-format off */
758 static ctl_handler handlers[] = {
759     timeout_common,                       /* LCB_CNTL_OP_TIMEOUT */
760     timeout_common,                       /* LCB_CNTL_VIEW_TIMEOUT */
761     noop_handler,                         /* LCB_CNTL_RBUFSIZE */
762     noop_handler,                         /* LCB_CNTL_WBUFSIZE */
763     get_htype,                            /* LCB_CNTL_HANDLETYPE */
764     get_vbconfig,                         /* LCB_CNTL_VBCONFIG */
765     get_iops,                             /* LCB_CNTL_IOPS */
766     get_kvb,                              /* LCB_CNTL_VBMAP */
767     conninfo,                             /* LCB_CNTL_MEMDNODE_INFO */
768     conninfo,                             /* LCB_CNTL_CONFIGNODE_INFO */
769     nullptr,                                 /* deprecated LCB_CNTL_SYNCMODE (0x0a) */
770     ippolicy,                             /* LCB_CNTL_IP6POLICY */
771     confthresh,                           /* LCB_CNTL_CONFERRTHRESH */
772     timeout_common,                       /* LCB_CNTL_DURABILITY_INTERVAL */
773     timeout_common,                       /* LCB_CNTL_DURABILITY_TIMEOUT */
774     timeout_common,                       /* LCB_CNTL_HTTP_TIMEOUT */
775     lcb_iops_cntl_handler,                /* LCB_CNTL_IOPS_DEFAULT_TYPES */
776     lcb_iops_cntl_handler,                /* LCB_CNTL_IOPS_DLOPEN_DEBUG */
777     timeout_common,                       /* LCB_CNTL_CONFIGURATION_TIMEOUT */
778     noop_handler,                         /* LCB_CNTL_SKIP_CONFIGURATION_ERRORS_ON_CONNECT */
779     randomize_bootstrap_hosts_handler,    /* LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS */
780     config_cache_loaded_handler,          /* LCB_CNTL_CONFIG_CACHE_LOADED */
781     force_sasl_mech_handler,              /* LCB_CNTL_FORCE_SASL_MECH */
782     max_redirects,                        /* LCB_CNTL_MAX_REDIRECTS */
783     logprocs_handler,                     /* LCB_CNTL_LOGGER */
784     timeout_common,                       /* LCB_CNTL_CONFDELAY_THRESH */
785     config_transport,                     /* LCB_CNTL_CONFIG_TRANSPORT */
786     timeout_common,                       /* LCB_CNTL_CONFIG_NODE_TIMEOUT */
787     timeout_common,                       /* LCB_CNTL_HTCONFIG_IDLE_TIMEOUT */
788     config_nodes,                         /* LCB_CNTL_CONFIG_HTTP_NODES */
789     config_nodes,                         /* LCB_CNTL_CONFIG_CCCP_NODES */
790     get_changeset,                        /* LCB_CNTL_CHANGESET */
791     nullptr,                                 /* deprecated LCB_CNTL_CONFIG_ALL_NODES (0x20) */
792     config_cache_handler,                 /* LCB_CNTL_CONFIGCACHE */
793     ssl_mode_handler,                     /* LCB_CNTL_SSL_MODE */
794     ssl_certpath_handler,                 /* LCB_CNTL_SSL_CERT */
795     retrymode_handler,                    /* LCB_CNTL_RETRYMODE */
796     htconfig_urltype_handler,             /* LCB_CNTL_HTCONFIG_URLTYPE */
797     compmode_handler,                     /* LCB_CNTL_COMPRESSION_OPTS */
798     allocfactory_handler,                 /* LCB_CNTL_RDBALLOCFACTORY */
799     syncdtor_handler,                     /* LCB_CNTL_SYNCDESTROY */
800     console_log_handler,                  /* LCB_CNTL_CONLOGGER_LEVEL */
801     detailed_errcode_handler,             /* LCB_CNTL_DETAILED_ERRCODES */
802     reinit_spec_handler,                  /* LCB_CNTL_REINIT_CONNSTR */
803     timeout_common,                       /* LCB_CNTL_RETRY_INTERVAL */
804     nullptr,                                 /* deprecated LCB_CNTL_RETRY_BACKOFF (0x2D) */
805     http_poolsz_handler,                  /* LCB_CNTL_HTTP_POOLSIZE */
806     http_refresh_config_handler,          /* LCB_CNTL_HTTP_REFRESH_CONFIG_ON_ERROR */
807     bucketname_handler,                   /* LCB_CNTL_BUCKETNAME */
808     schedflush_handler,                   /* LCB_CNTL_SCHED_IMPLICIT_FLUSH */
809     vbguess_handler,                      /* LCB_CNTL_VBGUESS_PERSIST */
810     unsafe_optimize,                      /* LCB_CNTL_UNSAFE_OPTIMIZE */
811     fetch_mutation_tokens_handler,        /* LCB_CNTL_ENABLE_MUTATION_TOKENS */
812     nullptr,                                 /* deprecated LCB_CNTL_DURABILITY_MUTATION_TOKENS (0x35) */
813     config_cache_handler,                 /* LCB_CNTL_CONFIGCACHE_READONLY */
814     nmv_imm_retry_handler,                /* LCB_CNTL_RETRY_NMV_IMM */
815     mutation_tokens_supported_handler,    /* LCB_CNTL_MUTATION_TOKENS_SUPPORTED */
816     tcp_nodelay_handler,                  /* LCB_CNTL_TCP_NODELAY */
817     readj_ts_wait_handler,                /* LCB_CNTL_RESET_TIMEOUT_ON_WAIT */
818     console_fp_handler,                   /* LCB_CNTL_CONLOGGER_FP */
819     kv_hg_handler,                        /* LCB_CNTL_KVTIMINGS */
820     timeout_common,                       /* LCB_CNTL_QUERY_TIMEOUT */
821     n1ql_cache_clear_handler,             /* LCB_CNTL_N1QL_CLEARCACHE */
822     client_string_handler,                /* LCB_CNTL_CLIENT_STRING */
823     bucket_auth_handler,                  /* LCB_CNTL_BUCKET_CRED */
824     timeout_common,                       /* LCB_CNTL_RETRY_NMV_DELAY */
825     read_chunk_size_handler,              /* LCB_CNTL_READ_CHUNKSIZE */
826     nullptr,                                 /* deprecated LCB_CNTL_ENABLE_ERRMAP (0x43) */
827     select_bucket_handler,                /* LCB_CNTL_SELECT_BUCKET */
828     tcp_keepalive_handler,                /* LCB_CNTL_TCP_KEEPALIVE */
829     config_poll_interval_handler,         /* LCB_CNTL_CONFIG_POLL_INTERVAL */
830     nullptr,                                 /* deprecated LCB_CNTL_SEND_HELLO (0x47) */
831     buckettype_handler,                   /* LCB_CNTL_BUCKETTYPE */
832     metrics_handler,                      /* LCB_CNTL_METRICS */
833     collections_handler,                  /* LCB_CNTL_ENABLE_COLLECTIONS */
834     ssl_keypath_handler,                  /* LCB_CNTL_SSL_KEY */
835     log_redaction_handler,                /* LCB_CNTL_LOG_REDACTION */
836     ssl_truststorepath_handler,           /* LCB_CNTL_SSL_TRUSTSTORE */
837     enable_tracing_handler,               /* LCB_CNTL_ENABLE_TRACING */
838     timeout_common,                       /* LCB_CNTL_TRACING_ORPHANED_QUEUE_FLUSH_INTERVAL */
839     tracing_orphaned_queue_size_handler,  /* LCB_CNTL_TRACING_ORPHANED_QUEUE_SIZE */
840     timeout_common,                       /* LCB_CNTL_TRACING_THRESHOLD_QUEUE_FLUSH_INTERVAL */
841     tracing_threshold_queue_size_handler, /* LCB_CNTL_TRACING_THRESHOLD_QUEUE_SIZE */
842     timeout_common,                       /* LCB_CNTL_TRACING_THRESHOLD_KV */
843     timeout_common,                       /* LCB_CNTL_TRACING_THRESHOLD_QUERY */
844     timeout_common,                       /* LCB_CNTL_TRACING_THRESHOLD_VIEW */
845     timeout_common,                       /* LCB_CNTL_TRACING_THRESHOLD_SEARCH */
846     timeout_common,                       /* LCB_CNTL_TRACING_THRESHOLD_ANALYTICS */
847     comp_min_size_handler,                /* LCB_CNTL_COMPRESSION_MIN_SIZE */
848     comp_min_ratio_handler,               /* LCB_CNTL_COMPRESSION_MIN_RATIO */
849     vb_noremap_handler,                   /* LCB_CNTL_VB_NOREMAP */
850     network_handler,                      /* LCB_CNTL_NETWORK */
851     wait_for_config_handler,              /* LCB_CNTL_WAIT_FOR_CONFIG */
852     http_pooltmo_handler,                 /* LCB_CNTL_HTTP_POOL_TIMEOUT */
853     durable_write_handler,                /* LCB_CNTL_ENABLE_DURABLE_WRITE */
854     timeout_common,                       /* LCB_CNTL_PERSISTENCE_TIMEOUT_FLOOR */
855     allow_static_config_handler,          /* LCB_CNTL_ALLOW_STATIC_CONFIG */
856     timeout_common,                       /* LCB_CNTL_ANALYTICS_TIMEOUT */
857     unordered_execution_handler,          /* LCB_CNTL_ENABLE_UNORDERED_EXECUTION */
858     timeout_common,                       /* LCB_CNTL_SEARCH_TIMEOUT */
859     timeout_common,                       /* LCB_CNTL_QUERY_GRACE_PERIOD */
860     enable_errmap_handler,                /* LCB_CNTL_ENABLE_ERRMAP */
861     timeout_common,                       /* LCB_CNTL_OP_METRICS_FLUSH_INTERVAL */
862     enable_op_metrics_handler,            /* LCB_CNTL_ENABLE_OP_METRICS */
863     nullptr
864 };
865 /* clang-format on */
866 
867 /* Union used for conversion to/from string functions */
868 typedef union {
869     std::uint32_t u32;
870     std::size_t sz;
871     int i;
872     float f;
873     void *p;
874 } u_STRCONVERT;
875 
876 /* This handler should convert the string argument to the appropriate
877  * type needed for the actual control handler. It should return an error if the
878  * argument is invalid.
879  */
880 typedef lcb_STATUS (*ctl_str_cb)(const char *value, u_STRCONVERT *u);
881 
882 typedef struct {
883     const char *key;
884     int opcode;
885     ctl_str_cb converter;
886 } cntl_OPCODESTRS;
887 
convert_timevalue(const char *arg, u_STRCONVERT *u)888 static lcb_STATUS convert_timevalue(const char *arg, u_STRCONVERT *u)
889 {
890     try {
891         auto tmo_ns = lcb_parse_golang_duration(arg);
892         u->u32 = static_cast<std::uint32_t>(std::chrono::duration_cast<std::chrono::microseconds>(tmo_ns).count());
893     } catch (const lcb_duration_parse_error &) {
894         double dtmp;
895         char *end = nullptr;
896         errno = 0;
897         dtmp = std::strtod(arg, &end);
898         if (errno == ERANGE || end == arg) {
899             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
900         }
901         u->u32 = static_cast<std::uint32_t>(dtmp * (double)1000000);
902     }
903     return LCB_SUCCESS;
904 }
905 
convert_intbool(const char *arg, u_STRCONVERT *u)906 static lcb_STATUS convert_intbool(const char *arg, u_STRCONVERT *u)
907 {
908     if (!strcmp(arg, "true") || !strcmp(arg, "on")) {
909         u->i = 1;
910     } else if (!strcmp(arg, "false") || !strcmp(arg, "off")) {
911         u->i = 0;
912     } else {
913         char *end = nullptr;
914         errno = 0;
915         long val = std::strtol(arg, &end, 10);
916         if (errno == ERANGE || end == arg) {
917             return LCB_ERR_CONTROL_INVALID_ARGUMENT;
918         }
919         u->i = static_cast<int>(val);
920     }
921     return LCB_SUCCESS;
922 }
923 
convert_passthru(const char *arg, u_STRCONVERT *u)924 static lcb_STATUS convert_passthru(const char *arg, u_STRCONVERT *u)
925 {
926     u->p = (void *)arg;
927     return LCB_SUCCESS;
928 }
929 
convert_int(const char *arg, u_STRCONVERT *u)930 static lcb_STATUS convert_int(const char *arg, u_STRCONVERT *u)
931 {
932     char *end = nullptr;
933     errno = 0;
934     long val = std::strtol(arg, &end, 10);
935     if (errno == ERANGE || end == arg) {
936         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
937     }
938     u->i = static_cast<int>(val);
939     return LCB_SUCCESS;
940 }
941 
convert_u32(const char *arg, u_STRCONVERT *u)942 static lcb_STATUS convert_u32(const char *arg, u_STRCONVERT *u)
943 {
944     char *end = nullptr;
945     errno = 0;
946     unsigned long val = std::strtoul(arg, &end, 10);
947     if (errno == ERANGE || end == arg) {
948         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
949     }
950     u->u32 = static_cast<std::uint32_t>(val);
951     return LCB_SUCCESS;
952 }
953 
convert_float(const char *arg, u_STRCONVERT *u)954 static lcb_STATUS convert_float(const char *arg, u_STRCONVERT *u)
955 {
956     char *end = nullptr;
957     errno = 0;
958     double val = std::strtod(arg, &end);
959     if (errno == ERANGE || end == arg) {
960         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
961     }
962     u->f = static_cast<float>(val);
963     return LCB_SUCCESS;
964 }
965 
convert_SIZE(const char *arg, u_STRCONVERT *u)966 static lcb_STATUS convert_SIZE(const char *arg, u_STRCONVERT *u)
967 {
968     char *end = nullptr;
969     errno = 0;
970     unsigned long long val = std::strtoll(arg, &end, 10);
971     if (errno == ERANGE || end == arg) {
972         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
973     }
974     u->sz = val;
975     return LCB_SUCCESS;
976 }
977 
convert_compression(const char *arg, u_STRCONVERT *u)978 static lcb_STATUS convert_compression(const char *arg, u_STRCONVERT *u)
979 {
980     static const STR_u32MAP optmap[] = {
981         {"on", LCB_COMPRESS_INOUT},
982         {"off", LCB_COMPRESS_NONE},
983         {"inflate_only", LCB_COMPRESS_IN},
984         {"deflate_only", LCB_COMPRESS_OUT},
985         {"force", LCB_COMPRESS_INOUT | LCB_COMPRESS_FORCE},
986         {nullptr},
987     };
988     DO_CONVERT_STR2NUM(arg, optmap, u->i)
989     return LCB_SUCCESS;
990 }
991 
convert_retrymode(const char *arg, u_STRCONVERT *u)992 static lcb_STATUS convert_retrymode(const char *arg, u_STRCONVERT *u)
993 {
994     static const STR_u32MAP modemap[] = {
995         {"topochange", LCB_RETRY_ON_TOPOCHANGE},
996         {"sockerr", LCB_RETRY_ON_SOCKERR},
997         {"maperr", LCB_RETRY_ON_VBMAPERR},
998         {"missingnode", LCB_RETRY_ON_MISSINGNODE},
999         {nullptr},
1000     };
1001     static const STR_u32MAP polmap[] = {
1002         {"all", LCB_RETRY_CMDS_ALL},
1003         {"get", LCB_RETRY_CMDS_GET},
1004         {"safe", LCB_RETRY_CMDS_SAFE},
1005         {"none", LCB_RETRY_CMDS_NONE},
1006         {nullptr},
1007     };
1008 
1009     std::uint32_t polval, modeval;
1010     const char *polstr = strchr(arg, ':');
1011     if (!polstr) {
1012         return LCB_ERR_CONTROL_INVALID_ARGUMENT;
1013     }
1014     polstr++;
1015     DO_CONVERT_STR2NUM(arg, modemap, modeval)
1016     DO_CONVERT_STR2NUM(polstr, polmap, polval)
1017     u->u32 = LCB_RETRYOPT_CREATE(modeval, polval);
1018     return LCB_SUCCESS;
1019 }
1020 
convert_ipv6(const char *arg, u_STRCONVERT *u)1021 static lcb_STATUS convert_ipv6(const char *arg, u_STRCONVERT *u)
1022 {
1023     static const STR_u32MAP optmap[] = {
1024         {"disabled", LCB_IPV6_DISABLED},
1025         {"only", LCB_IPV6_ONLY},
1026         {"allow", LCB_IPV6_ALLOW},
1027         {nullptr},
1028     };
1029     DO_CONVERT_STR2NUM(arg, optmap, u->i);
1030     return LCB_SUCCESS;
1031 }
1032 
1033 static cntl_OPCODESTRS stropcode_map[] = {
1034     {"operation_timeout", LCB_CNTL_OP_TIMEOUT, convert_timevalue},
1035     {"timeout", LCB_CNTL_OP_TIMEOUT, convert_timevalue},
1036     {"views_timeout", LCB_CNTL_VIEW_TIMEOUT, convert_timevalue},
1037     {"query_timeout", LCB_CNTL_QUERY_TIMEOUT, convert_timevalue},
1038     {"durability_timeout", LCB_CNTL_DURABILITY_TIMEOUT, convert_timevalue},
1039     {"durability_interval", LCB_CNTL_DURABILITY_INTERVAL, convert_timevalue},
1040     {"http_timeout", LCB_CNTL_HTTP_TIMEOUT, convert_timevalue},
1041     {"randomize_nodes", LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS, convert_intbool},
1042     {"sasl_mech_force", LCB_CNTL_FORCE_SASL_MECH, convert_passthru},
1043     {"error_thresh_count", LCB_CNTL_CONFERRTHRESH, convert_SIZE},
1044     {"error_thresh_delay", LCB_CNTL_CONFDELAY_THRESH, convert_timevalue},
1045     {"config_total_timeout", LCB_CNTL_CONFIGURATION_TIMEOUT, convert_timevalue},
1046     {"config_node_timeout", LCB_CNTL_CONFIG_NODE_TIMEOUT, convert_timevalue},
1047     {"compression", LCB_CNTL_COMPRESSION_OPTS, convert_compression},
1048     {"console_log_level", LCB_CNTL_CONLOGGER_LEVEL, convert_u32},
1049     {"config_cache", LCB_CNTL_CONFIGCACHE, convert_passthru},
1050     {"config_cache_ro", LCB_CNTL_CONFIGCACHE_RO, convert_passthru},
1051     {"detailed_errcodes", LCB_CNTL_DETAILED_ERRCODES, convert_intbool},
1052     {"retry_policy", LCB_CNTL_RETRYMODE, convert_retrymode},
1053     {"http_urlmode", LCB_CNTL_HTCONFIG_URLTYPE, convert_int},
1054     {"sync_dtor", LCB_CNTL_SYNCDESTROY, convert_intbool},
1055     {"_reinit_connstr", LCB_CNTL_REINIT_CONNSTR},
1056     {"", -1}, /* deprecated "retry_backoff" */
1057     {"retry_interval", LCB_CNTL_RETRY_INTERVAL, convert_timevalue},
1058     {"http_poolsize", LCB_CNTL_HTTP_POOLSIZE, convert_SIZE},
1059     {"vbguess_persist", LCB_CNTL_VBGUESS_PERSIST, convert_intbool},
1060     {"unsafe_optimize", LCB_CNTL_UNSAFE_OPTIMIZE, convert_intbool},
1061     {"enable_mutation_tokens", LCB_CNTL_ENABLE_MUTATION_TOKENS, convert_intbool},
1062     {"", -1, nullptr}, /* removed dur_mutation_tokens */
1063     {"retry_nmv_imm", LCB_CNTL_RETRY_NMV_IMM, convert_intbool},
1064     {"tcp_nodelay", LCB_CNTL_TCP_NODELAY, convert_intbool},
1065     {"readj_ts_wait", LCB_CNTL_RESET_TIMEOUT_ON_WAIT, convert_intbool},
1066     {"console_log_file", LCB_CNTL_CONLOGGER_FP, nullptr},
1067     {"client_string", LCB_CNTL_CLIENT_STRING, convert_passthru},
1068     {"retry_nmv_delay", LCB_CNTL_RETRY_NMV_INTERVAL, convert_timevalue},
1069     {"bucket_cred", LCB_CNTL_BUCKET_CRED, nullptr},
1070     {"read_chunk_size", LCB_CNTL_READ_CHUNKSIZE, convert_u32},
1071     {"select_bucket", LCB_CNTL_SELECT_BUCKET, convert_intbool},
1072     {"tcp_keepalive", LCB_CNTL_TCP_KEEPALIVE, convert_intbool},
1073     {"config_poll_interval", LCB_CNTL_CONFIG_POLL_INTERVAL, convert_timevalue},
1074     {"ipv6", LCB_CNTL_IP6POLICY, convert_ipv6},
1075     {"metrics", LCB_CNTL_METRICS, convert_intbool},
1076     {"log_redaction", LCB_CNTL_LOG_REDACTION, convert_intbool},
1077     {"enable_tracing", LCB_CNTL_ENABLE_TRACING, convert_intbool},
1078     {"tracing_orphaned_queue_flush_interval", LCB_CNTL_TRACING_ORPHANED_QUEUE_FLUSH_INTERVAL, convert_timevalue},
1079     {"tracing_orphaned_queue_size", LCB_CNTL_TRACING_ORPHANED_QUEUE_SIZE, convert_u32},
1080     {"tracing_threshold_queue_flush_interval", LCB_CNTL_TRACING_THRESHOLD_QUEUE_FLUSH_INTERVAL, convert_timevalue},
1081     {"tracing_threshold_queue_size", LCB_CNTL_TRACING_THRESHOLD_QUEUE_SIZE, convert_u32},
1082     {"tracing_threshold_kv", LCB_CNTL_TRACING_THRESHOLD_KV, convert_timevalue},
1083     {"tracing_threshold_query", LCB_CNTL_TRACING_THRESHOLD_QUERY, convert_timevalue},
1084     {"tracing_threshold_view", LCB_CNTL_TRACING_THRESHOLD_VIEW, convert_timevalue},
1085     {"tracing_threshold_search", LCB_CNTL_TRACING_THRESHOLD_SEARCH, convert_timevalue},
1086     {"tracing_threshold_analytics", LCB_CNTL_TRACING_THRESHOLD_ANALYTICS, convert_timevalue},
1087     {"compression_min_size", LCB_CNTL_COMPRESSION_MIN_SIZE, convert_u32},
1088     {"compression_min_ratio", LCB_CNTL_COMPRESSION_MIN_RATIO, convert_float},
1089     {"vb_noremap", LCB_CNTL_VB_NOREMAP, convert_intbool},
1090     {"network", LCB_CNTL_NETWORK, convert_passthru},
1091     {"wait_for_config", LCB_CNTL_WAIT_FOR_CONFIG, convert_intbool},
1092     {"http_pool_timeout", LCB_CNTL_HTTP_POOL_TIMEOUT, convert_timevalue},
1093     {"enable_collections", LCB_CNTL_ENABLE_COLLECTIONS, convert_intbool},
1094     {"enable_durable_write", LCB_CNTL_ENABLE_DURABLE_WRITE, convert_intbool},
1095     {"persistence_timeout_floor", LCB_CNTL_PERSISTENCE_TIMEOUT_FLOOR, convert_timevalue},
1096     {"allow_static_config", LCB_CNTL_ALLOW_STATIC_CONFIG, convert_intbool},
1097     {"analytics_timeout", LCB_CNTL_ANALYTICS_TIMEOUT, convert_timevalue},
1098     {"enable_unordered_execution", LCB_CNTL_ENABLE_UNORDERED_EXECUTION, convert_intbool},
1099     {"search_timeout", LCB_CNTL_SEARCH_TIMEOUT, convert_timevalue},
1100     {"query_grace_period", LCB_CNTL_QUERY_GRACE_PERIOD, convert_timevalue},
1101     {"enable_errmap", LCB_CNTL_ENABLE_ERRMAP, convert_intbool},
1102     {"operation_metrics_flush_interval", LCB_CNTL_OP_METRICS_FLUSH_INTERVAL, convert_timevalue},
1103     {"enable_operation_metrics", LCB_CNTL_ENABLE_OP_METRICS, convert_intbool},
1104     {nullptr, -1}};
1105 
1106 #define CNTL_NUM_HANDLERS (sizeof(handlers) / sizeof(handlers[0]))
1107 
wrap_return(lcb_INSTANCE *instance, lcb_STATUS retval)1108 static lcb_STATUS wrap_return(lcb_INSTANCE *instance, lcb_STATUS retval)
1109 {
1110     if (retval == LCB_SUCCESS) {
1111         return retval;
1112     }
1113     if (instance && LCBT_SETTING(instance, detailed_neterr) == 0) {
1114         switch (retval) {
1115             case LCB_ERR_CONTROL_UNKNOWN_CODE:
1116             case LCB_ERR_CONTROL_UNSUPPORTED_MODE:
1117                 return LCB_ERR_UNSUPPORTED_OPERATION;
1118             case LCB_ERR_CONTROL_INVALID_ARGUMENT:
1119                 return LCB_ERR_INVALID_ARGUMENT;
1120             default:
1121                 return retval;
1122         }
1123     } else {
1124         return retval;
1125     }
1126 }
1127 
1128 LIBCOUCHBASE_API
lcb_cntl(lcb_INSTANCE *instance, int mode, int cmd, void *arg)1129 lcb_STATUS lcb_cntl(lcb_INSTANCE *instance, int mode, int cmd, void *arg)
1130 {
1131     ctl_handler handler;
1132     if (cmd >= (int)CNTL_NUM_HANDLERS || cmd < 0) {
1133         return wrap_return(instance, LCB_ERR_CONTROL_UNKNOWN_CODE);
1134     }
1135 
1136     handler = handlers[cmd];
1137 
1138     if (!handler) {
1139         return wrap_return(instance, LCB_ERR_CONTROL_UNKNOWN_CODE);
1140     }
1141 
1142     return wrap_return(instance, handler(mode, instance, cmd, arg));
1143 }
1144 
1145 LIBCOUCHBASE_API
lcb_cntl_string(lcb_INSTANCE *instance, const char *key, const char *value)1146 lcb_STATUS lcb_cntl_string(lcb_INSTANCE *instance, const char *key, const char *value)
1147 {
1148     cntl_OPCODESTRS *cur;
1149     u_STRCONVERT u;
1150     lcb_STATUS err;
1151 
1152     for (cur = stropcode_map; cur->key; cur++) {
1153         if (!strcmp(cur->key, key)) {
1154             if (cur->opcode < 0) {
1155                 return LCB_ERR_CONTROL_UNKNOWN_CODE;
1156             }
1157             if (cur->converter) {
1158                 err = cur->converter(value, &u);
1159                 if (err != LCB_SUCCESS) {
1160                     return err;
1161                 }
1162                 if (cur->converter == convert_passthru) {
1163                     return lcb_cntl(instance, LCB_CNTL_SET, cur->opcode, u.p);
1164                 } else {
1165                     return lcb_cntl(instance, LCB_CNTL_SET, cur->opcode, &u);
1166                 }
1167             }
1168 
1169             return lcb_cntl(instance, CNTL__MODE_SETSTRING, cur->opcode, (void *)value);
1170         }
1171     }
1172     return wrap_return(instance, LCB_ERR_UNSUPPORTED_OPERATION);
1173 }
1174 
1175 LIBCOUCHBASE_API
lcb_cntl_exists(int ctl)1176 int lcb_cntl_exists(int ctl)
1177 {
1178     if (ctl >= (int)CNTL_NUM_HANDLERS || ctl < 0) {
1179         return 0;
1180     }
1181     return handlers[ctl] != nullptr;
1182 }
1183 
1184 LIBCOUCHBASE_API
lcb_cntl_setu32(lcb_INSTANCE *instance, int cmd, std::uint32_t arg)1185 lcb_STATUS lcb_cntl_setu32(lcb_INSTANCE *instance, int cmd, std::uint32_t arg)
1186 {
1187     return lcb_cntl(instance, LCB_CNTL_SET, cmd, &arg);
1188 }
1189 
1190 LIBCOUCHBASE_API
lcb_cntl_getu32(lcb_INSTANCE *instance, int cmd)1191 std::uint32_t lcb_cntl_getu32(lcb_INSTANCE *instance, int cmd)
1192 {
1193     std::uint32_t ret = 0;
1194     lcb_cntl(instance, LCB_CNTL_GET, cmd, &ret);
1195     return ret;
1196 }
1197