1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2010-2013 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 #include "internal.h"
18 #include "auth-priv.h"
19 #include "connspec.h"
20 #include "logging.h"
21 #include "hostlist.h"
22 #include "rnd.h"
23 #include "http/http.h"
24 #include "bucketconfig/clconfig.h"
25 #include <lcbio/iotable.h>
26 #include <lcbio/ssl.h>
27 #define LOGARGS(obj,lvl) (obj)->settings, "instance", LCB_LOG_##lvl, __FILE__, __LINE__
28
29 using namespace lcb;
30
31 LIBCOUCHBASE_API
lcb_get_version(lcb_uint32_t * version)32 const char *lcb_get_version(lcb_uint32_t *version)
33 {
34 if (version != NULL) {
35 *version = (lcb_uint32_t)LCB_VERSION;
36 }
37
38 return LCB_VERSION_STRING;
39 }
40
41 const lcb_U32 lcb_version_g = LCB_VERSION;
42
43 LIBCOUCHBASE_API
lcb_set_cookie(lcb_t instance,const void * cookie)44 void lcb_set_cookie(lcb_t instance, const void *cookie)
45 {
46 instance->cookie = cookie;
47 }
48
49 LIBCOUCHBASE_API
lcb_get_cookie(lcb_t instance)50 const void *lcb_get_cookie(lcb_t instance)
51 {
52 return instance->cookie;
53 }
54
55 LIBCOUCHBASE_API
56 void
lcb_set_auth(lcb_t instance,lcb_AUTHENTICATOR * auth)57 lcb_set_auth(lcb_t instance, lcb_AUTHENTICATOR *auth)
58 {
59 if (LCBT_SETTING(instance, keypath)) {
60 lcb_log(LOGARGS(instance, WARN),
61 "Custom authenticator ignored when SSL client certificate authentication in use");
62 return;
63 }
64 /* First increase refcount in case they are the same object(!) */
65 lcbauth_ref(auth);
66 lcbauth_unref(instance->settings->auth);
67 instance->settings->auth = auth;
68 }
69
70 void
add_bs_host(const char * host,int port,unsigned bstype)71 lcb_st::add_bs_host(const char *host, int port, unsigned bstype)
72 {
73 const char *tname = NULL;
74 lcb::Hostlist* target;
75 if (bstype == LCB_CONFIG_TRANSPORT_CCCP) {
76 tname = "CCCP";
77 target = mc_nodes;
78 } else {
79 tname = "HTTP";
80 target = ht_nodes;
81 }
82 bool ipv6 = strchr(host, ':') != NULL;
83 lcb_log(LOGARGS(this, DEBUG), "Adding host " LCB_LOG_SPEC("%s%s%s:%d") " to initial %s bootstrap list",
84 this->settings->log_redaction ? LCB_LOG_SD_OTAG : "",
85 ipv6 ? "[" : "", host, ipv6 ? "]" : "", port,
86 this->settings->log_redaction ? LCB_LOG_SD_CTAG : "", tname);
87 target->add(host, port);
88 }
89
90 void
add_bs_host(const lcb::Spechost & host,int defl_http,int defl_cccp)91 lcb_st::add_bs_host(const lcb::Spechost& host, int defl_http, int defl_cccp)
92 {
93 if (host.isTypeless()) {
94 add_bs_host(host.hostname.c_str(), defl_http, LCB_CONFIG_TRANSPORT_HTTP);
95 add_bs_host(host.hostname.c_str(), defl_cccp, LCB_CONFIG_TRANSPORT_CCCP);
96 return;
97 } else {
98 add_bs_host(host.hostname.c_str(), host.port,
99 host.isAnyHttp()
100 ? LCB_CONFIG_TRANSPORT_HTTP : LCB_CONFIG_TRANSPORT_CCCP);
101 }
102 }
103
104 void
populate_nodes(const Connspec & spec)105 lcb_st::populate_nodes(const Connspec& spec)
106 {
107 int has_ssl = settings->sslopts & LCB_SSL_ENABLED;
108 int defl_http, defl_cccp;
109
110 if (spec.default_port() == LCB_CONFIG_MCCOMPAT_PORT) {
111 defl_http = -1;
112 defl_cccp = LCB_CONFIG_MCCOMPAT_PORT;
113
114 } else if (has_ssl) {
115 defl_http = LCB_CONFIG_HTTP_SSL_PORT;
116 defl_cccp = LCB_CONFIG_MCD_SSL_PORT;
117 } else {
118 defl_http = LCB_CONFIG_HTTP_PORT;
119 defl_cccp = LCB_CONFIG_MCD_PORT;
120 }
121
122 for (size_t ii = 0; ii < spec.hosts().size(); ++ii) {
123 const Spechost &dh = spec.hosts()[ii];
124 add_bs_host(dh, defl_http, defl_cccp);
125 }
126 lcb_log(LOGARGS(this, TRACE), "Bootstrap hosts loaded (cccp:%d, http:%d)", (int)mc_nodes->size(),
127 (int)ht_nodes->size());
128 }
129
130 lcb_error_t
process_dns_srv(Connspec & spec)131 lcb_st::process_dns_srv(Connspec& spec)
132 {
133 if (!spec.can_dnssrv()) {
134 return LCB_SUCCESS;
135 }
136 if (spec.hosts().empty()) {
137 lcb_log(LOGARGS(this, ERR), "Cannot use DNS SRV without a hostname");
138 return spec.is_explicit_dnssrv() ? LCB_EINVAL : LCB_SUCCESS;
139 }
140
141 const Spechost& host = spec.hosts().front();
142 lcb_error_t rc = LCB_ERROR;
143 Hostlist* hl = dnssrv_getbslist(host.hostname.c_str(), spec.sslopts() & LCB_SSL_ENABLED, rc);
144
145 if (hl == NULL) {
146 lcb_log(LOGARGS(this, INFO), "DNS SRV lookup failed: %s. Ignore this if not relying on DNS SRV records", lcb_strerror(this, rc));
147 if (spec.is_explicit_dnssrv()) {
148 return rc;
149 } else {
150 return LCB_SUCCESS;
151 }
152 }
153
154 spec.clear_hosts();
155 for (size_t ii = 0; ii < hl->size(); ++ii) {
156 const lcb_host_t& src = (*hl)[ii];
157 Spechost sh;
158 sh.hostname = src.host;
159 sh.port = std::atoi(src.port);
160 sh.type = spec.default_port();
161 spec.add_host(sh);
162 bool ipv6 = sh.hostname.find(':') != std::string::npos;
163 lcb_log(LOGARGS(this, INFO), "Found host %s%s%s:%d via DNS SRV", ipv6 ? "[" : "", sh.hostname.c_str(),
164 ipv6 ? "]" : "", (int)sh.port);
165 }
166 delete hl;
167
168 return LCB_SUCCESS;
169 }
170
171 static lcb_error_t
init_providers(lcb_t obj,const Connspec & spec)172 init_providers(lcb_t obj, const Connspec &spec)
173 {
174 using namespace lcb::clconfig;
175 Provider *http, *cccp, *mcraw;
176 http = obj->confmon->get_provider(CLCONFIG_HTTP);
177 cccp = obj->confmon->get_provider(CLCONFIG_CCCP);
178 mcraw = obj->confmon->get_provider(CLCONFIG_MCRAW);
179
180 if (spec.default_port() == LCB_CONFIG_MCCOMPAT_PORT) {
181 obj->confmon->set_active(CLCONFIG_MCRAW, true);
182 mcraw->configure_nodes(*obj->mc_nodes);
183 return LCB_SUCCESS;
184 }
185
186 bool cccp_found = spec.is_bs_cccp();
187 bool http_found = spec.is_bs_http();
188 bool cccp_enabled = true, http_enabled = true;
189
190 if (cccp_found || http_found || spec.is_bs_file()) {
191 http_enabled = http_found;
192 cccp_enabled = cccp_found;
193
194 }
195
196 if (lcb_getenv_boolean("LCB_NO_CCCP")) {
197 cccp_enabled = false;
198 }
199 if (lcb_getenv_boolean("LCB_NO_HTTP")) {
200 http_enabled = false;
201 }
202 if (spec.is_bs_file()) {
203 cccp_found = false;
204 http_found = false;
205 }
206
207 if (cccp_enabled == 0 && http_enabled == 0) {
208 if (spec.is_bs_file()) {
209 /* If the 'file_only' provider is set, just assume something else
210 * will provide us with the config, and forget about it. */
211 Provider *prov = obj->confmon->get_provider(CLCONFIG_FILE);
212 if (prov && prov->enabled) {
213 return LCB_SUCCESS;
214 }
215 }
216 if (obj->type == LCB_TYPE_CLUSTER) {
217 /* Cluster-level connection always falls back to static config */
218 Provider *cladmin;
219 cladmin = obj->confmon->get_provider(CLCONFIG_CLADMIN);
220 cladmin->enable();
221 cladmin->configure_nodes(*obj->ht_nodes);
222 } else {
223 return LCB_BAD_ENVIRONMENT;
224 }
225 }
226
227 if (http_enabled) {
228 http->enable();
229 http->configure_nodes(*obj->ht_nodes);
230 } else {
231 obj->confmon->set_active(CLCONFIG_HTTP, false);
232 }
233
234 if (cccp_enabled && obj->type != LCB_TYPE_CLUSTER) {
235 cccp->enable(obj);
236 cccp->configure_nodes(*obj->mc_nodes);
237 } else {
238 obj->confmon->set_active(CLCONFIG_CCCP, false);
239 }
240 return LCB_SUCCESS;
241 }
242
243 static lcb_error_t
setup_ssl(lcb_t obj,const Connspec & params)244 setup_ssl(lcb_t obj, const Connspec& params)
245 {
246 char optbuf[4096];
247 int env_policy = -1;
248 lcb_settings *settings = obj->settings;
249 lcb_error_t err = LCB_SUCCESS;
250
251 if (lcb_getenv_nonempty("LCB_SSL_CACERT", optbuf, sizeof optbuf)) {
252 lcb_log(LOGARGS(obj, INFO), "SSL CA certificate %s specified on environment", optbuf);
253 settings->certpath = strdup(optbuf);
254 }
255
256 if (lcb_getenv_nonempty("LCB_SSL_KEY", optbuf, sizeof optbuf)) {
257 lcb_log(LOGARGS(obj, INFO), "SSL key %s specified on environment", optbuf);
258 settings->keypath = strdup(optbuf);
259 }
260
261 if (lcb_getenv_nonempty("LCB_SSL_MODE", optbuf, sizeof optbuf)) {
262 if (sscanf(optbuf, "%d", &env_policy) != 1) {
263 lcb_log(LOGARGS(obj, ERR), "Invalid value for environment LCB_SSL. (%s)", optbuf);
264 return LCB_BAD_ENVIRONMENT;
265 } else {
266 lcb_log(LOGARGS(obj, INFO), "SSL modified from environment. Policy is 0x%x", env_policy);
267 settings->sslopts = env_policy;
268 }
269 }
270
271 if (settings->truststorepath == NULL && !params.truststorepath().empty()) {
272 settings->truststorepath = strdup(params.truststorepath().c_str());
273 }
274
275 if (settings->certpath == NULL && !params.certpath().empty()) {
276 settings->certpath = strdup(params.certpath().c_str());
277 }
278
279 if (settings->keypath == NULL && !params.keypath().empty()) {
280 settings->keypath = strdup(params.keypath().c_str());
281 }
282
283 if (env_policy == -1) {
284 settings->sslopts = params.sslopts();
285 }
286
287 if (settings->sslopts & LCB_SSL_ENABLED) {
288 if (! (settings->sslopts & LCB_SSL_NOGLOBALINIT)) {
289 lcbio_ssl_global_init();
290 } else {
291 lcb_log(LOGARGS(obj, INFO), "ssl=no_global_init. Not initializing openssl globals");
292 }
293 if (settings->keypath && !settings->certpath) {
294 lcb_log(LOGARGS(obj, ERR), "SSL key have to be specified with certificate");
295 return LCB_EINVAL;
296 }
297 settings->ssl_ctx =
298 lcbio_ssl_new(settings->truststorepath, settings->certpath, settings->keypath,
299 settings->sslopts & LCB_SSL_NOVERIFY, &err, settings);
300 if (!settings->ssl_ctx) {
301 return err;
302 }
303 } else {
304 // keypath might be used to flag, that library is using SSL authentication
305 // To avoid skipping other authentication mechanisms, cleanup the keypath.
306 free(settings->keypath);
307 settings->keypath = NULL;
308 }
309 return LCB_SUCCESS;
310 }
311
312 static lcb_error_t
apply_spec_options(lcb_t obj,const Connspec & params)313 apply_spec_options(lcb_t obj, const Connspec& params)
314 {
315 lcb_error_t err;
316 Connspec::Options::const_iterator ii = params.options().begin();
317 for (; ii != params.options().end(); ++ii) {
318 lcb_log(LOGARGS(obj, DEBUG), "Applying initial cntl %s=%s",
319 ii->first.c_str(), ii->second.c_str());
320
321 err = lcb_cntl_string(obj, ii->first.c_str(), ii->second.c_str());
322 if (err != LCB_SUCCESS) {
323 return err;
324 }
325 }
326 return LCB_SUCCESS;
327 }
328
329 static lcb_error_t
apply_env_options(lcb_t obj)330 apply_env_options(lcb_t obj)
331 {
332 Connspec tmpspec;
333 const char *options = getenv("LCB_OPTIONS");
334
335 if (!options) {
336 return LCB_SUCCESS;
337 }
338
339 std::string tmp("couchbase://?");
340 tmp.append(options);
341 if (tmpspec.parse(tmp.c_str()) != LCB_SUCCESS) {
342 return LCB_BAD_ENVIRONMENT;
343 }
344 return apply_spec_options(obj, tmpspec);
345 }
346
347 lcb_error_t
lcb_init_providers2(lcb_t obj,const struct lcb_create_st2 * options)348 lcb_init_providers2(lcb_t obj, const struct lcb_create_st2 *options)
349 {
350 Connspec params;
351 lcb_error_t err;
352 struct lcb_create_st cropts;
353 cropts.version = 2;
354 cropts.v.v2 = *options;
355 err = params.load(cropts);
356 if (err == LCB_SUCCESS) {
357 err = init_providers(obj, params);
358 }
359 return err;
360 }
361
362 lcb_error_t
lcb_reinit3(lcb_t obj,const char * connstr)363 lcb_reinit3(lcb_t obj, const char *connstr)
364 {
365 Connspec params;
366 lcb_error_t err;
367 const char *errmsg = NULL;
368 err = params.parse(connstr, &errmsg);
369
370 if (err != LCB_SUCCESS) {
371 lcb_log(LOGARGS(obj, ERROR), "Couldn't reinit: %s", errmsg);
372 }
373
374 if (params.sslopts() != LCBT_SETTING(obj, sslopts) ||
375 !params.certpath().empty()) {
376 lcb_log(LOGARGS(obj, WARN), "Ignoring SSL reinit options");
377 }
378
379 /* apply the options */
380 err = apply_spec_options(obj, params);
381 if (err != LCB_SUCCESS) {
382 goto GT_DONE;
383 }
384 obj->populate_nodes(params);
385 err = init_providers(obj, params);
386 if (err != LCB_SUCCESS) {
387 goto GT_DONE;
388 }
389
390 GT_DONE:
391 return err;
392 }
393
394 LIBCOUCHBASE_API
lcb_create(lcb_t * instance,const struct lcb_create_st * options)395 lcb_error_t lcb_create(lcb_t *instance,
396 const struct lcb_create_st *options)
397 {
398 Connspec spec;
399 struct lcb_io_opt_st *io_priv = NULL;
400 lcb_type_t type = LCB_TYPE_BUCKET;
401 lcb_t obj = NULL;
402 lcb_error_t err;
403 lcb_settings *settings;
404
405 #if !defined(COMPILER_SUPPORTS_CXX11) || (defined(_MSC_VER) && _MSC_VER < 1600)
406 lcb_rnd_global_init();
407 #endif
408
409 if (options) {
410 io_priv = options->v.v0.io;
411 if (options->version > 0) {
412 type = options->v.v1.type;
413 }
414 err = spec.load(*options);
415 } else {
416 const char *errmsg;
417 err = spec.parse("couchbase://", &errmsg);
418 }
419 if (err != LCB_SUCCESS) {
420 goto GT_DONE;
421 }
422
423 if ((obj = (lcb_t)calloc(1, sizeof(*obj))) == NULL) {
424 err = LCB_CLIENT_ENOMEM;
425 goto GT_DONE;
426 }
427 obj->crypto = new std::map<std::string, lcbcrypto_PROVIDER*>();
428 if (!(settings = lcb_settings_new())) {
429 err = LCB_CLIENT_ENOMEM;
430 goto GT_DONE;
431 }
432
433 /* initialize the settings */
434 obj->type = type;
435 obj->settings = settings;
436 obj->settings->conntype = type;
437 obj->settings->ipv6 = spec.ipv6_policy();
438
439 settings->bucket = strdup(spec.bucket().c_str());
440
441 if (!spec.username().empty()) {
442 settings->auth->set_mode(LCBAUTH_MODE_RBAC);
443 err = settings->auth->add(spec.username(), spec.password(),
444 LCBAUTH_F_CLUSTER);
445 } else {
446 settings->auth->set_mode(LCBAUTH_MODE_CLASSIC);
447 err = settings->auth->add(spec.bucket(), spec.password(),
448 LCBAUTH_F_BUCKET);
449 }
450 if (err != LCB_SUCCESS) {
451 goto GT_DONE;
452 }
453
454 settings->logger = spec.logger();
455 if (settings->logger == NULL) {
456 settings->logger = lcb_init_console_logger();
457 }
458 settings->iid = lcb_next_rand32();
459 if (spec.loglevel()) {
460 lcb_U32 val = spec.loglevel();
461 lcb_cntl(obj, LCB_CNTL_SET, LCB_CNTL_CONLOGGER_LEVEL, &val);
462 }
463 settings->log_redaction = spec.logredact();
464 if (settings->log_redaction) {
465 lcb_log(LOGARGS(obj, INFO), "Logging redaction enabled. Logs have reduced identifying information. Diagnosis "
466 "and support of issues may be challenging or not possible in this configuration");
467 }
468
469 lcb_log(LOGARGS(obj, INFO), "Version=%s, Changeset=%s", lcb_get_version(NULL), LCB_VERSION_CHANGESET);
470 lcb_log(LOGARGS(obj, INFO), "Effective connection string: " LCB_LOG_SPEC("%s") ". Bucket=" LCB_LOG_SPEC("%s"),
471 settings->log_redaction ? LCB_LOG_SD_OTAG : "", spec.connstr().c_str(), settings->log_redaction ? LCB_LOG_SD_CTAG : "",
472 settings->log_redaction ? LCB_LOG_MD_OTAG : "", settings->bucket, settings->log_redaction ? LCB_LOG_MD_CTAG : "");
473
474 if (io_priv == NULL) {
475 lcb_io_opt_t ops;
476 if ((err = lcb_create_io_ops(&ops, NULL)) != LCB_SUCCESS) {
477 goto GT_DONE;
478 }
479 io_priv = ops;
480 LCB_IOPS_BASEFLD(io_priv, need_cleanup) = 1;
481 }
482
483 obj->cmdq.cqdata = obj;
484 obj->iotable = lcbio_table_new(io_priv);
485 obj->memd_sockpool = new io::Pool(settings, obj->iotable);
486 obj->http_sockpool = new io::Pool(settings, obj->iotable);
487
488 {
489 // Needs its own scope because there are prior GOTOs
490 io::Pool::Options pool_opts;
491 pool_opts.maxidle = 1;
492 pool_opts.tmoidle = LCB_MS2US(10000); // 10 seconds
493 obj->memd_sockpool->set_options(pool_opts);
494 obj->http_sockpool->set_options(pool_opts);
495 }
496
497 obj->confmon = new clconfig::Confmon(settings, obj->iotable, obj);
498 obj->ht_nodes = new Hostlist();
499 obj->mc_nodes = new Hostlist();
500 obj->retryq = new RetryQueue(&obj->cmdq, obj->iotable, obj->settings);
501 obj->n1ql_cache = lcb_n1qlcache_create();
502 lcb_initialize_packet_handlers(obj);
503 lcb_aspend_init(&obj->pendops);
504
505 if ((err = setup_ssl(obj, spec)) != LCB_SUCCESS) {
506 goto GT_DONE;
507 }
508
509 if ((err = apply_spec_options(obj, spec)) != LCB_SUCCESS) {
510 goto GT_DONE;
511 }
512 if ((err = apply_env_options(obj)) != LCB_SUCCESS) {
513 goto GT_DONE;
514 }
515
516 if ((err = obj->process_dns_srv(spec)) != LCB_SUCCESS) {
517 goto GT_DONE;
518 }
519
520 obj->populate_nodes(spec);
521 if ((err = init_providers(obj, spec)) != LCB_SUCCESS) {
522 goto GT_DONE;
523 }
524 #ifdef LCB_TRACING
525 if (settings->use_tracing) {
526 settings->tracer = lcbtrace_new(obj, LCBTRACE_F_THRESHOLD);
527 }
528 #endif
529
530 obj->last_error = err;
531 GT_DONE:
532 if (err != LCB_SUCCESS && obj) {
533 lcb_destroy(obj);
534 *instance = NULL;
535 } else {
536 *instance = obj;
537 }
538 return err;
539 }
540
541 LIBCOUCHBASE_API
lcb_is_redacting_logs(lcb_t instance)542 int lcb_is_redacting_logs(lcb_t instance)
543 {
544 return instance && instance->settings && instance->settings->log_redaction;
545 }
546
547 typedef struct {
548 lcbio_pTABLE table;
549 lcbio_pTIMER timer;
550 int stopped;
551 } SYNCDTOR;
552
553 static void
sync_dtor_cb(void * arg)554 sync_dtor_cb(void *arg)
555 {
556 SYNCDTOR *sd = (SYNCDTOR*)arg;
557 if (sd->table->refcount == 2) {
558 lcbio_timer_destroy(sd->timer);
559 IOT_STOP(sd->table);
560 sd->stopped = 1;
561 }
562 }
563
564 extern "C" {
565 void lcbdur_destroy(void*);
566 }
567
do_pool_shutdown(io::Pool * pool)568 static void do_pool_shutdown(io::Pool *pool) { pool->shutdown(); }
569
570 LIBCOUCHBASE_API
lcb_destroy(lcb_t instance)571 void lcb_destroy(lcb_t instance)
572 {
573 #define DESTROY(fn,fld) if(instance->fld){fn(instance->fld);instance->fld=NULL;}
574
575 lcb_ASPEND *po = &instance->pendops;
576 lcb_ASPEND_SETTYPE::iterator it;
577 lcb_ASPEND_SETTYPE *pendq;
578
579 if (instance->cur_configinfo) {
580 instance->cur_configinfo->decref();
581 instance->cur_configinfo = NULL;
582 }
583 instance->cmdq.config = NULL;
584 DESTROY(delete, bs_state);
585 DESTROY(delete, ht_nodes);
586 DESTROY(delete, mc_nodes);
587
588 if ((pendq = po->items[LCB_PENDTYPE_TIMER])) {
589 for (it = pendq->begin(); it != pendq->end(); ++it) {
590 lcb__timer_destroy_nowarn(instance, (lcb_timer_t)*it);
591 }
592 }
593
594 if ((pendq = po->items[LCB_PENDTYPE_DURABILITY])) {
595 std::vector<void*> dsets(pendq->begin(), pendq->end());
596 for (size_t ii = 0; ii < dsets.size(); ++ii) {
597 lcbdur_destroy(dsets[ii]);
598 }
599 pendq->clear();
600 }
601
602 for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ++ii) {
603 instance->get_server(ii)->close();
604 }
605
606 if ((pendq = po->items[LCB_PENDTYPE_HTTP])) {
607 for (it = pendq->begin(); it != pendq->end(); ++it) {
608 http::Request *htreq = reinterpret_cast<http::Request*>(*it);
609 htreq->block_callback();
610 htreq->finish(LCB_ERROR);
611 }
612 }
613
614 DESTROY(delete, retryq);
615 DESTROY(delete, confmon);
616 DESTROY(do_pool_shutdown, memd_sockpool);
617 DESTROY(do_pool_shutdown, http_sockpool);
618 DESTROY(lcb_vbguess_destroy, vbguess);
619 DESTROY(lcb_n1qlcache_destroy, n1ql_cache);
620
621 if (instance->cmdq.pipelines) {
622 unsigned ii;
623 for (ii = 0; ii < instance->cmdq.npipelines; ii++) {
624 lcb::Server *server = static_cast<lcb::Server*>(instance->cmdq.pipelines[ii]);
625 if (server) {
626 server->instance = NULL;
627 }
628 }
629 }
630 mcreq_queue_cleanup(&instance->cmdq);
631 lcb_aspend_cleanup(po);
632
633 #ifdef LCB_TRACING
634 if (instance->settings && instance->settings->tracer) {
635 lcbtrace_destroy(instance->settings->tracer);
636 instance->settings->tracer = NULL;
637 }
638 #endif
639
640 if (instance->iotable && instance->iotable->refcount > 1 &&
641 instance->settings && instance->settings->syncdtor) {
642 /* create an async object */
643 SYNCDTOR sd;
644 sd.table = instance->iotable;
645 sd.timer = lcbio_timer_new(instance->iotable, &sd, sync_dtor_cb);
646 sd.stopped = 0;
647 lcbio_async_signal(sd.timer);
648 lcb_log(LOGARGS(instance, WARN), "Running event loop to drain any pending I/O events");
649 do {
650 IOT_START(instance->iotable);
651 } while (!sd.stopped);
652 }
653
654 DESTROY(lcbio_table_unref, iotable);
655 DESTROY(lcb_settings_unref, settings);
656 DESTROY(lcb_histogram_destroy, kv_timings);
657 if (instance->scratch) {
658 delete instance->scratch;
659 instance->scratch = NULL;
660 }
661
662 for (std::map< std::string, lcbcrypto_PROVIDER * >::iterator ii = instance->crypto->begin();
663 ii != instance->crypto->end(); ++ii) {
664 lcbcrypto_unref(ii->second);
665 }
666 delete instance->crypto;
667 instance->crypto = NULL;
668
669 delete[] instance->dcpinfo;
670 memset(instance, 0xff, sizeof(*instance));
671 free(instance);
672 #undef DESTROY
673 }
674
675 static void
destroy_cb(void * arg)676 destroy_cb(void *arg)
677 {
678 lcb_t instance = (lcb_t)arg;
679 lcbio_timer_destroy(instance->dtor_timer);
680 lcb_destroy(instance);
681 }
682
683 LIBCOUCHBASE_API
684 void
lcb_destroy_async(lcb_t instance,const void * arg)685 lcb_destroy_async(lcb_t instance, const void *arg)
686 {
687 instance->dtor_timer = lcbio_timer_new(instance->iotable, instance, destroy_cb);
688 instance->settings->dtorarg = (void *)arg;
689 lcbio_async_signal(instance->dtor_timer);
690 }
691
692 lcb::Server *
find_server(const lcb_host_t & host) const693 lcb_st::find_server(const lcb_host_t& host) const
694 {
695 unsigned ii;
696 for (ii = 0; ii < cmdq.npipelines; ii++) {
697 lcb::Server *server = static_cast<lcb::Server*>(cmdq.pipelines[ii]);
698 if (server && lcb_host_equals(&server->get_host(), &host)) {
699 return server;
700 }
701 }
702 return NULL;
703 }
704
705 LIBCOUCHBASE_API
lcb_connect(lcb_t instance)706 lcb_error_t lcb_connect(lcb_t instance)
707 {
708 lcb_error_t err = instance->bootstrap(BS_REFRESH_INITIAL);
709 if (err == LCB_SUCCESS) {
710 SYNCMODE_INTERCEPT(instance);
711 } else {
712 return err;
713 }
714 }
715
716 LIBCOUCHBASE_API
lcb_mem_alloc(lcb_size_t size)717 void *lcb_mem_alloc(lcb_size_t size)
718 {
719 return malloc(size);
720 }
721
722 LIBCOUCHBASE_API
lcb_mem_free(void * ptr)723 void lcb_mem_free(void *ptr)
724 {
725 free(ptr);
726 }
727
728 LCB_INTERNAL_API
lcb_run_loop(lcb_t instance)729 void lcb_run_loop(lcb_t instance)
730 {
731 IOT_START(instance->iotable);
732 }
733
734 LCB_INTERNAL_API
lcb_stop_loop(lcb_t instance)735 void lcb_stop_loop(lcb_t instance)
736 {
737 IOT_STOP(instance->iotable);
738 }
739
740 void
lcb_aspend_init(lcb_ASPEND * ops)741 lcb_aspend_init(lcb_ASPEND *ops)
742 {
743 unsigned ii;
744 for (ii = 0; ii < LCB_PENDTYPE_MAX; ++ii) {
745 ops->items[ii] = new lcb_ASPEND_SETTYPE();
746 }
747 ops->count = 0;
748 }
749
750 void
lcb_aspend_add(lcb_ASPEND * ops,lcb_ASPENDTYPE type,const void * item)751 lcb_aspend_add(lcb_ASPEND *ops, lcb_ASPENDTYPE type, const void *item)
752 {
753 ops->count++;
754 if (type == LCB_PENDTYPE_COUNTER) {
755 return;
756 }
757 ops->items[type]->insert(const_cast<void*>(item));
758 }
759
760 void
lcb_aspend_del(lcb_ASPEND * ops,lcb_ASPENDTYPE type,const void * item)761 lcb_aspend_del(lcb_ASPEND *ops, lcb_ASPENDTYPE type, const void *item)
762 {
763 if (type == LCB_PENDTYPE_COUNTER) {
764 ops->count--;
765 return;
766 }
767 if (ops->items[type]->erase(const_cast<void*>(item)) != 0) {
768 ops->count--;
769 }
770 }
771
772 void
lcb_aspend_cleanup(lcb_ASPEND * ops)773 lcb_aspend_cleanup(lcb_ASPEND *ops)
774 {
775 unsigned ii;
776 for (ii = 0; ii < LCB_PENDTYPE_MAX; ii++) {
777 delete ops->items[ii];
778 }
779 }
780
781 LIBCOUCHBASE_API
782 void
lcb_sched_enter(lcb_t instance)783 lcb_sched_enter(lcb_t instance)
784 {
785 mcreq_sched_enter(&instance->cmdq);
786 }
787 LIBCOUCHBASE_API
788 void
lcb_sched_leave(lcb_t instance)789 lcb_sched_leave(lcb_t instance)
790 {
791 mcreq_sched_leave(&instance->cmdq, LCBT_SETTING(instance, sched_implicit_flush));
792 }
793 LIBCOUCHBASE_API
794 void
lcb_sched_fail(lcb_t instance)795 lcb_sched_fail(lcb_t instance)
796 {
797 mcreq_sched_fail(&instance->cmdq);
798 }
799
800 LIBCOUCHBASE_API
801 int
lcb_supports_feature(int n)802 lcb_supports_feature(int n)
803 {
804 if (n == LCB_SUPPORTS_TRACING) {
805 #ifdef LCB_TRACING
806 return 1;
807 #else
808 return 0;
809 #endif
810 }
811 if (n == LCB_SUPPORTS_SNAPPY) {
812 return 1;
813 }
814 if (n == LCB_SUPPORTS_SSL) {
815 return lcbio_ssl_supported();
816 } else {
817 return 0;
818 }
819 }
820
821
lcb_loop_ref(lcb_t instance)822 LCB_INTERNAL_API void lcb_loop_ref(lcb_t instance) {
823 lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL);
824 }
lcb_loop_unref(lcb_t instance)825 LCB_INTERNAL_API void lcb_loop_unref(lcb_t instance) {
826 lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL);
827 lcb_maybe_breakout(instance);
828 }
829
830 LIBCOUCHBASE_API
lcb_enable_timings(lcb_t instance)831 lcb_error_t lcb_enable_timings(lcb_t instance)
832 {
833 if (instance->kv_timings != NULL) {
834 return LCB_KEY_EEXISTS;
835 }
836 instance->kv_timings = lcb_histogram_create();
837 return instance->kv_timings == NULL ? LCB_CLIENT_ENOMEM : LCB_SUCCESS;
838 }
839
840 LIBCOUCHBASE_API
lcb_disable_timings(lcb_t instance)841 lcb_error_t lcb_disable_timings(lcb_t instance)
842 {
843 if (instance->kv_timings == NULL) {
844 return LCB_KEY_ENOENT;
845 }
846 lcb_histogram_destroy(instance->kv_timings);
847 instance->kv_timings = NULL;
848 return LCB_SUCCESS;
849 }
850
851 typedef struct {
852 lcb_t instance;
853 const void *real_cookie;
854 lcb_timings_callback real_cb;
855 } timings_wrapper;
856
857 static void
timings_wrapper_callback(const void * cookie,lcb_timeunit_t unit,lcb_U32 start,lcb_U32 end,lcb_U32 val,lcb_U32 max)858 timings_wrapper_callback(const void *cookie, lcb_timeunit_t unit, lcb_U32 start,
859 lcb_U32 end, lcb_U32 val, lcb_U32 max)
860 {
861 const timings_wrapper *wrap = (const timings_wrapper*)cookie;
862 wrap->real_cb(wrap->instance, wrap->real_cookie, unit, start, end, val, max);
863 }
864
865 LIBCOUCHBASE_API
866 lcb_error_t
lcb_get_timings(lcb_t instance,const void * cookie,lcb_timings_callback cb)867 lcb_get_timings(lcb_t instance, const void *cookie, lcb_timings_callback cb)
868 {
869 timings_wrapper wrap;
870 wrap.instance = instance;
871 wrap.real_cookie = cookie;
872 wrap.real_cb = cb;
873
874 if (!instance->kv_timings) {
875 return LCB_KEY_ENOENT;
876 }
877 lcb_histogram_read(instance->kv_timings, &wrap, timings_wrapper_callback);
878 return LCB_SUCCESS;
879 }
880
881 LIBCOUCHBASE_API
lcb_strerror(lcb_t instance,lcb_error_t error)882 const char *lcb_strerror(lcb_t instance, lcb_error_t error)
883 {
884 #define X(c, v, t, s) if (error == c) { return s; }
885 LCB_XERR(X)
886 #undef X
887
888 (void)instance;
889 return "Unknown error";
890 }
891
892 LCB_INTERNAL_API
lcb_strerror_short(lcb_error_t error)893 const char *lcb_strerror_short(lcb_error_t error)
894 {
895 #define X(c, v, t, s) if (error == c) { return #c " (" #v ")"; }
896 LCB_XERR(X)
897 #undef X
898 return "<FIXME: Not an LCB error>";
899 }
900
901 LCB_INTERNAL_API
lcb_strerror_long(lcb_error_t error)902 const char *lcb_strerror_long(lcb_error_t error)
903 {
904 #define X(c, v, t, s) if (error == c) { return #c " (" #v "): " s; }
905 LCB_XERR(X)
906 #undef X
907 return "<FIXME: Not an LCB error>";
908 }
909
910 LIBCOUCHBASE_API
lcb_get_errtype(lcb_error_t err)911 int lcb_get_errtype(lcb_error_t err)
912 {
913 #define X(c, v, t, s) if (err == c) { return t; }
914 LCB_XERR(X)
915 #undef X
916 return -1;
917 }
918