1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017-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 "http/http.h"
20 #include "auth-priv.h"
21 
22 #include "capi/cmd_diag.hh"
23 #include "capi/cmd_ping.hh"
24 #include "capi/cmd_http.hh"
25 
lcb_respping_status(const lcb_RESPPING *resp)26 LIBCOUCHBASE_API lcb_STATUS lcb_respping_status(const lcb_RESPPING *resp)
27 {
28     return resp->ctx.rc;
29 }
30 
lcb_respping_cookie(const lcb_RESPPING *resp, void **cookie)31 LIBCOUCHBASE_API lcb_STATUS lcb_respping_cookie(const lcb_RESPPING *resp, void **cookie)
32 {
33     *cookie = resp->cookie;
34     return LCB_SUCCESS;
35 }
36 
lcb_respping_value(const lcb_RESPPING *resp, const char **json, size_t *json_len)37 LIBCOUCHBASE_API lcb_STATUS lcb_respping_value(const lcb_RESPPING *resp, const char **json, size_t *json_len)
38 {
39     *json = resp->json;
40     *json_len = resp->njson;
41     return LCB_SUCCESS;
42 }
43 
lcb_respping_report_id(const lcb_RESPPING *resp, const char **report_id, size_t *report_id_len)44 LIBCOUCHBASE_API lcb_STATUS lcb_respping_report_id(const lcb_RESPPING *resp, const char **report_id,
45                                                    size_t *report_id_len)
46 {
47     *report_id = resp->id.data();
48     *report_id_len = resp->id.size();
49     return LCB_SUCCESS;
50 }
51 
lcb_respping_result_size(const lcb_RESPPING *resp)52 LIBCOUCHBASE_API size_t lcb_respping_result_size(const lcb_RESPPING *resp)
53 {
54     return resp->nservices;
55 }
56 
lcb_respping_result_status(const lcb_RESPPING *resp, size_t index)57 LIBCOUCHBASE_API lcb_PING_STATUS lcb_respping_result_status(const lcb_RESPPING *resp, size_t index)
58 {
59     if (index >= resp->nservices) {
60         return LCB_PING_STATUS_INVALID;
61     }
62     return resp->services[index].status;
63 }
64 
lcb_respping_result_id(const lcb_RESPPING *resp, size_t index, const char **endpoint_id, size_t *endpoint_id_len)65 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_id(const lcb_RESPPING *resp, size_t index, const char **endpoint_id,
66                                                    size_t *endpoint_id_len)
67 {
68     if (index >= resp->nservices) {
69         return LCB_ERR_OPTIONS_CONFLICT;
70     }
71     *endpoint_id = resp->services[index].id;
72     *endpoint_id_len = strlen(*endpoint_id);
73     return LCB_SUCCESS;
74 }
75 
lcb_respping_result_service(const lcb_RESPPING *resp, size_t index, lcb_PING_SERVICE *type)76 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_service(const lcb_RESPPING *resp, size_t index, lcb_PING_SERVICE *type)
77 {
78     if (index >= resp->nservices) {
79         return LCB_ERR_OPTIONS_CONFLICT;
80     }
81     *type = resp->services[index].type;
82     return LCB_SUCCESS;
83 }
84 
lcb_respping_result_remote(const lcb_RESPPING *resp, size_t index, const char **address, size_t *address_len)85 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_remote(const lcb_RESPPING *resp, size_t index, const char **address,
86                                                        size_t *address_len)
87 {
88     if (index >= resp->nservices) {
89         return LCB_ERR_OPTIONS_CONFLICT;
90     }
91     *address = resp->services[index].server;
92     *address_len = *address ? strlen(*address) : 0;
93     return LCB_SUCCESS;
94 }
95 
lcb_respping_result_local(const lcb_RESPPING *resp, size_t index, const char **address, size_t *address_len)96 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_local(const lcb_RESPPING *resp, size_t index, const char **address,
97                                                       size_t *address_len)
98 {
99     if (index >= resp->nservices) {
100         return LCB_ERR_OPTIONS_CONFLICT;
101     }
102     *address = resp->services[index].local;
103     *address_len = strlen(*address);
104     return LCB_SUCCESS;
105 }
106 
lcb_respping_result_latency(const lcb_RESPPING *resp, size_t index, uint64_t *latency)107 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_latency(const lcb_RESPPING *resp, size_t index, uint64_t *latency)
108 {
109     if (index >= resp->nservices) {
110         return LCB_ERR_OPTIONS_CONFLICT;
111     }
112     *latency = resp->services[index].latency;
113     return LCB_SUCCESS;
114 }
115 
lcb_respping_result_namespace(const lcb_RESPPING *resp, size_t index, const char **name, size_t *name_len)116 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_namespace(const lcb_RESPPING *resp, size_t index, const char **name,
117                                                           size_t *name_len)
118 {
119     if (index >= resp->nservices) {
120         return LCB_ERR_OPTIONS_CONFLICT;
121     }
122     *name = resp->services[index].scope;
123     *name_len = (*name == nullptr) ? 0 : strlen(*name);
124     return LCB_SUCCESS;
125 }
126 
lcb_respping_result_scope(const lcb_RESPPING *resp, size_t index, const char **name, size_t *name_len)127 LIBCOUCHBASE_API lcb_STATUS lcb_respping_result_scope(const lcb_RESPPING *resp, size_t index, const char **name,
128                                                       size_t *name_len)
129 {
130     return lcb_respping_result_namespace(resp, index, name, name_len);
131 }
132 
lcb_cmdping_create(lcb_CMDPING **cmd)133 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_create(lcb_CMDPING **cmd)
134 {
135     *cmd = (lcb_CMDPING *)calloc(1, sizeof(lcb_CMDPING));
136     return LCB_SUCCESS;
137 }
138 
lcb_cmdping_destroy(lcb_CMDPING *cmd)139 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_destroy(lcb_CMDPING *cmd)
140 {
141     free(cmd);
142     return LCB_SUCCESS;
143 }
144 
lcb_cmdping_parent_span(lcb_CMDPING *cmd, lcbtrace_SPAN *span)145 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_parent_span(lcb_CMDPING *cmd, lcbtrace_SPAN *span)
146 {
147     cmd->pspan = span;
148     return LCB_SUCCESS;
149 }
150 
lcb_cmdping_report_id(lcb_CMDPING *cmd, const char *report_id, size_t report_id_len)151 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_report_id(lcb_CMDPING *cmd, const char *report_id, size_t report_id_len)
152 {
153     cmd->id = report_id;
154     cmd->nid = report_id_len;
155     return LCB_SUCCESS;
156 }
157 
lcb_cmdping_timeout(lcb_CMDPING *cmd, uint32_t timeout)158 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_timeout(lcb_CMDPING *cmd, uint32_t timeout)
159 {
160     cmd->timeout = timeout;
161     return LCB_SUCCESS;
162 }
163 
lcb_cmdping_all(lcb_CMDPING *cmd)164 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_all(lcb_CMDPING *cmd)
165 {
166     cmd->services =
167         LCB_PINGSVC_F_KV | LCB_PINGSVC_F_N1QL | LCB_PINGSVC_F_VIEWS | LCB_PINGSVC_F_FTS | LCB_PINGSVC_F_ANALYTICS;
168     return LCB_SUCCESS;
169 }
170 
lcb_cmdping_kv(lcb_CMDPING *cmd, int enable)171 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_kv(lcb_CMDPING *cmd, int enable)
172 {
173     if (enable) {
174         cmd->services |= LCB_PINGSVC_F_KV;
175     } else {
176         cmd->services &= ~LCB_PINGSVC_F_KV;
177     }
178     return LCB_SUCCESS;
179 }
180 
lcb_cmdping_query(lcb_CMDPING *cmd, int enable)181 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_query(lcb_CMDPING *cmd, int enable)
182 {
183     if (enable) {
184         cmd->services |= LCB_PINGSVC_F_N1QL;
185     } else {
186         cmd->services &= ~LCB_PINGSVC_F_N1QL;
187     }
188     return LCB_SUCCESS;
189 }
190 
lcb_cmdping_views(lcb_CMDPING *cmd, int enable)191 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_views(lcb_CMDPING *cmd, int enable)
192 {
193     if (enable) {
194         cmd->services |= LCB_PINGSVC_F_VIEWS;
195     } else {
196         cmd->services &= ~LCB_PINGSVC_F_VIEWS;
197     }
198     return LCB_SUCCESS;
199 }
200 
lcb_cmdping_search(lcb_CMDPING *cmd, int enable)201 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_search(lcb_CMDPING *cmd, int enable)
202 {
203     if (enable) {
204         cmd->services |= LCB_PINGSVC_F_FTS;
205     } else {
206         cmd->services &= ~LCB_PINGSVC_F_FTS;
207     }
208     return LCB_SUCCESS;
209 }
210 
lcb_cmdping_analytics(lcb_CMDPING *cmd, int enable)211 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_analytics(lcb_CMDPING *cmd, int enable)
212 {
213     if (enable) {
214         cmd->services |= LCB_PINGSVC_F_ANALYTICS;
215     } else {
216         cmd->services &= ~LCB_PINGSVC_F_ANALYTICS;
217     }
218     return LCB_SUCCESS;
219 }
220 
lcb_cmdping_no_metrics(lcb_CMDPING *cmd, int enable)221 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_no_metrics(lcb_CMDPING *cmd, int enable)
222 {
223     if (enable) {
224         cmd->options |= LCB_PINGOPT_F_NOMETRICS;
225     } else {
226         cmd->options &= ~LCB_PINGOPT_F_NOMETRICS;
227     }
228     return LCB_SUCCESS;
229 }
230 
lcb_cmdping_encode_json(lcb_CMDPING *cmd, int enable, int pretty, int with_details)231 LIBCOUCHBASE_API lcb_STATUS lcb_cmdping_encode_json(lcb_CMDPING *cmd, int enable, int pretty, int with_details)
232 {
233     if (enable) {
234         uint32_t flags = LCB_PINGOPT_F_JSON;
235         if (pretty) {
236             flags |= LCB_PINGOPT_F_JSONPRETTY;
237         }
238         if (with_details) {
239             flags |= LCB_PINGOPT_F_JSONDETAILS;
240         }
241         cmd->options |= flags;
242     } else {
243         cmd->options &= ~(LCB_PINGOPT_F_JSON | LCB_PINGOPT_F_JSONPRETTY | LCB_PINGOPT_F_JSONDETAILS);
244     }
245     return LCB_SUCCESS;
246 }
247 
248 static void refcnt_dtor_ping(mc_PACKET *);
249 static void handle_ping(mc_PIPELINE *, mc_PACKET *, lcb_CALLBACK_TYPE /* cbtype */, lcb_STATUS, const void *);
250 
251 static mc_REQDATAPROCS ping_procs = {handle_ping, refcnt_dtor_ping};
252 
253 struct PingCookie : mc_REQDATAEX {
254     int remaining;
255     int options;
256     std::list<lcb_PINGSVC> responses;
257     std::string id;
258 
PingCookiePingCookie259     PingCookie(void *cookie_, int _options)
260         : mc_REQDATAEX(cookie_, ping_procs, gethrtime()), remaining(0), options(_options)
261     {
262     }
263 
~PingCookiePingCookie264     ~PingCookie()
265     {
266         for (auto &response : responses) {
267             if (response.server) {
268                 free((void *)response.server);
269                 response.server = nullptr;
270                 free((void *)response.local);
271                 response.local = nullptr;
272                 free((void *)response.id);
273                 response.id = nullptr;
274             }
275         }
276     }
277 
needMetricsPingCookie278     bool needMetrics() const
279     {
280         return (options & LCB_PINGOPT_F_NOMETRICS) == 0;
281     }
282 
needJSONPingCookie283     bool needJSON() const
284     {
285         return options & LCB_PINGOPT_F_JSON;
286     }
287 
needDetailsPingCookie288     bool needDetails() const
289     {
290         return options & LCB_PINGOPT_F_JSONDETAILS;
291     }
292 
needPrettyPingCookie293     bool needPretty() const
294     {
295         return options & LCB_PINGOPT_F_JSONPRETTY;
296     }
297 };
298 
refcnt_dtor_ping(mc_PACKET *pkt)299 static void refcnt_dtor_ping(mc_PACKET *pkt)
300 {
301     auto *ck = static_cast<PingCookie *>(pkt->u_rdata.exdata);
302     if (!--ck->remaining) {
303         delete ck;
304     }
305 }
306 
svc_to_string(const lcb_PING_SERVICE type)307 static const char *svc_to_string(const lcb_PING_SERVICE type)
308 {
309     switch (type) {
310         case LCB_PING_SERVICE_KV:
311             return "kv";
312         case LCB_PING_SERVICE_VIEWS:
313             return "views";
314         case LCB_PING_SERVICE_QUERY:
315             return "n1ql";
316         case LCB_PING_SERVICE_SEARCH:
317             return "fts";
318         case LCB_PING_SERVICE_ANALYTICS:
319             return "cbas";
320         default:
321             return "unknown";
322     }
323 }
324 
build_ping_json(lcb_INSTANCE *instance, lcb_RESPPING &ping, Json::Value &root, PingCookie *ck)325 static void build_ping_json(lcb_INSTANCE *instance, lcb_RESPPING &ping, Json::Value &root, PingCookie *ck)
326 {
327     Json::Value services;
328     for (size_t ii = 0; ii < ping.nservices; ii++) {
329         lcb_PINGSVC &svc = ping.services[ii];
330         Json::Value service;
331         if (svc.server) {
332             service["remote"] = svc.server;
333         }
334         if (svc.local) {
335             service["local"] = svc.local;
336         }
337         if (svc.id) {
338             service["id"] = svc.id;
339         }
340         if (svc.scope) {
341             service["namespace"] = svc.scope;
342         }
343 
344         service["latency_us"] = (Json::Value::UInt64)LCB_NS2US(svc.latency);
345         switch (svc.status) {
346             case LCB_PING_STATUS_OK:
347                 service["status"] = "ok";
348                 break;
349             case LCB_PING_STATUS_TIMEOUT:
350                 service["status"] = "timeout";
351                 break;
352             default:
353                 service["status"] = "error";
354                 if (ck->needDetails()) {
355                     service["details"] = lcb_strerror_long(svc.rc);
356                 }
357         }
358         services[svc_to_string(svc.type)].append(service);
359     }
360     root["services"] = services;
361     root["version"] = 1;
362 
363     std::string sdk("libcouchbase/" LCB_VERSION_STRING);
364     if (LCBT_SETTING(instance, client_string)) {
365         sdk.append(" ").append(LCBT_SETTING(instance, client_string));
366     }
367     root["sdk"] = sdk.c_str();
368     root["id"] = ck->id;
369 
370     int64_t config_rev = -1;
371     if (instance->cur_configinfo) {
372         lcb::clconfig::ConfigInfo *cfg = instance->cur_configinfo;
373         config_rev = cfg->vbc->revid;
374     }
375     root["config_rev"] = (Json::Int64)config_rev;
376 }
377 
invoke_ping_callback(lcb_INSTANCE *instance, PingCookie *ck)378 static void invoke_ping_callback(lcb_INSTANCE *instance, PingCookie *ck)
379 {
380     lcb_RESPPING ping{};
381     std::string json;
382     size_t idx = 0;
383     if (ck->needMetrics()) {
384         ping.id = ck->id;
385         ping.nservices = ck->responses.size();
386         ping.services = new lcb_PINGSVC[ping.nservices];
387         for (std::list<lcb_PINGSVC>::const_iterator it = ck->responses.begin(); it != ck->responses.end(); ++it) {
388             ping.services[idx++] = *it;
389         }
390         if (ck->needJSON()) {
391             Json::Value root;
392             build_ping_json(instance, ping, root, ck);
393             Json::Writer *w;
394             if (ck->needPretty()) {
395                 w = new Json::StyledWriter();
396             } else {
397                 w = new Json::FastWriter();
398             }
399             json = w->write(root);
400             delete w;
401             ping.njson = json.size();
402             ping.json = json.c_str();
403         }
404     }
405     lcb_RESPCALLBACK callback;
406     callback = lcb_find_callback(instance, LCB_CALLBACK_PING);
407     ping.cookie = ck->cookie;
408     callback(instance, LCB_CALLBACK_PING, (lcb_RESPBASE *)&ping);
409     delete[] ping.services;
410     delete ck;
411 }
412 
handle_ping(mc_PIPELINE *pipeline, mc_PACKET *req, lcb_CALLBACK_TYPE , lcb_STATUS err, const void *)413 static void handle_ping(mc_PIPELINE *pipeline, mc_PACKET *req, lcb_CALLBACK_TYPE /* cbtype */, lcb_STATUS err,
414                         const void *)
415 {
416     auto *server = static_cast<lcb::Server *>(pipeline);
417     auto *ck = (PingCookie *)req->u_rdata.exdata;
418 
419     if (ck->needMetrics()) {
420         lcb_PINGSVC svc = {};
421         if (server->has_valid_host()) {
422             const lcb_host_t &remote = server->get_host();
423             std::string hh;
424             if (remote.ipv6) {
425                 hh.append("[").append(remote.host).append("]:").append(remote.port);
426             } else {
427                 hh.append(remote.host).append(":").append(remote.port);
428             }
429             svc.server = lcb_strdup(hh.c_str());
430         }
431         svc.type = LCB_PING_SERVICE_KV;
432         svc.latency = gethrtime() - MCREQ_PKT_RDATA(req)->start;
433         svc.rc = err;
434         switch (err) {
435             case LCB_ERR_TIMEOUT:
436                 svc.status = LCB_PING_STATUS_TIMEOUT;
437                 break;
438             case LCB_SUCCESS:
439                 svc.status = LCB_PING_STATUS_OK;
440                 break;
441             default:
442                 svc.status = LCB_PING_STATUS_ERROR;
443                 break;
444         }
445         lcbio_CTX *ctx = server->connctx;
446         if (ctx) {
447             char id[20] = {0};
448             svc.local = lcb_strdup(ctx->sock->info->ep_local_host_and_port);
449             snprintf(id, sizeof(id), "%p", (void *)ctx->sock);
450             svc.id = lcb_strdup(id);
451         }
452         svc.scope = server->get_instance()->get_bucketname();
453 
454         ck->responses.push_back(svc);
455     }
456 
457     if (--ck->remaining) {
458         return;
459     }
460     invoke_ping_callback(server->get_instance(), ck);
461 }
462 
handle_http(lcb_INSTANCE *instance, lcb_PING_SERVICE type, const lcb_RESPHTTP *resp)463 static void handle_http(lcb_INSTANCE *instance, lcb_PING_SERVICE type, const lcb_RESPHTTP *resp)
464 {
465     if ((resp->rflags & LCB_RESP_F_FINAL) == 0) {
466         return;
467     }
468     auto *ck = (PingCookie *)resp->cookie;
469     auto *htreq = reinterpret_cast<lcb::http::Request *>(resp->_htreq);
470 
471     if (ck->needMetrics()) {
472         lcb_PINGSVC svc = {};
473         svc.type = type;
474         std::string hh;
475         if (htreq->ipv6) {
476             hh = "[" + std::string(htreq->host) + "]:" + std::string(htreq->port);
477         } else {
478             hh = std::string(htreq->host) + ":" + std::string(htreq->port);
479         }
480         svc.server = lcb_strdup(hh.c_str());
481         svc.latency = gethrtime() - htreq->start;
482         svc.rc = resp->ctx.rc;
483         switch (resp->ctx.rc) {
484             case LCB_ERR_TIMEOUT:
485                 svc.status = LCB_PING_STATUS_TIMEOUT;
486                 break;
487             case LCB_SUCCESS:
488                 svc.status = LCB_PING_STATUS_OK;
489                 break;
490             default:
491                 svc.status = LCB_PING_STATUS_ERROR;
492                 break;
493         }
494         lcbio_CTX *ctx = htreq->ioctx;
495         if (ctx) {
496             char id[20] = {0};
497             snprintf(id, sizeof(id), "%p", (void *)ctx->sock);
498             svc.id = lcb_strdup(id);
499             svc.local = lcb_strdup(ctx->sock->info->ep_local_host_and_port);
500         }
501         ck->responses.push_back(svc);
502     }
503     if (--ck->remaining) {
504         return;
505     }
506     invoke_ping_callback(instance, ck);
507 }
508 
handle_n1ql(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)509 static void handle_n1ql(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)
510 {
511     handle_http(instance, LCB_PING_SERVICE_QUERY, (const lcb_RESPHTTP *)resp);
512 }
513 
handle_views(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)514 static void handle_views(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)
515 {
516     handle_http(instance, LCB_PING_SERVICE_VIEWS, (const lcb_RESPHTTP *)resp);
517 }
518 
handle_fts(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)519 static void handle_fts(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)
520 {
521     handle_http(instance, LCB_PING_SERVICE_SEARCH, (const lcb_RESPHTTP *)resp);
522 }
523 
handle_analytics(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)524 static void handle_analytics(lcb_INSTANCE *instance, int, const lcb_RESPBASE *resp)
525 {
526     handle_http(instance, LCB_PING_SERVICE_ANALYTICS, (const lcb_RESPHTTP *)resp);
527 }
528 
ping_type_to_service(lcbvb_SVCTYPE type)529 static lcbauth_SERVICE ping_type_to_service(lcbvb_SVCTYPE type)
530 {
531     switch (type) {
532         case LCBVB_SVCTYPE_DATA:
533             return LCBAUTH_SERVICE_KEY_VALUE;
534 
535         case LCBVB_SVCTYPE_VIEWS:
536             return LCBAUTH_SERVICE_VIEWS;
537 
538         case LCBVB_SVCTYPE_MGMT:
539             return LCBAUTH_SERVICE_MANAGEMENT;
540 
541         case LCBVB_SVCTYPE_IXQUERY:
542         case LCBVB_SVCTYPE_IXADMIN:
543         case LCBVB_SVCTYPE_QUERY:
544             return LCBAUTH_SERVICE_QUERY;
545 
546         case LCBVB_SVCTYPE_SEARCH:
547             return LCBAUTH_SERVICE_SEARCH;
548 
549         case LCBVB_SVCTYPE_ANALYTICS:
550             return LCBAUTH_SERVICE_ANALYTICS;
551 
552         case LCBVB_SVCTYPE_EVENTING:
553             return LCBAUTH_SERVICE_EVENTING;
554 
555         default:
556             return LCBAUTH_SERVICE_UNSPECIFIED;
557     }
558 }
559 
560 LIBCOUCHBASE_API
lcb_ping(lcb_INSTANCE *instance, void *cookie, const lcb_CMDPING *cmd)561 lcb_STATUS lcb_ping(lcb_INSTANCE *instance, void *cookie, const lcb_CMDPING *cmd)
562 {
563     mc_CMDQUEUE *cq = &instance->cmdq;
564     unsigned ii;
565 
566     if (!cq->config) {
567         return LCB_ERR_NO_CONFIGURATION;
568     }
569 
570     auto *ckwrap = new PingCookie(cookie, cmd->options);
571     {
572         char id[20] = {0};
573         snprintf(id, sizeof(id), "%p", (void *)instance);
574         ckwrap->id = id;
575         if (cmd->id) {
576             ckwrap->id.append("/").append(cmd->id);
577         } else {
578             snprintf(id, sizeof(id), "%016" PRIx64, lcb_next_rand64());
579             ckwrap->id.append("/").append(id);
580         }
581     }
582 
583     hrtime_t timeout = LCB_US2NS(cmd->timeout ? cmd->timeout : LCBT_SETTING(instance, operation_timeout));
584     lcbvb_CONFIG *cfg = LCBT_VBCONFIG(instance);
585     const lcbvb_SVCMODE mode = LCBT_SETTING_SVCMODE(instance);
586     if (cmd->services & LCB_PINGSVC_F_KV) {
587         for (ii = 0; ii < cq->npipelines; ii++) {
588             unsigned port = lcbvb_get_port(cfg, ii, LCBVB_SVCTYPE_DATA, mode);
589             if (!port) {
590                 continue;
591             }
592 
593             mc_PIPELINE *pl = cq->pipelines[ii];
594             mc_PACKET *pkt = mcreq_allocate_packet(pl);
595             protocol_binary_request_header hdr;
596             memset(&hdr, 0, sizeof(hdr));
597 
598             if (!pkt) {
599                 return LCB_ERR_NO_MEMORY;
600             }
601 
602             ckwrap->deadline = ckwrap->start + timeout;
603             pkt->u_rdata.exdata = ckwrap;
604             pkt->flags |= MCREQ_F_REQEXT;
605 
606             hdr.request.magic = PROTOCOL_BINARY_REQ;
607             hdr.request.opaque = pkt->opaque;
608             hdr.request.opcode = PROTOCOL_BINARY_CMD_NOOP;
609 
610             mcreq_reserve_header(pl, pkt, MCREQ_PKT_BASESIZE);
611             memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes));
612             mcreq_sched_add(pl, pkt);
613             ckwrap->remaining++;
614         }
615     }
616 
617     for (int idx = 0; idx < (int)LCBVB_NSERVERS(cfg); idx++) {
618 #define PING_HTTP(SVC, PATH, TMO, CB)                                                                                  \
619     lcb_STATUS rc;                                                                                                     \
620     lcb_HTTP_HANDLE *htreq;                                                                                            \
621     lcb_CMDHTTP *htcmd;                                                                                                \
622     char buf[1024] = {0};                                                                                              \
623     unsigned port;                                                                                                     \
624     port = lcbvb_get_port(cfg, idx, SVC, mode);                                                                        \
625     if (port) {                                                                                                        \
626         lcbvb_SERVER *srv = LCBVB_GET_SERVER(cfg, idx);                                                                \
627         bool ipv6 = strchr(srv->hostname, ':');                                                                        \
628         snprintf(buf, sizeof(buf), "%s://%s%s%s:%d%s", (mode == LCBVB_SVCMODE_PLAIN) ? "http" : "https",               \
629                  ipv6 ? "[" : "", srv->hostname, ipv6 ? "]" : "", port, PATH);                                         \
630         lcb_cmdhttp_create(&htcmd, LCB_HTTP_TYPE_PING);                                                                \
631         lcb_cmdhttp_host(htcmd, buf, strlen(buf));                                                                     \
632         lcb_cmdhttp_method(htcmd, LCB_HTTP_METHOD_GET);                                                                \
633         lcb_cmdhttp_handle(htcmd, &htreq);                                                                             \
634         const lcb::Authenticator &auth = *instance->settings->auth;                                                    \
635         auto creds = auth.credentials_for(ping_type_to_service(SVC), LCBAUTH_REASON_NEW_OPERATION, nullptr, nullptr,   \
636                                           LCBT_SETTING(instance, bucket));                                             \
637         lcb_cmdhttp_username(htcmd, creds.username().c_str(), creds.username().size());                                \
638         lcb_cmdhttp_password(htcmd, creds.password().c_str(), creds.password().size());                                \
639         lcb_cmdhttp_timeout(htcmd, LCB_NS2US(timeout));                                                                \
640         rc = lcb_http(instance, ckwrap, htcmd);                                                                        \
641         lcb_cmdhttp_destroy(htcmd);                                                                                    \
642         if (rc == LCB_SUCCESS) {                                                                                       \
643             htreq->set_callback(CB);                                                                                   \
644             ckwrap->remaining++;                                                                                       \
645         }                                                                                                              \
646     }
647 
648         if (cmd->services & LCB_PINGSVC_F_N1QL) {
649             PING_HTTP(LCBVB_SVCTYPE_QUERY, "/admin/ping", n1ql_timeout, handle_n1ql);
650         }
651         if (cmd->services & LCB_PINGSVC_F_VIEWS) {
652             PING_HTTP(LCBVB_SVCTYPE_VIEWS, "/", views_timeout, handle_views);
653         }
654         if (cmd->services & LCB_PINGSVC_F_FTS) {
655             PING_HTTP(LCBVB_SVCTYPE_SEARCH, "/api/ping", search_timeout, handle_fts);
656         }
657         if (cmd->services & LCB_PINGSVC_F_ANALYTICS) {
658             PING_HTTP(LCBVB_SVCTYPE_ANALYTICS, "/admin/ping", analytics_timeout, handle_analytics);
659         }
660 #undef PING_HTTP
661     }
662 
663     if (ckwrap->remaining == 0) {
664         delete ckwrap;
665         return LCB_ERR_NO_MATCHING_SERVER;
666     }
667     MAYBE_SCHEDLEAVE(instance);
668     return LCB_SUCCESS;
669 }
670 
lcb_respdiag_status(const lcb_RESPDIAG *resp)671 LIBCOUCHBASE_API lcb_STATUS lcb_respdiag_status(const lcb_RESPDIAG *resp)
672 {
673     return resp->ctx.rc;
674 }
675 
lcb_respdiag_cookie(const lcb_RESPDIAG *resp, void **cookie)676 LIBCOUCHBASE_API lcb_STATUS lcb_respdiag_cookie(const lcb_RESPDIAG *resp, void **cookie)
677 {
678     *cookie = resp->cookie;
679     return LCB_SUCCESS;
680 }
681 
lcb_respdiag_value(const lcb_RESPDIAG *resp, const char **json, size_t *json_len)682 LIBCOUCHBASE_API lcb_STATUS lcb_respdiag_value(const lcb_RESPDIAG *resp, const char **json, size_t *json_len)
683 {
684     *json = resp->json;
685     *json_len = resp->njson;
686     return LCB_SUCCESS;
687 }
688 
lcb_cmddiag_create(lcb_CMDDIAG **cmd)689 LIBCOUCHBASE_API lcb_STATUS lcb_cmddiag_create(lcb_CMDDIAG **cmd)
690 {
691     *cmd = (lcb_CMDDIAG *)calloc(1, sizeof(lcb_CMDDIAG));
692     return LCB_SUCCESS;
693 }
694 
lcb_cmddiag_destroy(lcb_CMDDIAG *cmd)695 LIBCOUCHBASE_API lcb_STATUS lcb_cmddiag_destroy(lcb_CMDDIAG *cmd)
696 {
697     free(cmd);
698     return LCB_SUCCESS;
699 }
700 
lcb_cmddiag_report_id(lcb_CMDDIAG *cmd, const char *report_id, size_t report_id_len)701 LIBCOUCHBASE_API lcb_STATUS lcb_cmddiag_report_id(lcb_CMDDIAG *cmd, const char *report_id, size_t report_id_len)
702 {
703     cmd->id = report_id;
704     cmd->nid = report_id_len;
705     return LCB_SUCCESS;
706 }
707 
lcb_cmddiag_prettify(lcb_CMDDIAG *cmd, int enable)708 LIBCOUCHBASE_API lcb_STATUS lcb_cmddiag_prettify(lcb_CMDDIAG *cmd, int enable)
709 {
710     if (enable) {
711         cmd->options |= LCB_PINGOPT_F_JSONPRETTY;
712     } else {
713         cmd->options &= ~LCB_PINGOPT_F_JSONPRETTY;
714     }
715     return LCB_SUCCESS;
716 }
717 
718 LIBCOUCHBASE_API
lcb_diag(lcb_INSTANCE *instance, void *cookie, const lcb_CMDDIAG *cmd)719 lcb_STATUS lcb_diag(lcb_INSTANCE *instance, void *cookie, const lcb_CMDDIAG *cmd)
720 {
721     Json::Value root;
722     hrtime_t now = LCB_NS2US(gethrtime());
723 
724     root["version"] = 1;
725 
726     std::string sdk("libcouchbase/" LCB_VERSION_STRING);
727     if (LCBT_SETTING(instance, client_string)) {
728         sdk.append(" ").append(LCBT_SETTING(instance, client_string));
729     }
730     root["sdk"] = sdk.c_str();
731     {
732         char id[20] = {0};
733         snprintf(id, sizeof(id), "%p", (void *)instance);
734         std::string idstr(id);
735         if (cmd->id) {
736             idstr.append("/").append(cmd->id);
737         }
738         root["id"] = idstr;
739     }
740 
741     size_t ii;
742     Json::Value kv;
743     for (ii = 0; ii < instance->cmdq.npipelines; ii++) {
744         auto *server = static_cast<lcb::Server *>(instance->cmdq.pipelines[ii]);
745         lcbio_CTX *ctx = server->connctx;
746         if (ctx) {
747             Json::Value endpoint;
748             char id[20] = {0};
749             snprintf(id, sizeof(id), "%016" PRIx64, ctx->sock ? ctx->sock->id : (lcb_U64)0);
750             endpoint["id"] = id;
751             if (server->curhost->ipv6) {
752                 endpoint["remote"] =
753                     "[" + std::string(server->curhost->host) + "]:" + std::string(server->curhost->port);
754             } else {
755                 endpoint["remote"] = std::string(server->curhost->host) + ":" + std::string(server->curhost->port);
756             }
757             if (!server->bucket.empty()) {
758                 endpoint["namespace"] = server->bucket;
759             }
760             if (ctx->sock) {
761                 if (ctx->sock->info) {
762                     endpoint["local"] = ctx->sock->info->ep_local_host_and_port;
763                 }
764                 endpoint["last_activity_us"] =
765                     (Json::Value::UInt64)(now > ctx->sock->atime ? now - ctx->sock->atime : 0);
766                 endpoint["status"] = "connected";
767                 root[lcbio_svcstr(ctx->sock->service)].append(endpoint);
768             }
769         }
770     }
771     instance->memd_sockpool->toJSON(now, root);
772     instance->http_sockpool->toJSON(now, root);
773     {
774         Json::Value cur;
775         lcb_ASPEND_SETTYPE::iterator it;
776         lcb_ASPEND_SETTYPE *pendq;
777         if ((pendq = instance->pendops.items[LCB_PENDTYPE_HTTP])) {
778             for (it = pendq->begin(); it != pendq->end(); ++it) {
779                 lcb::http::Request *htreq = reinterpret_cast<lcb::http::Request *>(*it);
780                 lcbio_CTX *ctx = htreq->ioctx;
781                 if (ctx) {
782                     Json::Value endpoint;
783                     char id[20] = {0};
784                     snprintf(id, sizeof(id), "%016" PRIx64, ctx->sock ? ctx->sock->id : (lcb_U64)0);
785                     endpoint["id"] = id;
786                     if (htreq->ipv6) {
787                         endpoint["remote"] = "[" + std::string(htreq->host) + "]:" + std::string(htreq->port);
788                     } else {
789                         endpoint["remote"] = std::string(htreq->host) + ":" + std::string(htreq->port);
790                     }
791                     if (ctx->sock) {
792                         if (ctx->sock->info) {
793                             endpoint["local"] = ctx->sock->info->ep_local_host_and_port;
794                         }
795                         endpoint["last_activity_us"] =
796                             (Json::Value::UInt64)(now > ctx->sock->atime ? now - ctx->sock->atime : 0);
797                         endpoint["status"] = "connected";
798                         root[lcbio_svcstr(ctx->sock->service)].append(endpoint);
799                     }
800                 }
801             }
802         }
803     }
804 
805     Json::Writer *w;
806     if (cmd->options & LCB_PINGOPT_F_JSONPRETTY) {
807         w = new Json::StyledWriter();
808     } else {
809         w = new Json::FastWriter();
810     }
811     std::string json = w->write(root);
812     delete w;
813 
814     lcb_RESPDIAG resp{};
815     lcb_RESPCALLBACK callback;
816 
817     resp.njson = json.size();
818     resp.json = json.c_str();
819 
820     callback = lcb_find_callback(instance, LCB_CALLBACK_DIAG);
821     resp.cookie = const_cast<void *>(cookie);
822     callback(instance, LCB_CALLBACK_DIAG, (lcb_RESPBASE *)&resp);
823 
824     return LCB_SUCCESS;
825 }
826