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