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 +--------------------------------------------------------------------+
25 struct client_options {
26 static const int first_flag = '/';
27 static const int last_flag = 'z';
28 static const int flag_count = last_flag - first_flag + 1;
30 enum class flag : int {
87 unsigned long concurrency;
98 const char * const *positional;
101 option avail[flag_count];
102 const char *help[flag_count];
104 std::string short_opts;
105 std::vector<struct option> long_opts;
107 const char *progname;
108 const char *progvers;
109 const char *progdesc;
111 client_options(const char *prg, const char *ver, const char *dsc, const std::vector<client_options::flag> &f)
123 # define opt_flag(s, a, h) opt_flag_ex(#s, s, a, h)
124 # define opt_flag_ex(n, v, a, h) \
125 [static_cast<int>(flag::v)-first_flag] = {n, a, &flags.v, static_cast<int>(flag::v)}; \
126 help[static_cast<int>(flag::v)-first_flag] = h
128 # define opt_data(s, a, h) opt_data_ex(#s, s, a, h)
129 # define opt_data_ex(n, v, a, h) \
130 [static_cast<int>(flag::v)-first_flag] = {n, a, nullptr, static_cast<int>(flag::v)}; \
131 help[static_cast<int>(flag::v)-first_flag] = h
133 avail opt_flag( absolute, no_argument, "Use absolute path names.");
134 avail opt_flag( flush, no_argument, "Flush the server(s).");
135 avail opt_flag_ex("add", add_cmd, no_argument, "Perform ADD operations.");
136 avail opt_flag( buffer, no_argument, "Buffer requests.");
137 avail opt_data( concurrency,required_argument,"Level of concurrency.");
138 avail opt_flag( flags, no_argument, "Print or set associated flags for key(s).");
139 avail opt_data( hash, required_argument,"Select key hash.");
140 avail opt_data( load, required_argument,"Initial load.");
141 avail opt_flag_ex("tcp-nodelay",tcp_nodelay,no_argument, "Disable Nagle's algorithm.");
142 avail opt_data( query, no_argument, "Query arguments.");
143 avail opt_flag_ex("replace", replace_cmd,no_argument, "Perform REPLACE operations.");
144 avail opt_flag_ex("set", set_cmd, no_argument, "Perform SET operations.");
145 avail opt_flag( udp, no_argument, "Use UDP.");
146 avail opt_flag( version, no_argument, "Print program version.");
147 avail opt_flag( analyze, no_argument, "Analyze the server's statistics.");
148 avail opt_flag( binary, no_argument, "Use the binary memcached protocol.");
149 avail opt_flag( debug, no_argument, "Print output useful only for debugging.");
150 avail opt_data( expire, required_argument,"Set associated expiry time for key(s).");
151 avail opt_data( file, required_argument,"File to save to or read from.");
152 avail opt_flag( help, no_argument, "Print this help.");
153 avail opt_flag_ex("non-block", non_block, no_argument, "Use non blocking connections.");
154 avail opt_data( password, required_argument,"SASL password.");
155 avail opt_flag( quiet, no_argument, "Print no output, not even errors.");
156 avail opt_data( repeat, required_argument,"Repeat operation n times.");
157 avail opt_data( servers, required_argument,"List of servers to connect to.");
158 avail opt_data( test, required_argument,"Test to perform.");
159 avail opt_data( username, required_argument,"SASL username.");
160 avail opt_flag( verbose, no_argument, "Print more informational output.");
161 avail opt_flag( zero, no_argument, "Zero.");
163 long_opts.reserve(f.size() + 1);
164 short_opts.reserve(f.size() * 3);
167 auto &opt = avail[static_cast<int>(o)-first_flag];
169 long_opts.push_back(opt);
170 short_opts.push_back(opt.val);
172 for (int i = 0; i < opt.has_arg; ++i) {
173 short_opts.push_back(':');
177 long_opts.push_back(option{});
180 void printVersion() const {
181 std::cout << progname << " v" << progvers
182 << " (libmemcached v" << LIBMEMCACHED_VERSION_STRING << ")"
186 void printHelp(const char *positional_args, const char *extra_doc = nullptr) const {
188 std::cout << "\n\t" << progdesc << "\n\n";
189 std::cout << "Usage:\n\t" << progname << " -[";
190 for (auto &opt : long_opts) {
192 std::cout << (char) opt.val;
195 std::cout << "] [--";
196 for (auto &opt : long_opts) {
198 std::cout << (char) opt.val;
199 if ((&opt)+1 != &*long_opts.rbegin())
203 std::cout << " <arg>] ";
205 std::cout << positional_args << "\n\n";
206 std::cout << "Options:\n";
207 for (auto &opt : long_opts) {
209 std::cout << "\t-" << (char) opt.val << "|--" << opt.name;
211 if (opt.has_arg == optional_argument) {
215 if (opt.has_arg == optional_argument) {
219 std::cout << "\n\t\t" << help[opt.val - first_flag];
220 std::cout << std::endl;
225 std::cout << extra_doc << std::endl;
229 void printLastError(memcached_st *memc) const {
231 std::cerr << memcached_last_error_message(memc);
235 memcached_hash_t getHash() const {
236 memcached_hash_t hash = MEMCACHED_HASH_DEFAULT;
237 if (args.hash && *args.hash) {
239 std::cerr << "Checking for hash '" << args.hash << "'.\n";
241 for (int h = hash; h < MEMCACHED_HASH_MAX; ++h) {
242 auto hash_type = static_cast<memcached_hash_t>(h);
243 std::string hash_string{libmemcached_string_hash(hash_type)};
244 std::string hash_wanted{args.hash};
246 if (hash_wanted.length() == hash_string.length()) {
247 auto ci = std::equal(hash_string.begin(), hash_string.end(), hash_wanted.begin(), [](int a, int b) {
248 return std::tolower(a) == std::tolower(b);
256 if (hash == MEMCACHED_HASH_DEFAULT) {
258 std::cerr << "Could not find hash '" << args.hash << "'.\n";
265 memcached_server_st *getServers() {
268 std::cerr << "Checking environment for a server list in MEMCACHED_SERVERS.\n";
270 args.servers = getenv("MEMCACHED_SERVERS");
271 if (!args.servers || !*args.servers) {
273 std::cerr << "No servers provided.\n";
279 auto servers = memcached_servers_parse(args.servers);
280 if (!servers || !memcached_server_list_count(servers)) {
282 std::cerr << "Invalid server list provided: '" << args.servers << "'\n";
285 memcached_server_list_free(servers);
293 bool setupServers(memcached_st *memc) {
294 auto servers = getServers();
298 if (MEMCACHED_SUCCESS != memcached_server_push(memc, servers)) {
299 printLastError(memc);
300 memcached_server_list_free(servers);
303 memcached_server_list_free(servers);
307 bool setupBehavior(memcached_st *memc) const {
308 if (MEMCACHED_SUCCESS != memcached_behavior_set_key_hash(memc, getHash())) {
311 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, flags.binary)) {
314 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, flags.buffer)) {
317 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, flags.tcp_nodelay)) {
320 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, flags.non_block)) {
323 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, flags.udp)) {
329 printLastError(memc);
333 bool setupSASL(memcached_st *memc) const {
335 if (!LIBMEMCACHED_WITH_SASL_SUPPORT) {
337 std::cerr << "SASL username was supplied, but binary was not built with SASL support.\n";
341 if (MEMCACHED_SUCCESS != memcached_set_sasl_auth_data(memc, args.username, args.password)) {
342 printLastError(memc);
349 bool apply(memcached_st *memc) {
350 if (!setupBehavior(memc)) {
353 if (!setupServers(memc)) {
356 if (!setupSASL(memc)) {
361 if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
362 std::cerr << "Socket Initialization Error.\n";
365 #endif // #if defined(_WIN32)
370 bool parse(int argc, char *argv[]) {
374 auto opt = getopt_long(argc, argv, short_opts.c_str(), long_opts.data(), nullptr);
376 if (flags.debug && opt != -1 && opt != '?') {
377 std::cerr << "Processing option '" << (char) opt << "'\n";
380 switch (static_cast<flag>(opt)) {
382 args.positional = &argv[optind];
387 flags.absolute = true;
393 flags.add_cmd = true;
394 flags.replace_cmd = false;
395 flags.set_cmd = false;
400 case flag::concurrency:
401 args.concurrency = std::stoul(optarg);
410 args.load = std::stoul(optarg);
412 case flag::tcp_nodelay:
413 flags.tcp_nodelay = true;
418 case flag::replace_cmd:
419 flags.add_cmd = false;
420 flags.replace_cmd = true;
421 flags.set_cmd = false;
424 flags.add_cmd = false;
425 flags.replace_cmd = false;
426 flags.set_cmd = true;
432 flags.version = true;
435 flags.analyze = true;
443 flags.verbose = true;
446 args.expire = std::stoul(optarg);
454 case flag::non_block:
455 flags.non_block = true;
458 args.password = optarg;
463 flags.verbose = false;
466 args.repeat = std::stoul(optarg);
469 args.servers = optarg;
475 args.username = optarg;
479 flags.verbose = true;