2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
27 #include "libmemcached/common.h"
29 class client_options {
32 struct extended_option {
35 std::function<bool(client_options &, extended_option &)> parse;
36 std::function<bool(const client_options &, const extended_option &, memcached_st *)> apply;
41 std::vector<extended_option> options;
42 std::vector<extended_option> defaults;
44 const char *prog_name;
45 const char *prog_vers;
46 const char *prog_desc;
47 const char *prog_argp;
49 client_options(const char *prg, const char *ver, const char *dsc, const char *arg = nullptr)
58 def("help", 'h', no_argument, "Print this help.")
59 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
66 def("version", 'V', no_argument, "Print program version.")
67 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
75 def("verbose", 'v', no_argument, "Print more informational output.")
76 .parse = [](client_options &opt, extended_option &) {
80 def("debug", 'd', no_argument, "Print output useful only for debugging.")
81 .parse = [](client_options &opt, extended_option &) {
86 def("quiet", 'q', no_argument, "Print no output, not even errors.")
87 .parse = [](client_options &opt, extended_option &) {
93 def("password", 'p', required_argument, "SASL password.");
94 def("username", 'u', required_argument, "SASL username.")
95 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
96 if (auto username = ext.arg) {
97 if (!LIBMEMCACHED_WITH_SASL_SUPPORT) {
98 if (!opt.isset("quiet")) {
100 << "SASL username was supplied, but binary was not built with SASL support.\n";
105 if (MEMCACHED_SUCCESS
106 != memcached_set_sasl_auth_data(memc, username, opt.argof("password"))) {
107 if (!opt.isset("quiet")) {
108 std::cerr << memcached_last_error_message(memc);
117 def("binary", 'b', no_argument, "Use the binary memcached protocol.")
118 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
119 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, ext.set)) {
120 if(!opt.isset("quiet")) {
121 std::cerr << memcached_last_error_message(memc);
127 def("buffer", 'B', no_argument, "Buffer requests.")
128 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
129 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, ext.set)) {
130 if(!opt.isset("quiet")) {
131 std::cerr << memcached_last_error_message(memc);
137 def("non-blocking", 'n', no_argument, "Use non-blocking connections.")
138 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
139 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, ext.set)) {
140 if(!opt.isset("quiet")) {
141 std::cerr << memcached_last_error_message(memc);
147 def("tcp-nodelay", 'N', no_argument, "Disable Nagle's algorithm.")
148 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
149 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, ext.set)) {
150 if(!opt.isset("quiet")) {
151 std::cerr << memcached_last_error_message(memc);
157 def("servers", 's', required_argument, "List of servers to connect to.")
158 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
159 auto servers = ext.arg;
161 if (opt.isset("verbose")) {
162 std::cerr << "Checking environment for a server list in MEMCACHED_SERVERS.\n";
164 servers = getenv("MEMCACHED_SERVERS");
165 if (!servers || !*servers) {
166 if (!opt.isset("quiet")) {
167 std::cerr << "No servers provided.\n";
173 auto server_list = memcached_servers_parse(servers);
174 if (!server_list || !memcached_server_list_count(server_list)) {
175 if (!opt.isset("quiet")) {
176 std::cerr << "Invalid server list provided: '" << servers << "'\n";
179 memcached_server_list_free(server_list);
184 if (MEMCACHED_SUCCESS != memcached_server_push(memc, server_list)) {
185 if (!opt.isset("quiet")) {
186 std::cerr << memcached_last_error_message(memc);
188 memcached_server_list_free(server_list);
191 memcached_server_list_free(server_list);
194 def("hash", 'H', required_argument, "Key hashing method.")
195 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
197 std::string hash_wanted{ext.arg};
198 memcached_hash_t hash = MEMCACHED_HASH_DEFAULT;
200 std::transform(hash_wanted.begin(), hash_wanted.end(), hash_wanted.begin(), ::toupper);
202 if (opt.isset("verbose")) {
203 std::cerr << "Checking for hash '" << hash_wanted << "'.\n";
205 for (int h = MEMCACHED_HASH_DEFAULT; h < MEMCACHED_HASH_MAX; ++h) {
206 auto hash_type = static_cast<memcached_hash_t>(h);
207 std::string hash_string{libmemcached_string_hash(hash_type)};
209 if (hash_wanted.length() == hash_string.length()) {
210 auto ci = std::equal(hash_string.begin(), hash_string.end(), hash_wanted.begin(),
211 [](int a, int b) { return ::toupper(a) == b; });
218 if (hash == MEMCACHED_HASH_DEFAULT) {
219 if (!opt.isset("quiet")) {
220 std::cerr << "Could not find hash '" << hash_wanted << "'.\n";
223 if (MEMCACHED_SUCCESS != memcached_behavior_set_key_hash(memc, hash)) {
224 if (!opt.isset("quiet")) {
225 std::cerr << memcached_last_error_message(memc);
234 extended_option &def(option opt, std::string help) {
235 defaults.emplace_back(extended_option{opt, std::move(help), {}, {}, nullptr, false});
236 return defaults.back();
239 extended_option &def(const char *name, char flag, int has_arg, const char *help) {
240 return def(option{name, has_arg, nullptr, flag}, help);
243 extended_option &add(extended_option ext) {
244 options.emplace_back(std::move(ext));
245 return options.back();
248 extended_option &add(option opt, std::string help) {
249 options.emplace_back(extended_option{opt, std::move(help), nullptr, nullptr, nullptr, false});
250 return options.back();
253 extended_option &add(const char *name, char flag, int has_arg, const char *help) {
254 return add(option{name, has_arg, nullptr, flag}, help);
257 extended_option &get(const std::string &name) {
261 extended_option &get(int c) {
266 const extended_option &get(const std::string &name) const {
270 const extended_option &get(int c) const {
275 bool has(const std::string &name) const {
276 auto found = find(name);
277 return found != options.cend();
279 bool has(int c) const {
280 auto found = find(c);
281 return found != options.cend();
284 bool isset(const std::string &name) const {
285 return has(name) && get(name).set;
287 bool isset(int c) const {
288 return has(c) && get(c).set;
291 void unset(const std::string &name) {
298 void set(const std::string &name, bool set_ = true, char *optarg_ = nullptr) {
300 auto &opt = get(name);
305 void set(int c, bool set_ = true, char *optarg_ = nullptr) {
313 const char *argof(const std::string &name) const {
315 return get(name).arg;
319 const char *argof(int c) const {
326 const extended_option &operator[](const std::string &name) const {
329 const extended_option &operator[](int c) const {
333 void print_version() const;
334 void print_help() const;
336 bool parse(int argc, char *argv[], char ***argp = nullptr);
337 bool apply(memcached_st *memc);
340 using iterator = std::vector<extended_option>::iterator;
341 using const_iterator = std::vector<extended_option>::const_iterator;
342 using predicate = std::function<bool(const extended_option &ext)>;
344 static option null_opt;
345 static const extended_option null_ext_opt;
347 const_iterator find(const predicate &pred) const {
348 return std::find_if(options.cbegin(), options.cend(), pred);
350 const_iterator find(const std::string &name) const {
351 return find([&name](const extended_option &ext) {
352 return ext.opt.name && ext.opt.name == name;
355 const_iterator find(int c) const {
356 return find([c](const extended_option &ext) {
357 return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
361 iterator find(const predicate &pred) {
362 return std::find_if(options.begin(), options.end(), pred);
364 iterator find(const std::string &name) {
365 return find([&name](const extended_option &ext) {
366 return ext.opt.name && ext.opt.name == name;
369 iterator find(int c) {
370 return find([c](const extended_option &ext) {
371 return ext.opt.val == c || (c == 1 && ext.opt.val == '-');