1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2011-2018 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 #define NOMINMAX
19 #include <map>
20 #include <sstream>
21 #include <iostream>
22 #include <iomanip>
23 #include <fstream>
24 #include <algorithm>
25 #include <libcouchbase/vbucket.h>
26 #include <libcouchbase/views.h>
27 #include <libcouchbase/n1ql.h>
28 #include <limits>
29 #include <stddef.h>
30 #include <errno.h>
31 #include "common/options.h"
32 #include "common/histogram.h"
33 #include "cbc-handlers.h"
34 #include "connspec.h"
35 #include "contrib/lcb-jsoncpp/lcb-jsoncpp.h"
36
37 #ifndef LCB_NO_SSL
38 #include <openssl/crypto.h>
39 #endif
40 #include <snappy-stubs-public.h>
41
42 using namespace cbc;
43
44 using std::string;
45 using std::map;
46 using std::vector;
47 using std::stringstream;
48
getRespKey(const lcb_RESPBASE * resp)49 string getRespKey(const lcb_RESPBASE* resp)
50 {
51 if (!resp->nkey) {
52 return "";
53 }
54
55 return string((const char *)resp->key, resp->nkey);
56 }
57
58 static void
printKeyError(string & key,int cbtype,const lcb_RESPBASE * resp,const char * additional=NULL)59 printKeyError(string& key, int cbtype, const lcb_RESPBASE *resp, const char *additional = NULL)
60 {
61 fprintf(stderr, "%-20s %s\n", key.c_str(), lcb_strerror_short(resp->rc));
62 const char *ctx = lcb_resp_get_error_context(cbtype, resp);
63 if (ctx != NULL) {
64 fprintf(stderr, "%-20s %s\n", "", ctx);
65 }
66 const char *ref = lcb_resp_get_error_ref(cbtype, resp);
67 if (ref != NULL) {
68 fprintf(stderr, "%-20s Ref: %s\n", "", ref);
69 }
70 if (additional) {
71 fprintf(stderr, "%-20s %s\n", "", additional);
72 }
73 }
74
75 static void
printKeyCasStatus(string & key,int cbtype,const lcb_RESPBASE * resp,const char * message=NULL)76 printKeyCasStatus(string& key, int cbtype, const lcb_RESPBASE *resp,
77 const char *message = NULL)
78 {
79 fprintf(stderr, "%-20s", key.c_str());
80 if (message != NULL) {
81 fprintf(stderr, "%s ", message);
82 }
83 fprintf(stderr, "CAS=0x%" PRIx64 "\n", resp->cas);
84 const lcb_MUTATION_TOKEN *st = lcb_resp_get_mutation_token(cbtype, resp);
85 if (st != NULL) {
86 fprintf(stderr, "%-20sSYNCTOKEN=%u,%" PRIu64 ",%" PRIu64 "\n",
87 "", st->vbid_, st->uuid_, st->seqno_);
88 }
89 }
90
91 extern "C" {
92 static void
get_callback(lcb_t,lcb_CALLBACKTYPE cbtype,const lcb_RESPGET * resp)93 get_callback(lcb_t, lcb_CALLBACKTYPE cbtype, const lcb_RESPGET *resp)
94 {
95 string key = getRespKey((const lcb_RESPBASE *)resp);
96 if (resp->rc == LCB_SUCCESS) {
97 fprintf(stderr, "%-20s CAS=0x%" PRIx64 ", Flags=0x%x, Size=%lu, Datatype=0x%02x",
98 key.c_str(), resp->cas, resp->itmflags, (unsigned long)resp->nvalue,
99 (int)resp->datatype);
100 if (resp->datatype) {
101 int nflags = 0;
102 fprintf(stderr, "(");
103 if (resp->datatype & LCB_VALUE_F_JSON) {
104 fprintf(stderr, "JSON");
105 nflags++;
106 }
107 if (resp->datatype & LCB_VALUE_F_SNAPPYCOMP) {
108 fprintf(stderr, "%sSNAPPY", nflags > 0 ? "," : "");
109 nflags++;
110 }
111 fprintf(stderr, ")");
112 }
113 fprintf(stderr, "\n");
114 fflush(stderr);
115 fwrite(resp->value, 1, resp->nvalue, stdout);
116 fflush(stdout);
117 fprintf(stderr, "\n");
118 } else {
119 printKeyError(key, cbtype, (const lcb_RESPBASE *)resp);
120 }
121 }
122
123 static void
store_callback(lcb_t,lcb_CALLBACKTYPE cbtype,const lcb_RESPBASE * resp)124 store_callback(lcb_t, lcb_CALLBACKTYPE cbtype, const lcb_RESPBASE *resp)
125 {
126 string key = getRespKey((const lcb_RESPBASE*)resp);
127
128 if (cbtype == LCB_CALLBACK_STOREDUR) {
129 // Storage with durability
130 const lcb_RESPSTOREDUR *dresp =
131 reinterpret_cast<const lcb_RESPSTOREDUR *>(resp);
132 char buf[4096];
133 if (resp->rc == LCB_SUCCESS) {
134 sprintf(buf, "Stored. Persisted(%u). Replicated(%u)",
135 dresp->dur_resp->npersisted, dresp->dur_resp->nreplicated);
136 printKeyCasStatus(key, cbtype, resp, buf);
137 } else {
138 if (dresp->store_ok) {
139 sprintf(buf, "Store OK, but durability failed. Persisted(%u). Replicated(%u)",
140 dresp->dur_resp->npersisted, dresp->dur_resp->nreplicated);
141 } else {
142 sprintf(buf, "%s", "Store failed");
143 }
144 printKeyError(key, cbtype, resp);
145 }
146 } else {
147 if (resp->rc == LCB_SUCCESS) {
148 printKeyCasStatus(key, cbtype, resp, "Stored.");
149 } else {
150 printKeyError(key, cbtype, resp);
151 }
152 }
153 }
154
155 static void
common_callback(lcb_t,int type,const lcb_RESPBASE * resp)156 common_callback(lcb_t, int type, const lcb_RESPBASE *resp)
157 {
158 string key = getRespKey(resp);
159 if (resp->rc != LCB_SUCCESS) {
160 printKeyError(key, type, resp);
161 return;
162 }
163 switch (type) {
164 case LCB_CALLBACK_UNLOCK:
165 fprintf(stderr, "%-20s Unlocked\n", key.c_str());
166 break;
167 case LCB_CALLBACK_REMOVE:
168 printKeyCasStatus(key, type, resp, "Deleted.");
169 break;
170 case LCB_CALLBACK_TOUCH:
171 printKeyCasStatus(key, type, resp, "Touched.");
172 break;
173 default:
174 abort(); // didn't request it
175 }
176 }
177
178 static void
observe_callback(lcb_t,lcb_CALLBACKTYPE cbtype,const lcb_RESPOBSERVE * resp)179 observe_callback(lcb_t, lcb_CALLBACKTYPE cbtype, const lcb_RESPOBSERVE *resp)
180 {
181 if (resp->nkey == 0) {
182 return;
183 }
184
185 string key = getRespKey((const lcb_RESPBASE *)resp);
186 if (resp->rc == LCB_SUCCESS) {
187 fprintf(stderr,
188 "%-20s [%s] Status=0x%x, CAS=0x%" PRIx64 "\n", key.c_str(),
189 resp->ismaster ? "Master" : "Replica",
190 resp->status, resp->cas);
191 } else {
192 printKeyError(key, cbtype, (const lcb_RESPBASE *)resp);
193 }
194 }
195
196 static void
obseqno_callback(lcb_t,lcb_CALLBACKTYPE,const lcb_RESPOBSEQNO * resp)197 obseqno_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPOBSEQNO *resp)
198 {
199 int ix = resp->server_index;
200 if (resp->rc != LCB_SUCCESS) {
201 fprintf(stderr,
202 "[%d] ERROR %s\n", ix, lcb_strerror_long(resp->rc));
203 return;
204 }
205 lcb_U64 uuid, seq_disk, seq_mem;
206 if (resp->old_uuid) {
207 seq_disk = seq_mem = resp->old_seqno;
208 uuid = resp->old_uuid;
209 } else {
210 uuid = resp->cur_uuid;
211 seq_disk = resp->persisted_seqno;
212 seq_mem = resp->mem_seqno;
213 }
214 fprintf(stderr, "[%d] UUID=0x%" PRIx64 ", Cache=%" PRIu64 ", Disk=%" PRIu64,
215 ix, uuid, seq_mem, seq_disk);
216 if (resp->old_uuid) {
217 fprintf(stderr, "\n");
218 fprintf(stderr, " FAILOVER. New: UUID=%" PRIx64 ", Cache=%" PRIu64 ", Disk=%" PRIu64,
219 resp->cur_uuid, resp->mem_seqno, resp->persisted_seqno);
220 }
221 fprintf(stderr, "\n");
222 }
223
224 static void
stats_callback(lcb_t,lcb_CALLBACKTYPE,const lcb_RESPSTATS * resp)225 stats_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPSTATS *resp)
226 {
227 if (resp->rc != LCB_SUCCESS) {
228 fprintf(stderr, "ERROR %s\n", lcb_strerror_long(resp->rc));
229 return;
230 }
231 if (resp->server == NULL || resp->key == NULL) {
232 return;
233 }
234
235 string server = resp->server;
236 string key = getRespKey((const lcb_RESPBASE *)resp);
237 string value;
238 if (resp->nvalue > 0) {
239 value.assign((const char *)resp->value, resp->nvalue);
240 }
241 fprintf(stdout, "%s\t%s", server.c_str(), key.c_str());
242 if (!value.empty()) {
243 if (*static_cast<bool*>(resp->cookie) && key == "key_flags") {
244 // Is keystats
245 // Flip the bits so the display formats correctly
246 unsigned flags_u = 0;
247 sscanf(value.c_str(), "%u", &flags_u);
248 flags_u = htonl(flags_u);
249 fprintf(stdout, "\t%u (cbc: converted via htonl)", flags_u);
250 } else {
251 fprintf(stdout, "\t%s", value.c_str());
252 }
253 }
254 fprintf(stdout, "\n");
255 }
256
257 static void
watch_callback(lcb_t,lcb_CALLBACKTYPE,const lcb_RESPSTATS * resp)258 watch_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPSTATS *resp)
259 {
260 if (resp->rc != LCB_SUCCESS) {
261 fprintf(stderr, "ERROR %s\n", lcb_strerror_long(resp->rc));
262 return;
263 }
264 if (resp->server == NULL || resp->key == NULL) {
265 return;
266 }
267
268 string key = getRespKey((const lcb_RESPBASE *)resp);
269 if (resp->nvalue > 0) {
270 char *nptr = NULL;
271 uint64_t val =
272 #ifdef _WIN32
273 _strtoi64
274 #else
275 strtoll
276 #endif
277 ((const char *)resp->value, &nptr, 10);
278 if (nptr != (const char *)resp->value) {
279 map<string, int64_t> *entry = reinterpret_cast< map<string, int64_t> *>(resp->cookie);
280 (*entry)[key] += val;
281 }
282 }
283 }
284
285 static void
common_server_callback(lcb_t,int cbtype,const lcb_RESPSERVERBASE * sbase)286 common_server_callback(lcb_t, int cbtype, const lcb_RESPSERVERBASE *sbase)
287 {
288 string msg;
289 if (cbtype == LCB_CALLBACK_VERBOSITY) {
290 msg = "Set verbosity";
291 } else if (cbtype == LCB_CALLBACK_FLUSH) {
292 msg = "Flush";
293 } else if (cbtype == LCB_CALLBACK_VERSIONS) {
294 const lcb_RESPMCVERSION *resp = (const lcb_RESPMCVERSION *)sbase;
295 msg = string(resp->mcversion, resp->nversion);
296 } else {
297 msg = "";
298 }
299 if (!sbase->server) {
300 return;
301 }
302 if (sbase->rc != LCB_SUCCESS) {
303 fprintf(stderr, "%s failed for server %s: %s\n", msg.c_str(), sbase->server,
304 lcb_strerror_short(sbase->rc));
305 } else {
306 fprintf(stderr, "%s: %s\n", msg.c_str(), sbase->server);
307 }
308 }
309
310 static void
ping_callback(lcb_t,int,const lcb_RESPPING * resp)311 ping_callback(lcb_t, int, const lcb_RESPPING *resp)
312 {
313 if (resp->rc != LCB_SUCCESS) {
314 fprintf(stderr, "failed: %s\n", lcb_strerror_short(resp->rc));
315 } else {
316 if (resp->njson) {
317 printf("%.*s", (int)resp->njson, resp->json);
318 }
319 }
320 }
321
322 static void
arithmetic_callback(lcb_t,lcb_CALLBACKTYPE type,const lcb_RESPCOUNTER * resp)323 arithmetic_callback(lcb_t, lcb_CALLBACKTYPE type, const lcb_RESPCOUNTER *resp)
324 {
325 string key = getRespKey((const lcb_RESPBASE *)resp);
326 if (resp->rc != LCB_SUCCESS) {
327 printKeyError(key, type, (lcb_RESPBASE *)resp);
328 } else {
329 char buf[4096] = { 0 };
330 sprintf(buf, "Current value is %" PRIu64 ".", resp->value);
331 printKeyCasStatus(key, type, (const lcb_RESPBASE *)resp, buf);
332 }
333 }
334
335 static void
http_callback(lcb_t,int,const lcb_RESPHTTP * resp)336 http_callback(lcb_t, int, const lcb_RESPHTTP *resp)
337 {
338 HttpReceiver *ctx = (HttpReceiver *)resp->cookie;
339 ctx->maybeInvokeStatus(resp);
340 if (resp->nbody) {
341 ctx->onChunk((const char*)resp->body, resp->nbody);
342 }
343 if (resp->rflags & LCB_RESP_F_FINAL) {
344 ctx->onDone();
345 }
346 }
347
348 static void
view_callback(lcb_t,int,const lcb_RESPVIEWQUERY * resp)349 view_callback(lcb_t, int, const lcb_RESPVIEWQUERY *resp)
350 {
351 if (resp->rflags & LCB_RESP_F_FINAL) {
352 fprintf(stderr, "View query complete!\n");
353 }
354
355 if (resp->rc != LCB_SUCCESS) {
356 fprintf(stderr, "View query failed: %s\n",
357 lcb_strerror_short(resp->rc));
358
359 if (resp->rc == LCB_HTTP_ERROR) {
360 if (resp->htresp != NULL) {
361 HttpReceiver ctx;
362 ctx.maybeInvokeStatus(resp->htresp);
363 if (resp->htresp->nbody) {
364 fprintf(stderr, "%.*s", (int)resp->htresp->nbody,
365 static_cast<const char *>(resp->htresp->body));
366 }
367 }
368 }
369 }
370
371 if (resp->rflags & LCB_RESP_F_FINAL) {
372 if (resp->value) {
373 fprintf(stderr, "Non-row data: %.*s\n",
374 (int)resp->nvalue, resp->value);
375 }
376 return;
377 }
378
379 printf("KEY: %.*s\n", (int)resp->nkey, static_cast<const char*>(resp->key));
380 printf(" VALUE: %.*s\n", (int)resp->nvalue, resp->value);
381 printf(" DOCID: %.*s\n", (int)resp->ndocid, resp->docid);
382 if (resp->docresp) {
383 get_callback(NULL, LCB_CALLBACK_GET, resp->docresp);
384 }
385 if (resp->geometry) {
386 printf(" GEO: %.*s\n", (int)resp->ngeometry, resp->geometry);
387 }
388 }
389 }
390
391
Handler(const char * name)392 Handler::Handler(const char *name) : parser(name), instance(NULL)
393 {
394 if (name != NULL) {
395 cmdname = name;
396 }
397 }
398
~Handler()399 Handler::~Handler()
400 {
401 if (params.shouldDump()) {
402 lcb_dump(instance, stderr, LCB_DUMP_ALL);
403 }
404 if (instance) {
405 lcb_destroy(instance);
406 }
407 }
408
409 void
execute(int argc,char ** argv)410 Handler::execute(int argc, char **argv)
411 {
412 addOptions();
413 parser.default_settings.argstring = usagestr();
414 parser.default_settings.shortdesc = description();
415 parser.parse(argc, argv, true);
416 run();
417 if (instance != NULL && params.useTimings()) {
418 fprintf(stderr, "Output command timings as requested (--timings)\n");
419 hg.write();
420 }
421 }
422
423 void
addOptions()424 Handler::addOptions()
425 {
426 params.addToParser(parser);
427 }
428
429 void
run()430 Handler::run()
431 {
432 lcb_create_st cropts;
433 memset(&cropts, 0, sizeof cropts);
434 params.fillCropts(cropts);
435 lcb_error_t err;
436 err = lcb_create(&instance, &cropts);
437 if (err != LCB_SUCCESS) {
438 throw LcbError(err, "Failed to create instance");
439 }
440 params.doCtls(instance);
441 err = lcb_connect(instance);
442 if (err != LCB_SUCCESS) {
443 throw LcbError(err, "Failed to connect instance");
444 }
445 lcb_wait(instance);
446 err = lcb_get_bootstrap_status(instance);
447 if (err != LCB_SUCCESS) {
448 throw LcbError(err, "Failed to bootstrap instance");
449 }
450
451 if (params.useTimings()) {
452 hg.install(instance, stdout);
453 }
454 }
455
456 const string&
getLoneArg(bool required)457 Handler::getLoneArg(bool required)
458 {
459 static string empty("");
460
461 const vector<string>& args = parser.getRestArgs();
462 if (args.empty() || args.size() != 1) {
463 if (required) {
464 throw std::runtime_error("Command requires single argument");
465 }
466 return empty;
467 }
468 return args[0];
469 }
470
471 void
addOptions()472 GetHandler::addOptions()
473 {
474 Handler::addOptions();
475 o_exptime.abbrev('e');
476 if (isLock()) {
477 o_exptime.description("Time the lock should be held for");
478 } else {
479 o_exptime.description("Update the expiration time for the item");
480 o_replica.abbrev('r');
481 o_replica.description("Read from replica. Possible values are 'first': read from first available replica. 'all': read from all replicas, and <N>, where 0 < N < nreplicas");
482 parser.addOption(o_replica);
483 }
484 parser.addOption(o_exptime);
485 }
486
487 void
run()488 GetHandler::run()
489 {
490 Handler::run();
491 lcb_install_callback3(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
492 lcb_install_callback3(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPCALLBACK)get_callback);
493 const vector<string>& keys = parser.getRestArgs();
494 std::string replica_mode = o_replica.result();
495
496 lcb_sched_enter(instance);
497 for (size_t ii = 0; ii < keys.size(); ++ii) {
498 lcb_error_t err;
499 if (o_replica.passed()) {
500 lcb_CMDGETREPLICA cmd = { 0 };
501 const string& key = keys[ii];
502 LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
503 if (replica_mode == "first") {
504 cmd.strategy = LCB_REPLICA_FIRST;
505 } else if (replica_mode == "all") {
506 cmd.strategy = LCB_REPLICA_ALL;
507 } else {
508 cmd.strategy = LCB_REPLICA_SELECT;
509 cmd.index = std::atoi(replica_mode.c_str());
510 }
511 err = lcb_rget3(instance, this, &cmd);
512 } else {
513 lcb_CMDGET cmd = { 0 };
514 const string& key = keys[ii];
515 LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
516 if (o_exptime.passed()) {
517 cmd.exptime = o_exptime.result();
518 }
519 if (isLock()) {
520 cmd.lock = 1;
521 }
522 err = lcb_get3(instance, this, &cmd);
523 }
524 if (err != LCB_SUCCESS) {
525 throw LcbError(err);
526 }
527 }
528 lcb_sched_leave(instance);
529 lcb_wait(instance);
530 }
531
532 void
addOptions()533 TouchHandler::addOptions()
534 {
535 Handler::addOptions();
536 parser.addOption(o_exptime);
537 }
538
539 void
run()540 TouchHandler::run()
541 {
542 Handler::run();
543 lcb_install_callback3(instance, LCB_CALLBACK_TOUCH, (lcb_RESPCALLBACK)common_callback);
544 const vector<string>& keys = parser.getRestArgs();
545 lcb_error_t err;
546 lcb_sched_enter(instance);
547 for (size_t ii = 0; ii < keys.size(); ++ii) {
548 lcb_CMDTOUCH cmd = { 0 };
549 const string& key = keys[ii];
550 LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size());
551 cmd.exptime = o_exptime.result();
552 err = lcb_touch3(instance, this, &cmd);
553 if (err != LCB_SUCCESS) {
554 throw LcbError(err);
555 }
556 }
557 lcb_sched_leave(instance);
558 lcb_wait(instance);
559 }
560
561 void
addOptions()562 SetHandler::addOptions()
563 {
564 Handler::addOptions();
565 parser.addOption(o_mode);
566 parser.addOption(o_flags);
567 parser.addOption(o_exp);
568 parser.addOption(o_add);
569 parser.addOption(o_persist);
570 parser.addOption(o_replicate);
571 if (!hasFileList()) {
572 parser.addOption(o_value);
573 }
574 parser.addOption(o_json);
575 }
576
577 lcb_storage_t
mode()578 SetHandler::mode()
579 {
580 if (o_add.passed()) {
581 return LCB_ADD;
582 }
583
584 string s = o_mode.const_result();
585 std::transform(s.begin(), s.end(), s.begin(), ::tolower);
586 if (s == "upsert") {
587 return LCB_SET;
588 } else if (s == "replace") {
589 return LCB_REPLACE;
590 } else if (s == "insert") {
591 return LCB_ADD;
592 } else if (s == "append") {
593 return LCB_APPEND;
594 } else if (s == "prepend") {
595 return LCB_PREPEND;
596 } else {
597 throw BadArg(string("Mode must be one of upsert, insert, replace. Got ") + s);
598 return LCB_SET;
599 }
600 }
601
602 void
storeItem(const string & key,const char * value,size_t nvalue)603 SetHandler::storeItem(const string& key, const char *value, size_t nvalue)
604 {
605 lcb_error_t err;
606 lcb_CMDSTOREDUR cmd = { 0 };
607 LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size());
608 cmd.value.vtype = LCB_KV_COPY;
609 cmd.value.u_buf.contig.bytes = value;
610 cmd.value.u_buf.contig.nbytes = nvalue;
611 cmd.operation = mode();
612
613 if (o_json.result()) {
614 cmd.datatype = LCB_VALUE_F_JSON;
615 }
616 if (o_exp.passed()) {
617 cmd.exptime = o_exp.result();
618 }
619 if (o_flags.passed()) {
620 cmd.flags = o_flags.result();
621 }
622 if (o_persist.passed() || o_replicate.passed()) {
623 cmd.persist_to = o_persist.result();
624 cmd.replicate_to = o_replicate.result();
625 err = lcb_storedur3(instance, NULL, &cmd);
626 } else {
627 err = lcb_store3(instance, NULL, reinterpret_cast<lcb_CMDSTORE*>(&cmd));
628 }
629 if (err != LCB_SUCCESS) {
630 throw LcbError(err);
631 }
632 }
633
634 void
storeItem(const string & key,FILE * input)635 SetHandler::storeItem(const string& key, FILE *input)
636 {
637 char tmpbuf[4096];
638 vector<char> vbuf;
639 size_t nr;
640 while ((nr = fread(tmpbuf, 1, sizeof tmpbuf, input))) {
641 vbuf.insert(vbuf.end(), tmpbuf, &tmpbuf[nr]);
642 }
643 storeItem(key, &vbuf[0], vbuf.size());
644 }
645
646 void
run()647 SetHandler::run()
648 {
649 Handler::run();
650 lcb_install_callback3(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_callback);
651 lcb_install_callback3(instance, LCB_CALLBACK_STOREDUR, (lcb_RESPCALLBACK)store_callback);
652 const vector<string>& keys = parser.getRestArgs();
653
654 lcb_sched_enter(instance);
655
656 if (hasFileList()) {
657 for (size_t ii = 0; ii < keys.size(); ii++) {
658 const string& key = keys[ii];
659 FILE *fp = fopen(key.c_str(), "rb");
660 if (fp == NULL) {
661 perror(key.c_str());
662 continue;
663 }
664 storeItem(key, fp);
665 fclose(fp);
666 }
667 } else if (keys.size() > 1 || keys.empty()) {
668 throw BadArg("create must be passed a single key");
669 } else {
670 const string& key = keys[0];
671 if (o_value.passed()) {
672 const string& value = o_value.const_result();
673 storeItem(key, value.c_str(), value.size());
674 } else {
675 storeItem(key, stdin);
676 }
677 }
678
679 lcb_sched_leave(instance);
680 lcb_wait(instance);
681 }
682
683 void
run()684 HashHandler::run()
685 {
686 Handler::run();
687
688 lcbvb_CONFIG *vbc;
689 lcb_error_t err;
690 err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc);
691 if (err != LCB_SUCCESS) {
692 throw LcbError(err);
693 }
694
695 const vector<string>& args = parser.getRestArgs();
696 for (size_t ii = 0; ii < args.size(); ii++) {
697 const string& key = args[ii];
698 const void *vkey = (const void *)key.c_str();
699 int vbid, srvix;
700 lcbvb_map_key(vbc, vkey, key.size(), &vbid, &srvix);
701 fprintf(stderr, "%s: [vBucket=%d, Index=%d]", key.c_str(), vbid, srvix);
702 if (srvix != -1) {
703 fprintf(stderr, " Server: %s",
704 lcbvb_get_hostport(vbc, srvix, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN));
705 const char *vapi = lcbvb_get_capibase(vbc, srvix, LCBVB_SVCMODE_PLAIN);
706 if (vapi) {
707 fprintf(stderr, ", CouchAPI: %s", vapi);
708 }
709 }
710 fprintf(stderr, "\n");
711
712 for (size_t jj = 0; jj < lcbvb_get_nreplicas(vbc); jj++) {
713 int rix = lcbvb_vbreplica(vbc, vbid, jj);
714 const char *rname = NULL;
715 if (rix >= 0) {
716 rname = lcbvb_get_hostport(vbc, rix, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);
717 }
718 if (rname == NULL) {
719 rname = "N/A";
720 }
721 fprintf(stderr, "Replica #%d: Index=%d, Host=%s\n", (int)jj, rix, rname);
722 }
723 }
724 }
725
726 void
run()727 ObserveHandler::run()
728 {
729 Handler::run();
730 lcb_install_callback3(instance, LCB_CALLBACK_OBSERVE, (lcb_RESPCALLBACK)observe_callback);
731 const vector<string>& keys = parser.getRestArgs();
732 lcb_MULTICMD_CTX *mctx = lcb_observe3_ctxnew(instance);
733 if (mctx == NULL) {
734 throw std::bad_alloc();
735 }
736
737 lcb_error_t err;
738 for (size_t ii = 0; ii < keys.size(); ii++) {
739 lcb_CMDOBSERVE cmd = { 0 };
740 LCB_KREQ_SIMPLE(&cmd.key, keys[ii].c_str(), keys[ii].size());
741 err = mctx->addcmd(mctx, (lcb_CMDBASE*)&cmd);
742 if (err != LCB_SUCCESS) {
743 throw LcbError(err);
744 }
745 }
746
747 lcb_sched_enter(instance);
748 err = mctx->done(mctx, NULL);
749 if (err == LCB_SUCCESS) {
750 lcb_sched_leave(instance);
751 lcb_wait(instance);
752 } else {
753 lcb_sched_fail(instance);
754 throw LcbError(err);
755 }
756 }
757
758 void
run()759 ObserveSeqnoHandler::run()
760 {
761 Handler::run();
762 lcb_install_callback3(instance, LCB_CALLBACK_OBSEQNO, (lcb_RESPCALLBACK)obseqno_callback);
763 const vector<string>& infos = parser.getRestArgs();
764 lcb_CMDOBSEQNO cmd = { 0 };
765 lcbvb_CONFIG *vbc;
766 lcb_error_t rc;
767
768 rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc);
769 if (rc != LCB_SUCCESS) {
770 throw LcbError(rc);
771 }
772
773 lcb_sched_enter(instance);
774
775 for (size_t ii = 0; ii < infos.size(); ++ii) {
776 const string& cur = infos[ii];
777 unsigned vbid;
778 unsigned long long uuid;
779 int rv = sscanf(cur.c_str(), "%u,%llu", &vbid, &uuid);
780 if (rv != 2) {
781 throw BadArg("Must pass sequences of base10 vbid and base16 uuids");
782 }
783 cmd.uuid = uuid;
784 cmd.vbid = vbid;
785 for (size_t jj = 0; jj < lcbvb_get_nreplicas(vbc) + 1; ++jj) {
786 int ix = lcbvb_vbserver(vbc, vbid, jj);
787 if (ix < 0) {
788 fprintf(stderr, "Server %d unavailable (skipping)\n", ix);
789 }
790 cmd.server_index = ix;
791 rc = lcb_observe_seqno3(instance, NULL, &cmd);
792 if (rc != LCB_SUCCESS) {
793 throw LcbError(rc);
794 }
795 }
796 }
797 lcb_sched_leave(instance);
798 lcb_wait(instance);
799 }
800
801 void
run()802 UnlockHandler::run()
803 {
804 Handler::run();
805 lcb_install_callback3(instance, LCB_CALLBACK_UNLOCK, common_callback);
806 const vector<string>& args = parser.getRestArgs();
807
808 if (args.size() % 2) {
809 throw BadArg("Expect key-cas pairs. Argument list must be even");
810 }
811
812 lcb_sched_enter(instance);
813 for (size_t ii = 0; ii < args.size(); ii += 2) {
814 const string& key = args[ii];
815 lcb_CAS cas;
816 int rv;
817 rv = sscanf(args[ii+1].c_str(), "0x%" PRIx64, &cas);
818 if (rv != 1) {
819 throw BadArg("CAS must be formatted as a hex string beginning with '0x'");
820 }
821
822 lcb_CMDUNLOCK cmd;
823 memset(&cmd, 0, sizeof cmd);
824 LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
825 cmd.cas = cas;
826 lcb_error_t err = lcb_unlock3(instance, NULL, &cmd);
827 if (err != LCB_SUCCESS) {
828 throw LcbError(err);
829 }
830 }
831 lcb_sched_leave(instance);
832 lcb_wait(instance);
833 }
834
835 static const char *
iops_to_string(lcb_io_ops_type_t type)836 iops_to_string(lcb_io_ops_type_t type)
837 {
838 switch (type) {
839 case LCB_IO_OPS_LIBEV: return "libev";
840 case LCB_IO_OPS_LIBEVENT: return "libevent";
841 case LCB_IO_OPS_LIBUV: return "libuv";
842 case LCB_IO_OPS_SELECT: return "select";
843 case LCB_IO_OPS_WINIOCP: return "iocp";
844 case LCB_IO_OPS_INVALID: return "user-defined";
845 default: return "invalid";
846 }
847 }
848
849 void
run()850 VersionHandler::run()
851 {
852 const char *changeset;
853 lcb_error_t err;
854 err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_CHANGESET, (void*)&changeset);
855 if (err != LCB_SUCCESS) {
856 changeset = "UNKNOWN";
857 }
858 fprintf(stderr, "cbc:\n");
859 fprintf(stderr, " Runtime: Version=%s, Changeset=%s\n",
860 lcb_get_version(NULL), changeset);
861 fprintf(stderr, " Headers: Version=%s, Changeset=%s\n",
862 LCB_VERSION_STRING, LCB_VERSION_CHANGESET);
863 fprintf(stderr, " Build Timestamp: %s\n", LCB_BUILD_TIMESTAMP);
864
865 struct lcb_cntl_iops_info_st info;
866 memset(&info, 0, sizeof info);
867 err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &info);
868 if (err == LCB_SUCCESS) {
869 fprintf(stderr, " IO: Default=%s, Current=%s, Accessible=",
870 iops_to_string(info.v.v0.os_default), iops_to_string(info.v.v0.effective));
871 }
872 {
873 size_t ii;
874 char buf[256] = {0}, *p = buf;
875 lcb_io_ops_type_t known_io[] = {
876 LCB_IO_OPS_WINIOCP,
877 LCB_IO_OPS_LIBEVENT,
878 LCB_IO_OPS_LIBUV,
879 LCB_IO_OPS_LIBEV,
880 LCB_IO_OPS_SELECT
881 };
882
883
884 for (ii = 0; ii < sizeof(known_io) / sizeof(known_io[0]); ii++) {
885 struct lcb_create_io_ops_st cio = {0};
886 lcb_io_opt_t io = NULL;
887
888 cio.v.v0.type = known_io[ii];
889 if (lcb_create_io_ops(&io, &cio) == LCB_SUCCESS) {
890 p += sprintf(p, "%s,", iops_to_string(known_io[ii]));
891 lcb_destroy_io_ops(io);
892 }
893 }
894 *(--p) = '\n';
895 fprintf(stderr, "%s", buf);
896 }
897
898 if (lcb_supports_feature(LCB_SUPPORTS_SSL)) {
899 #ifdef LCB_NO_SSL
900 printf(" SSL: SUPPORTED\n");
901 #else
902 #if defined(OPENSSL_VERSION)
903 printf(" SSL Runtime: %s\n", OpenSSL_version(OPENSSL_VERSION));
904 #elif defined(SSLEAY_VERSION)
905 printf(" SSL Runtime: %s\n", SSLeay_version(SSLEAY_VERSION));
906 #endif
907 printf(" SSL Headers: %s\n", OPENSSL_VERSION_TEXT);
908 #endif
909 } else {
910 printf(" SSL: NOT SUPPORTED\n");
911 }
912 if (lcb_supports_feature(LCB_SUPPORTS_SNAPPY)) {
913 #define EXPAND(VAR) VAR ## 1
914 #define IS_EMPTY(VAR) EXPAND(VAR)
915
916 #if defined(SNAPPY_MAJOR) && (IS_EMPTY(SNAPPY_MAJOR) != 1)
917 printf(" Snappy: %d.%d.%d\n", SNAPPY_MAJOR, SNAPPY_MINOR, SNAPPY_PATCHLEVEL);
918 #else
919 printf(" Snappy: unknown\n");
920 #endif
921 } else {
922 printf(" Snappy: NOT SUPPORTED\n");
923 }
924 printf(" Tracing: %sSUPPORTED\n", lcb_supports_feature(LCB_SUPPORTS_TRACING) ? "" : "NOT ");
925 printf(" System: %s; %s\n", LCB_SYSTEM, LCB_SYSTEM_PROCESSOR);
926 printf(" CC: %s; %s\n", LCB_C_COMPILER, LCB_C_FLAGS);
927 printf(" CXX: %s; %s\n", LCB_CXX_COMPILER, LCB_CXX_FLAGS);
928 }
929
930 void
run()931 RemoveHandler::run()
932 {
933 Handler::run();
934 const vector<string> &keys = parser.getRestArgs();
935 lcb_sched_enter(instance);
936 lcb_install_callback3(instance, LCB_CALLBACK_REMOVE, common_callback);
937 for (size_t ii = 0; ii < keys.size(); ++ii) {
938 lcb_CMDREMOVE cmd;
939 const string& key = keys[ii];
940 memset(&cmd, 0, sizeof cmd);
941 LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
942 lcb_error_t err = lcb_remove3(instance, NULL, &cmd);
943 if (err != LCB_SUCCESS) {
944 throw LcbError(err);
945 }
946 }
947 lcb_sched_leave(instance);
948 lcb_wait(instance);
949 }
950
951 void
run()952 StatsHandler::run()
953 {
954 Handler::run();
955 lcb_install_callback3(instance, LCB_CALLBACK_STATS, (lcb_RESPCALLBACK)stats_callback);
956 vector<string> keys = parser.getRestArgs();
957 if (keys.empty()) {
958 keys.push_back("");
959 }
960 lcb_sched_enter(instance);
961 for (size_t ii = 0; ii < keys.size(); ii++) {
962 lcb_CMDSTATS cmd = { 0 };
963 const string& key = keys[ii];
964 if (!key.empty()) {
965 LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
966 if (o_keystats.result()) {
967 cmd.cmdflags = LCB_CMDSTATS_F_KV;
968 }
969 }
970 bool is_keystats = o_keystats.result();
971 lcb_error_t err = lcb_stats3(instance, &is_keystats, &cmd);
972 if (err != LCB_SUCCESS) {
973 throw LcbError(err);
974 }
975 }
976 lcb_sched_leave(instance);
977 lcb_wait(instance);
978 }
979
980 void
run()981 WatchHandler::run()
982 {
983 Handler::run();
984 lcb_install_callback3(instance, LCB_CALLBACK_STATS, (lcb_RESPCALLBACK)watch_callback);
985 vector<string> keys = parser.getRestArgs();
986 if (keys.empty()) {
987 keys.push_back("cmd_total_ops");
988 keys.push_back("cmd_total_gets");
989 keys.push_back("cmd_total_sets");
990 }
991 int interval = o_interval.result();
992
993 map<string, int64_t> prev;
994
995 bool first = true;
996 while (true) {
997 map<string, int64_t> entry;
998 lcb_sched_enter(instance);
999 lcb_CMDSTATS cmd = { 0 };
1000 lcb_error_t err = lcb_stats3(instance, (void *)&entry, &cmd);
1001 if (err != LCB_SUCCESS) {
1002 throw LcbError(err);
1003 }
1004 lcb_sched_leave(instance);
1005 lcb_wait(instance);
1006 if (first) {
1007 for (vector<string>::iterator it = keys.begin(); it != keys.end(); ++it) {
1008 fprintf(stderr, "%s: %" PRId64 "\n", it->c_str(), entry[*it]);
1009 }
1010 first = false;
1011 } else {
1012 #ifndef _WIN32
1013 if (isatty(STDERR_FILENO)) {
1014 fprintf(stderr, "\033[%dA", (int)keys.size());
1015 }
1016 #endif
1017 for (vector<string>::iterator it = keys.begin(); it != keys.end(); ++it) {
1018 fprintf(stderr, "%s: %" PRId64 "%20s\n", it->c_str(), (entry[*it] - prev[*it]) / interval, "");
1019 }
1020 }
1021 prev = entry;
1022 #ifdef _WIN32
1023 Sleep(interval * 1000);
1024 #else
1025 sleep(interval);
1026 #endif
1027 }
1028 }
1029
1030
1031 void
run()1032 VerbosityHandler::run()
1033 {
1034 Handler::run();
1035
1036 const string& slevel = getRequiredArg();
1037 lcb_verbosity_level_t level;
1038 if (slevel == "detail") {
1039 level = LCB_VERBOSITY_DETAIL;
1040 } else if (slevel == "debug") {
1041 level = LCB_VERBOSITY_DEBUG;
1042 } else if (slevel == "info") {
1043 level = LCB_VERBOSITY_INFO;
1044 } else if (slevel == "warning") {
1045 level = LCB_VERBOSITY_WARNING;
1046 } else {
1047 throw BadArg("Verbosity level must be {detail,debug,info,warning}");
1048 }
1049
1050 lcb_install_callback3(instance, LCB_CALLBACK_VERBOSITY, (lcb_RESPCALLBACK)common_server_callback);
1051 lcb_CMDVERBOSITY cmd = { 0 };
1052 cmd.level = level;
1053 lcb_error_t err;
1054 lcb_sched_enter(instance);
1055 err = lcb_server_verbosity3(instance, NULL, &cmd);
1056 if (err != LCB_SUCCESS) {
1057 throw LcbError(err);
1058 }
1059 lcb_sched_leave(instance);
1060 lcb_wait(instance);
1061 }
1062
1063 void
run()1064 McVersionHandler::run()
1065 {
1066 Handler::run();
1067
1068 lcb_install_callback3(instance, LCB_CALLBACK_VERSIONS, (lcb_RESPCALLBACK)common_server_callback);
1069 lcb_CMDBASE cmd = { 0 };
1070 lcb_error_t err;
1071 lcb_sched_enter(instance);
1072 err = lcb_server_versions3(instance, NULL, &cmd);
1073 if (err != LCB_SUCCESS) {
1074 throw LcbError(err);
1075 }
1076 lcb_sched_leave(instance);
1077 lcb_wait(instance);
1078 }
1079
1080 void
run()1081 KeygenHandler::run()
1082 {
1083 Handler::run();
1084
1085 lcbvb_CONFIG *vbc;
1086 lcb_error_t err;
1087 err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc);
1088 if (err != LCB_SUCCESS) {
1089 throw LcbError(err);
1090 }
1091
1092 unsigned num_vbuckets = lcbvb_get_nvbuckets(vbc);
1093 if (num_vbuckets == 0) {
1094 throw LcbError(LCB_EINVAL, "the configuration does not contain any vBuckets");
1095 }
1096 unsigned num_keys_per_vbucket = o_keys_per_vbucket.result();
1097 vector < vector < string > > keys(num_vbuckets);
1098 #define MAX_KEY_SIZE 16
1099 char buf[MAX_KEY_SIZE] = {0};
1100 unsigned i = 0;
1101 int left = num_keys_per_vbucket * num_vbuckets;
1102 while (left > 0 && i < UINT_MAX) {
1103 int nbuf = snprintf(buf, MAX_KEY_SIZE, "key_%010u", i++);
1104 if (nbuf <= 0) {
1105 throw LcbError(LCB_ERROR, "unable to render new key into buffer");
1106 }
1107 int vbid, srvix;
1108 lcbvb_map_key(vbc, buf, nbuf, &vbid, &srvix);
1109 if (keys[vbid].size() < num_keys_per_vbucket) {
1110 keys[vbid].push_back(buf);
1111 left--;
1112 }
1113 }
1114 for (i = 0; i < num_vbuckets; i++) {
1115 for (vector<string>::iterator it = keys[i].begin(); it != keys[i].end(); ++it) {
1116 printf("%s %u\n", it->c_str(), i);
1117 }
1118 }
1119 if (left > 0) {
1120 fprintf(stderr, "some vBuckets don't have enough keys\n");
1121 }
1122 }
1123
1124 void
run()1125 PingHandler::run()
1126 {
1127 Handler::run();
1128
1129 lcb_install_callback3(instance, LCB_CALLBACK_PING, (lcb_RESPCALLBACK)ping_callback);
1130 lcb_CMDPING cmd = { 0 };
1131 lcb_error_t err;
1132 cmd.services = LCB_PINGSVC_F_KV | LCB_PINGSVC_F_N1QL | LCB_PINGSVC_F_VIEWS | LCB_PINGSVC_F_FTS | LCB_PINGSVC_F_ANALYTICS;
1133 cmd.options = LCB_PINGOPT_F_JSON | LCB_PINGOPT_F_JSONPRETTY;
1134 if (o_details.passed()) {
1135 cmd.options |= LCB_PINGOPT_F_JSONDETAILS;
1136 }
1137 lcb_sched_enter(instance);
1138 err = lcb_ping3(instance, NULL, &cmd);
1139 if (err != LCB_SUCCESS) {
1140 throw LcbError(err);
1141 }
1142 lcb_sched_leave(instance);
1143 lcb_wait(instance);
1144 }
1145
1146 void
run()1147 McFlushHandler::run()
1148 {
1149 Handler::run();
1150
1151 lcb_CMDFLUSH cmd = { 0 };
1152 lcb_error_t err;
1153 lcb_install_callback3(instance, LCB_CALLBACK_FLUSH, (lcb_RESPCALLBACK)common_server_callback);
1154 lcb_sched_enter(instance);
1155 err = lcb_flush3(instance, NULL, &cmd);
1156 if (err != LCB_SUCCESS) {
1157 throw LcbError(err);
1158 }
1159 lcb_sched_leave(instance);
1160 lcb_wait(instance);
1161 }
1162
1163
1164 extern "C" {
cbFlushCb(lcb_t,int,const lcb_RESPBASE * resp)1165 static void cbFlushCb(lcb_t, int, const lcb_RESPBASE *resp)
1166 {
1167 if (resp->rc == LCB_SUCCESS) {
1168 fprintf(stderr, "Flush OK\n");
1169 } else {
1170 fprintf(stderr, "Flush failed: %s\n", lcb_strerror_short(resp->rc));
1171 }
1172 }
1173 }
1174 void
run()1175 BucketFlushHandler::run()
1176 {
1177 Handler::run();
1178 lcb_CMDCBFLUSH cmd = { 0 };
1179 lcb_error_t err;
1180 lcb_install_callback3(instance, LCB_CALLBACK_CBFLUSH, cbFlushCb);
1181 err = lcb_cbflush3(instance, NULL, &cmd);
1182 if (err != LCB_SUCCESS) {
1183 throw LcbError(err);
1184 } else {
1185 lcb_wait(instance);
1186 }
1187 }
1188
1189 void
run()1190 ArithmeticHandler::run()
1191 {
1192 Handler::run();
1193
1194 const vector<string>& keys = parser.getRestArgs();
1195 lcb_install_callback3(instance, LCB_CALLBACK_COUNTER, (lcb_RESPCALLBACK)arithmetic_callback);
1196 lcb_sched_enter(instance);
1197 for (size_t ii = 0; ii < keys.size(); ++ii) {
1198 const string& key = keys[ii];
1199 lcb_CMDCOUNTER cmd = { 0 };
1200 LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
1201 if (o_initial.passed()) {
1202 cmd.create = 1;
1203 cmd.initial = o_initial.result();
1204 }
1205 uint64_t delta = o_delta.result();
1206 if (delta > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
1207 throw BadArg("Delta too big");
1208 }
1209 cmd.delta = static_cast<int64_t>(delta);
1210 if (shouldInvert()) {
1211 cmd.delta *= -1;
1212 }
1213 cmd.exptime = o_expiry.result();
1214 lcb_error_t err = lcb_counter3(instance, NULL, &cmd);
1215 if (err != LCB_SUCCESS) {
1216 throw LcbError(err);
1217 }
1218 }
1219 lcb_sched_leave(instance);
1220 lcb_wait(instance);
1221 }
1222
1223 void
run()1224 ViewsHandler::run()
1225 {
1226 Handler::run();
1227
1228 const string& s = getRequiredArg();
1229 size_t pos = s.find('/');
1230 if (pos == string::npos) {
1231 throw BadArg("View must be in the format of design/view");
1232 }
1233
1234 string ddoc = s.substr(0, pos);
1235 string view = s.substr(pos+1);
1236 string opts = o_params.result();
1237
1238 lcb_CMDVIEWQUERY cmd = { 0 };
1239 lcb_view_query_initcmd(&cmd,
1240 ddoc.c_str(), view.c_str(), opts.c_str(), view_callback);
1241 if (o_spatial) {
1242 cmd.cmdflags |= LCB_CMDVIEWQUERY_F_SPATIAL;
1243 }
1244 if (o_incdocs) {
1245 cmd.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS;
1246 }
1247
1248 lcb_error_t rc;
1249 rc = lcb_view_query(instance, NULL, &cmd);
1250 if (rc != LCB_SUCCESS) {
1251 throw LcbError(rc);
1252 }
1253 lcb_wait(instance);
1254 }
1255
1256 static void
splitKvParam(const string & src,string & key,string & value)1257 splitKvParam(const string& src, string& key, string& value)
1258 {
1259 size_t pp = src.find('=');
1260 if (pp == string::npos) {
1261 throw BadArg("Param must be in the form of key=value");
1262 }
1263
1264 key = src.substr(0, pp);
1265 value = src.substr(pp+1);
1266 }
1267
1268 extern "C" {
n1qlCallback(lcb_t,int,const lcb_RESPN1QL * resp)1269 static void n1qlCallback(lcb_t, int, const lcb_RESPN1QL *resp)
1270 {
1271 if (resp->rflags & LCB_RESP_F_FINAL) {
1272 fprintf(stderr, "---> Query response finished\n");
1273 if (resp->rc != LCB_SUCCESS) {
1274 fprintf(stderr, "---> Query failed with library code %s\n", lcb_strerror_short(resp->rc));
1275 if (resp->htresp) {
1276 fprintf(stderr, "---> Inner HTTP request failed with library code %s and HTTP status %d\n",
1277 lcb_strerror_short(resp->htresp->rc), resp->htresp->htstatus);
1278 }
1279 }
1280 if (resp->row) {
1281 printf("%.*s\n", (int)resp->nrow, resp->row);
1282 }
1283 } else {
1284 printf("%.*s,\n", (int)resp->nrow, resp->row);
1285 }
1286 }
1287 }
1288
1289 void
run()1290 N1qlHandler::run()
1291 {
1292 Handler::run();
1293 const string& qstr = getRequiredArg();
1294
1295 lcb_N1QLPARAMS *nparams = lcb_n1p_new();
1296 lcb_error_t rc;
1297
1298 rc = lcb_n1p_setquery(nparams, qstr.c_str(), -1, LCB_N1P_QUERY_STATEMENT);
1299 if (rc != LCB_SUCCESS) {
1300 throw LcbError(rc);
1301 }
1302
1303 const vector<string>& vv_args = o_args.const_result();
1304 for (size_t ii = 0; ii < vv_args.size(); ii++) {
1305 string key, value;
1306 splitKvParam(vv_args[ii], key, value);
1307 string ktmp = "$" + key;
1308 rc = lcb_n1p_namedparamz(nparams, ktmp.c_str(), value.c_str());
1309 if (rc != LCB_SUCCESS) {
1310 throw LcbError(rc);
1311 }
1312 }
1313
1314 const vector<string>& vv_opts = o_opts.const_result();
1315 for (size_t ii = 0; ii < vv_opts.size(); ii++) {
1316 string key, value;
1317 splitKvParam(vv_opts[ii], key, value);
1318 rc = lcb_n1p_setoptz(nparams, key.c_str(), value.c_str());
1319 if (rc != LCB_SUCCESS) {
1320 throw LcbError(rc);
1321 }
1322 }
1323
1324 lcb_CMDN1QL cmd = { 0 };
1325 rc = lcb_n1p_mkcmd(nparams, &cmd);
1326 if (rc != LCB_SUCCESS) {
1327 throw LcbError(rc);
1328 }
1329 if (o_prepare.passed()) {
1330 cmd.cmdflags |= LCB_CMDN1QL_F_PREPCACHE;
1331 }
1332 if (o_analytics.passed()) {
1333 cmd.cmdflags |= LCB_CMDN1QL_F_ANALYTICSQUERY;
1334 }
1335 fprintf(stderr, "---> Encoded query: %.*s\n", (int)cmd.nquery, cmd.query);
1336 cmd.callback = n1qlCallback;
1337 rc = lcb_n1ql_query(instance, NULL, &cmd);
1338 if (rc != LCB_SUCCESS) {
1339 throw LcbError(rc);
1340 }
1341 lcb_n1p_free(nparams);
1342 lcb_wait(instance);
1343 }
1344
1345 void
install(lcb_t instance)1346 HttpReceiver::install(lcb_t instance)
1347 {
1348 lcb_install_callback3(instance, LCB_CALLBACK_HTTP,
1349 (lcb_RESPCALLBACK)http_callback);
1350 }
1351
1352 void
maybeInvokeStatus(const lcb_RESPHTTP * resp)1353 HttpReceiver::maybeInvokeStatus(const lcb_RESPHTTP *resp)
1354 {
1355 if (statusInvoked) {
1356 return;
1357 }
1358
1359 statusInvoked = true;
1360 if (resp->headers) {
1361 for (const char * const *cur = resp->headers; *cur; cur += 2) {
1362 string key = cur[0];
1363 string value = cur[1];
1364 headers[key] = value;
1365 }
1366 }
1367 handleStatus(resp->rc, resp->htstatus);
1368 }
1369
1370 void
run()1371 HttpBaseHandler::run()
1372 {
1373 Handler::run();
1374 install(instance);
1375 lcb_http_cmd_st cmd;
1376 memset(&cmd, 0, sizeof cmd);
1377 string uri = getURI();
1378 const string& body = getBody();
1379
1380 cmd.v.v0.method = getMethod();
1381 cmd.v.v0.chunked = 1;
1382 cmd.v.v0.path = uri.c_str();
1383 cmd.v.v0.npath = uri.size();
1384 if (!body.empty()) {
1385 cmd.v.v0.body = body.c_str();
1386 cmd.v.v0.nbody = body.size();
1387 }
1388 string ctype = getContentType();
1389 if (!ctype.empty()) {
1390 cmd.v.v0.content_type = ctype.c_str();
1391 }
1392
1393 lcb_http_request_t dummy;
1394 lcb_error_t err;
1395 err = lcb_make_http_request(instance, (HttpReceiver*)this,
1396 isAdmin() ? LCB_HTTP_TYPE_MANAGEMENT : LCB_HTTP_TYPE_VIEW,
1397 &cmd, &dummy);
1398 if (err != LCB_SUCCESS) {
1399 throw LcbError(err);
1400 }
1401
1402 lcb_wait(instance);
1403 }
1404
1405 lcb_http_method_t
getMethod()1406 HttpBaseHandler::getMethod()
1407 {
1408 string smeth = o_method.result();
1409 if (smeth == "GET") {
1410 return LCB_HTTP_METHOD_GET;
1411 } else if (smeth == "POST") {
1412 return LCB_HTTP_METHOD_POST;
1413 } else if (smeth == "DELETE") {
1414 return LCB_HTTP_METHOD_DELETE;
1415 } else if (smeth == "PUT") {
1416 return LCB_HTTP_METHOD_PUT;
1417 } else {
1418 throw BadArg("Unrecognized method string");
1419 }
1420 }
1421
1422 const string&
getBody()1423 HttpBaseHandler::getBody()
1424 {
1425 if (!body_cached.empty()) {
1426 return body_cached;
1427 }
1428 lcb_http_method_t meth = getMethod();
1429 if (meth == LCB_HTTP_METHOD_GET || meth == LCB_HTTP_METHOD_DELETE) {
1430 return body_cached; // empty
1431 }
1432
1433 char buf[4096];
1434 size_t nr;
1435 while ( (nr = fread(buf, 1, sizeof buf, stdin)) != 0) {
1436 body_cached.append(buf, nr);
1437 }
1438 return body_cached;
1439 }
1440
1441 void
handleStatus(lcb_error_t err,int code)1442 HttpBaseHandler::handleStatus(lcb_error_t err, int code)
1443 {
1444 if (err != LCB_SUCCESS) {
1445 fprintf(stderr, "ERROR: %s ", lcb_strerror_short(err));
1446 }
1447 fprintf(stderr, "%d\n", code);
1448 map<string,string>::const_iterator ii = headers.begin();
1449 for (; ii != headers.end(); ii++) {
1450 fprintf(stderr, " %s: %s\n", ii->first.c_str(), ii->second.c_str());
1451 }
1452 }
1453
1454 string
getURI()1455 AdminHandler::getURI()
1456 {
1457 return getRequiredArg();
1458 }
1459
1460 void
run()1461 AdminHandler::run()
1462 {
1463 fprintf(stderr, "Requesting %s\n", getURI().c_str());
1464 HttpBaseHandler::run();
1465 printf("%s\n", resbuf.c_str());
1466 }
1467
1468 void
run()1469 BucketCreateHandler::run()
1470 {
1471 const string& name = getRequiredArg();
1472 const string& btype = o_btype.const_result();
1473 stringstream ss;
1474
1475 if (btype == "couchbase" || btype == "membase") {
1476 isMemcached = false;
1477 } else if (btype == "memcached") {
1478 isMemcached = true;
1479 } else {
1480 throw BadArg("Unrecognized bucket type");
1481 }
1482 if (o_proxyport.passed() && o_bpass.passed()) {
1483 throw BadArg("Custom ASCII port is only available for auth-less buckets");
1484 }
1485
1486 ss << "name=" << name;
1487 ss << "&bucketType=" << btype;
1488 ss << "&ramQuotaMB=" << o_ramquota.result();
1489 if (o_proxyport.passed()) {
1490 ss << "&authType=none&proxyPort=" << o_proxyport.result();
1491 } else {
1492 ss << "&authType=sasl&saslPassword=" << o_bpass.result();
1493 }
1494
1495 ss << "&replicaNumber=" << o_replicas.result();
1496 body_s = ss.str();
1497
1498 AdminHandler::run();
1499 }
1500
1501 void
run()1502 RbacHandler::run()
1503 {
1504 fprintf(stderr, "Requesting %s\n", getURI().c_str());
1505 HttpBaseHandler::run();
1506 if (o_raw.result()) {
1507 printf("%s\n", resbuf.c_str());
1508 } else {
1509 format();
1510 }
1511 }
1512
1513 void
format()1514 RoleListHandler::format()
1515 {
1516 Json::Value json;
1517 if (!Json::Reader().parse(resbuf, json)) {
1518 fprintf(stderr, "Failed to parse response as JSON, falling back to raw mode\n");
1519 printf("%s\n", resbuf.c_str());
1520 }
1521
1522 std::map<string, string> roles;
1523 size_t max_width = 0;
1524 for (Json::Value::iterator i = json.begin(); i != json.end(); i++) {
1525 Json::Value role = *i;
1526 string role_id = role["role"].asString() + ": ";
1527 roles[role_id] = role["desc"].asString();
1528 if (max_width < role_id.size()) {
1529 max_width = role_id.size();
1530 }
1531 }
1532 for (map<string, string>::iterator i = roles.begin(); i != roles.end(); i++) {
1533 std::cout << std::left << std::setw(max_width) << i->first << i->second << std::endl;
1534 }
1535 }
1536
1537 void
format()1538 UserListHandler::format()
1539 {
1540 Json::Value json;
1541 if (!Json::Reader().parse(resbuf, json)) {
1542 fprintf(stderr, "Failed to parse response as JSON, falling back to raw mode\n");
1543 printf("%s\n", resbuf.c_str());
1544 }
1545
1546 map<string, map<string, string> > users;
1547 size_t max_width = 0;
1548 for (Json::Value::iterator i = json.begin(); i != json.end(); i++) {
1549 Json::Value user = *i;
1550 string domain = user["domain"].asString();
1551 string user_id = user["id"].asString();
1552 string user_name = user["name"].asString();
1553 if (!user_name.empty()) {
1554 user_id += " (" + user_name + "): ";
1555 }
1556 stringstream roles;
1557 Json::Value roles_ary = user["roles"];
1558 for (Json::Value::iterator j = roles_ary.begin(); j != roles_ary.end(); j++) {
1559 Json::Value role = *j;
1560 roles << "\n - " << role["role"].asString();
1561 if (!role["bucket_name"].empty()) {
1562 roles << "[" << role["bucket_name"].asString() << "]";
1563 }
1564 }
1565 if (max_width < user_id.size()) {
1566 max_width = user_id.size();
1567 }
1568 users[domain][user_id] = roles.str();
1569 }
1570 if (!users["local"].empty()) {
1571 std::cout << "Local users:" << std::endl;
1572 int j = 1;
1573 for (map<string, string>::iterator i = users["local"].begin(); i != users["local"].end(); i++, j++) {
1574 std::cout << j << ". " << std::left << std::setw(max_width) << i->first << i->second << std::endl;
1575 }
1576 }
1577 if (!users["external"].empty()) {
1578 std::cout << "External users:" << std::endl;
1579 int j = 1;
1580 for (map<string, string>::iterator i = users["external"].begin(); i != users["external"].end(); i++, j++) {
1581 std::cout << j << ". " << std::left << std::setw(max_width) << i->first << i->second << std::endl;
1582 }
1583 }
1584 }
1585
1586 void
run()1587 UserUpsertHandler::run()
1588 {
1589 stringstream ss;
1590
1591 name = getRequiredArg();
1592 domain = o_domain.result();
1593 if (domain != "local" && domain != "external") {
1594 throw BadArg("Unrecognized domain type");
1595 }
1596 if (!o_roles.passed()) {
1597 throw BadArg("At least one role has to be specified");
1598 }
1599 std::vector<std::string> roles = o_roles.result();
1600 std::string roles_param;
1601 for (size_t ii = 0; ii < roles.size(); ii++) {
1602 if (roles_param.empty()) {
1603 roles_param += roles[ii];
1604 } else {
1605 roles_param += std::string(",") + roles[ii];
1606 }
1607 }
1608 ss << "roles=" << roles_param;
1609 if (o_full_name.passed()) {
1610 ss << "&name=" << o_full_name.result();
1611 }
1612 if (o_password.passed()) {
1613 ss << "&password=" << o_password.result();
1614 }
1615 body = ss.str();
1616
1617 AdminHandler::run();
1618 }
1619
1620 struct HostEnt {
1621 string protostr;
1622 string hostname;
HostEntHostEnt1623 HostEnt(const std::string& host, const char *proto) {
1624 protostr = proto;
1625 hostname = host;
1626 }
HostEntHostEnt1627 HostEnt(const std::string& host, const char* proto, int port) {
1628 protostr = proto;
1629 hostname = host;
1630 stringstream ss;
1631 ss << std::dec << port;
1632 hostname += ":";
1633 hostname += ss.str();
1634 }
1635 };
1636
1637 void
run()1638 ConnstrHandler::run()
1639 {
1640 const string& connstr_s = getRequiredArg();
1641 lcb_error_t err;
1642 const char *errmsg;
1643 lcb::Connspec spec;
1644 err = spec.parse(connstr_s.c_str(), &errmsg);
1645 if (err != LCB_SUCCESS) {
1646 throw BadArg(errmsg);
1647 }
1648
1649 printf("Bucket: %s\n", spec.bucket().c_str());
1650 printf("Implicit port: %d\n", spec.default_port());
1651 string sslOpts;
1652 if (spec.sslopts() & LCB_SSL_ENABLED) {
1653 sslOpts = "ENABLED";
1654 if (spec.sslopts() & LCB_SSL_NOVERIFY) {
1655 sslOpts += "|NOVERIFY";
1656 }
1657 } else {
1658 sslOpts = "DISABLED";
1659 }
1660 printf("SSL: %s\n", sslOpts.c_str());
1661
1662 printf("Boostrap Protocols: ");
1663 string bsStr;
1664 if (spec.is_bs_cccp()) {
1665 bsStr += "CCCP, ";
1666 }
1667 if (spec.is_bs_http()) {
1668 bsStr += "HTTP, ";
1669 }
1670 if (bsStr.empty()) {
1671 bsStr = "CCCP,HTTP";
1672 } else {
1673 bsStr.erase(bsStr.size()-1, 1);
1674 }
1675 printf("%s\n", bsStr.c_str());
1676 printf("Hosts:\n");
1677 vector<HostEnt> hosts;
1678
1679 for (size_t ii = 0; ii < spec.hosts().size(); ++ii) {
1680 const lcb::Spechost *dh = &spec.hosts()[ii];
1681 lcb_U16 port = dh->port;
1682 if (!port) {
1683 port = spec.default_port();
1684 }
1685
1686 if (dh->type == LCB_CONFIG_MCD_PORT) {
1687 hosts.push_back(HostEnt(dh->hostname, "memcached", port));
1688 } else if (dh->type == LCB_CONFIG_MCD_SSL_PORT) {
1689 hosts.push_back(HostEnt(dh->hostname, "memcached+ssl", port));
1690 } else if (dh->type == LCB_CONFIG_HTTP_PORT) {
1691 hosts.push_back(HostEnt(dh->hostname, "restapi", port));
1692 } else if (dh->type == LCB_CONFIG_HTTP_SSL_PORT) {
1693 hosts.push_back(HostEnt(dh->hostname, "restapi+ssl", port));
1694 } else {
1695 if (spec.sslopts()) {
1696 hosts.push_back(HostEnt(dh->hostname, "memcached+ssl", LCB_CONFIG_MCD_SSL_PORT));
1697 hosts.push_back(HostEnt(dh->hostname, "restapi+ssl", LCB_CONFIG_HTTP_SSL_PORT));
1698 } else {
1699 hosts.push_back(HostEnt(dh->hostname, "memcached", LCB_CONFIG_MCD_PORT));
1700 hosts.push_back(HostEnt(dh->hostname, "restapi", LCB_CONFIG_HTTP_PORT));
1701 }
1702 }
1703 }
1704 for (size_t ii = 0; ii < hosts.size(); ii++) {
1705 HostEnt& ent = hosts[ii];
1706 string protostr = "[" + ent.protostr + "]";
1707 printf(" %-20s%s\n", protostr.c_str(), ent.hostname.c_str());
1708 }
1709
1710 printf("Options: \n");
1711 lcb::Connspec::Options::const_iterator it = spec.options().begin();
1712 for (; it != spec.options().end(); ++it) {
1713 printf(" %s=%s\n", it->first.c_str(), it->second.c_str());
1714 }
1715 }
1716
1717 void
run()1718 WriteConfigHandler::run()
1719 {
1720 lcb_create_st cropts;
1721 params.fillCropts(cropts);
1722 string outname = getLoneArg();
1723 if (outname.empty()) {
1724 outname = ConnParams::getConfigfileName();
1725 }
1726 // Generate the config
1727 params.writeConfig(outname);
1728 }
1729
1730 static map<string,Handler*> handlers;
1731 static map<string,Handler*> handlers_s;
1732 static const char* optionsOrder[] = {
1733 "help",
1734 "cat",
1735 "create",
1736 "touch",
1737 "observe",
1738 "observe-seqno",
1739 "incr",
1740 "decr",
1741 "mcflush",
1742 "hash",
1743 "lock",
1744 "unlock",
1745 "cp",
1746 "rm",
1747 "stats",
1748 // "verify,
1749 "version",
1750 "verbosity",
1751 "view",
1752 "query",
1753 "admin",
1754 "bucket-create",
1755 "bucket-delete",
1756 "bucket-flush",
1757 "role-list",
1758 "user-list",
1759 "user-upsert",
1760 "user-delete",
1761 "connstr",
1762 "write-config",
1763 "strerror",
1764 "ping",
1765 "watch",
1766 "keygen",
1767 NULL
1768 };
1769
1770 class HelpHandler : public Handler {
1771 public:
HelpHandler()1772 HelpHandler() : Handler("help") {}
1773 HANDLER_DESCRIPTION("Show help")
1774 protected:
run()1775 void run() {
1776 fprintf(stderr, "Usage: cbc <command> [options]\n");
1777 fprintf(stderr, "command may be:\n");
1778 for (const char ** cur = optionsOrder; *cur; cur++) {
1779 const Handler *handler = handlers[*cur];
1780 fprintf(stderr, " %-20s", *cur);
1781 fprintf(stderr, "%s\n", handler->description());
1782 }
1783 }
1784 };
1785
1786 class StrErrorHandler : public Handler {
1787 public:
StrErrorHandler()1788 StrErrorHandler() : Handler("strerror") {}
1789 HANDLER_DESCRIPTION("Decode library error code")
1790 HANDLER_USAGE("HEX OR DECIMAL CODE")
1791 protected:
handleOptions()1792 void handleOptions() { }
run()1793 void run() {
1794 std::string nn = getRequiredArg();
1795 // Try to parse it as a hexadecimal number
1796 unsigned errcode;
1797 int rv = sscanf(nn.c_str(), "0x%x", &errcode);
1798 if (rv != 1) {
1799 rv = sscanf(nn.c_str(), "%u", &errcode);
1800 if (rv != 1) {
1801 throw BadArg("Need decimal or hex code!");
1802 }
1803 }
1804
1805 #define X(cname, code, cat, desc) \
1806 if (code == errcode) { \
1807 fprintf(stderr, "%s\n", #cname); \
1808 fprintf(stderr, " Type: 0x%x\n", cat); \
1809 fprintf(stderr, " Description: %s\n", desc); \
1810 return; \
1811 } \
1812
1813 LCB_XERR(X)
1814 #undef X
1815
1816 fprintf(stderr, "-- Error code not found in header. Trying runtime..\n");
1817 fprintf(stderr, "%s\n", lcb_strerror_long((lcb_error_t)errcode));
1818 }
1819 };
1820
1821 static void
setupHandlers()1822 setupHandlers()
1823 {
1824 handlers_s["get"] = new GetHandler();
1825 handlers_s["create"] = new SetHandler();
1826 handlers_s["hash"] = new HashHandler();
1827 handlers_s["help"] = new HelpHandler();
1828 handlers_s["lock"] = new GetHandler("lock");
1829 handlers_s["observe"] = new ObserveHandler();
1830 handlers_s["unlock"] = new UnlockHandler();
1831 handlers_s["version"] = new VersionHandler();
1832 handlers_s["rm"] = new RemoveHandler();
1833 handlers_s["cp"] = new SetHandler("cp");
1834 handlers_s["stats"] = new StatsHandler();
1835 handlers_s["watch"] = new WatchHandler();
1836 handlers_s["verbosity"] = new VerbosityHandler();
1837 handlers_s["ping"] = new PingHandler();
1838 handlers_s["mcflush"] = new McFlushHandler();
1839 handlers_s["incr"] = new IncrHandler();
1840 handlers_s["decr"] = new DecrHandler();
1841 handlers_s["admin"] = new AdminHandler();
1842 handlers_s["bucket-create"] = new BucketCreateHandler();
1843 handlers_s["bucket-delete"] = new BucketDeleteHandler();
1844 handlers_s["bucket-flush"] = new BucketFlushHandler();
1845 handlers_s["view"] = new ViewsHandler();
1846 handlers_s["query"] = new N1qlHandler();
1847 handlers_s["connstr"] = new ConnstrHandler();
1848 handlers_s["write-config"] = new WriteConfigHandler();
1849 handlers_s["strerror"] = new StrErrorHandler();
1850 handlers_s["observe-seqno"] = new ObserveSeqnoHandler();
1851 handlers_s["touch"] = new TouchHandler();
1852 handlers_s["role-list"] = new RoleListHandler();
1853 handlers_s["user-list"] = new UserListHandler();
1854 handlers_s["user-upsert"] = new UserUpsertHandler();
1855 handlers_s["user-delete"] = new UserDeleteHandler();
1856 handlers_s["mcversion"] = new McVersionHandler();
1857 handlers_s["keygen"] = new KeygenHandler();
1858
1859 map<string,Handler*>::iterator ii;
1860 for (ii = handlers_s.begin(); ii != handlers_s.end(); ++ii) {
1861 handlers[ii->first] = ii->second;
1862 }
1863
1864 handlers["cat"] = handlers["get"];
1865 handlers["n1ql"] = handlers["query"];
1866 }
1867
1868 #if _POSIX_VERSION >= 200112L
1869 #include <libgen.h>
1870 #define HAVE_BASENAME
1871 #endif
1872
1873 static void
parseCommandname(string & cmdname,int &,char ** & argv)1874 parseCommandname(string& cmdname, int&, char**& argv)
1875 {
1876 #ifdef HAVE_BASENAME
1877 cmdname = basename(argv[0]);
1878 size_t dashpos;
1879
1880 if (cmdname.find("cbc") != 0) {
1881 cmdname.clear();
1882 // Doesn't start with cbc
1883 return;
1884 }
1885
1886 if ((dashpos = cmdname.find('-')) != string::npos &&
1887 cmdname.find("cbc") != string::npos &&
1888 dashpos+1 < cmdname.size()) {
1889
1890 // Get the actual command name
1891 cmdname = cmdname.substr(dashpos+1);
1892 return;
1893 }
1894 #else
1895 (void)argv;
1896 #endif
1897 cmdname.clear();
1898 }
1899
1900 static void
wrapExternalBinary(int argc,char ** argv,const std::string & name)1901 wrapExternalBinary(int argc, char **argv, const std::string& name)
1902 {
1903 #ifdef _POSIX_VERSION
1904 vector<char *> args;
1905 string exePath(argv[0]);
1906 size_t cbc_pos = exePath.find("cbc");
1907
1908 if (cbc_pos == string::npos) {
1909 fprintf(stderr, "Failed to invoke %s (%s)\n", name.c_str(), exePath.c_str());
1910 exit(EXIT_FAILURE);
1911 }
1912
1913 exePath.replace(cbc_pos, 3, name);
1914 args.push_back((char*)exePath.c_str());
1915
1916 // { "cbc", "name" }
1917 argv += 2; argc -= 2;
1918 for (int ii = 0; ii < argc; ii++) {
1919 args.push_back(argv[ii]);
1920 }
1921 args.push_back((char*)NULL);
1922 execvp(exePath.c_str(), &args[0]);
1923 fprintf(stderr, "Failed to execute execute %s (%s): %s\n", name.c_str(), exePath.c_str(), strerror(errno));
1924 #else
1925 fprintf(stderr, "Can't wrap around %s on non-POSIX environments", name.c_str());
1926 #endif
1927 exit(EXIT_FAILURE);
1928 }
1929
cleanupHandlers()1930 static void cleanupHandlers()
1931 {
1932 map<string,Handler*>::iterator iter = handlers_s.begin();
1933 for (; iter != handlers_s.end(); iter++) {
1934 delete iter->second;
1935 }
1936 }
1937
main(int argc,char ** argv)1938 int main(int argc, char **argv)
1939 {
1940
1941 // Wrap external binaries immediately
1942 if (argc >= 2) {
1943 if (strcmp(argv[1], "pillowfight") == 0) {
1944 wrapExternalBinary(argc, argv, "cbc-pillowfight");
1945 } else if (strcmp(argv[1], "n1qlback") == 0) {
1946 wrapExternalBinary(argc, argv, "cbc-n1qlback");
1947 } else if (strcmp(argv[1], "subdoc") == 0) {
1948 wrapExternalBinary(argc, argv, "cbc-subdoc");
1949 } else if (strcmp(argv[1], "proxy") == 0) {
1950 wrapExternalBinary(argc, argv, "cbc-proxy");
1951 }
1952 }
1953
1954 setupHandlers();
1955 std::atexit(cleanupHandlers);
1956
1957 string cmdname;
1958 parseCommandname(cmdname, argc, argv);
1959
1960 if (cmdname.empty()) {
1961 if (argc < 2) {
1962 fprintf(stderr, "Must provide an option name\n");
1963 try {
1964 HelpHandler().execute(argc, argv);
1965 } catch (std::exception& exc) {
1966 std::cerr << exc.what() << std::endl;
1967 }
1968 exit(EXIT_FAILURE);
1969 } else {
1970 cmdname = argv[1];
1971 argv++;
1972 argc--;
1973 }
1974 }
1975
1976 Handler *handler = handlers[cmdname];
1977 if (handler == NULL) {
1978 fprintf(stderr, "Unknown command %s\n", cmdname.c_str());
1979 HelpHandler().execute(argc, argv);
1980 exit(EXIT_FAILURE);
1981 }
1982
1983 try {
1984 handler->execute(argc, argv);
1985
1986 } catch (std::exception& err) {
1987 fprintf(stderr, "%s\n", err.what());
1988 exit(EXIT_FAILURE);
1989 }
1990 }
1991