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