Bug Summary

File:src/redis-cli.c
Warning:line 7479, column 13
Array access (from variable 'types') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name redis-cli.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mthread-model posix -mframe-pointer=none -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/llvm-10/lib/clang/10.0.0 -D REDIS_STATIC= -I ../deps/hiredis -I ../deps/linenoise -I ../deps/lua/src -I ../deps/hdr_histogram -D USE_JEMALLOC -I ../deps/jemalloc/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-10/lib/clang/10.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-c11-extensions -Wno-missing-field-initializers -std=c11 -fdebug-compilation-dir /home/netto/Desktop/redis-6.2.1/src -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-03-14-133648-8817-1 -x c redis-cli.c
1/* Redis CLI (command line interface)
2 *
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "fmacros.h"
32#include "version.h"
33
34#include <stdio.h>
35#include <string.h>
36#include <stdlib.h>
37#include <signal.h>
38#include <unistd.h>
39#include <time.h>
40#include <ctype.h>
41#include <errno(*__errno_location ()).h>
42#include <sys/stat.h>
43#include <sys/time.h>
44#include <assert.h>
45#include <fcntl.h>
46#include <limits.h>
47#include <math.h>
48
49#include <hiredis.h>
50#ifdef USE_OPENSSL
51#include <openssl/ssl.h>
52#include <openssl/err.h>
53#include <hiredis_ssl.h>
54#endif
55#include <sdscompat.h> /* Use hiredis' sds compat header that maps sds calls to their hi_ variants */
56#include <sdshisds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
57#include "dict.h"
58#include "adlist.h"
59#include "zmalloc.h"
60#include "linenoise.h"
61#include "help.h"
62#include "anet.h"
63#include "ae.h"
64#include "cli_common.h"
65#include "mt19937-64.h"
66
67#define UNUSED(V)((void) V) ((void) V)
68
69#define OUTPUT_STANDARD0 0
70#define OUTPUT_RAW1 1
71#define OUTPUT_CSV2 2
72#define REDIS_CLI_KEEPALIVE_INTERVAL15 15 /* seconds */
73#define REDIS_CLI_DEFAULT_PIPE_TIMEOUT30 30 /* seconds */
74#define REDIS_CLI_HISTFILE_ENV"REDISCLI_HISTFILE" "REDISCLI_HISTFILE"
75#define REDIS_CLI_HISTFILE_DEFAULT".rediscli_history" ".rediscli_history"
76#define REDIS_CLI_RCFILE_ENV"REDISCLI_RCFILE" "REDISCLI_RCFILE"
77#define REDIS_CLI_RCFILE_DEFAULT".redisclirc" ".redisclirc"
78#define REDIS_CLI_AUTH_ENV"REDISCLI_AUTH" "REDISCLI_AUTH"
79#define REDIS_CLI_CLUSTER_YES_ENV"REDISCLI_CLUSTER_YES" "REDISCLI_CLUSTER_YES"
80
81#define CLUSTER_MANAGER_SLOTS16384 16384
82#define CLUSTER_MANAGER_MIGRATE_TIMEOUT60000 60000
83#define CLUSTER_MANAGER_MIGRATE_PIPELINE10 10
84#define CLUSTER_MANAGER_REBALANCE_THRESHOLD2 2
85
86#define CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
\
87 "[ERR] Invalid arguments: you need to pass either a valid " \
88 "address (ie. 120.0.0.1:7000) or space separated IP " \
89 "and port (ie. 120.0.0.1 7000)\n"
90#define CLUSTER_MANAGER_MODE()(config.cluster_manager_command.name != ((void*)0)) (config.cluster_manager_command.name != NULL((void*)0))
91#define CLUSTER_MANAGER_MASTERS_COUNT(nodes, replicas)(nodes/(replicas + 1)) (nodes/(replicas + 1))
92#define CLUSTER_MANAGER_COMMAND(n,...)(redisCommand(n->context, ...)) \
93 (redisCommand(n->context, __VA_ARGS__))
94
95#define CLUSTER_MANAGER_NODE_ARRAY_FREE(array)zfree(array->alloc) zfree(array->alloc)
96
97#define CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n
->ip, n->port, err);
\
98 clusterManagerLogErr("Node %s:%d replied with error:\n%s\n", \clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n
->ip, n->port, err)
99 n->ip, n->port, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n
->ip, n->port, err)
;
100
101#define clusterManagerLogInfo(...)clusterManagerLog(1,...) \
102 clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_INFO1,__VA_ARGS__)
103
104#define clusterManagerLogErr(...)clusterManagerLog(3,...) \
105 clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_ERR3,__VA_ARGS__)
106
107#define clusterManagerLogWarn(...)clusterManagerLog(2,...) \
108 clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_WARN2,__VA_ARGS__)
109
110#define clusterManagerLogOk(...)clusterManagerLog(4,...) \
111 clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_SUCCESS4,__VA_ARGS__)
112
113#define CLUSTER_MANAGER_FLAG_MYSELF1 << 0 1 << 0
114#define CLUSTER_MANAGER_FLAG_SLAVE1 << 1 1 << 1
115#define CLUSTER_MANAGER_FLAG_FRIEND1 << 2 1 << 2
116#define CLUSTER_MANAGER_FLAG_NOADDR1 << 3 1 << 3
117#define CLUSTER_MANAGER_FLAG_DISCONNECT1 << 4 1 << 4
118#define CLUSTER_MANAGER_FLAG_FAIL1 << 5 1 << 5
119
120#define CLUSTER_MANAGER_CMD_FLAG_FIX1 << 0 1 << 0
121#define CLUSTER_MANAGER_CMD_FLAG_SLAVE1 << 1 1 << 1
122#define CLUSTER_MANAGER_CMD_FLAG_YES1 << 2 1 << 2
123#define CLUSTER_MANAGER_CMD_FLAG_AUTOWEIGHTS1 << 3 1 << 3
124#define CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER1 << 4 1 << 4
125#define CLUSTER_MANAGER_CMD_FLAG_SIMULATE1 << 5 1 << 5
126#define CLUSTER_MANAGER_CMD_FLAG_REPLACE1 << 6 1 << 6
127#define CLUSTER_MANAGER_CMD_FLAG_COPY1 << 7 1 << 7
128#define CLUSTER_MANAGER_CMD_FLAG_COLOR1 << 8 1 << 8
129#define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS1 << 9 1 << 9
130#define CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS1 << 10 1 << 10
131#define CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY1 << 11 1 << 11
132#define CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY1 << 12 1 << 12
133
134#define CLUSTER_MANAGER_OPT_GETFRIENDS1 << 0 1 << 0
135#define CLUSTER_MANAGER_OPT_COLD1 << 1 1 << 1
136#define CLUSTER_MANAGER_OPT_UPDATE1 << 2 1 << 2
137#define CLUSTER_MANAGER_OPT_QUIET1 << 6 1 << 6
138#define CLUSTER_MANAGER_OPT_VERBOSE1 << 7 1 << 7
139
140#define CLUSTER_MANAGER_LOG_LVL_INFO1 1
141#define CLUSTER_MANAGER_LOG_LVL_WARN2 2
142#define CLUSTER_MANAGER_LOG_LVL_ERR3 3
143#define CLUSTER_MANAGER_LOG_LVL_SUCCESS4 4
144
145#define CLUSTER_JOIN_CHECK_AFTER20 20
146
147#define LOG_COLOR_BOLD"29;1m" "29;1m"
148#define LOG_COLOR_RED"31;1m" "31;1m"
149#define LOG_COLOR_GREEN"32;1m" "32;1m"
150#define LOG_COLOR_YELLOW"33;1m" "33;1m"
151#define LOG_COLOR_RESET"0m" "0m"
152
153/* cliConnect() flags. */
154#define CC_FORCE(1<<0) (1<<0) /* Re-connect if already connected. */
155#define CC_QUIET(1<<1) (1<<1) /* Don't log connecting errors. */
156
157/* --latency-dist palettes. */
158int spectrum_palette_color_size = 19;
159int spectrum_palette_color[] = {0,233,234,235,237,239,241,243,245,247,144,143,142,184,226,214,208,202,196};
160
161int spectrum_palette_mono_size = 13;
162int spectrum_palette_mono[] = {0,233,234,235,237,239,241,243,245,247,249,251,253};
163
164/* The actual palette in use. */
165int *spectrum_palette;
166int spectrum_palette_size;
167
168/* Dict Helpers */
169
170static uint64_t dictSdsHash(const void *key);
171static int dictSdsKeyCompare(void *privdata, const void *key1,
172 const void *key2);
173static void dictSdsDestructor(void *privdata, void *val);
174static void dictListDestructor(void *privdata, void *val);
175
176/* Cluster Manager Command Info */
177typedef struct clusterManagerCommand {
178 char *name;
179 int argc;
180 char **argv;
181 int flags;
182 int replicas;
183 char *from;
184 char *to;
185 char **weight;
186 int weight_argc;
187 char *master_id;
188 int slots;
189 int timeout;
190 int pipeline;
191 float threshold;
192 char *backup_dir;
193 char *from_user;
194 char *from_pass;
195 int from_askpass;
196} clusterManagerCommand;
197
198static void createClusterManagerCommand(char *cmdname, int argc, char **argv);
199
200
201static redisContext *context;
202static struct config {
203 char *hostip;
204 int hostport;
205 char *hostsocket;
206 int tls;
207 cliSSLconfig sslconfig;
208 long repeat;
209 long interval;
210 int dbnum;
211 int interactive;
212 int shutdown;
213 int monitor_mode;
214 int pubsub_mode;
215 int latency_mode;
216 int latency_dist_mode;
217 int latency_history;
218 int lru_test_mode;
219 long long lru_test_sample_size;
220 int cluster_mode;
221 int cluster_reissue_command;
222 int slave_mode;
223 int pipe_mode;
224 int pipe_timeout;
225 int getrdb_mode;
226 int stat_mode;
227 int scan_mode;
228 int intrinsic_latency_mode;
229 int intrinsic_latency_duration;
230 char *pattern;
231 char *rdb_filename;
232 int bigkeys;
233 int memkeys;
234 unsigned memkeys_samples;
235 int hotkeys;
236 int stdinarg; /* get last arg from stdin. (-x option) */
237 char *auth;
238 int askpass;
239 char *user;
240 int output; /* output mode, see OUTPUT_* defines */
241 int push_output; /* Should we display spontaneous PUSH replies */
242 sdshisds mb_delim;
243 sdshisds cmd_delim;
244 char prompt[128];
245 char *eval;
246 int eval_ldb;
247 int eval_ldb_sync; /* Ask for synchronous mode of the Lua debugger. */
248 int eval_ldb_end; /* Lua debugging session ended. */
249 int enable_ldb_on_eval; /* Handle manual SCRIPT DEBUG + EVAL commands. */
250 int last_cmd_type;
251 int verbose;
252 int set_errcode;
253 clusterManagerCommand cluster_manager_command;
254 int no_auth_warning;
255 int resp3;
256 int in_multi;
257 int pre_multi_dbnum;
258} config;
259
260/* User preferences. */
261static struct pref {
262 int hints;
263} pref;
264
265static volatile sig_atomic_t force_cancel_loop = 0;
266static void usage(void);
267static void slaveMode(void);
268char *redisGitSHA1(void);
269char *redisGitDirty(void);
270static int cliConnect(int force);
271
272static char *getInfoField(char *info, char *field);
273static long getLongInfoField(char *info, char *field);
274
275/*------------------------------------------------------------------------------
276 * Utility functions
277 *--------------------------------------------------------------------------- */
278
279static void cliPushHandler(void *, void *);
280
281uint16_t crc16(const char *buf, int len);
282
283static long long ustime(void) {
284 struct timeval tv;
285 long long ust;
286
287 gettimeofday(&tv, NULL((void*)0));
288 ust = ((long long)tv.tv_sec)*1000000;
289 ust += tv.tv_usec;
290 return ust;
291}
292
293static long long mstime(void) {
294 return ustime()/1000;
295}
296
297static void cliRefreshPrompt(void) {
298 if (config.eval_ldb) return;
299
300 sdshisds prompt = sdsemptyhi_sdsempty();
301 if (config.hostsocket != NULL((void*)0)) {
302 prompt = sdscatfmthi_sdscatfmt(prompt,"redis %s",config.hostsocket);
303 } else {
304 char addr[256];
305 anetFormatAddr(addr, sizeof(addr), config.hostip, config.hostport);
306 prompt = sdscatlenhi_sdscatlen(prompt,addr,strlen(addr));
307 }
308
309 /* Add [dbnum] if needed */
310 if (config.dbnum != 0)
311 prompt = sdscatfmthi_sdscatfmt(prompt,"[%i]",config.dbnum);
312
313 /* Add TX if in transaction state*/
314 if (config.in_multi)
315 prompt = sdscatlenhi_sdscatlen(prompt,"(TX)",4);
316
317 /* Copy the prompt in the static buffer. */
318 prompt = sdscatlenhi_sdscatlen(prompt,"> ",2);
319 snprintf(config.prompt,sizeof(config.prompt),"%s",prompt);
320 sdsfreehi_sdsfree(prompt);
321}
322
323/* Return the name of the dotfile for the specified 'dotfilename'.
324 * Normally it just concatenates user $HOME to the file specified
325 * in 'dotfilename'. However if the environment variable 'envoverride'
326 * is set, its value is taken as the path.
327 *
328 * The function returns NULL (if the file is /dev/null or cannot be
329 * obtained for some error), or an SDS string that must be freed by
330 * the user. */
331static sdshisds getDotfilePath(char *envoverride, char *dotfilename) {
332 char *path = NULL((void*)0);
333 sdshisds dotPath = NULL((void*)0);
334
335 /* Check the env for a dotfile override. */
336 path = getenv(envoverride);
337 if (path != NULL((void*)0) && *path != '\0') {
338 if (!strcmp("/dev/null", path)) {
339 return NULL((void*)0);
340 }
341
342 /* If the env is set, return it. */
343 dotPath = sdsnewhi_sdsnew(path);
344 } else {
345 char *home = getenv("HOME");
346 if (home != NULL((void*)0) && *home != '\0') {
347 /* If no override is set use $HOME/<dotfilename>. */
348 dotPath = sdscatprintfhi_sdscatprintf(sdsemptyhi_sdsempty(), "%s/%s", home, dotfilename);
349 }
350 }
351 return dotPath;
352}
353
354/* URL-style percent decoding. */
355#define isHexChar(c)(((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int)
_ISdigit) || (c >= 'a' && c <= 'f'))
(isdigit(c)((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int) _ISdigit
)
|| (c >= 'a' && c <= 'f'))
356#define decodeHexChar(c)(((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int)
_ISdigit) ? c - '0' : c - 'a' + 10)
(isdigit(c)((*__ctype_b_loc ())[(int) ((c))] & (unsigned short int) _ISdigit
)
? c - '0' : c - 'a' + 10)
357#define decodeHex(h, l)(((((*__ctype_b_loc ())[(int) ((h))] & (unsigned short int
) _ISdigit) ? h - '0' : h - 'a' + 10) << 4) + (((*__ctype_b_loc
())[(int) ((l))] & (unsigned short int) _ISdigit) ? l - '0'
: l - 'a' + 10))
((decodeHexChar(h)(((*__ctype_b_loc ())[(int) ((h))] & (unsigned short int)
_ISdigit) ? h - '0' : h - 'a' + 10)
<< 4) + decodeHexChar(l)(((*__ctype_b_loc ())[(int) ((l))] & (unsigned short int)
_ISdigit) ? l - '0' : l - 'a' + 10)
)
358
359static sdshisds percentDecode(const char *pe, size_t len) {
360 const char *end = pe + len;
361 sdshisds ret = sdsemptyhi_sdsempty();
362 const char *curr = pe;
363
364 while (curr < end) {
365 if (*curr == '%') {
366 if ((end - curr) < 2) {
367 fprintf(stderrstderr, "Incomplete URI encoding\n");
368 exit(1);
369 }
370
371 char h = tolower(*(++curr))(__extension__ ({ int __res; if (sizeof (*(++curr)) > 1) {
if (__builtin_constant_p (*(++curr))) { int __c = (*(++curr)
); __res = __c < -128 || __c > 255 ? __c : (*__ctype_tolower_loc
())[__c]; } else __res = tolower (*(++curr)); } else __res =
(*__ctype_tolower_loc ())[(int) (*(++curr))]; __res; }))
;
372 char l = tolower(*(++curr))(__extension__ ({ int __res; if (sizeof (*(++curr)) > 1) {
if (__builtin_constant_p (*(++curr))) { int __c = (*(++curr)
); __res = __c < -128 || __c > 255 ? __c : (*__ctype_tolower_loc
())[__c]; } else __res = tolower (*(++curr)); } else __res =
(*__ctype_tolower_loc ())[(int) (*(++curr))]; __res; }))
;
373 if (!isHexChar(h)(((*__ctype_b_loc ())[(int) ((h))] & (unsigned short int)
_ISdigit) || (h >= 'a' && h <= 'f'))
|| !isHexChar(l)(((*__ctype_b_loc ())[(int) ((l))] & (unsigned short int)
_ISdigit) || (l >= 'a' && l <= 'f'))
) {
374 fprintf(stderrstderr, "Illegal character in URI encoding\n");
375 exit(1);
376 }
377 char c = decodeHex(h, l)(((((*__ctype_b_loc ())[(int) ((h))] & (unsigned short int
) _ISdigit) ? h - '0' : h - 'a' + 10) << 4) + (((*__ctype_b_loc
())[(int) ((l))] & (unsigned short int) _ISdigit) ? l - '0'
: l - 'a' + 10))
;
378 ret = sdscatlenhi_sdscatlen(ret, &c, 1);
379 curr++;
380 } else {
381 ret = sdscatlenhi_sdscatlen(ret, curr++, 1);
382 }
383 }
384
385 return ret;
386}
387
388/* Parse a URI and extract the server connection information.
389 * URI scheme is based on the the provisional specification[1] excluding support
390 * for query parameters. Valid URIs are:
391 * scheme: "redis://"
392 * authority: [[<username> ":"] <password> "@"] [<hostname> [":" <port>]]
393 * path: ["/" [<db>]]
394 *
395 * [1]: https://www.iana.org/assignments/uri-schemes/prov/redis */
396static void parseRedisUri(const char *uri) {
397
398 const char *scheme = "redis://";
399 const char *tlsscheme = "rediss://";
400 const char *curr = uri;
401 const char *end = uri + strlen(uri);
402 const char *userinfo, *username, *port, *host, *path;
403
404 /* URI must start with a valid scheme. */
405 if (!strncasecmp(tlsscheme, curr, strlen(tlsscheme))) {
406#ifdef USE_OPENSSL
407 config.tls = 1;
408#else
409 fprintf(stderrstderr,"rediss:// is only supported when redis-cli is compiled with OpenSSL\n");
410 exit(1);
411#endif
412 } else if (strncasecmp(scheme, curr, strlen(scheme))) {
413 fprintf(stderrstderr,"Invalid URI scheme\n");
414 exit(1);
415 }
416 curr += strlen(scheme);
417 if (curr == end) return;
418
419 /* Extract user info. */
420 if ((userinfo = strchr(curr,'@'))) {
421 if ((username = strchr(curr, ':')) && username < userinfo) {
422 config.user = percentDecode(curr, username - curr);
423 curr = username + 1;
424 }
425
426 config.auth = percentDecode(curr, userinfo - curr);
427 curr = userinfo + 1;
428 }
429 if (curr == end) return;
430
431 /* Extract host and port. */
432 path = strchr(curr, '/');
433 if (*curr != '/') {
434 host = path ? path - 1 : end;
435 if ((port = strchr(curr, ':'))) {
436 config.hostport = atoi(port + 1);
437 host = port - 1;
438 }
439 config.hostip = sdsnewlenhi_sdsnewlen(curr, host - curr + 1);
440 }
441 curr = path ? path + 1 : end;
442 if (curr == end) return;
443
444 /* Extract database number. */
445 config.dbnum = atoi(curr);
446}
447
448static uint64_t dictSdsHash(const void *key) {
449 return dictGenHashFunction((unsigned char*)key, sdslenhi_sdslen((char*)key));
450}
451
452static int dictSdsKeyCompare(void *privdata, const void *key1,
453 const void *key2)
454{
455 int l1,l2;
456 DICT_NOTUSED(privdata)((void) privdata);
457
458 l1 = sdslenhi_sdslen((sdshisds)key1);
459 l2 = sdslenhi_sdslen((sdshisds)key2);
460 if (l1 != l2) return 0;
461 return memcmp(key1, key2, l1) == 0;
462}
463
464static void dictSdsDestructor(void *privdata, void *val)
465{
466 DICT_NOTUSED(privdata)((void) privdata);
467 sdsfreehi_sdsfree(val);
468}
469
470void dictListDestructor(void *privdata, void *val)
471{
472 DICT_NOTUSED(privdata)((void) privdata);
473 listRelease((list*)val);
474}
475
476/* _serverAssert is needed by dict */
477void _serverAssert(const char *estr, const char *file, int line) {
478 fprintf(stderrstderr, "=== ASSERTION FAILED ===");
479 fprintf(stderrstderr, "==> %s:%d '%s' is not true",file,line,estr);
480 *((char*)-1) = 'x';
481}
482
483/*------------------------------------------------------------------------------
484 * Help functions
485 *--------------------------------------------------------------------------- */
486
487#define CLI_HELP_COMMAND1 1
488#define CLI_HELP_GROUP2 2
489
490typedef struct {
491 int type;
492 int argc;
493 sdshisds *argv;
494 sdshisds full;
495
496 /* Only used for help on commands */
497 struct commandHelp *org;
498} helpEntry;
499
500static helpEntry *helpEntries;
501static int helpEntriesLen;
502
503static sdshisds cliVersion(void) {
504 sdshisds version;
505 version = sdscatprintfhi_sdscatprintf(sdsemptyhi_sdsempty(), "%s", REDIS_VERSION"6.2.1");
506
507 /* Add git commit and working tree status when available */
508 if (strtoll(redisGitSHA1(),NULL((void*)0),16)) {
509 version = sdscatprintfhi_sdscatprintf(version, " (git:%s", redisGitSHA1());
510 if (strtoll(redisGitDirty(),NULL((void*)0),10))
511 version = sdscatprintfhi_sdscatprintf(version, "-dirty");
512 version = sdscathi_sdscat(version, ")");
513 }
514 return version;
515}
516
517static void cliInitHelp(void) {
518 int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
519 int groupslen = sizeof(commandGroups)/sizeof(char*);
520 int i, len, pos = 0;
521 helpEntry tmp;
522
523 helpEntriesLen = len = commandslen+groupslen;
524 helpEntries = zmalloc(sizeof(helpEntry)*len);
525
526 for (i = 0; i < groupslen; i++) {
527 tmp.argc = 1;
528 tmp.argv = zmalloc(sizeof(sdshisds));
529 tmp.argv[0] = sdscatprintfhi_sdscatprintf(sdsemptyhi_sdsempty(),"@%s",commandGroups[i]);
530 tmp.full = tmp.argv[0];
531 tmp.type = CLI_HELP_GROUP2;
532 tmp.org = NULL((void*)0);
533 helpEntries[pos++] = tmp;
534 }
535
536 for (i = 0; i < commandslen; i++) {
537 tmp.argv = sdssplitargshi_sdssplitargs(commandHelp[i].name,&tmp.argc);
538 tmp.full = sdsnewhi_sdsnew(commandHelp[i].name);
539 tmp.type = CLI_HELP_COMMAND1;
540 tmp.org = &commandHelp[i];
541 helpEntries[pos++] = tmp;
542 }
543}
544
545/* cliInitHelp() setups the helpEntries array with the command and group
546 * names from the help.h file. However the Redis instance we are connecting
547 * to may support more commands, so this function integrates the previous
548 * entries with additional entries obtained using the COMMAND command
549 * available in recent versions of Redis. */
550static void cliIntegrateHelp(void) {
551 if (cliConnect(CC_QUIET(1<<1)) == REDIS_ERR-1) return;
552
553 redisReply *reply = redisCommand(context, "COMMAND");
554 if(reply == NULL((void*)0) || reply->type != REDIS_REPLY_ARRAY2) return;
555
556 /* Scan the array reported by COMMAND and fill only the entries that
557 * don't already match what we have. */
558 for (size_t j = 0; j < reply->elements; j++) {
559 redisReply *entry = reply->element[j];
560 if (entry->type != REDIS_REPLY_ARRAY2 || entry->elements < 4 ||
561 entry->element[0]->type != REDIS_REPLY_STRING1 ||
562 entry->element[1]->type != REDIS_REPLY_INTEGER3 ||
563 entry->element[3]->type != REDIS_REPLY_INTEGER3) return;
564 char *cmdname = entry->element[0]->str;
565 int i;
566
567 for (i = 0; i < helpEntriesLen; i++) {
568 helpEntry *he = helpEntries+i;
569 if (!strcasecmp(he->argv[0],cmdname))
570 break;
571 }
572 if (i != helpEntriesLen) continue;
573
574 helpEntriesLen++;
575 helpEntries = zrealloc(helpEntries,sizeof(helpEntry)*helpEntriesLen);
576 helpEntry *new = helpEntries+(helpEntriesLen-1);
577
578 new->argc = 1;
579 new->argv = zmalloc(sizeof(sdshisds));
580 new->argv[0] = sdsnewhi_sdsnew(cmdname);
581 new->full = new->argv[0];
582 new->type = CLI_HELP_COMMAND1;
583 sdstoupperhi_sdstoupper(new->argv[0]);
584
585 struct commandHelp *ch = zmalloc(sizeof(*ch));
586 ch->name = new->argv[0];
587 ch->params = sdsemptyhi_sdsempty();
588 int args = llabs(entry->element[1]->integer);
589 args--; /* Remove the command name itself. */
590 if (entry->element[3]->integer == 1) {
591 ch->params = sdscathi_sdscat(ch->params,"key ");
592 args--;
593 }
594 while(args-- > 0) ch->params = sdscathi_sdscat(ch->params,"arg ");
595 if (entry->element[1]->integer < 0)
596 ch->params = sdscathi_sdscat(ch->params,"...options...");
597 ch->summary = "Help not available";
598 ch->group = 0;
599 ch->since = "not known";
600 new->org = ch;
601 }
602 freeReplyObject(reply);
603}
604
605/* Output command help to stdout. */
606static void cliOutputCommandHelp(struct commandHelp *help, int group) {
607 printf("\r\n \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help->name, help->params);
608 printf(" \x1b[33msummary:\x1b[0m %s\r\n", help->summary);
609 printf(" \x1b[33msince:\x1b[0m %s\r\n", help->since);
610 if (group) {
611 printf(" \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups[help->group]);
612 }
613}
614
615/* Print generic help. */
616static void cliOutputGenericHelp(void) {
617 sdshisds version = cliVersion();
618 printf(
619 "redis-cli %s\n"
620 "To get help about Redis commands type:\n"
621 " \"help @<group>\" to get a list of commands in <group>\n"
622 " \"help <command>\" for help on <command>\n"
623 " \"help <tab>\" to get a list of possible help topics\n"
624 " \"quit\" to exit\n"
625 "\n"
626 "To set redis-cli preferences:\n"
627 " \":set hints\" enable online hints\n"
628 " \":set nohints\" disable online hints\n"
629 "Set your preferences in ~/.redisclirc\n",
630 version
631 );
632 sdsfreehi_sdsfree(version);
633}
634
635/* Output all command help, filtering by group or command name. */
636static void cliOutputHelp(int argc, char **argv) {
637 int i, j, len;
638 int group = -1;
639 helpEntry *entry;
640 struct commandHelp *help;
641
642 if (argc == 0) {
643 cliOutputGenericHelp();
644 return;
645 } else if (argc > 0 && argv[0][0] == '@') {
646 len = sizeof(commandGroups)/sizeof(char*);
647 for (i = 0; i < len; i++) {
648 if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {
649 group = i;
650 break;
651 }
652 }
653 }
654
655 assert(argc > 0)((argc > 0) ? (void) (0) : __assert_fail ("argc > 0", "redis-cli.c"
, 655, __extension__ __PRETTY_FUNCTION__))
;
656 for (i = 0; i < helpEntriesLen; i++) {
657 entry = &helpEntries[i];
658 if (entry->type != CLI_HELP_COMMAND1) continue;
659
660 help = entry->org;
661 if (group == -1) {
662 /* Compare all arguments */
663 if (argc == entry->argc) {
664 for (j = 0; j < argc; j++) {
665 if (strcasecmp(argv[j],entry->argv[j]) != 0) break;
666 }
667 if (j == argc) {
668 cliOutputCommandHelp(help,1);
669 }
670 }
671 } else {
672 if (group == help->group) {
673 cliOutputCommandHelp(help,0);
674 }
675 }
676 }
677 printf("\r\n");
678}
679
680/* Linenoise completion callback. */
681static void completionCallback(const char *buf, linenoiseCompletions *lc) {
682 size_t startpos = 0;
683 int mask;
684 int i;
685 size_t matchlen;
686 sdshisds tmp;
687
688 if (strncasecmp(buf,"help ",5) == 0) {
689 startpos = 5;
690 while (isspace(buf[startpos])((*__ctype_b_loc ())[(int) ((buf[startpos]))] & (unsigned
short int) _ISspace)
) startpos++;
691 mask = CLI_HELP_COMMAND1 | CLI_HELP_GROUP2;
692 } else {
693 mask = CLI_HELP_COMMAND1;
694 }
695
696 for (i = 0; i < helpEntriesLen; i++) {
697 if (!(helpEntries[i].type & mask)) continue;
698
699 matchlen = strlen(buf+startpos);
700 if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {
701 tmp = sdsnewlenhi_sdsnewlen(buf,startpos);
702 tmp = sdscathi_sdscat(tmp,helpEntries[i].full);
703 linenoiseAddCompletion(lc,tmp);
704 sdsfreehi_sdsfree(tmp);
705 }
706 }
707}
708
709/* Linenoise hints callback. */
710static char *hintsCallback(const char *buf, int *color, int *bold) {
711 if (!pref.hints) return NULL((void*)0);
712
713 int i, argc, buflen = strlen(buf);
714 sdshisds *argv = sdssplitargshi_sdssplitargs(buf,&argc);
715 int endspace = buflen && isspace(buf[buflen-1])((*__ctype_b_loc ())[(int) ((buf[buflen-1]))] & (unsigned
short int) _ISspace)
;
716
717 /* Check if the argument list is empty and return ASAP. */
718 if (argc == 0) {
719 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
720 return NULL((void*)0);
721 }
722
723 for (i = 0; i < helpEntriesLen; i++) {
724 if (!(helpEntries[i].type & CLI_HELP_COMMAND1)) continue;
725
726 if (strcasecmp(argv[0],helpEntries[i].full) == 0 ||
727 strcasecmp(buf,helpEntries[i].full) == 0)
728 {
729 *color = 90;
730 *bold = 0;
731 sdshisds hint = sdsnewhi_sdsnew(helpEntries[i].org->params);
732
733 /* Remove arguments from the returned hint to show only the
734 * ones the user did not yet typed. */
735 int toremove = argc-1;
736 while(toremove > 0 && sdslenhi_sdslen(hint)) {
737 if (hint[0] == '[') break;
738 if (hint[0] == ' ') toremove--;
739 sdsrangehi_sdsrange(hint,1,-1);
740 }
741
742 /* Add an initial space if needed. */
743 if (!endspace) {
744 sdshisds newhint = sdsnewlenhi_sdsnewlen(" ",1);
745 newhint = sdscatsdshi_sdscatsds(newhint,hint);
746 sdsfreehi_sdsfree(hint);
747 hint = newhint;
748 }
749
750 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
751 return hint;
752 }
753 }
754 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
755 return NULL((void*)0);
756}
757
758static void freeHintsCallback(void *ptr) {
759 sdsfreehi_sdsfree(ptr);
760}
761
762/*------------------------------------------------------------------------------
763 * Networking / parsing
764 *--------------------------------------------------------------------------- */
765
766/* Send AUTH command to the server */
767static int cliAuth(redisContext *ctx, char *user, char *auth) {
768 redisReply *reply;
769 if (auth == NULL((void*)0)) return REDIS_OK0;
770
771 if (user == NULL((void*)0))
772 reply = redisCommand(ctx,"AUTH %s",auth);
773 else
774 reply = redisCommand(ctx,"AUTH %s %s",user,auth);
775 if (reply != NULL((void*)0)) {
776 if (reply->type == REDIS_REPLY_ERROR6)
777 fprintf(stderrstderr,"Warning: AUTH failed\n");
778 freeReplyObject(reply);
779 return REDIS_OK0;
780 }
781 return REDIS_ERR-1;
782}
783
784/* Send SELECT dbnum to the server */
785static int cliSelect(void) {
786 redisReply *reply;
787 if (config.dbnum == 0) return REDIS_OK0;
788
789 reply = redisCommand(context,"SELECT %d",config.dbnum);
790 if (reply != NULL((void*)0)) {
791 int result = REDIS_OK0;
792 if (reply->type == REDIS_REPLY_ERROR6) result = REDIS_ERR-1;
793 freeReplyObject(reply);
794 return result;
795 }
796 return REDIS_ERR-1;
797}
798
799/* Select RESP3 mode if redis-cli was started with the -3 option. */
800static int cliSwitchProto(void) {
801 redisReply *reply;
802 if (config.resp3 == 0) return REDIS_OK0;
803
804 reply = redisCommand(context,"HELLO 3");
805 if (reply != NULL((void*)0)) {
806 int result = REDIS_OK0;
807 if (reply->type == REDIS_REPLY_ERROR6) result = REDIS_ERR-1;
808 freeReplyObject(reply);
809 return result;
810 }
811 return REDIS_ERR-1;
812}
813
814/* Connect to the server. It is possible to pass certain flags to the function:
815 * CC_FORCE: The connection is performed even if there is already
816 * a connected socket.
817 * CC_QUIET: Don't print errors if connection fails. */
818static int cliConnect(int flags) {
819 if (context == NULL((void*)0) || flags & CC_FORCE(1<<0)) {
820 if (context != NULL((void*)0)) {
821 redisFree(context);
822 config.dbnum = 0;
823 config.in_multi = 0;
824 cliRefreshPrompt();
825 }
826
827 if (config.hostsocket == NULL((void*)0)) {
828 context = redisConnect(config.hostip,config.hostport);
829 } else {
830 context = redisConnectUnix(config.hostsocket);
831 }
832
833 if (!context->err && config.tls) {
834 const char *err = NULL((void*)0);
835 if (cliSecureConnection(context, config.sslconfig, &err) == REDIS_ERR-1 && err) {
836 fprintf(stderrstderr, "Could not negotiate a TLS connection: %s\n", err);
837 redisFree(context);
838 context = NULL((void*)0);
839 return REDIS_ERR-1;
840 }
841 }
842
843 if (context->err) {
844 if (!(flags & CC_QUIET(1<<1))) {
845 fprintf(stderrstderr,"Could not connect to Redis at ");
846 if (config.hostsocket == NULL((void*)0))
847 fprintf(stderrstderr,"%s:%d: %s\n",
848 config.hostip,config.hostport,context->errstr);
849 else
850 fprintf(stderrstderr,"%s: %s\n",
851 config.hostsocket,context->errstr);
852 }
853 redisFree(context);
854 context = NULL((void*)0);
855 return REDIS_ERR-1;
856 }
857
858
859 /* Set aggressive KEEP_ALIVE socket option in the Redis context socket
860 * in order to prevent timeouts caused by the execution of long
861 * commands. At the same time this improves the detection of real
862 * errors. */
863 anetKeepAlive(NULL((void*)0), context->fd, REDIS_CLI_KEEPALIVE_INTERVAL15);
864
865 /* Do AUTH, select the right DB, switch to RESP3 if needed. */
866 if (cliAuth(context, config.user, config.auth) != REDIS_OK0)
867 return REDIS_ERR-1;
868 if (cliSelect() != REDIS_OK0)
869 return REDIS_ERR-1;
870 if (cliSwitchProto() != REDIS_OK0)
871 return REDIS_ERR-1;
872 }
873
874 /* Set a PUSH handler if configured to do so. */
875 if (config.push_output) {
876 redisSetPushCallback(context, cliPushHandler);
877 }
878
879 return REDIS_OK0;
880}
881
882static void cliPrintContextError(void) {
883 if (context == NULL((void*)0)) return;
884 fprintf(stderrstderr,"Error: %s\n",context->errstr);
885}
886
887static int isInvalidateReply(redisReply *reply) {
888 return reply->type == REDIS_REPLY_PUSH12 && reply->elements == 2 &&
889 reply->element[0]->type == REDIS_REPLY_STRING1 &&
890 !strncmp(reply->element[0]->str, "invalidate", 10) &&
891 reply->element[1]->type == REDIS_REPLY_ARRAY2;
892}
893
894/* Special display handler for RESP3 'invalidate' messages.
895 * This function does not validate the reply, so it should
896 * already be confirmed correct */
897static sdshisds cliFormatInvalidateTTY(redisReply *r) {
898 sdshisds out = sdsnewhi_sdsnew("-> invalidate: ");
899
900 for (size_t i = 0; i < r->element[1]->elements; i++) {
901 redisReply *key = r->element[1]->element[i];
902 assert(key->type == REDIS_REPLY_STRING)((key->type == 1) ? (void) (0) : __assert_fail ("key->type == REDIS_REPLY_STRING"
, "redis-cli.c", 902, __extension__ __PRETTY_FUNCTION__))
;
903
904 out = sdscatfmthi_sdscatfmt(out, "'%s'", key->str, key->len);
905 if (i < r->element[1]->elements - 1)
906 out = sdscatlenhi_sdscatlen(out, ", ", 2);
907 }
908
909 return sdscatlenhi_sdscatlen(out, "\n", 1);
910}
911
912static sdshisds cliFormatReplyTTY(redisReply *r, char *prefix) {
913 sdshisds out = sdsemptyhi_sdsempty();
914 switch (r->type) {
915 case REDIS_REPLY_ERROR6:
916 out = sdscatprintfhi_sdscatprintf(out,"(error) %s\n", r->str);
917 break;
918 case REDIS_REPLY_STATUS5:
919 out = sdscathi_sdscat(out,r->str);
920 out = sdscathi_sdscat(out,"\n");
921 break;
922 case REDIS_REPLY_INTEGER3:
923 out = sdscatprintfhi_sdscatprintf(out,"(integer) %lld\n",r->integer);
924 break;
925 case REDIS_REPLY_DOUBLE7:
926 out = sdscatprintfhi_sdscatprintf(out,"(double) %s\n",r->str);
927 break;
928 case REDIS_REPLY_STRING1:
929 case REDIS_REPLY_VERB14:
930 /* If you are producing output for the standard output we want
931 * a more interesting output with quoted characters and so forth,
932 * unless it's a verbatim string type. */
933 if (r->type == REDIS_REPLY_STRING1) {
934 out = sdscatreprhi_sdscatrepr(out,r->str,r->len);
935 out = sdscathi_sdscat(out,"\n");
936 } else {
937 out = sdscatlenhi_sdscatlen(out,r->str,r->len);
938 out = sdscathi_sdscat(out,"\n");
939 }
940 break;
941 case REDIS_REPLY_NIL4:
942 out = sdscathi_sdscat(out,"(nil)\n");
943 break;
944 case REDIS_REPLY_BOOL8:
945 out = sdscathi_sdscat(out,r->integer ? "(true)\n" : "(false)\n");
946 break;
947 case REDIS_REPLY_ARRAY2:
948 case REDIS_REPLY_MAP9:
949 case REDIS_REPLY_SET10:
950 case REDIS_REPLY_PUSH12:
951 if (r->elements == 0) {
952 if (r->type == REDIS_REPLY_ARRAY2)
953 out = sdscathi_sdscat(out,"(empty array)\n");
954 else if (r->type == REDIS_REPLY_MAP9)
955 out = sdscathi_sdscat(out,"(empty hash)\n");
956 else if (r->type == REDIS_REPLY_SET10)
957 out = sdscathi_sdscat(out,"(empty set)\n");
958 else if (r->type == REDIS_REPLY_PUSH12)
959 out = sdscathi_sdscat(out,"(empty push)\n");
960 else
961 out = sdscathi_sdscat(out,"(empty aggregate type)\n");
962 } else {
963 unsigned int i, idxlen = 0;
964 char _prefixlen[16];
965 char _prefixfmt[16];
966 sdshisds _prefix;
967 sdshisds tmp;
968
969 /* Calculate chars needed to represent the largest index */
970 i = r->elements;
971 if (r->type == REDIS_REPLY_MAP9) i /= 2;
972 do {
973 idxlen++;
974 i /= 10;
975 } while(i);
976
977 /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
978 memset(_prefixlen,' ',idxlen+2);
979 _prefixlen[idxlen+2] = '\0';
980 _prefix = sdscathi_sdscat(sdsnewhi_sdsnew(prefix),_prefixlen);
981
982 /* Setup prefix format for every entry */
983 char numsep;
984 if (r->type == REDIS_REPLY_SET10) numsep = '~';
985 else if (r->type == REDIS_REPLY_MAP9) numsep = '#';
986 else numsep = ')';
987 snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%ud%c ",idxlen,numsep);
988
989 for (i = 0; i < r->elements; i++) {
990 unsigned int human_idx = (r->type == REDIS_REPLY_MAP9) ?
991 i/2 : i;
992 human_idx++; /* Make it 1-based. */
993
994 /* Don't use the prefix for the first element, as the parent
995 * caller already prepended the index number. */
996 out = sdscatprintfhi_sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,human_idx);
997
998 /* Format the multi bulk entry */
999 tmp = cliFormatReplyTTY(r->element[i],_prefix);
1000 out = sdscatlenhi_sdscatlen(out,tmp,sdslenhi_sdslen(tmp));
1001 sdsfreehi_sdsfree(tmp);
1002
1003 /* For maps, format the value as well. */
1004 if (r->type == REDIS_REPLY_MAP9) {
1005 i++;
1006 sdsrangehi_sdsrange(out,0,-2);
1007 out = sdscathi_sdscat(out," => ");
1008 tmp = cliFormatReplyTTY(r->element[i],_prefix);
1009 out = sdscatlenhi_sdscatlen(out,tmp,sdslenhi_sdslen(tmp));
1010 sdsfreehi_sdsfree(tmp);
1011 }
1012 }
1013 sdsfreehi_sdsfree(_prefix);
1014 }
1015 break;
1016 default:
1017 fprintf(stderrstderr,"Unknown reply type: %d\n", r->type);
1018 exit(1);
1019 }
1020 return out;
1021}
1022
1023int isColorTerm(void) {
1024 char *t = getenv("TERM");
1025 return t != NULL((void*)0) && strstr(t,"xterm") != NULL((void*)0);
1026}
1027
1028/* Helper function for sdsCatColorizedLdbReply() appending colorize strings
1029 * to an SDS string. */
1030sdshisds sdscatcolor(sdshisds o, char *s, size_t len, char *color) {
1031 if (!isColorTerm()) return sdscatlenhi_sdscatlen(o,s,len);
1032
1033 int bold = strstr(color,"bold") != NULL((void*)0);
1034 int ccode = 37; /* Defaults to white. */
1035 if (strstr(color,"red")) ccode = 31;
1036 else if (strstr(color,"green")) ccode = 32;
1037 else if (strstr(color,"yellow")) ccode = 33;
1038 else if (strstr(color,"blue")) ccode = 34;
1039 else if (strstr(color,"magenta")) ccode = 35;
1040 else if (strstr(color,"cyan")) ccode = 36;
1041 else if (strstr(color,"white")) ccode = 37;
1042
1043 o = sdscatfmthi_sdscatfmt(o,"\033[%i;%i;49m",bold,ccode);
1044 o = sdscatlenhi_sdscatlen(o,s,len);
1045 o = sdscathi_sdscat(o,"\033[0m");
1046 return o;
1047}
1048
1049/* Colorize Lua debugger status replies according to the prefix they
1050 * have. */
1051sdshisds sdsCatColorizedLdbReply(sdshisds o, char *s, size_t len) {
1052 char *color = "white";
1053
1054 if (strstr(s,"<debug>")) color = "bold";
1055 if (strstr(s,"<redis>")) color = "green";
1056 if (strstr(s,"<reply>")) color = "cyan";
1057 if (strstr(s,"<error>")) color = "red";
1058 if (strstr(s,"<hint>")) color = "bold";
1059 if (strstr(s,"<value>") || strstr(s,"<retval>")) color = "magenta";
1060 if (len > 4 && isdigit(s[3])((*__ctype_b_loc ())[(int) ((s[3]))] & (unsigned short int
) _ISdigit)
) {
1061 if (s[1] == '>') color = "yellow"; /* Current line. */
1062 else if (s[2] == '#') color = "bold"; /* Break point. */
1063 }
1064 return sdscatcolor(o,s,len,color);
1065}
1066
1067static sdshisds cliFormatReplyRaw(redisReply *r) {
1068 sdshisds out = sdsemptyhi_sdsempty(), tmp;
1069 size_t i;
1070
1071 switch (r->type) {
1072 case REDIS_REPLY_NIL4:
1073 /* Nothing... */
1074 break;
1075 case REDIS_REPLY_ERROR6:
1076 out = sdscatlenhi_sdscatlen(out,r->str,r->len);
1077 out = sdscatlenhi_sdscatlen(out,"\n",1);
1078 break;
1079 case REDIS_REPLY_STATUS5:
1080 case REDIS_REPLY_STRING1:
1081 case REDIS_REPLY_VERB14:
1082 if (r->type == REDIS_REPLY_STATUS5 && config.eval_ldb) {
1083 /* The Lua debugger replies with arrays of simple (status)
1084 * strings. We colorize the output for more fun if this
1085 * is a debugging session. */
1086
1087 /* Detect the end of a debugging session. */
1088 if (strstr(r->str,"<endsession>") == r->str) {
1089 config.enable_ldb_on_eval = 0;
1090 config.eval_ldb = 0;
1091 config.eval_ldb_end = 1; /* Signal the caller session ended. */
1092 config.output = OUTPUT_STANDARD0;
1093 cliRefreshPrompt();
1094 } else {
1095 out = sdsCatColorizedLdbReply(out,r->str,r->len);
1096 }
1097 } else {
1098 out = sdscatlenhi_sdscatlen(out,r->str,r->len);
1099 }
1100 break;
1101 case REDIS_REPLY_BOOL8:
1102 out = sdscathi_sdscat(out,r->integer ? "(true)" : "(false)");
1103 break;
1104 case REDIS_REPLY_INTEGER3:
1105 out = sdscatprintfhi_sdscatprintf(out,"%lld",r->integer);
1106 break;
1107 case REDIS_REPLY_DOUBLE7:
1108 out = sdscatprintfhi_sdscatprintf(out,"%s",r->str);
1109 break;
1110 case REDIS_REPLY_ARRAY2:
1111 case REDIS_REPLY_PUSH12:
1112 for (i = 0; i < r->elements; i++) {
1113 if (i > 0) out = sdscathi_sdscat(out,config.mb_delim);
1114 tmp = cliFormatReplyRaw(r->element[i]);
1115 out = sdscatlenhi_sdscatlen(out,tmp,sdslenhi_sdslen(tmp));
1116 sdsfreehi_sdsfree(tmp);
1117 }
1118 break;
1119 case REDIS_REPLY_MAP9:
1120 for (i = 0; i < r->elements; i += 2) {
1121 if (i > 0) out = sdscathi_sdscat(out,config.mb_delim);
1122 tmp = cliFormatReplyRaw(r->element[i]);
1123 out = sdscatlenhi_sdscatlen(out,tmp,sdslenhi_sdslen(tmp));
1124 sdsfreehi_sdsfree(tmp);
1125
1126 out = sdscatlenhi_sdscatlen(out," ",1);
1127 tmp = cliFormatReplyRaw(r->element[i+1]);
1128 out = sdscatlenhi_sdscatlen(out,tmp,sdslenhi_sdslen(tmp));
1129 sdsfreehi_sdsfree(tmp);
1130 }
1131 break;
1132 default:
1133 fprintf(stderrstderr,"Unknown reply type: %d\n", r->type);
1134 exit(1);
1135 }
1136 return out;
1137}
1138
1139static sdshisds cliFormatReplyCSV(redisReply *r) {
1140 unsigned int i;
1141
1142 sdshisds out = sdsemptyhi_sdsempty();
1143 switch (r->type) {
1144 case REDIS_REPLY_ERROR6:
1145 out = sdscathi_sdscat(out,"ERROR,");
1146 out = sdscatreprhi_sdscatrepr(out,r->str,strlen(r->str));
1147 break;
1148 case REDIS_REPLY_STATUS5:
1149 out = sdscatreprhi_sdscatrepr(out,r->str,r->len);
1150 break;
1151 case REDIS_REPLY_INTEGER3:
1152 out = sdscatprintfhi_sdscatprintf(out,"%lld",r->integer);
1153 break;
1154 case REDIS_REPLY_DOUBLE7:
1155 out = sdscatprintfhi_sdscatprintf(out,"%s",r->str);
1156 break;
1157 case REDIS_REPLY_STRING1:
1158 case REDIS_REPLY_VERB14:
1159 out = sdscatreprhi_sdscatrepr(out,r->str,r->len);
1160 break;
1161 case REDIS_REPLY_NIL4:
1162 out = sdscathi_sdscat(out,"NULL");
1163 break;
1164 case REDIS_REPLY_BOOL8:
1165 out = sdscathi_sdscat(out,r->integer ? "true" : "false");
1166 break;
1167 case REDIS_REPLY_ARRAY2:
1168 case REDIS_REPLY_PUSH12:
1169 case REDIS_REPLY_MAP9: /* CSV has no map type, just output flat list. */
1170 for (i = 0; i < r->elements; i++) {
1171 sdshisds tmp = cliFormatReplyCSV(r->element[i]);
1172 out = sdscatlenhi_sdscatlen(out,tmp,sdslenhi_sdslen(tmp));
1173 if (i != r->elements-1) out = sdscathi_sdscat(out,",");
1174 sdsfreehi_sdsfree(tmp);
1175 }
1176 break;
1177 default:
1178 fprintf(stderrstderr,"Unknown reply type: %d\n", r->type);
1179 exit(1);
1180 }
1181 return out;
1182}
1183
1184/* Generate reply strings in various output modes */
1185static sdshisds cliFormatReply(redisReply *reply, int mode, int verbatim) {
1186 sdshisds out;
1187
1188 if (verbatim) {
1189 out = cliFormatReplyRaw(reply);
1190 } else if (mode == OUTPUT_STANDARD0) {
1191 out = cliFormatReplyTTY(reply, "");
1192 } else if (mode == OUTPUT_RAW1) {
1193 out = cliFormatReplyRaw(reply);
1194 out = sdscatsdshi_sdscatsds(out, config.cmd_delim);
1195 } else if (mode == OUTPUT_CSV2) {
1196 out = cliFormatReplyCSV(reply);
1197 out = sdscatlenhi_sdscatlen(out, "\n", 1);
1198 } else {
1199 fprintf(stderrstderr, "Error: Unknown output encoding %d\n", mode);
1200 exit(1);
1201 }
1202
1203 return out;
1204}
1205
1206/* Output any spontaneous PUSH reply we receive */
1207static void cliPushHandler(void *privdata, void *reply) {
1208 UNUSED(privdata)((void) privdata);
1209 sdshisds out;
1210
1211 if (config.output == OUTPUT_STANDARD0 && isInvalidateReply(reply)) {
1212 out = cliFormatInvalidateTTY(reply);
1213 } else {
1214 out = cliFormatReply(reply, config.output, 0);
1215 }
1216
1217 fwrite(out, sdslenhi_sdslen(out), 1, stdoutstdout);
1218
1219 freeReplyObject(reply);
1220 sdsfreehi_sdsfree(out);
1221}
1222
1223static int cliReadReply(int output_raw_strings) {
1224 void *_reply;
1225 redisReply *reply;
1226 sdshisds out = NULL((void*)0);
1227 int output = 1;
1228
1229 if (redisGetReply(context,&_reply) != REDIS_OK0) {
1230 if (config.shutdown) {
1231 redisFree(context);
1232 context = NULL((void*)0);
1233 return REDIS_OK0;
1234 }
1235 if (config.interactive) {
1236 /* Filter cases where we should reconnect */
1237 if (context->err == REDIS_ERR_IO1 &&
1238 (errno(*__errno_location ()) == ECONNRESET104 || errno(*__errno_location ()) == EPIPE32))
1239 return REDIS_ERR-1;
1240 if (context->err == REDIS_ERR_EOF3)
1241 return REDIS_ERR-1;
1242 }
1243 cliPrintContextError();
1244 exit(1);
1245 return REDIS_ERR-1; /* avoid compiler warning */
1246 }
1247
1248 reply = (redisReply*)_reply;
1249
1250 config.last_cmd_type = reply->type;
1251
1252 /* Check if we need to connect to a different node and reissue the
1253 * request. */
1254 if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR6 &&
1255 (!strncmp(reply->str,"MOVED",5) || !strcmp(reply->str,"ASK")))
1256 {
1257 char *p = reply->str, *s;
1258 int slot;
1259
1260 output = 0;
1261 /* Comments show the position of the pointer as:
1262 *
1263 * [S] for pointer 's'
1264 * [P] for pointer 'p'
1265 */
1266 s = strchr(p,' '); /* MOVED[S]3999 127.0.0.1:6381 */
1267 p = strchr(s+1,' '); /* MOVED[S]3999[P]127.0.0.1:6381 */
1268 *p = '\0';
1269 slot = atoi(s+1);
1270 s = strrchr(p+1,':'); /* MOVED 3999[P]127.0.0.1[S]6381 */
1271 *s = '\0';
1272 sdsfreehi_sdsfree(config.hostip);
1273 config.hostip = sdsnewhi_sdsnew(p+1);
1274 config.hostport = atoi(s+1);
1275 if (config.interactive)
1276 printf("-> Redirected to slot [%d] located at %s:%d\n",
1277 slot, config.hostip, config.hostport);
1278 config.cluster_reissue_command = 1;
1279 cliRefreshPrompt();
1280 } else if (!config.interactive && config.set_errcode &&
1281 reply->type == REDIS_REPLY_ERROR6)
1282 {
1283 fprintf(stderrstderr,"%s\n",reply->str);
1284 exit(1);
1285 return REDIS_ERR-1; /* avoid compiler warning */
1286 }
1287
1288 if (output) {
1289 out = cliFormatReply(reply, config.output, output_raw_strings);
1290 fwrite(out,sdslenhi_sdslen(out),1,stdoutstdout);
1291 sdsfreehi_sdsfree(out);
1292 }
1293 freeReplyObject(reply);
1294 return REDIS_OK0;
1295}
1296
1297static int cliSendCommand(int argc, char **argv, long repeat) {
1298 char *command = argv[0];
1299 size_t *argvlen;
1300 int j, output_raw;
1301
1302 if (!config.eval_ldb && /* In debugging mode, let's pass "help" to Redis. */
1303 (!strcasecmp(command,"help") || !strcasecmp(command,"?"))) {
1304 cliOutputHelp(--argc, ++argv);
1305 return REDIS_OK0;
1306 }
1307
1308 if (context == NULL((void*)0)) return REDIS_ERR-1;
1309
1310 output_raw = 0;
1311 if (!strcasecmp(command,"info") ||
1312 !strcasecmp(command,"lolwut") ||
1313 (argc >= 2 && !strcasecmp(command,"debug") &&
1314 !strcasecmp(argv[1],"htstats")) ||
1315 (argc >= 2 && !strcasecmp(command,"debug") &&
1316 !strcasecmp(argv[1],"htstats-key")) ||
1317 (argc >= 2 && !strcasecmp(command,"memory") &&
1318 (!strcasecmp(argv[1],"malloc-stats") ||
1319 !strcasecmp(argv[1],"doctor"))) ||
1320 (argc == 2 && !strcasecmp(command,"cluster") &&
1321 (!strcasecmp(argv[1],"nodes") ||
1322 !strcasecmp(argv[1],"info"))) ||
1323 (argc >= 2 && !strcasecmp(command,"client") &&
1324 (!strcasecmp(argv[1],"list") ||
1325 !strcasecmp(argv[1],"info"))) ||
1326 (argc == 3 && !strcasecmp(command,"latency") &&
1327 !strcasecmp(argv[1],"graph")) ||
1328 (argc == 2 && !strcasecmp(command,"latency") &&
1329 !strcasecmp(argv[1],"doctor")) ||
1330 /* Format PROXY INFO command for Redis Cluster Proxy:
1331 * https://github.com/artix75/redis-cluster-proxy */
1332 (argc >= 2 && !strcasecmp(command,"proxy") &&
1333 !strcasecmp(argv[1],"info")))
1334 {
1335 output_raw = 1;
1336 }
1337
1338 if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
1339 if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;
1340 if (!strcasecmp(command,"subscribe") ||
1341 !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1;
1342 if (!strcasecmp(command,"sync") ||
1343 !strcasecmp(command,"psync")) config.slave_mode = 1;
1344
1345 /* When the user manually calls SCRIPT DEBUG, setup the activation of
1346 * debugging mode on the next eval if needed. */
1347 if (argc == 3 && !strcasecmp(argv[0],"script") &&
1348 !strcasecmp(argv[1],"debug"))
1349 {
1350 if (!strcasecmp(argv[2],"yes") || !strcasecmp(argv[2],"sync")) {
1351 config.enable_ldb_on_eval = 1;
1352 } else {
1353 config.enable_ldb_on_eval = 0;
1354 }
1355 }
1356
1357 /* Actually activate LDB on EVAL if needed. */
1358 if (!strcasecmp(command,"eval") && config.enable_ldb_on_eval) {
1359 config.eval_ldb = 1;
1360 config.output = OUTPUT_RAW1;
1361 }
1362
1363 /* Setup argument length */
1364 argvlen = zmalloc(argc*sizeof(size_t));
1365 for (j = 0; j < argc; j++)
1366 argvlen[j] = sdslenhi_sdslen(argv[j]);
1367
1368 /* Negative repeat is allowed and causes infinite loop,
1369 works well with the interval option. */
1370 while(repeat < 0 || repeat-- > 0) {
1371 redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
1372 while (config.monitor_mode) {
1373 if (cliReadReply(output_raw) != REDIS_OK0) exit(1);
1374 fflush(stdoutstdout);
1375 }
1376
1377 if (config.pubsub_mode) {
1378 if (config.output != OUTPUT_RAW1)
1379 printf("Reading messages... (press Ctrl-C to quit)\n");
1380
1381 /* Unset our default PUSH handler so this works in RESP2/RESP3 */
1382 redisSetPushCallback(context, NULL((void*)0));
1383
1384 while (config.pubsub_mode) {
1385 if (cliReadReply(output_raw) != REDIS_OK0) exit(1);
1386 if (config.last_cmd_type == REDIS_REPLY_ERROR6) {
1387 if (config.push_output) {
1388 redisSetPushCallback(context, cliPushHandler);
1389 }
1390 config.pubsub_mode = 0;
1391 }
1392 }
1393 continue;
1394 }
1395
1396 if (config.slave_mode) {
1397 printf("Entering replica output mode... (press Ctrl-C to quit)\n");
1398 slaveMode();
1399 config.slave_mode = 0;
1400 zfree(argvlen);
1401 return REDIS_ERR-1; /* Error = slaveMode lost connection to master */
1402 }
1403
1404 if (cliReadReply(output_raw) != REDIS_OK0) {
1405 zfree(argvlen);
1406 return REDIS_ERR-1;
1407 } else {
1408 /* Store database number when SELECT was successfully executed. */
1409 if (!strcasecmp(command,"select") && argc == 2 &&
1410 config.last_cmd_type != REDIS_REPLY_ERROR6)
1411 {
1412 config.dbnum = atoi(argv[1]);
1413 cliRefreshPrompt();
1414 } else if (!strcasecmp(command,"auth") && (argc == 2 || argc == 3)) {
1415 cliSelect();
1416 } else if (!strcasecmp(command,"multi") && argc == 1 &&
1417 config.last_cmd_type != REDIS_REPLY_ERROR6)
1418 {
1419 config.in_multi = 1;
1420 config.pre_multi_dbnum = config.dbnum;
1421 cliRefreshPrompt();
1422 } else if (!strcasecmp(command,"exec") && argc == 1 && config.in_multi) {
1423 config.in_multi = 0;
1424 if (config.last_cmd_type == REDIS_REPLY_ERROR6) {
1425 config.dbnum = config.pre_multi_dbnum;
1426 }
1427 cliRefreshPrompt();
1428 } else if (!strcasecmp(command,"discard") && argc == 1 &&
1429 config.last_cmd_type != REDIS_REPLY_ERROR6)
1430 {
1431 config.in_multi = 0;
1432 config.dbnum = config.pre_multi_dbnum;
1433 cliRefreshPrompt();
1434 }
1435 }
1436 if (config.cluster_reissue_command){
1437 /* If we need to reissue the command, break to prevent a
1438 further 'repeat' number of dud interations */
1439 break;
1440 }
1441 if (config.interval) usleep(config.interval);
1442 fflush(stdoutstdout); /* Make it grep friendly */
1443 }
1444
1445 zfree(argvlen);
1446 return REDIS_OK0;
1447}
1448
1449/* Send a command reconnecting the link if needed. */
1450static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {
1451 redisReply *reply = NULL((void*)0);
1452 int tries = 0;
1453 va_list ap;
1454
1455 assert(!c->err)((!c->err) ? (void) (0) : __assert_fail ("!c->err", "redis-cli.c"
, 1455, __extension__ __PRETTY_FUNCTION__))
;
1456 while(reply == NULL((void*)0)) {
1457 while (c->err & (REDIS_ERR_IO1 | REDIS_ERR_EOF3)) {
1458 printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
1459 printf("Reconnecting... %d\r", ++tries);
1460 fflush(stdoutstdout);
1461
1462 redisFree(c);
1463 c = redisConnect(config.hostip,config.hostport);
1464 if (!c->err && config.tls) {
1465 const char *err = NULL((void*)0);
1466 if (cliSecureConnection(c, config.sslconfig, &err) == REDIS_ERR-1 && err) {
1467 fprintf(stderrstderr, "TLS Error: %s\n", err);
1468 exit(1);
1469 }
1470 }
1471 usleep(1000000);
1472 }
1473
1474 va_start(ap,fmt)__builtin_va_start(ap, fmt);
1475 reply = redisvCommand(c,fmt,ap);
1476 va_end(ap)__builtin_va_end(ap);
1477
1478 if (c->err && !(c->err & (REDIS_ERR_IO1 | REDIS_ERR_EOF3))) {
1479 fprintf(stderrstderr, "Error: %s\n", c->errstr);
1480 exit(1);
1481 } else if (tries > 0) {
1482 printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
1483 }
1484 }
1485
1486 context = c;
1487 return reply;
1488}
1489
1490/*------------------------------------------------------------------------------
1491 * User interface
1492 *--------------------------------------------------------------------------- */
1493
1494static int parseOptions(int argc, char **argv) {
1495 int i;
1496
1497 for (i = 1; i < argc; i++) {
1498 int lastarg = i==argc-1;
1499
1500 if (!strcmp(argv[i],"-h") && !lastarg) {
1501 sdsfreehi_sdsfree(config.hostip);
1502 config.hostip = sdsnewhi_sdsnew(argv[++i]);
1503 } else if (!strcmp(argv[i],"-h") && lastarg) {
1504 usage();
1505 } else if (!strcmp(argv[i],"--help")) {
1506 usage();
1507 } else if (!strcmp(argv[i],"-x")) {
1508 config.stdinarg = 1;
1509 } else if (!strcmp(argv[i],"-p") && !lastarg) {
1510 config.hostport = atoi(argv[++i]);
1511 } else if (!strcmp(argv[i],"-s") && !lastarg) {
1512 config.hostsocket = argv[++i];
1513 } else if (!strcmp(argv[i],"-r") && !lastarg) {
1514 config.repeat = strtoll(argv[++i],NULL((void*)0),10);
1515 } else if (!strcmp(argv[i],"-i") && !lastarg) {
1516 double seconds = atof(argv[++i]);
1517 config.interval = seconds*1000000;
1518 } else if (!strcmp(argv[i],"-n") && !lastarg) {
1519 config.dbnum = atoi(argv[++i]);
1520 } else if (!strcmp(argv[i], "--no-auth-warning")) {
1521 config.no_auth_warning = 1;
1522 } else if (!strcmp(argv[i], "--askpass")) {
1523 config.askpass = 1;
1524 } else if ((!strcmp(argv[i],"-a") || !strcmp(argv[i],"--pass"))
1525 && !lastarg)
1526 {
1527 config.auth = argv[++i];
1528 } else if (!strcmp(argv[i],"--user") && !lastarg) {
1529 config.user = argv[++i];
1530 } else if (!strcmp(argv[i],"-u") && !lastarg) {
1531 parseRedisUri(argv[++i]);
1532 } else if (!strcmp(argv[i],"--raw")) {
1533 config.output = OUTPUT_RAW1;
1534 } else if (!strcmp(argv[i],"--no-raw")) {
1535 config.output = OUTPUT_STANDARD0;
1536 } else if (!strcmp(argv[i],"--csv")) {
1537 config.output = OUTPUT_CSV2;
1538 } else if (!strcmp(argv[i],"--latency")) {
1539 config.latency_mode = 1;
1540 } else if (!strcmp(argv[i],"--latency-dist")) {
1541 config.latency_dist_mode = 1;
1542 } else if (!strcmp(argv[i],"--mono")) {
1543 spectrum_palette = spectrum_palette_mono;
1544 spectrum_palette_size = spectrum_palette_mono_size;
1545 } else if (!strcmp(argv[i],"--latency-history")) {
1546 config.latency_mode = 1;
1547 config.latency_history = 1;
1548 } else if (!strcmp(argv[i],"--lru-test") && !lastarg) {
1549 config.lru_test_mode = 1;
1550 config.lru_test_sample_size = strtoll(argv[++i],NULL((void*)0),10);
1551 } else if (!strcmp(argv[i],"--slave")) {
1552 config.slave_mode = 1;
1553 } else if (!strcmp(argv[i],"--replica")) {
1554 config.slave_mode = 1;
1555 } else if (!strcmp(argv[i],"--stat")) {
1556 config.stat_mode = 1;
1557 } else if (!strcmp(argv[i],"--scan")) {
1558 config.scan_mode = 1;
1559 } else if (!strcmp(argv[i],"--pattern") && !lastarg) {
1560 config.pattern = argv[++i];
1561 } else if (!strcmp(argv[i],"--intrinsic-latency") && !lastarg) {
1562 config.intrinsic_latency_mode = 1;
1563 config.intrinsic_latency_duration = atoi(argv[++i]);
1564 } else if (!strcmp(argv[i],"--rdb") && !lastarg) {
1565 config.getrdb_mode = 1;
1566 config.rdb_filename = argv[++i];
1567 } else if (!strcmp(argv[i],"--pipe")) {
1568 config.pipe_mode = 1;
1569 } else if (!strcmp(argv[i],"--pipe-timeout") && !lastarg) {
1570 config.pipe_timeout = atoi(argv[++i]);
1571 } else if (!strcmp(argv[i],"--bigkeys")) {
1572 config.bigkeys = 1;
1573 } else if (!strcmp(argv[i],"--memkeys")) {
1574 config.memkeys = 1;
1575 config.memkeys_samples = 0; /* use redis default */
1576 } else if (!strcmp(argv[i],"--memkeys-samples")) {
1577 config.memkeys = 1;
1578 config.memkeys_samples = atoi(argv[++i]);
1579 } else if (!strcmp(argv[i],"--hotkeys")) {
1580 config.hotkeys = 1;
1581 } else if (!strcmp(argv[i],"--eval") && !lastarg) {
1582 config.eval = argv[++i];
1583 } else if (!strcmp(argv[i],"--ldb")) {
1584 config.eval_ldb = 1;
1585 config.output = OUTPUT_RAW1;
1586 } else if (!strcmp(argv[i],"--ldb-sync-mode")) {
1587 config.eval_ldb = 1;
1588 config.eval_ldb_sync = 1;
1589 config.output = OUTPUT_RAW1;
1590 } else if (!strcmp(argv[i],"-c")) {
1591 config.cluster_mode = 1;
1592 } else if (!strcmp(argv[i],"-d") && !lastarg) {
1593 sdsfreehi_sdsfree(config.mb_delim);
1594 config.mb_delim = sdsnewhi_sdsnew(argv[++i]);
1595 } else if (!strcmp(argv[i],"-D") && !lastarg) {
1596 sdsfreehi_sdsfree(config.cmd_delim);
1597 config.cmd_delim = sdsnewhi_sdsnew(argv[++i]);
1598 } else if (!strcmp(argv[i],"-e")) {
1599 config.set_errcode = 1;
1600 } else if (!strcmp(argv[i],"--verbose")) {
1601 config.verbose = 1;
1602 } else if (!strcmp(argv[i],"--cluster") && !lastarg) {
1603 if (CLUSTER_MANAGER_MODE()(config.cluster_manager_command.name != ((void*)0))) usage();
1604 char *cmd = argv[++i];
1605 int j = i;
1606 while (j < argc && argv[j][0] != '-') j++;
1607 if (j > i) j--;
1608 createClusterManagerCommand(cmd, j - i, argv + i + 1);
1609 i = j;
1610 } else if (!strcmp(argv[i],"--cluster") && lastarg) {
1611 usage();
1612 } else if ((!strcmp(argv[i],"--cluster-only-masters"))) {
1613 config.cluster_manager_command.flags |=
1614 CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY1 << 11;
1615 } else if ((!strcmp(argv[i],"--cluster-only-replicas"))) {
1616 config.cluster_manager_command.flags |=
1617 CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY1 << 12;
1618 } else if (!strcmp(argv[i],"--cluster-replicas") && !lastarg) {
1619 config.cluster_manager_command.replicas = atoi(argv[++i]);
1620 } else if (!strcmp(argv[i],"--cluster-master-id") && !lastarg) {
1621 config.cluster_manager_command.master_id = argv[++i];
1622 } else if (!strcmp(argv[i],"--cluster-from") && !lastarg) {
1623 config.cluster_manager_command.from = argv[++i];
1624 } else if (!strcmp(argv[i],"--cluster-to") && !lastarg) {
1625 config.cluster_manager_command.to = argv[++i];
1626 } else if (!strcmp(argv[i],"--cluster-from-user") && !lastarg) {
1627 config.cluster_manager_command.from_user = argv[++i];
1628 } else if (!strcmp(argv[i],"--cluster-from-pass") && !lastarg) {
1629 config.cluster_manager_command.from_pass = argv[++i];
1630 } else if (!strcmp(argv[i], "--cluster-from-askpass")) {
1631 config.cluster_manager_command.from_askpass = 1;
1632 } else if (!strcmp(argv[i],"--cluster-weight") && !lastarg) {
1633 if (config.cluster_manager_command.weight != NULL((void*)0)) {
1634 fprintf(stderrstderr, "WARNING: you cannot use --cluster-weight "
1635 "more than once.\n"
1636 "You can set more weights by adding them "
1637 "as a space-separated list, ie:\n"
1638 "--cluster-weight n1=w n2=w\n");
1639 exit(1);
1640 }
1641 int widx = i + 1;
1642 char **weight = argv + widx;
1643 int wargc = 0;
1644 for (; widx < argc; widx++) {
1645 if (strstr(argv[widx], "--") == argv[widx]) break;
1646 if (strchr(argv[widx], '=') == NULL((void*)0)) break;
1647 wargc++;
1648 }
1649 if (wargc > 0) {
1650 config.cluster_manager_command.weight = weight;
1651 config.cluster_manager_command.weight_argc = wargc;
1652 i += wargc;
1653 }
1654 } else if (!strcmp(argv[i],"--cluster-slots") && !lastarg) {
1655 config.cluster_manager_command.slots = atoi(argv[++i]);
1656 } else if (!strcmp(argv[i],"--cluster-timeout") && !lastarg) {
1657 config.cluster_manager_command.timeout = atoi(argv[++i]);
1658 } else if (!strcmp(argv[i],"--cluster-pipeline") && !lastarg) {
1659 config.cluster_manager_command.pipeline = atoi(argv[++i]);
1660 } else if (!strcmp(argv[i],"--cluster-threshold") && !lastarg) {
1661 config.cluster_manager_command.threshold = atof(argv[++i]);
1662 } else if (!strcmp(argv[i],"--cluster-yes")) {
1663 config.cluster_manager_command.flags |=
1664 CLUSTER_MANAGER_CMD_FLAG_YES1 << 2;
1665 } else if (!strcmp(argv[i],"--cluster-simulate")) {
1666 config.cluster_manager_command.flags |=
1667 CLUSTER_MANAGER_CMD_FLAG_SIMULATE1 << 5;
1668 } else if (!strcmp(argv[i],"--cluster-replace")) {
1669 config.cluster_manager_command.flags |=
1670 CLUSTER_MANAGER_CMD_FLAG_REPLACE1 << 6;
1671 } else if (!strcmp(argv[i],"--cluster-copy")) {
1672 config.cluster_manager_command.flags |=
1673 CLUSTER_MANAGER_CMD_FLAG_COPY1 << 7;
1674 } else if (!strcmp(argv[i],"--cluster-slave")) {
1675 config.cluster_manager_command.flags |=
1676 CLUSTER_MANAGER_CMD_FLAG_SLAVE1 << 1;
1677 } else if (!strcmp(argv[i],"--cluster-use-empty-masters")) {
1678 config.cluster_manager_command.flags |=
1679 CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER1 << 4;
1680 } else if (!strcmp(argv[i],"--cluster-search-multiple-owners")) {
1681 config.cluster_manager_command.flags |=
1682 CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS1 << 9;
1683 } else if (!strcmp(argv[i],"--cluster-fix-with-unreachable-masters")) {
1684 config.cluster_manager_command.flags |=
1685 CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS1 << 10;
1686#ifdef USE_OPENSSL
1687 } else if (!strcmp(argv[i],"--tls")) {
1688 config.tls = 1;
1689 } else if (!strcmp(argv[i],"--sni") && !lastarg) {
1690 config.sslconfig.sni = argv[++i];
1691 } else if (!strcmp(argv[i],"--cacertdir") && !lastarg) {
1692 config.sslconfig.cacertdir = argv[++i];
1693 } else if (!strcmp(argv[i],"--cacert") && !lastarg) {
1694 config.sslconfig.cacert = argv[++i];
1695 } else if (!strcmp(argv[i],"--cert") && !lastarg) {
1696 config.sslconfig.cert = argv[++i];
1697 } else if (!strcmp(argv[i],"--key") && !lastarg) {
1698 config.sslconfig.key = argv[++i];
1699 } else if (!strcmp(argv[i],"--tls-ciphers") && !lastarg) {
1700 config.sslconfig.ciphers = argv[++i];
1701 } else if (!strcmp(argv[i],"--insecure")) {
1702 config.sslconfig.skip_cert_verify = 1;
1703 #ifdef TLS1_3_VERSION
1704 } else if (!strcmp(argv[i],"--tls-ciphersuites") && !lastarg) {
1705 config.sslconfig.ciphersuites = argv[++i];
1706 #endif
1707#endif
1708 } else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
1709 sdshisds version = cliVersion();
1710 printf("redis-cli %s\n", version);
1711 sdsfreehi_sdsfree(version);
1712 exit(0);
1713 } else if (!strcmp(argv[i],"-3")) {
1714 config.resp3 = 1;
1715 } else if (!strcmp(argv[i],"--show-pushes") && !lastarg) {
1716 char *argval = argv[++i];
1717 if (!strncasecmp(argval, "n", 1)) {
1718 config.push_output = 0;
1719 } else if (!strncasecmp(argval, "y", 1)) {
1720 config.push_output = 1;
1721 } else {
1722 fprintf(stderrstderr, "Unknown --show-pushes value '%s' "
1723 "(valid: '[y]es', '[n]o')\n", argval);
1724 }
1725 } else if (CLUSTER_MANAGER_MODE()(config.cluster_manager_command.name != ((void*)0)) && argv[i][0] != '-') {
1726 if (config.cluster_manager_command.argc == 0) {
1727 int j = i + 1;
1728 while (j < argc && argv[j][0] != '-') j++;
1729 int cmd_argc = j - i;
1730 config.cluster_manager_command.argc = cmd_argc;
1731 config.cluster_manager_command.argv = argv + i;
1732 if (cmd_argc > 1) i = j - 1;
1733 }
1734 } else {
1735 if (argv[i][0] == '-') {
1736 fprintf(stderrstderr,
1737 "Unrecognized option or bad number of args for: '%s'\n",
1738 argv[i]);
1739 exit(1);
1740 } else {
1741 /* Likely the command name, stop here. */
1742 break;
1743 }
1744 }
1745 }
1746
1747 /* --ldb requires --eval. */
1748 if (config.eval_ldb && config.eval == NULL((void*)0)) {
1749 fprintf(stderrstderr,"Options --ldb and --ldb-sync-mode require --eval.\n");
1750 fprintf(stderrstderr,"Try %s --help for more information.\n", argv[0]);
1751 exit(1);
1752 }
1753
1754 if (!config.no_auth_warning && config.auth != NULL((void*)0)) {
1755 fputs("Warning: Using a password with '-a' or '-u' option on the command"
1756 " line interface may not be safe.\n", stderrstderr);
1757 }
1758
1759 return i;
1760}
1761
1762static void parseEnv() {
1763 /* Set auth from env, but do not overwrite CLI arguments if passed */
1764 char *auth = getenv(REDIS_CLI_AUTH_ENV"REDISCLI_AUTH");
1765 if (auth != NULL((void*)0) && config.auth == NULL((void*)0)) {
1766 config.auth = auth;
1767 }
1768
1769 char *cluster_yes = getenv(REDIS_CLI_CLUSTER_YES_ENV"REDISCLI_CLUSTER_YES");
1770 if (cluster_yes != NULL((void*)0) && !strcmp(cluster_yes, "1")) {
1771 config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_YES1 << 2;
1772 }
1773}
1774
1775static sdshisds readArgFromStdin(void) {
1776 char buf[1024];
1777 sdshisds arg = sdsemptyhi_sdsempty();
1778
1779 while(1) {
1780 int nread = read(fileno(stdinstdin),buf,1024);
1781
1782 if (nread == 0) break;
1783 else if (nread == -1) {
1784 perror("Reading from standard input");
1785 exit(1);
1786 }
1787 arg = sdscatlenhi_sdscatlen(arg,buf,nread);
1788 }
1789 return arg;
1790}
1791
1792static void usage(void) {
1793 sdshisds version = cliVersion();
1794 fprintf(stderrstderr,
1795"redis-cli %s\n"
1796"\n"
1797"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
1798" -h <hostname> Server hostname (default: 127.0.0.1).\n"
1799" -p <port> Server port (default: 6379).\n"
1800" -s <socket> Server socket (overrides hostname and port).\n"
1801" -a <password> Password to use when connecting to the server.\n"
1802" You can also use the " REDIS_CLI_AUTH_ENV"REDISCLI_AUTH" " environment\n"
1803" variable to pass this password more safely\n"
1804" (if both are used, this argument takes precedence).\n"
1805" --user <username> Used to send ACL style 'AUTH username pass'. Needs -a.\n"
1806" --pass <password> Alias of -a for consistency with the new --user option.\n"
1807" --askpass Force user to input password with mask from STDIN.\n"
1808" If this argument is used, '-a' and " REDIS_CLI_AUTH_ENV"REDISCLI_AUTH" "\n"
1809" environment variable will be ignored.\n"
1810" -u <uri> Server URI.\n"
1811" -r <repeat> Execute specified command N times.\n"
1812" -i <interval> When -r is used, waits <interval> seconds per command.\n"
1813" It is possible to specify sub-second times like -i 0.1.\n"
1814" -n <db> Database number.\n"
1815" -3 Start session in RESP3 protocol mode.\n"
1816" -x Read last argument from STDIN.\n"
1817" -d <delimiter> Delimiter between response bulks for raw formatting (default: \\n).\n"
1818" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
1819" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
1820" -e Return exit error code when command execution fails.\n"
1821#ifdef USE_OPENSSL
1822" --tls Establish a secure TLS connection.\n"
1823" --sni <host> Server name indication for TLS.\n"
1824" --cacert <file> CA Certificate file to verify with.\n"
1825" --cacertdir <dir> Directory where trusted CA certificates are stored.\n"
1826" If neither cacert nor cacertdir are specified, the default\n"
1827" system-wide trusted root certs configuration will apply.\n"
1828" --insecure Allow insecure TLS connection by skipping cert validation.\n"
1829" --cert <file> Client certificate to authenticate with.\n"
1830" --key <file> Private key file to authenticate with.\n"
1831" --tls-ciphers <list> Sets the list of prefered ciphers (TLSv1.2 and below)\n"
1832" in order of preference from highest to lowest separated by colon (\":\").\n"
1833" See the ciphers(1ssl) manpage for more information about the syntax of this string.\n"
1834#ifdef TLS1_3_VERSION
1835" --tls-ciphersuites <list> Sets the list of prefered ciphersuites (TLSv1.3)\n"
1836" in order of preference from highest to lowest separated by colon (\":\").\n"
1837" See the ciphers(1ssl) manpage for more information about the syntax of this string,\n"
1838" and specifically for TLSv1.3 ciphersuites.\n"
1839#endif
1840#endif
1841" --raw Use raw formatting for replies (default when STDOUT is\n"
1842" not a tty).\n"
1843" --no-raw Force formatted output even when STDOUT is not a tty.\n"
1844" --csv Output in CSV format.\n"
1845" --show-pushes <yn> Whether to print RESP3 PUSH messages. Enabled by default when\n"
1846" STDOUT is a tty but can be overriden with --show-pushes no.\n"
1847" --stat Print rolling stats about server: mem, clients, ...\n"
1848" --latency Enter a special mode continuously sampling latency.\n"
1849" If you use this mode in an interactive session it runs\n"
1850" forever displaying real-time stats. Otherwise if --raw or\n"
1851" --csv is specified, or if you redirect the output to a non\n"
1852" TTY, it samples the latency for 1 second (you can use\n"
1853" -i to change the interval), then produces a single output\n"
1854" and exits.\n",version);
1855
1856 fprintf(stderrstderr,
1857" --latency-history Like --latency but tracking latency changes over time.\n"
1858" Default time interval is 15 sec. Change it using -i.\n"
1859" --latency-dist Shows latency as a spectrum, requires xterm 256 colors.\n"
1860" Default time interval is 1 sec. Change it using -i.\n"
1861" --lru-test <keys> Simulate a cache workload with an 80-20 distribution.\n"
1862" --replica Simulate a replica showing commands received from the master.\n"
1863" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
1864" --pipe Transfer raw Redis protocol from stdin to server.\n"
1865" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
1866" no reply is received within <n> seconds.\n"
1867" Default timeout: %d. Use 0 to wait forever.\n",
1868 REDIS_CLI_DEFAULT_PIPE_TIMEOUT30);
1869 fprintf(stderrstderr,
1870" --bigkeys Sample Redis keys looking for keys with many elements (complexity).\n"
1871" --memkeys Sample Redis keys looking for keys consuming a lot of memory.\n"
1872" --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.\n"
1873" And define number of key elements to sample\n"
1874" --hotkeys Sample Redis keys looking for hot keys.\n"
1875" only works when maxmemory-policy is *lfu.\n"
1876" --scan List all keys using the SCAN command.\n"
1877" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys\n"
1878" options (default: *).\n"
1879" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
1880" The test will run for the specified amount of seconds.\n"
1881" --eval <file> Send an EVAL command using the Lua script at <file>.\n"
1882" --ldb Used with --eval enable the Redis Lua debugger.\n"
1883" --ldb-sync-mode Like --ldb but uses the synchronous Lua debugger, in\n"
1884" this mode the server is blocked and script changes are\n"
1885" not rolled back from the server memory.\n"
1886" --cluster <command> [args...] [opts...]\n"
1887" Cluster Manager command and arguments (see below).\n"
1888" --verbose Verbose mode.\n"
1889" --no-auth-warning Don't show warning message when using password on command\n"
1890" line interface.\n"
1891" --help Output this help and exit.\n"
1892" --version Output version and exit.\n"
1893"\n");
1894 /* Using another fprintf call to avoid -Woverlength-strings compile warning */
1895 fprintf(stderrstderr,
1896"Cluster Manager Commands:\n"
1897" Use --cluster help to list all available cluster manager commands.\n"
1898"\n"
1899"Examples:\n"
1900" cat /etc/passwd | redis-cli -x set mypasswd\n"
1901" redis-cli get mypasswd\n"
1902" redis-cli -r 100 lpush mylist x\n"
1903" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
1904" redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n"
1905" redis-cli --scan --pattern '*:12345*'\n"
1906"\n"
1907" (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n"
1908"\n"
1909"When no command is given, redis-cli starts in interactive mode.\n"
1910"Type \"help\" in interactive mode for information on available commands\n"
1911"and settings.\n"
1912"\n");
1913 sdsfreehi_sdsfree(version);
1914 exit(1);
1915}
1916
1917static int confirmWithYes(char *msg, int ignore_force) {
1918 /* if --cluster-yes option is set and ignore_force is false,
1919 * do not prompt for an answer */
1920 if (!ignore_force &&
1921 (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_YES1 << 2)) {
1922 return 1;
1923 }
1924
1925 printf("%s (type 'yes' to accept): ", msg);
1926 fflush(stdoutstdout);
1927 char buf[4];
1928 int nread = read(fileno(stdinstdin),buf,4);
1929 buf[3] = '\0';
1930 return (nread != 0 && !strcmp("yes", buf));
1931}
1932
1933/* Turn the plain C strings into Sds strings */
1934static char **convertToSds(int count, char** args) {
1935 int j;
1936 char **sdshisds = zmalloc(sizeof(char*)*count);
1937
1938 for(j = 0; j < count; j++)
1939 sdshisds[j] = sdsnewhi_sdsnew(args[j]);
1940
1941 return sdshisds;
1942}
1943
1944static void freeConvertedSds(int count, char **sdshisds) {
1945 int j;
1946 for (j = 0; j < count; j++)
1947 sdsfreehi_sdsfree(sdshisds[j]);
1948 zfree(sdshisds);
1949}
1950
1951static int issueCommandRepeat(int argc, char **argv, long repeat) {
1952 while (1) {
1953 config.cluster_reissue_command = 0;
1954 if (cliSendCommand(argc,argv,repeat) != REDIS_OK0) {
1955 cliConnect(CC_FORCE(1<<0));
1956
1957 /* If we still cannot send the command print error.
1958 * We'll try to reconnect the next time. */
1959 if (cliSendCommand(argc,argv,repeat) != REDIS_OK0) {
1960 cliPrintContextError();
1961 return REDIS_ERR-1;
1962 }
1963 }
1964 /* Issue the command again if we got redirected in cluster mode */
1965 if (config.cluster_mode && config.cluster_reissue_command) {
1966 cliConnect(CC_FORCE(1<<0));
1967 } else {
1968 break;
1969 }
1970 }
1971 return REDIS_OK0;
1972}
1973
1974static int issueCommand(int argc, char **argv) {
1975 return issueCommandRepeat(argc, argv, config.repeat);
1976}
1977
1978/* Split the user provided command into multiple SDS arguments.
1979 * This function normally uses sdssplitargs() from sds.c which is able
1980 * to understand "quoted strings", escapes and so forth. However when
1981 * we are in Lua debugging mode and the "eval" command is used, we want
1982 * the remaining Lua script (after "e " or "eval ") to be passed verbatim
1983 * as a single big argument. */
1984static sdshisds *cliSplitArgs(char *line, int *argc) {
1985 if (config.eval_ldb && (strstr(line,"eval ") == line ||
1986 strstr(line,"e ") == line))
1987 {
1988 sdshisds *argv = sds_mallochi_sds_malloc(sizeof(sdshisds)*2);
1989 *argc = 2;
1990 int len = strlen(line);
1991 int elen = line[1] == ' ' ? 2 : 5; /* "e " or "eval "? */
1992 argv[0] = sdsnewlenhi_sdsnewlen(line,elen-1);
1993 argv[1] = sdsnewlenhi_sdsnewlen(line+elen,len-elen);
1994 return argv;
1995 } else {
1996 return sdssplitargshi_sdssplitargs(line,argc);
1997 }
1998}
1999
2000/* Set the CLI preferences. This function is invoked when an interactive
2001 * ":command" is called, or when reading ~/.redisclirc file, in order to
2002 * set user preferences. */
2003void cliSetPreferences(char **argv, int argc, int interactive) {
2004 if (!strcasecmp(argv[0],":set") && argc >= 2) {
2005 if (!strcasecmp(argv[1],"hints")) pref.hints = 1;
2006 else if (!strcasecmp(argv[1],"nohints")) pref.hints = 0;
2007 else {
2008 printf("%sunknown redis-cli preference '%s'\n",
2009 interactive ? "" : ".redisclirc: ",
2010 argv[1]);
2011 }
2012 } else {
2013 printf("%sunknown redis-cli internal command '%s'\n",
2014 interactive ? "" : ".redisclirc: ",
2015 argv[0]);
2016 }
2017}
2018
2019/* Load the ~/.redisclirc file if any. */
2020void cliLoadPreferences(void) {
2021 sdshisds rcfile = getDotfilePath(REDIS_CLI_RCFILE_ENV"REDISCLI_RCFILE",REDIS_CLI_RCFILE_DEFAULT".redisclirc");
2022 if (rcfile == NULL((void*)0)) return;
2023 FILE *fp = fopen(rcfile,"r");
2024 char buf[1024];
2025
2026 if (fp) {
2027 while(fgets(buf,sizeof(buf),fp) != NULL((void*)0)) {
2028 sdshisds *argv;
2029 int argc;
2030
2031 argv = sdssplitargshi_sdssplitargs(buf,&argc);
2032 if (argc > 0) cliSetPreferences(argv,argc,0);
2033 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
2034 }
2035 fclose(fp);
2036 }
2037 sdsfreehi_sdsfree(rcfile);
2038}
2039
2040static void repl(void) {
2041 sdshisds historyfile = NULL((void*)0);
2042 int history = 0;
2043 char *line;
2044 int argc;
2045 sdshisds *argv;
2046
2047 /* Initialize the help and, if possible, use the COMMAND command in order
2048 * to retrieve missing entries. */
2049 cliInitHelp();
2050 cliIntegrateHelp();
2051
2052 config.interactive = 1;
2053 linenoiseSetMultiLine(1);
2054 linenoiseSetCompletionCallback(completionCallback);
2055 linenoiseSetHintsCallback(hintsCallback);
2056 linenoiseSetFreeHintsCallback(freeHintsCallback);
2057
2058 /* Only use history and load the rc file when stdin is a tty. */
2059 if (isatty(fileno(stdinstdin))) {
2060 historyfile = getDotfilePath(REDIS_CLI_HISTFILE_ENV"REDISCLI_HISTFILE",REDIS_CLI_HISTFILE_DEFAULT".rediscli_history");
2061 //keep in-memory history always regardless if history file can be determined
2062 history = 1;
2063 if (historyfile != NULL((void*)0)) {
2064 linenoiseHistoryLoad(historyfile);
2065 }
2066 cliLoadPreferences();
2067 }
2068
2069 cliRefreshPrompt();
2070 while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL((void*)0)) {
2071 if (line[0] != '\0') {
2072 long repeat = 1;
2073 int skipargs = 0;
2074 char *endptr = NULL((void*)0);
2075
2076 argv = cliSplitArgs(line,&argc);
2077
2078 /* check if we have a repeat command option and
2079 * need to skip the first arg */
2080 if (argv && argc > 0) {
2081 errno(*__errno_location ()) = 0;
2082 repeat = strtol(argv[0], &endptr, 10);
2083 if (argc > 1 && *endptr == '\0') {
2084 if (errno(*__errno_location ()) == ERANGE34 || errno(*__errno_location ()) == EINVAL22 || repeat <= 0) {
2085 fputs("Invalid redis-cli repeat command option value.\n", stdoutstdout);
2086 sdsfreesplitreshi_sdsfreesplitres(argv, argc);
2087 linenoiseFree(line);
2088 continue;
2089 }
2090 skipargs = 1;
2091 } else {
2092 repeat = 1;
2093 }
2094 }
2095
2096 /* Won't save auth or acl setuser commands in history file */
2097 int dangerous = 0;
2098 if (argv && argc > 0) {
2099 if (!strcasecmp(argv[skipargs], "auth")) {
2100 dangerous = 1;
2101 } else if (skipargs+1 < argc &&
2102 !strcasecmp(argv[skipargs], "acl") &&
2103 !strcasecmp(argv[skipargs+1], "setuser"))
2104 {
2105 dangerous = 1;
2106 }
2107 }
2108
2109 if (!dangerous) {
2110 if (history) linenoiseHistoryAdd(line);
2111 if (historyfile) linenoiseHistorySave(historyfile);
2112 }
2113
2114 if (argv == NULL((void*)0)) {
2115 printf("Invalid argument(s)\n");
2116 fflush(stdoutstdout);
2117 linenoiseFree(line);
2118 continue;
2119 } else if (argc > 0) {
2120 if (strcasecmp(argv[0],"quit") == 0 ||
2121 strcasecmp(argv[0],"exit") == 0)
2122 {
2123 exit(0);
2124 } else if (argv[0][0] == ':') {
2125 cliSetPreferences(argv,argc,1);
2126 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
2127 linenoiseFree(line);
2128 continue;
2129 } else if (strcasecmp(argv[0],"restart") == 0) {
2130 if (config.eval) {
2131 config.eval_ldb = 1;
2132 config.output = OUTPUT_RAW1;
2133 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
2134 linenoiseFree(line);
2135 return; /* Return to evalMode to restart the session. */
2136 } else {
2137 printf("Use 'restart' only in Lua debugging mode.");
2138 }
2139 } else if (argc == 3 && !strcasecmp(argv[0],"connect")) {
2140 sdsfreehi_sdsfree(config.hostip);
2141 config.hostip = sdsnewhi_sdsnew(argv[1]);
2142 config.hostport = atoi(argv[2]);
2143 cliRefreshPrompt();
2144 cliConnect(CC_FORCE(1<<0));
2145 } else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
2146 linenoiseClearScreen();
2147 } else {
2148 long long start_time = mstime(), elapsed;
2149
2150 issueCommandRepeat(argc-skipargs, argv+skipargs, repeat);
2151
2152 /* If our debugging session ended, show the EVAL final
2153 * reply. */
2154 if (config.eval_ldb_end) {
2155 config.eval_ldb_end = 0;
2156 cliReadReply(0);
2157 printf("\n(Lua debugging session ended%s)\n\n",
2158 config.eval_ldb_sync ? "" :
2159 " -- dataset changes rolled back");
2160 }
2161
2162 elapsed = mstime()-start_time;
2163 if (elapsed >= 500 &&
2164 config.output == OUTPUT_STANDARD0)
2165 {
2166 printf("(%.2fs)\n",(double)elapsed/1000);
2167 }
2168 }
2169 }
2170 /* Free the argument vector */
2171 sdsfreesplitreshi_sdsfreesplitres(argv,argc);
2172 }
2173 /* linenoise() returns malloc-ed lines like readline() */
2174 linenoiseFree(line);
2175 }
2176 exit(0);
2177}
2178
2179static int noninteractive(int argc, char **argv) {
2180 int retval = 0;
2181
2182 argv = convertToSds(argc, argv);
2183 if (config.stdinarg) {
2184 argv = zrealloc(argv, (argc+1)*sizeof(char*));
2185 argv[argc] = readArgFromStdin();
2186 retval = issueCommand(argc+1, argv);
2187 sdsfreehi_sdsfree(argv[argc]);
2188 } else {
2189 retval = issueCommand(argc, argv);
2190 }
2191 freeConvertedSds(argc, argv);
2192 return retval;
2193}
2194
2195/*------------------------------------------------------------------------------
2196 * Eval mode
2197 *--------------------------------------------------------------------------- */
2198
2199static int evalMode(int argc, char **argv) {
2200 sdshisds script = NULL((void*)0);
2201 FILE *fp;
2202 char buf[1024];
2203 size_t nread;
2204 char **argv2;
2205 int j, got_comma, keys;
2206 int retval = REDIS_OK0;
2207
2208 while(1) {
2209 if (config.eval_ldb) {
2210 printf(
2211 "Lua debugging session started, please use:\n"
2212 "quit -- End the session.\n"
2213 "restart -- Restart the script in debug mode again.\n"
2214 "help -- Show Lua script debugging commands.\n\n"
2215 );
2216 }
2217
2218 sdsfreehi_sdsfree(script);
2219 script = sdsemptyhi_sdsempty();
2220 got_comma = 0;
2221 keys = 0;
2222
2223 /* Load the script from the file, as an sds string. */
2224 fp = fopen(config.eval,"r");
2225 if (!fp) {
2226 fprintf(stderrstderr,
2227 "Can't open file '%s': %s\n", config.eval, strerror(errno(*__errno_location ())));
2228 exit(1);
2229 }
2230 while((nread = fread(buf,1,sizeof(buf),fp)) != 0) {
2231 script = sdscatlenhi_sdscatlen(script,buf,nread);
2232 }
2233 fclose(fp);
2234
2235 /* If we are debugging a script, enable the Lua debugger. */
2236 if (config.eval_ldb) {
2237 redisReply *reply = redisCommand(context,
2238 config.eval_ldb_sync ?
2239 "SCRIPT DEBUG sync": "SCRIPT DEBUG yes");
2240 if (reply) freeReplyObject(reply);
2241 }
2242
2243 /* Create our argument vector */
2244 argv2 = zmalloc(sizeof(sdshisds)*(argc+3));
2245 argv2[0] = sdsnewhi_sdsnew("EVAL");
2246 argv2[1] = script;
2247 for (j = 0; j < argc; j++) {
2248 if (!got_comma && argv[j][0] == ',' && argv[j][1] == 0) {
2249 got_comma = 1;
2250 continue;
2251 }
2252 argv2[j+3-got_comma] = sdsnewhi_sdsnew(argv[j]);
2253 if (!got_comma) keys++;
2254 }
2255 argv2[2] = sdscatprintfhi_sdscatprintf(sdsemptyhi_sdsempty(),"%d",keys);
2256
2257 /* Call it */
2258 int eval_ldb = config.eval_ldb; /* Save it, may be reverted. */
2259 retval = issueCommand(argc+3-got_comma, argv2);
2260 if (eval_ldb) {
2261 if (!config.eval_ldb) {
2262 /* If the debugging session ended immediately, there was an
2263 * error compiling the script. Show it and they don't enter
2264 * the REPL at all. */
2265 printf("Eval debugging session can't start:\n");
2266 cliReadReply(0);
2267 break; /* Return to the caller. */
2268 } else {
2269 strncpy(config.prompt,"lua debugger> ",sizeof(config.prompt));
2270 repl();
2271 /* Restart the session if repl() returned. */
2272 cliConnect(CC_FORCE(1<<0));
2273 printf("\n");
2274 }
2275 } else {
2276 break; /* Return to the caller. */
2277 }
2278 }
2279 return retval;
2280}
2281
2282/*------------------------------------------------------------------------------
2283 * Cluster Manager
2284 *--------------------------------------------------------------------------- */
2285
2286/* The Cluster Manager global structure */
2287static struct clusterManager {
2288 list *nodes; /* List of nodes in the configuration. */
2289 list *errors;
2290 int unreachable_masters; /* Masters we are not able to reach. */
2291} cluster_manager;
2292
2293/* Used by clusterManagerFixSlotsCoverage */
2294dict *clusterManagerUncoveredSlots = NULL((void*)0);
2295
2296typedef struct clusterManagerNode {
2297 redisContext *context;
2298 sdshisds name;
2299 char *ip;
2300 int port;
2301 uint64_t current_epoch;
2302 time_t ping_sent;
2303 time_t ping_recv;
2304 int flags;
2305 list *flags_str; /* Flags string representations */
2306 sdshisds replicate; /* Master ID if node is a slave */
2307 int dirty; /* Node has changes that can be flushed */
2308 uint8_t slots[CLUSTER_MANAGER_SLOTS16384];
2309 int slots_count;
2310 int replicas_count;
2311 list *friends;
2312 sdshisds *migrating; /* An array of sds where even strings are slots and odd
2313 * strings are the destination node IDs. */
2314 sdshisds *importing; /* An array of sds where even strings are slots and odd
2315 * strings are the source node IDs. */
2316 int migrating_count; /* Length of the migrating array (migrating slots*2) */
2317 int importing_count; /* Length of the importing array (importing slots*2) */
2318 float weight; /* Weight used by rebalance */
2319 int balance; /* Used by rebalance */
2320} clusterManagerNode;
2321
2322/* Data structure used to represent a sequence of cluster nodes. */
2323typedef struct clusterManagerNodeArray {
2324 clusterManagerNode **nodes; /* Actual nodes array */
2325 clusterManagerNode **alloc; /* Pointer to the allocated memory */
2326 int len; /* Actual length of the array */
2327 int count; /* Non-NULL nodes count */
2328} clusterManagerNodeArray;
2329
2330/* Used for the reshard table. */
2331typedef struct clusterManagerReshardTableItem {
2332 clusterManagerNode *source;
2333 int slot;
2334} clusterManagerReshardTableItem;
2335
2336/* Info about a cluster internal link. */
2337
2338typedef struct clusterManagerLink {
2339 sdshisds node_name;
2340 sdshisds node_addr;
2341 int connected;
2342 int handshaking;
2343} clusterManagerLink;
2344
2345static dictType clusterManagerDictType = {
2346 dictSdsHash, /* hash function */
2347 NULL((void*)0), /* key dup */
2348 NULL((void*)0), /* val dup */
2349 dictSdsKeyCompare, /* key compare */
2350 NULL((void*)0), /* key destructor */
2351 dictSdsDestructor, /* val destructor */
2352 NULL((void*)0) /* allow to expand */
2353};
2354
2355static dictType clusterManagerLinkDictType = {
2356 dictSdsHash, /* hash function */
2357 NULL((void*)0), /* key dup */
2358 NULL((void*)0), /* val dup */
2359 dictSdsKeyCompare, /* key compare */
2360 dictSdsDestructor, /* key destructor */
2361 dictListDestructor, /* val destructor */
2362 NULL((void*)0) /* allow to expand */
2363};
2364
2365typedef int clusterManagerCommandProc(int argc, char **argv);
2366typedef int (*clusterManagerOnReplyError)(redisReply *reply,
2367 clusterManagerNode *n, int bulk_idx);
2368
2369/* Cluster Manager helper functions */
2370
2371static clusterManagerNode *clusterManagerNewNode(char *ip, int port);
2372static clusterManagerNode *clusterManagerNodeByName(const char *name);
2373static clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char *n);
2374static void clusterManagerNodeResetSlots(clusterManagerNode *node);
2375static int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err);
2376static void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,
2377 char *err);
2378static int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,
2379 char **err);
2380static int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts);
2381static int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err);
2382static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
2383 int ip_count, clusterManagerNode ***offending, int *offending_len);
2384static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,
2385 int ip_count);
2386static sdshisds clusterManagerNodeInfo(clusterManagerNode *node, int indent);
2387static void clusterManagerShowNodes(void);
2388static void clusterManagerShowClusterInfo(void);
2389static int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err);
2390static void clusterManagerWaitForClusterJoin(void);
2391static int clusterManagerCheckCluster(int quiet);
2392static void clusterManagerLog(int level, const char* fmt, ...);
2393static int clusterManagerIsConfigConsistent(void);
2394static dict *clusterManagerGetLinkStatus(void);
2395static void clusterManagerOnError(sdshisds err);
2396static void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,
2397 int len);
2398static void clusterManagerNodeArrayReset(clusterManagerNodeArray *array);
2399static void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,
2400 clusterManagerNode **nodeptr);
2401static void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,
2402 clusterManagerNode *node);
2403
2404/* Cluster Manager commands. */
2405
2406static int clusterManagerCommandCreate(int argc, char **argv);
2407static int clusterManagerCommandAddNode(int argc, char **argv);
2408static int clusterManagerCommandDeleteNode(int argc, char **argv);
2409static int clusterManagerCommandInfo(int argc, char **argv);
2410static int clusterManagerCommandCheck(int argc, char **argv);
2411static int clusterManagerCommandFix(int argc, char **argv);
2412static int clusterManagerCommandReshard(int argc, char **argv);
2413static int clusterManagerCommandRebalance(int argc, char **argv);
2414static int clusterManagerCommandSetTimeout(int argc, char **argv);
2415static int clusterManagerCommandImport(int argc, char **argv);
2416static int clusterManagerCommandCall(int argc, char **argv);
2417static int clusterManagerCommandHelp(int argc, char **argv);
2418static int clusterManagerCommandBackup(int argc, char **argv);
2419
2420typedef struct clusterManagerCommandDef {
2421 char *name;
2422 clusterManagerCommandProc *proc;
2423 int arity;
2424 char *args;
2425 char *options;
2426} clusterManagerCommandDef;
2427
2428clusterManagerCommandDef clusterManagerCommands[] = {
2429 {"create", clusterManagerCommandCreate, -2, "host1:port1 ... hostN:portN",
2430 "replicas <arg>"},
2431 {"check", clusterManagerCommandCheck, -1, "host:port",
2432 "search-multiple-owners"},
2433 {"info", clusterManagerCommandInfo, -1, "host:port", NULL((void*)0)},
2434 {"fix", clusterManagerCommandFix, -1, "host:port",
2435 "search-multiple-owners,fix-with-unreachable-masters"},
2436 {"reshard", clusterManagerCommandReshard, -1, "host:port",
2437 "from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,"
2438 "replace"},
2439 {"rebalance", clusterManagerCommandRebalance, -1, "host:port",
2440 "weight <node1=w1...nodeN=wN>,use-empty-masters,"
2441 "timeout <arg>,simulate,pipeline <arg>,threshold <arg>,replace"},
2442 {"add-node", clusterManagerCommandAddNode, 2,
2443 "new_host:new_port existing_host:existing_port", "slave,master-id <arg>"},
2444 {"del-node", clusterManagerCommandDeleteNode, 2, "host:port node_id",NULL((void*)0)},
2445 {"call", clusterManagerCommandCall, -2,
2446 "host:port command arg arg .. arg", "only-masters,only-replicas"},
2447 {"set-timeout", clusterManagerCommandSetTimeout, 2,
2448 "host:port milliseconds", NULL((void*)0)},
2449 {"import", clusterManagerCommandImport, 1, "host:port",
2450 "from <arg>,from-user <arg>,from-pass <arg>,from-askpass,copy,replace"},
2451 {"backup", clusterManagerCommandBackup, 2, "host:port backup_directory",
2452 NULL((void*)0)},
2453 {"help", clusterManagerCommandHelp, 0, NULL((void*)0), NULL((void*)0)}
2454};
2455
2456typedef struct clusterManagerOptionDef {
2457 char *name;
2458 char *desc;
2459} clusterManagerOptionDef;
2460
2461clusterManagerOptionDef clusterManagerOptions[] = {
2462 {"--cluster-yes", "Automatic yes to cluster commands prompts"}
2463};
2464
2465static void getRDB(clusterManagerNode *node);
2466
2467static void createClusterManagerCommand(char *cmdname, int argc, char **argv) {
2468 clusterManagerCommand *cmd = &config.cluster_manager_command;
2469 cmd->name = cmdname;
2470 cmd->argc = argc;
2471 cmd->argv = argc ? argv : NULL((void*)0);
2472 if (isColorTerm()) cmd->flags |= CLUSTER_MANAGER_CMD_FLAG_COLOR1 << 8;
2473}
2474
2475
2476static clusterManagerCommandProc *validateClusterManagerCommand(void) {
2477 int i, commands_count = sizeof(clusterManagerCommands) /
2478 sizeof(clusterManagerCommandDef);
2479 clusterManagerCommandProc *proc = NULL((void*)0);
2480 char *cmdname = config.cluster_manager_command.name;
2481 int argc = config.cluster_manager_command.argc;
2482 for (i = 0; i < commands_count; i++) {
2483 clusterManagerCommandDef cmddef = clusterManagerCommands[i];
2484 if (!strcmp(cmddef.name, cmdname)) {
2485 if ((cmddef.arity > 0 && argc != cmddef.arity) ||
2486 (cmddef.arity < 0 && argc < (cmddef.arity * -1))) {
2487 fprintf(stderrstderr, "[ERR] Wrong number of arguments for "
2488 "specified --cluster sub command\n");
2489 return NULL((void*)0);
2490 }
2491 proc = cmddef.proc;
2492 }
2493 }
2494 if (!proc) fprintf(stderrstderr, "Unknown --cluster subcommand\n");
2495 return proc;
2496}
2497
2498static int parseClusterNodeAddress(char *addr, char **ip_ptr, int *port_ptr,
2499 int *bus_port_ptr)
2500{
2501 char *c = strrchr(addr, '@');
2502 if (c != NULL((void*)0)) {
2503 *c = '\0';
2504 if (bus_port_ptr != NULL((void*)0))
2505 *bus_port_ptr = atoi(c + 1);
2506 }
2507 c = strrchr(addr, ':');
2508 if (c != NULL((void*)0)) {
2509 *c = '\0';
2510 *ip_ptr = addr;
2511 *port_ptr = atoi(++c);
2512 } else return 0;
2513 return 1;
2514}
2515
2516/* Get host ip and port from command arguments. If only one argument has
2517 * been provided it must be in the form of 'ip:port', elsewhere
2518 * the first argument must be the ip and the second one the port.
2519 * If host and port can be detected, it returns 1 and it stores host and
2520 * port into variables referenced by'ip_ptr' and 'port_ptr' pointers,
2521 * elsewhere it returns 0. */
2522static int getClusterHostFromCmdArgs(int argc, char **argv,
2523 char **ip_ptr, int *port_ptr) {
2524 int port = 0;
2525 char *ip = NULL((void*)0);
2526 if (argc == 1) {
2527 char *addr = argv[0];
2528 if (!parseClusterNodeAddress(addr, &ip, &port, NULL((void*)0))) return 0;
2529 } else {
2530 ip = argv[0];
2531 port = atoi(argv[1]);
2532 }
2533 if (!ip || !port) return 0;
2534 else {
2535 *ip_ptr = ip;
2536 *port_ptr = port;
2537 }
2538 return 1;
2539}
2540
2541static void freeClusterManagerNodeFlags(list *flags) {
2542 listIter li;
2543 listNode *ln;
2544 listRewind(flags, &li);
2545 while ((ln = listNext(&li)) != NULL((void*)0)) {
2546 sdshisds flag = ln->value;
2547 sdsfreehi_sdsfree(flag);
2548 }
2549 listRelease(flags);
2550}
2551
2552static void freeClusterManagerNode(clusterManagerNode *node) {
2553 if (node->context != NULL((void*)0)) redisFree(node->context);
2554 if (node->friends != NULL((void*)0)) {
2555 listIter li;
2556 listNode *ln;
2557 listRewind(node->friends,&li);
2558 while ((ln = listNext(&li)) != NULL((void*)0)) {
2559 clusterManagerNode *fn = ln->value;
2560 freeClusterManagerNode(fn);
2561 }
2562 listRelease(node->friends);
2563 node->friends = NULL((void*)0);
2564 }
2565 if (node->name != NULL((void*)0)) sdsfreehi_sdsfree(node->name);
2566 if (node->replicate != NULL((void*)0)) sdsfreehi_sdsfree(node->replicate);
2567 if ((node->flags & CLUSTER_MANAGER_FLAG_FRIEND1 << 2) && node->ip)
2568 sdsfreehi_sdsfree(node->ip);
2569 int i;
2570 if (node->migrating != NULL((void*)0)) {
2571 for (i = 0; i < node->migrating_count; i++) sdsfreehi_sdsfree(node->migrating[i]);
2572 zfree(node->migrating);
2573 }
2574 if (node->importing != NULL((void*)0)) {
2575 for (i = 0; i < node->importing_count; i++) sdsfreehi_sdsfree(node->importing[i]);
2576 zfree(node->importing);
2577 }
2578 if (node->flags_str != NULL((void*)0)) {
2579 freeClusterManagerNodeFlags(node->flags_str);
2580 node->flags_str = NULL((void*)0);
2581 }
2582 zfree(node);
2583}
2584
2585static void freeClusterManager(void) {
2586 listIter li;
2587 listNode *ln;
2588 if (cluster_manager.nodes != NULL((void*)0)) {
2589 listRewind(cluster_manager.nodes,&li);
2590 while ((ln = listNext(&li)) != NULL((void*)0)) {
2591 clusterManagerNode *n = ln->value;
2592 freeClusterManagerNode(n);
2593 }
2594 listRelease(cluster_manager.nodes);
2595 cluster_manager.nodes = NULL((void*)0);
2596 }
2597 if (cluster_manager.errors != NULL((void*)0)) {
2598 listRewind(cluster_manager.errors,&li);
2599 while ((ln = listNext(&li)) != NULL((void*)0)) {
2600 sdshisds err = ln->value;
2601 sdsfreehi_sdsfree(err);
2602 }
2603 listRelease(cluster_manager.errors);
2604 cluster_manager.errors = NULL((void*)0);
2605 }
2606 if (clusterManagerUncoveredSlots != NULL((void*)0))
2607 dictRelease(clusterManagerUncoveredSlots);
2608}
2609
2610static clusterManagerNode *clusterManagerNewNode(char *ip, int port) {
2611 clusterManagerNode *node = zmalloc(sizeof(*node));
2612 node->context = NULL((void*)0);
2613 node->name = NULL((void*)0);
2614 node->ip = ip;
2615 node->port = port;
2616 node->current_epoch = 0;
2617 node->ping_sent = 0;
2618 node->ping_recv = 0;
2619 node->flags = 0;
2620 node->flags_str = NULL((void*)0);
2621 node->replicate = NULL((void*)0);
2622 node->dirty = 0;
2623 node->friends = NULL((void*)0);
2624 node->migrating = NULL((void*)0);
2625 node->importing = NULL((void*)0);
2626 node->migrating_count = 0;
2627 node->importing_count = 0;
2628 node->replicas_count = 0;
2629 node->weight = 1.0f;
2630 node->balance = 0;
2631 clusterManagerNodeResetSlots(node);
2632 return node;
2633}
2634
2635static sdshisds clusterManagerGetNodeRDBFilename(clusterManagerNode *node) {
2636 assert(config.cluster_manager_command.backup_dir)((config.cluster_manager_command.backup_dir) ? (void) (0) : __assert_fail
("config.cluster_manager_command.backup_dir", "redis-cli.c",
2636, __extension__ __PRETTY_FUNCTION__))
;
2637 sdshisds filename = sdsnewhi_sdsnew(config.cluster_manager_command.backup_dir);
2638 if (filename[sdslenhi_sdslen(filename) - 1] != '/')
2639 filename = sdscathi_sdscat(filename, "/");
2640 filename = sdscatprintfhi_sdscatprintf(filename, "redis-node-%s-%d-%s.rdb", node->ip,
2641 node->port, node->name);
2642 return filename;
2643}
2644
2645/* Check whether reply is NULL or its type is REDIS_REPLY_ERROR. In the
2646 * latest case, if the 'err' arg is not NULL, it gets allocated with a copy
2647 * of reply error (it's up to the caller function to free it), elsewhere
2648 * the error is directly printed. */
2649static int clusterManagerCheckRedisReply(clusterManagerNode *n,
2650 redisReply *r, char **err)
2651{
2652 int is_err = 0;
2653 if (!r || (is_err = (r->type == REDIS_REPLY_ERROR6))) {
2654 if (is_err) {
2655 if (err != NULL((void*)0)) {
2656 *err = zmalloc((r->len + 1) * sizeof(char));
2657 strcpy(*err, r->str);
2658 } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, r->str)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n
->ip, n->port, r->str);
;
2659 }
2660 return 0;
2661 }
2662 return 1;
2663}
2664
2665/* Call MULTI command on a cluster node. */
2666static int clusterManagerStartTransaction(clusterManagerNode *node) {
2667 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "MULTI")(redisCommand(node->context, "MULTI"));
2668 int success = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
2669 if (reply) freeReplyObject(reply);
2670 return success;
2671}
2672
2673/* Call EXEC command on a cluster node. */
2674static int clusterManagerExecTransaction(clusterManagerNode *node,
2675 clusterManagerOnReplyError onerror)
2676{
2677 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "EXEC")(redisCommand(node->context, "EXEC"));
2678 int success = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
2679 if (success) {
2680 if (reply->type != REDIS_REPLY_ARRAY2) {
2681 success = 0;
2682 goto cleanup;
2683 }
2684 size_t i;
2685 for (i = 0; i < reply->elements; i++) {
2686 redisReply *r = reply->element[i];
2687 char *err = NULL((void*)0);
2688 success = clusterManagerCheckRedisReply(node, r, &err);
2689 if (!success && onerror) success = onerror(r, node, i);
2690 if (err) {
2691 if (!success)
2692 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, err);
;
2693 zfree(err);
2694 }
2695 if (!success) break;
2696 }
2697 }
2698cleanup:
2699 if (reply) freeReplyObject(reply);
2700 return success;
2701}
2702
2703static int clusterManagerNodeConnect(clusterManagerNode *node) {
2704 if (node->context) redisFree(node->context);
2705 node->context = redisConnect(node->ip, node->port);
2706 if (!node->context->err && config.tls) {
2707 const char *err = NULL((void*)0);
2708 if (cliSecureConnection(node->context, config.sslconfig, &err) == REDIS_ERR-1 && err) {
2709 fprintf(stderrstderr,"TLS Error: %s\n", err);
2710 redisFree(node->context);
2711 node->context = NULL((void*)0);
2712 return 0;
2713 }
2714 }
2715 if (node->context->err) {
2716 fprintf(stderrstderr,"Could not connect to Redis at ");
2717 fprintf(stderrstderr,"%s:%d: %s\n", node->ip, node->port,
2718 node->context->errstr);
2719 redisFree(node->context);
2720 node->context = NULL((void*)0);
2721 return 0;
2722 }
2723 /* Set aggressive KEEP_ALIVE socket option in the Redis context socket
2724 * in order to prevent timeouts caused by the execution of long
2725 * commands. At the same time this improves the detection of real
2726 * errors. */
2727 anetKeepAlive(NULL((void*)0), node->context->fd, REDIS_CLI_KEEPALIVE_INTERVAL15);
2728 if (config.auth) {
2729 redisReply *reply;
2730 if (config.user == NULL((void*)0))
2731 reply = redisCommand(node->context,"AUTH %s", config.auth);
2732 else
2733 reply = redisCommand(node->context,"AUTH %s %s",
2734 config.user,config.auth);
2735 int ok = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
2736 if (reply != NULL((void*)0)) freeReplyObject(reply);
2737 if (!ok) return 0;
2738 }
2739 return 1;
2740}
2741
2742static void clusterManagerRemoveNodeFromList(list *nodelist,
2743 clusterManagerNode *node) {
2744 listIter li;
2745 listNode *ln;
2746 listRewind(nodelist, &li);
2747 while ((ln = listNext(&li)) != NULL((void*)0)) {
2748 if (node == ln->value) {
2749 listDelNode(nodelist, ln);
2750 break;
2751 }
2752 }
2753}
2754
2755/* Return the node with the specified name (ID) or NULL. */
2756static clusterManagerNode *clusterManagerNodeByName(const char *name) {
2757 if (cluster_manager.nodes == NULL((void*)0)) return NULL((void*)0);
2758 clusterManagerNode *found = NULL((void*)0);
2759 sdshisds lcname = sdsemptyhi_sdsempty();
2760 lcname = sdscpyhi_sdscpy(lcname, name);
2761 sdstolowerhi_sdstolower(lcname);
2762 listIter li;
2763 listNode *ln;
2764 listRewind(cluster_manager.nodes, &li);
2765 while ((ln = listNext(&li)) != NULL((void*)0)) {
2766 clusterManagerNode *n = ln->value;
2767 if (n->name && !sdscmphi_sdscmp(n->name, lcname)) {
2768 found = n;
2769 break;
2770 }
2771 }
2772 sdsfreehi_sdsfree(lcname);
2773 return found;
2774}
2775
2776/* Like clusterManagerNodeByName but the specified name can be just the first
2777 * part of the node ID as long as the prefix in unique across the
2778 * cluster.
2779 */
2780static clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char*name)
2781{
2782 if (cluster_manager.nodes == NULL((void*)0)) return NULL((void*)0);
2783 clusterManagerNode *found = NULL((void*)0);
2784 sdshisds lcname = sdsemptyhi_sdsempty();
2785 lcname = sdscpyhi_sdscpy(lcname, name);
2786 sdstolowerhi_sdstolower(lcname);
2787 listIter li;
2788 listNode *ln;
2789 listRewind(cluster_manager.nodes, &li);
2790 while ((ln = listNext(&li)) != NULL((void*)0)) {
2791 clusterManagerNode *n = ln->value;
2792 if (n->name &&
2793 strstr(n->name, lcname) == n->name) {
2794 found = n;
2795 break;
2796 }
2797 }
2798 sdsfreehi_sdsfree(lcname);
2799 return found;
2800}
2801
2802static void clusterManagerNodeResetSlots(clusterManagerNode *node) {
2803 memset(node->slots, 0, sizeof(node->slots));
2804 node->slots_count = 0;
2805}
2806
2807/* Call "INFO" redis command on the specified node and return the reply. */
2808static redisReply *clusterManagerGetNodeRedisInfo(clusterManagerNode *node,
2809 char **err)
2810{
2811 redisReply *info = CLUSTER_MANAGER_COMMAND(node, "INFO")(redisCommand(node->context, "INFO"));
2812 if (err != NULL((void*)0)) *err = NULL((void*)0);
2813 if (info == NULL((void*)0)) return NULL((void*)0);
2814 if (info->type == REDIS_REPLY_ERROR6) {
2815 if (err != NULL((void*)0)) {
2816 *err = zmalloc((info->len + 1) * sizeof(char));
2817 strcpy(*err, info->str);
2818 }
2819 freeReplyObject(info);
2820 return NULL((void*)0);
2821 }
2822 return info;
2823}
2824
2825static int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err) {
2826 redisReply *info = clusterManagerGetNodeRedisInfo(node, err);
2827 if (info == NULL((void*)0)) return 0;
2828 int is_cluster = (int) getLongInfoField(info->str, "cluster_enabled");
2829 freeReplyObject(info);
2830 return is_cluster;
2831}
2832
2833/* Checks whether the node is empty. Node is considered not-empty if it has
2834 * some key or if it already knows other nodes */
2835static int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err) {
2836 redisReply *info = clusterManagerGetNodeRedisInfo(node, err);
2837 int is_empty = 1;
2838 if (info == NULL((void*)0)) return 0;
2839 if (strstr(info->str, "db0:") != NULL((void*)0)) {
2840 is_empty = 0;
2841 goto result;
2842 }
2843 freeReplyObject(info);
2844 info = CLUSTER_MANAGER_COMMAND(node, "CLUSTER INFO")(redisCommand(node->context, "CLUSTER INFO"));
2845 if (err != NULL((void*)0)) *err = NULL((void*)0);
2846 if (!clusterManagerCheckRedisReply(node, info, err)) {
2847 is_empty = 0;
2848 goto result;
2849 }
2850 long known_nodes = getLongInfoField(info->str, "cluster_known_nodes");
2851 is_empty = (known_nodes == 1);
2852result:
2853 freeReplyObject(info);
2854 return is_empty;
2855}
2856
2857/* Return the anti-affinity score, which is a measure of the amount of
2858 * violations of anti-affinity in the current cluster layout, that is, how
2859 * badly the masters and slaves are distributed in the different IP
2860 * addresses so that slaves of the same master are not in the master
2861 * host and are also in different hosts.
2862 *
2863 * The score is calculated as follows:
2864 *
2865 * SAME_AS_MASTER = 10000 * each slave in the same IP of its master.
2866 * SAME_AS_SLAVE = 1 * each slave having the same IP as another slave
2867 of the same master.
2868 * FINAL_SCORE = SAME_AS_MASTER + SAME_AS_SLAVE
2869 *
2870 * So a greater score means a worse anti-affinity level, while zero
2871 * means perfect anti-affinity.
2872 *
2873 * The anti affinity optimizator will try to get a score as low as
2874 * possible. Since we do not want to sacrifice the fact that slaves should
2875 * not be in the same host as the master, we assign 10000 times the score
2876 * to this violation, so that we'll optimize for the second factor only
2877 * if it does not impact the first one.
2878 *
2879 * The ipnodes argument is an array of clusterManagerNodeArray, one for
2880 * each IP, while ip_count is the total number of IPs in the configuration.
2881 *
2882 * The function returns the above score, and the list of
2883 * offending slaves can be stored into the 'offending' argument,
2884 * so that the optimizer can try changing the configuration of the
2885 * slaves violating the anti-affinity goals. */
2886static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
2887 int ip_count, clusterManagerNode ***offending, int *offending_len)
2888{
2889 int score = 0, i, j;
2890 int node_len = cluster_manager.nodes->len;
2891 clusterManagerNode **offending_p = NULL((void*)0);
2892 if (offending != NULL((void*)0)) {
2893 *offending = zcalloc(node_len * sizeof(clusterManagerNode*));
2894 offending_p = *offending;
2895 }
2896 /* For each set of nodes in the same host, split by
2897 * related nodes (masters and slaves which are involved in
2898 * replication of each other) */
2899 for (i = 0; i < ip_count; i++) {
2900 clusterManagerNodeArray *node_array = &(ipnodes[i]);
2901 dict *related = dictCreate(&clusterManagerDictType, NULL((void*)0));
2902 char *ip = NULL((void*)0);
2903 for (j = 0; j < node_array->len; j++) {
2904 clusterManagerNode *node = node_array->nodes[j];
2905 if (node == NULL((void*)0)) continue;
2906 if (!ip) ip = node->ip;
2907 sdshisds types;
2908 /* We always use the Master ID as key. */
2909 sdshisds key = (!node->replicate ? node->name : node->replicate);
2910 assert(key != NULL)((key != ((void*)0)) ? (void) (0) : __assert_fail ("key != NULL"
, "redis-cli.c", 2910, __extension__ __PRETTY_FUNCTION__))
;
2911 dictEntry *entry = dictFind(related, key);
2912 if (entry) types = sdsduphi_sdsdup((sdshisds) dictGetVal(entry)((entry)->v.val));
2913 else types = sdsemptyhi_sdsempty();
2914 /* Master type 'm' is always set as the first character of the
2915 * types string. */
2916 if (!node->replicate) types = sdscatprintfhi_sdscatprintf(types, "m%s", types);
2917 else types = sdscathi_sdscat(types, "s");
2918 dictReplace(related, key, types);
2919 }
2920 /* Now it's trivial to check, for each related group having the
2921 * same host, what is their local score. */
2922 dictIterator *iter = dictGetIterator(related);
2923 dictEntry *entry;
2924 while ((entry = dictNext(iter)) != NULL((void*)0)) {
2925 sdshisds types = (sdshisds) dictGetVal(entry)((entry)->v.val);
2926 sdshisds name = (sdshisds) dictGetKey(entry)((entry)->key);
2927 int typeslen = sdslenhi_sdslen(types);
2928 if (typeslen < 2) continue;
2929 if (types[0] == 'm') score += (10000 * (typeslen - 1));
2930 else score += (1 * typeslen);
2931 if (offending == NULL((void*)0)) continue;
2932 /* Populate the list of offending nodes. */
2933 listIter li;
2934 listNode *ln;
2935 listRewind(cluster_manager.nodes, &li);
2936 while ((ln = listNext(&li)) != NULL((void*)0)) {
2937 clusterManagerNode *n = ln->value;
2938 if (n->replicate == NULL((void*)0)) continue;
2939 if (!strcmp(n->replicate, name) && !strcmp(n->ip, ip)) {
2940 *(offending_p++) = n;
2941 if (offending_len != NULL((void*)0)) (*offending_len)++;
2942 break;
2943 }
2944 }
2945 }
2946 //if (offending_len != NULL) *offending_len = offending_p - *offending;
2947 dictReleaseIterator(iter);
2948 dictRelease(related);
2949 }
2950 return score;
2951}
2952
2953static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,
2954 int ip_count)
2955{
2956 clusterManagerNode **offenders = NULL((void*)0);
2957 int score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count,
2958 NULL((void*)0), NULL((void*)0));
2959 if (score == 0) goto cleanup;
2960 clusterManagerLogInfo(">>> Trying to optimize slaves allocation "clusterManagerLog(1,">>> Trying to optimize slaves allocation "
"for anti-affinity\n")
2961 "for anti-affinity\n")clusterManagerLog(1,">>> Trying to optimize slaves allocation "
"for anti-affinity\n")
;
2962 int node_len = cluster_manager.nodes->len;
2963 int maxiter = 500 * node_len; // Effort is proportional to cluster size...
2964 srand(time(NULL((void*)0)));
2965 while (maxiter > 0) {
2966 int offending_len = 0;
2967 if (offenders != NULL((void*)0)) {
2968 zfree(offenders);
2969 offenders = NULL((void*)0);
2970 }
2971 score = clusterManagerGetAntiAffinityScore(ipnodes,
2972 ip_count,
2973 &offenders,
2974 &offending_len);
2975 if (score == 0 || offending_len == 0) break; // Optimal anti affinity reached
2976 /* We'll try to randomly swap a slave's assigned master causing
2977 * an affinity problem with another random slave, to see if we
2978 * can improve the affinity. */
2979 int rand_idx = rand() % offending_len;
2980 clusterManagerNode *first = offenders[rand_idx],
2981 *second = NULL((void*)0);
2982 clusterManagerNode **other_replicas = zcalloc((node_len - 1) *
2983 sizeof(*other_replicas));
2984 int other_replicas_count = 0;
2985 listIter li;
2986 listNode *ln;
2987 listRewind(cluster_manager.nodes, &li);
2988 while ((ln = listNext(&li)) != NULL((void*)0)) {
2989 clusterManagerNode *n = ln->value;
2990 if (n != first && n->replicate != NULL((void*)0))
2991 other_replicas[other_replicas_count++] = n;
2992 }
2993 if (other_replicas_count == 0) {
2994 zfree(other_replicas);
2995 break;
2996 }
2997 rand_idx = rand() % other_replicas_count;
2998 second = other_replicas[rand_idx];
2999 char *first_master = first->replicate,
3000 *second_master = second->replicate;
3001 first->replicate = second_master, first->dirty = 1;
3002 second->replicate = first_master, second->dirty = 1;
3003 int new_score = clusterManagerGetAntiAffinityScore(ipnodes,
3004 ip_count,
3005 NULL((void*)0), NULL((void*)0));
3006 /* If the change actually makes thing worse, revert. Otherwise
3007 * leave as it is because the best solution may need a few
3008 * combined swaps. */
3009 if (new_score > score) {
3010 first->replicate = first_master;
3011 second->replicate = second_master;
3012 }
3013 zfree(other_replicas);
3014 maxiter--;
3015 }
3016 score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL((void*)0), NULL((void*)0));
3017 char *msg;
3018 int perfect = (score == 0);
3019 int log_level = (perfect ? CLUSTER_MANAGER_LOG_LVL_SUCCESS4 :
3020 CLUSTER_MANAGER_LOG_LVL_WARN2);
3021 if (perfect) msg = "[OK] Perfect anti-affinity obtained!";
3022 else if (score >= 10000)
3023 msg = ("[WARNING] Some slaves are in the same host as their master");
3024 else
3025 msg=("[WARNING] Some slaves of the same master are in the same host");
3026 clusterManagerLog(log_level, "%s\n", msg);
3027cleanup:
3028 zfree(offenders);
3029}
3030
3031/* Return a representable string of the node's flags */
3032static sdshisds clusterManagerNodeFlagString(clusterManagerNode *node) {
3033 sdshisds flags = sdsemptyhi_sdsempty();
3034 if (!node->flags_str) return flags;
3035 int empty = 1;
3036 listIter li;
3037 listNode *ln;
3038 listRewind(node->flags_str, &li);
3039 while ((ln = listNext(&li)) != NULL((void*)0)) {
3040 sdshisds flag = ln->value;
3041 if (strcmp(flag, "myself") == 0) continue;
3042 if (!empty) flags = sdscathi_sdscat(flags, ",");
3043 flags = sdscatfmthi_sdscatfmt(flags, "%S", flag);
3044 empty = 0;
3045 }
3046 return flags;
3047}
3048
3049/* Return a representable string of the node's slots */
3050static sdshisds clusterManagerNodeSlotsString(clusterManagerNode *node) {
3051 sdshisds slots = sdsemptyhi_sdsempty();
3052 int first_range_idx = -1, last_slot_idx = -1, i;
3053 for (i = 0; i < CLUSTER_MANAGER_SLOTS16384; i++) {
3054 int has_slot = node->slots[i];
3055 if (has_slot) {
3056 if (first_range_idx == -1) {
3057 if (sdslenhi_sdslen(slots)) slots = sdscathi_sdscat(slots, ",");
3058 first_range_idx = i;
3059 slots = sdscatfmthi_sdscatfmt(slots, "[%u", i);
3060 }
3061 last_slot_idx = i;
3062 } else {
3063 if (last_slot_idx >= 0) {
3064 if (first_range_idx == last_slot_idx)
3065 slots = sdscathi_sdscat(slots, "]");
3066 else slots = sdscatfmthi_sdscatfmt(slots, "-%u]", last_slot_idx);
3067 }
3068 last_slot_idx = -1;
3069 first_range_idx = -1;
3070 }
3071 }
3072 if (last_slot_idx >= 0) {
3073 if (first_range_idx == last_slot_idx) slots = sdscathi_sdscat(slots, "]");
3074 else slots = sdscatfmthi_sdscatfmt(slots, "-%u]", last_slot_idx);
3075 }
3076 return slots;
3077}
3078
3079static sdshisds clusterManagerNodeGetJSON(clusterManagerNode *node,
3080 unsigned long error_count)
3081{
3082 sdshisds json = sdsemptyhi_sdsempty();
3083 sdshisds replicate = sdsemptyhi_sdsempty();
3084 if (node->replicate)
3085 replicate = sdscatprintfhi_sdscatprintf(replicate, "\"%s\"", node->replicate);
3086 else
3087 replicate = sdscathi_sdscat(replicate, "null");
3088 sdshisds slots = clusterManagerNodeSlotsString(node);
3089 sdshisds flags = clusterManagerNodeFlagString(node);
3090 char *p = slots;
3091 while ((p = strchr(p, '-')) != NULL((void*)0))
3092 *(p++) = ',';
3093 json = sdscatprintfhi_sdscatprintf(json,
3094 " {\n"
3095 " \"name\": \"%s\",\n"
3096 " \"host\": \"%s\",\n"
3097 " \"port\": %d,\n"
3098 " \"replicate\": %s,\n"
3099 " \"slots\": [%s],\n"
3100 " \"slots_count\": %d,\n"
3101 " \"flags\": \"%s\",\n"
3102 " \"current_epoch\": %llu",
3103 node->name,
3104 node->ip,
3105 node->port,
3106 replicate,
3107 slots,
3108 node->slots_count,
3109 flags,
3110 (unsigned long long)node->current_epoch
3111 );
3112 if (error_count > 0) {
3113 json = sdscatprintfhi_sdscatprintf(json, ",\n \"cluster_errors\": %lu",
3114 error_count);
3115 }
3116 if (node->migrating_count > 0 && node->migrating != NULL((void*)0)) {
3117 int i = 0;
3118 sdshisds migrating = sdsemptyhi_sdsempty();
3119 for (; i < node->migrating_count; i += 2) {
3120 sdshisds slot = node->migrating[i];
3121 sdshisds dest = node->migrating[i + 1];
3122 if (slot && dest) {
3123 if (sdslenhi_sdslen(migrating) > 0) migrating = sdscathi_sdscat(migrating, ",");
3124 migrating = sdscatfmthi_sdscatfmt(migrating, "\"%S\": \"%S\"", slot, dest);
3125 }
3126 }
3127 if (sdslenhi_sdslen(migrating) > 0)
3128 json = sdscatfmthi_sdscatfmt(json, ",\n \"migrating\": {%S}", migrating);
3129 sdsfreehi_sdsfree(migrating);
3130 }
3131 if (node->importing_count > 0 && node->importing != NULL((void*)0)) {
3132 int i = 0;
3133 sdshisds importing = sdsemptyhi_sdsempty();
3134 for (; i < node->importing_count; i += 2) {
3135 sdshisds slot = node->importing[i];
3136 sdshisds from = node->importing[i + 1];
3137 if (slot && from) {
3138 if (sdslenhi_sdslen(importing) > 0) importing = sdscathi_sdscat(importing, ",");
3139 importing = sdscatfmthi_sdscatfmt(importing, "\"%S\": \"%S\"", slot, from);
3140 }
3141 }
3142 if (sdslenhi_sdslen(importing) > 0)
3143 json = sdscatfmthi_sdscatfmt(json, ",\n \"importing\": {%S}", importing);
3144 sdsfreehi_sdsfree(importing);
3145 }
3146 json = sdscathi_sdscat(json, "\n }");
3147 sdsfreehi_sdsfree(replicate);
3148 sdsfreehi_sdsfree(slots);
3149 sdsfreehi_sdsfree(flags);
3150 return json;
3151}
3152
3153
3154/* -----------------------------------------------------------------------------
3155 * Key space handling
3156 * -------------------------------------------------------------------------- */
3157
3158/* We have 16384 hash slots. The hash slot of a given key is obtained
3159 * as the least significant 14 bits of the crc16 of the key.
3160 *
3161 * However if the key contains the {...} pattern, only the part between
3162 * { and } is hashed. This may be useful in the future to force certain
3163 * keys to be in the same node (assuming no resharding is in progress). */
3164static unsigned int clusterManagerKeyHashSlot(char *key, int keylen) {
3165 int s, e; /* start-end indexes of { and } */
3166
3167 for (s = 0; s < keylen; s++)
3168 if (key[s] == '{') break;
3169
3170 /* No '{' ? Hash the whole key. This is the base case. */
3171 if (s == keylen) return crc16(key,keylen) & 0x3FFF;
3172
3173 /* '{' found? Check if we have the corresponding '}'. */
3174 for (e = s+1; e < keylen; e++)
3175 if (key[e] == '}') break;
3176
3177 /* No '}' or nothing between {} ? Hash the whole key. */
3178 if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
3179
3180 /* If we are here there is both a { and a } on its right. Hash
3181 * what is in the middle between { and }. */
3182 return crc16(key+s+1,e-s-1) & 0x3FFF;
3183}
3184
3185/* Return a string representation of the cluster node. */
3186static sdshisds clusterManagerNodeInfo(clusterManagerNode *node, int indent) {
3187 sdshisds info = sdsemptyhi_sdsempty();
3188 sdshisds spaces = sdsemptyhi_sdsempty();
3189 int i;
3190 for (i = 0; i < indent; i++) spaces = sdscathi_sdscat(spaces, " ");
3191 if (indent) info = sdscathi_sdscat(info, spaces);
3192 int is_master = !(node->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1);
3193 char *role = (is_master ? "M" : "S");
3194 sdshisds slots = NULL((void*)0);
3195 if (node->dirty && node->replicate != NULL((void*)0))
3196 info = sdscatfmthi_sdscatfmt(info, "S: %S %s:%u", node->name, node->ip, node->port);
3197 else {
3198 slots = clusterManagerNodeSlotsString(node);
3199 sdshisds flags = clusterManagerNodeFlagString(node);
3200 info = sdscatfmthi_sdscatfmt(info, "%s: %S %s:%u\n"
3201 "%s slots:%S (%u slots) "
3202 "%S",
3203 role, node->name, node->ip, node->port, spaces,
3204 slots, node->slots_count, flags);
3205 sdsfreehi_sdsfree(slots);
3206 sdsfreehi_sdsfree(flags);
3207 }
3208 if (node->replicate != NULL((void*)0))
3209 info = sdscatfmthi_sdscatfmt(info, "\n%s replicates %S", spaces, node->replicate);
3210 else if (node->replicas_count)
3211 info = sdscatfmthi_sdscatfmt(info, "\n%s %U additional replica(s)",
3212 spaces, node->replicas_count);
3213 sdsfreehi_sdsfree(spaces);
3214 return info;
3215}
3216
3217static void clusterManagerShowNodes(void) {
3218 listIter li;
3219 listNode *ln;
3220 listRewind(cluster_manager.nodes, &li);
3221 while ((ln = listNext(&li)) != NULL((void*)0)) {
3222 clusterManagerNode *node = ln->value;
3223 sdshisds info = clusterManagerNodeInfo(node, 0);
3224 printf("%s\n", (char *) info);
3225 sdsfreehi_sdsfree(info);
3226 }
3227}
3228
3229static void clusterManagerShowClusterInfo(void) {
3230 int masters = 0;
3231 int keys = 0;
3232 listIter li;
3233 listNode *ln;
3234 listRewind(cluster_manager.nodes, &li);
3235 while ((ln = listNext(&li)) != NULL((void*)0)) {
3236 clusterManagerNode *node = ln->value;
3237 if (!(node->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1)) {
3238 if (!node->name) continue;
3239 int replicas = 0;
3240 int dbsize = -1;
3241 char name[9];
3242 memcpy(name, node->name, 8);
3243 name[8] = '\0';
3244 listIter ri;
3245 listNode *rn;
3246 listRewind(cluster_manager.nodes, &ri);
3247 while ((rn = listNext(&ri)) != NULL((void*)0)) {
3248 clusterManagerNode *n = rn->value;
3249 if (n == node || !(n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1))
3250 continue;
3251 if (n->replicate && !strcmp(n->replicate, node->name))
3252 replicas++;
3253 }
3254 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "DBSIZE")(redisCommand(node->context, "DBSIZE"));
3255 if (reply != NULL((void*)0) && reply->type == REDIS_REPLY_INTEGER3)
3256 dbsize = reply->integer;
3257 if (dbsize < 0) {
3258 char *err = "";
3259 if (reply != NULL((void*)0) && reply->type == REDIS_REPLY_ERROR6)
3260 err = reply->str;
3261 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, err);
;
3262 if (reply != NULL((void*)0)) freeReplyObject(reply);
3263 return;
3264 };
3265 if (reply != NULL((void*)0)) freeReplyObject(reply);
3266 printf("%s:%d (%s...) -> %d keys | %d slots | %d slaves.\n",
3267 node->ip, node->port, name, dbsize,
3268 node->slots_count, replicas);
3269 masters++;
3270 keys += dbsize;
3271 }
3272 }
3273 clusterManagerLogOk("[OK] %d keys in %d masters.\n", keys, masters)clusterManagerLog(4,"[OK] %d keys in %d masters.\n", keys, masters
)
;
3274 float keys_per_slot = keys / (float) CLUSTER_MANAGER_SLOTS16384;
3275 printf("%.2f keys per slot on average.\n", keys_per_slot);
3276}
3277
3278/* Flush dirty slots configuration of the node by calling CLUSTER ADDSLOTS */
3279static int clusterManagerAddSlots(clusterManagerNode *node, char**err)
3280{
3281 redisReply *reply = NULL((void*)0);
3282 void *_reply = NULL((void*)0);
3283 int success = 1;
3284 /* First two args are used for the command itself. */
3285 int argc = node->slots_count + 2;
3286 sdshisds *argv = zmalloc(argc * sizeof(*argv));
3287 size_t *argvlen = zmalloc(argc * sizeof(*argvlen));
3288 argv[0] = "CLUSTER";
3289 argv[1] = "ADDSLOTS";
3290 argvlen[0] = 7;
3291 argvlen[1] = 8;
3292 *err = NULL((void*)0);
3293 int i, argv_idx = 2;
3294 for (i = 0; i < CLUSTER_MANAGER_SLOTS16384; i++) {
3295 if (argv_idx >= argc) break;
3296 if (node->slots[i]) {
3297 argv[argv_idx] = sdsfromlonglonghi_sdsfromlonglong((long long) i);
3298 argvlen[argv_idx] = sdslenhi_sdslen(argv[argv_idx]);
3299 argv_idx++;
3300 }
3301 }
3302 if (!argv_idx) {
3303 success = 0;
3304 goto cleanup;
3305 }
3306 redisAppendCommandArgv(node->context,argc,(const char**)argv,argvlen);
3307 if (redisGetReply(node->context, &_reply) != REDIS_OK0) {
3308 success = 0;
3309 goto cleanup;
3310 }
3311 reply = (redisReply*) _reply;
3312 success = clusterManagerCheckRedisReply(node, reply, err);
3313cleanup:
3314 zfree(argvlen);
3315 if (argv != NULL((void*)0)) {
3316 for (i = 2; i < argc; i++) sdsfreehi_sdsfree(argv[i]);
3317 zfree(argv);
3318 }
3319 if (reply != NULL((void*)0)) freeReplyObject(reply);
3320 return success;
3321}
3322
3323/* Get the node the slot is assigned to from the point of view of node *n.
3324 * If the slot is unassigned or if the reply is an error, return NULL.
3325 * Use the **err argument in order to check wether the slot is unassigned
3326 * or the reply resulted in an error. */
3327static clusterManagerNode *clusterManagerGetSlotOwner(clusterManagerNode *n,
3328 int slot, char **err)
3329{
3330 assert(slot >= 0 && slot < CLUSTER_MANAGER_SLOTS)((slot >= 0 && slot < 16384) ? (void) (0) : __assert_fail
("slot >= 0 && slot < CLUSTER_MANAGER_SLOTS", "redis-cli.c"
, 3330, __extension__ __PRETTY_FUNCTION__))
;
3331 clusterManagerNode *owner = NULL((void*)0);
3332 redisReply *reply = CLUSTER_MANAGER_COMMAND(n, "CLUSTER SLOTS")(redisCommand(n->context, "CLUSTER SLOTS"));
3333 if (clusterManagerCheckRedisReply(n, reply, err)) {
3334 assert(reply->type == REDIS_REPLY_ARRAY)((reply->type == 2) ? (void) (0) : __assert_fail ("reply->type == REDIS_REPLY_ARRAY"
, "redis-cli.c", 3334, __extension__ __PRETTY_FUNCTION__))
;
3335 size_t i;
3336 for (i = 0; i < reply->elements; i++) {
3337 redisReply *r = reply->element[i];
3338 assert(r->type == REDIS_REPLY_ARRAY && r->elements >= 3)((r->type == 2 && r->elements >= 3) ? (void)
(0) : __assert_fail ("r->type == REDIS_REPLY_ARRAY && r->elements >= 3"
, "redis-cli.c", 3338, __extension__ __PRETTY_FUNCTION__))
;
3339 int from, to;
3340 from = r->element[0]->integer;
3341 to = r->element[1]->integer;
3342 if (slot < from || slot > to) continue;
3343 redisReply *nr = r->element[2];
3344 assert(nr->type == REDIS_REPLY_ARRAY && nr->elements >= 2)((nr->type == 2 && nr->elements >= 2) ? (void
) (0) : __assert_fail ("nr->type == REDIS_REPLY_ARRAY && nr->elements >= 2"
, "redis-cli.c", 3344, __extension__ __PRETTY_FUNCTION__))
;
3345 char *name = NULL((void*)0);
3346 if (nr->elements >= 3)
3347 name = nr->element[2]->str;
3348 if (name != NULL((void*)0))
3349 owner = clusterManagerNodeByName(name);
3350 else {
3351 char *ip = nr->element[0]->str;
3352 assert(ip != NULL)((ip != ((void*)0)) ? (void) (0) : __assert_fail ("ip != NULL"
, "redis-cli.c", 3352, __extension__ __PRETTY_FUNCTION__))
;
3353 int port = (int) nr->element[1]->integer;
3354 listIter li;
3355 listNode *ln;
3356 listRewind(cluster_manager.nodes, &li);
3357 while ((ln = listNext(&li)) != NULL((void*)0)) {
3358 clusterManagerNode *nd = ln->value;
3359 if (strcmp(nd->ip, ip) == 0 && port == nd->port) {
3360 owner = nd;
3361 break;
3362 }
3363 }
3364 }
3365 if (owner) break;
3366 }
3367 }
3368 if (reply) freeReplyObject(reply);
3369 return owner;
3370}
3371
3372/* Set slot status to "importing" or "migrating" */
3373static int clusterManagerSetSlot(clusterManagerNode *node1,
3374 clusterManagerNode *node2,
3375 int slot, const char *status, char **err) {
3376 redisReply *reply = CLUSTER_MANAGER_COMMAND(node1, "CLUSTER "(redisCommand(node1->context, "CLUSTER " "SETSLOT %d %s %s"
, slot, status, (char *) node2->name))
3377 "SETSLOT %d %s %s",(redisCommand(node1->context, "CLUSTER " "SETSLOT %d %s %s"
, slot, status, (char *) node2->name))
3378 slot, status,(redisCommand(node1->context, "CLUSTER " "SETSLOT %d %s %s"
, slot, status, (char *) node2->name))
3379 (char *) node2->name)(redisCommand(node1->context, "CLUSTER " "SETSLOT %d %s %s"
, slot, status, (char *) node2->name))
;
3380 if (err != NULL((void*)0)) *err = NULL((void*)0);
3381 if (!reply) return 0;
3382 int success = 1;
3383 if (reply->type == REDIS_REPLY_ERROR6) {
3384 success = 0;
3385 if (err != NULL((void*)0)) {
3386 *err = zmalloc((reply->len + 1) * sizeof(char));
3387 strcpy(*err, reply->str);
3388 } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(node1, reply->str)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node1
->ip, node1->port, reply->str);
;
3389 goto cleanup;
3390 }
3391cleanup:
3392 freeReplyObject(reply);
3393 return success;
3394}
3395
3396static int clusterManagerClearSlotStatus(clusterManagerNode *node, int slot) {
3397 redisReply *reply = CLUSTER_MANAGER_COMMAND(node,(redisCommand(node->context, "CLUSTER SETSLOT %d %s", slot
, "STABLE"))
3398 "CLUSTER SETSLOT %d %s", slot, "STABLE")(redisCommand(node->context, "CLUSTER SETSLOT %d %s", slot
, "STABLE"))
;
3399 int success = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
3400 if (reply) freeReplyObject(reply);
3401 return success;
3402}
3403
3404static int clusterManagerDelSlot(clusterManagerNode *node, int slot,
3405 int ignore_unassigned_err)
3406{
3407 redisReply *reply = CLUSTER_MANAGER_COMMAND(node,(redisCommand(node->context, "CLUSTER DELSLOTS %d", slot))
3408 "CLUSTER DELSLOTS %d", slot)(redisCommand(node->context, "CLUSTER DELSLOTS %d", slot));
3409 char *err = NULL((void*)0);
3410 int success = clusterManagerCheckRedisReply(node, reply, &err);
3411 if (!success && reply && reply->type == REDIS_REPLY_ERROR6 &&
3412 ignore_unassigned_err)
3413 {
3414 char *get_owner_err = NULL((void*)0);
3415 clusterManagerNode *assigned_to =
3416 clusterManagerGetSlotOwner(node, slot, &get_owner_err);
3417 if (!assigned_to) {
3418 if (get_owner_err == NULL((void*)0)) success = 1;
3419 else {
3420 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, get_owner_err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, get_owner_err);
;
3421 zfree(get_owner_err);
3422 }
3423 }
3424 }
3425 if (!success && err != NULL((void*)0)) {
3426 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, err);
;
3427 zfree(err);
3428 }
3429 if (reply) freeReplyObject(reply);
3430 return success;
3431}
3432
3433static int clusterManagerAddSlot(clusterManagerNode *node, int slot) {
3434 redisReply *reply = CLUSTER_MANAGER_COMMAND(node,(redisCommand(node->context, "CLUSTER ADDSLOTS %d", slot))
3435 "CLUSTER ADDSLOTS %d", slot)(redisCommand(node->context, "CLUSTER ADDSLOTS %d", slot));
3436 int success = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
3437 if (reply) freeReplyObject(reply);
3438 return success;
3439}
3440
3441static signed int clusterManagerCountKeysInSlot(clusterManagerNode *node,
3442 int slot)
3443{
3444 redisReply *reply = CLUSTER_MANAGER_COMMAND(node,(redisCommand(node->context, "CLUSTER COUNTKEYSINSLOT %d",
slot))
3445 "CLUSTER COUNTKEYSINSLOT %d", slot)(redisCommand(node->context, "CLUSTER COUNTKEYSINSLOT %d",
slot))
;
3446 int count = -1;
3447 int success = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
3448 if (success && reply->type == REDIS_REPLY_INTEGER3) count = reply->integer;
3449 if (reply) freeReplyObject(reply);
3450 return count;
3451}
3452
3453static int clusterManagerBumpEpoch(clusterManagerNode *node) {
3454 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER BUMPEPOCH")(redisCommand(node->context, "CLUSTER BUMPEPOCH"));
3455 int success = clusterManagerCheckRedisReply(node, reply, NULL((void*)0));
3456 if (reply) freeReplyObject(reply);
3457 return success;
3458}
3459
3460/* Callback used by clusterManagerSetSlotOwner transaction. It should ignore
3461 * errors except for ADDSLOTS errors.
3462 * Return 1 if the error should be ignored. */
3463static int clusterManagerOnSetOwnerErr(redisReply *reply,
3464 clusterManagerNode *n, int bulk_idx)
3465{
3466 UNUSED(reply)((void) reply);
3467 UNUSED(n)((void) n);
3468 /* Only raise error when ADDSLOTS fail (bulk_idx == 1). */
3469 return (bulk_idx != 1);
3470}
3471
3472static int clusterManagerSetSlotOwner(clusterManagerNode *owner,
3473 int slot,
3474 int do_clear)
3475{
3476 int success = clusterManagerStartTransaction(owner);
3477 if (!success) return 0;
3478 /* Ensure the slot is not already assigned. */
3479 clusterManagerDelSlot(owner, slot, 1);
3480 /* Add the slot and bump epoch. */
3481 clusterManagerAddSlot(owner, slot);
3482 if (do_clear) clusterManagerClearSlotStatus(owner, slot);
3483 clusterManagerBumpEpoch(owner);
3484 success = clusterManagerExecTransaction(owner, clusterManagerOnSetOwnerErr);
3485 return success;
3486}
3487
3488/* Get the hash for the values of the specified keys in *keys_reply for the
3489 * specified nodes *n1 and *n2, by calling DEBUG DIGEST-VALUE redis command
3490 * on both nodes. Every key with same name on both nodes but having different
3491 * values will be added to the *diffs list. Return 0 in case of reply
3492 * error. */
3493static int clusterManagerCompareKeysValues(clusterManagerNode *n1,
3494 clusterManagerNode *n2,
3495 redisReply *keys_reply,
3496 list *diffs)
3497{
3498 size_t i, argc = keys_reply->elements + 2;
3499 static const char *hash_zero = "0000000000000000000000000000000000000000";
3500 char **argv = zcalloc(argc * sizeof(char *));
3501 size_t *argv_len = zcalloc(argc * sizeof(size_t));
3502 argv[0] = "DEBUG";
3503 argv_len[0] = 5;
3504 argv[1] = "DIGEST-VALUE";
3505 argv_len[1] = 12;
3506 for (i = 0; i < keys_reply->elements; i++) {
3507 redisReply *entry = keys_reply->element[i];
3508 int idx = i + 2;
3509 argv[idx] = entry->str;
3510 argv_len[idx] = entry->len;
3511 }
3512 int success = 0;
3513 void *_reply1 = NULL((void*)0), *_reply2 = NULL((void*)0);
3514 redisReply *r1 = NULL((void*)0), *r2 = NULL((void*)0);
3515 redisAppendCommandArgv(n1->context,argc, (const char**)argv,argv_len);
3516 success = (redisGetReply(n1->context, &_reply1) == REDIS_OK0);
3517 if (!success) goto cleanup;
3518 r1 = (redisReply *) _reply1;
3519 redisAppendCommandArgv(n2->context,argc, (const char**)argv,argv_len);
3520 success = (redisGetReply(n2->context, &_reply2) == REDIS_OK0);
3521 if (!success) goto cleanup;
3522 r2 = (redisReply *) _reply2;
3523 success = (r1->type != REDIS_REPLY_ERROR6 && r2->type != REDIS_REPLY_ERROR6);
3524 if (r1->type == REDIS_REPLY_ERROR6) {
3525 CLUSTER_MANAGER_PRINT_REPLY_ERROR(n1, r1->str)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n1
->ip, n1->port, r1->str);
;
3526 success = 0;
3527 }
3528 if (r2->type == REDIS_REPLY_ERROR6) {
3529 CLUSTER_MANAGER_PRINT_REPLY_ERROR(n2, r2->str)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n2
->ip, n2->port, r2->str);
;
3530 success = 0;
3531 }
3532 if (!success) goto cleanup;
3533 assert(keys_reply->elements == r1->elements &&((keys_reply->elements == r1->elements && keys_reply
->elements == r2->elements) ? (void) (0) : __assert_fail
("keys_reply->elements == r1->elements && keys_reply->elements == r2->elements"
, "redis-cli.c", 3534, __extension__ __PRETTY_FUNCTION__))
3534 keys_reply->elements == r2->elements)((keys_reply->elements == r1->elements && keys_reply
->elements == r2->elements) ? (void) (0) : __assert_fail
("keys_reply->elements == r1->elements && keys_reply->elements == r2->elements"
, "redis-cli.c", 3534, __extension__ __PRETTY_FUNCTION__))
;
3535 for (i = 0; i < keys_reply->elements; i++) {
3536 char *key = keys_reply->element[i]->str;
3537 char *hash1 = r1->element[i]->str;
3538 char *hash2 = r2->element[i]->str;
3539 /* Ignore keys that don't exist in both nodes. */
3540 if (strcmp(hash1, hash_zero) == 0 || strcmp(hash2, hash_zero) == 0)
3541 continue;
3542 if (strcmp(hash1, hash2) != 0) listAddNodeTail(diffs, key);
3543 }
3544cleanup:
3545 if (r1) freeReplyObject(r1);
3546 if (r2) freeReplyObject(r2);
3547 zfree(argv);
3548 zfree(argv_len);
3549 return success;
3550}
3551
3552/* Migrate keys taken from reply->elements. It returns the reply from the
3553 * MIGRATE command, or NULL if something goes wrong. If the argument 'dots'
3554 * is not NULL, a dot will be printed for every migrated key. */
3555static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
3556 clusterManagerNode *target,
3557 redisReply *reply,
3558 int replace, int timeout,
3559 char *dots)
3560{
3561 redisReply *migrate_reply = NULL((void*)0);
3562 char **argv = NULL((void*)0);
3563 size_t *argv_len = NULL((void*)0);
3564 int c = (replace ? 8 : 7);
3565 if (config.auth) c += 2;
3566 if (config.user) c += 1;
3567 size_t argc = c + reply->elements;
3568 size_t i, offset = 6; // Keys Offset
3569 argv = zcalloc(argc * sizeof(char *));
3570 argv_len = zcalloc(argc * sizeof(size_t));
3571 char portstr[255];
3572 char timeoutstr[255];
3573 snprintf(portstr, 10, "%d", target->port);
3574 snprintf(timeoutstr, 10, "%d", timeout);
3575 argv[0] = "MIGRATE";
3576 argv_len[0] = 7;
3577 argv[1] = target->ip;
3578 argv_len[1] = strlen(target->ip);
3579 argv[2] = portstr;
3580 argv_len[2] = strlen(portstr);
3581 argv[3] = "";
3582 argv_len[3] = 0;
3583 argv[4] = "0";
3584 argv_len[4] = 1;
3585 argv[5] = timeoutstr;
3586 argv_len[5] = strlen(timeoutstr);
3587 if (replace) {
3588 argv[offset] = "REPLACE";
3589 argv_len[offset] = 7;
3590 offset++;
3591 }
3592 if (config.auth) {
3593 if (config.user) {
3594 argv[offset] = "AUTH2";
3595 argv_len[offset] = 5;
3596 offset++;
3597 argv[offset] = config.user;
3598 argv_len[offset] = strlen(config.user);
3599 offset++;
3600 argv[offset] = config.auth;
3601 argv_len[offset] = strlen(config.auth);
3602 offset++;
3603 } else {
3604 argv[offset] = "AUTH";
3605 argv_len[offset] = 4;
3606 offset++;
3607 argv[offset] = config.auth;
3608 argv_len[offset] = strlen(config.auth);
3609 offset++;
3610 }
3611 }
3612 argv[offset] = "KEYS";
3613 argv_len[offset] = 4;
3614 offset++;
3615 for (i = 0; i < reply->elements; i++) {
3616 redisReply *entry = reply->element[i];
3617 size_t idx = i + offset;
3618 assert(entry->type == REDIS_REPLY_STRING)((entry->type == 1) ? (void) (0) : __assert_fail ("entry->type == REDIS_REPLY_STRING"
, "redis-cli.c", 3618, __extension__ __PRETTY_FUNCTION__))
;
3619 argv[idx] = (char *) sdsnewlenhi_sdsnewlen(entry->str, entry->len);
3620 argv_len[idx] = entry->len;
3621 if (dots) dots[i] = '.';
3622 }
3623 if (dots) dots[reply->elements] = '\0';
3624 void *_reply = NULL((void*)0);
3625 redisAppendCommandArgv(source->context,argc,
3626 (const char**)argv,argv_len);
3627 int success = (redisGetReply(source->context, &_reply) == REDIS_OK0);
3628 for (i = 0; i < reply->elements; i++) sdsfreehi_sdsfree(argv[i + offset]);
3629 if (!success) goto cleanup;
3630 migrate_reply = (redisReply *) _reply;
3631cleanup:
3632 zfree(argv);
3633 zfree(argv_len);
3634 return migrate_reply;
3635}
3636
3637/* Migrate all keys in the given slot from source to target.*/
3638static int clusterManagerMigrateKeysInSlot(clusterManagerNode *source,
3639 clusterManagerNode *target,
3640 int slot, int timeout,
3641 int pipeline, int verbose,
3642 char **err)
3643{
3644 int success = 1;
3645 int do_fix = config.cluster_manager_command.flags &
3646 CLUSTER_MANAGER_CMD_FLAG_FIX1 << 0;
3647 int do_replace = config.cluster_manager_command.flags &
3648 CLUSTER_MANAGER_CMD_FLAG_REPLACE1 << 6;
3649 while (1) {
3650 char *dots = NULL((void*)0);
3651 redisReply *reply = NULL((void*)0), *migrate_reply = NULL((void*)0);
3652 reply = CLUSTER_MANAGER_COMMAND(source, "CLUSTER "(redisCommand(source->context, "CLUSTER " "GETKEYSINSLOT %d %d"
, slot, pipeline))
3653 "GETKEYSINSLOT %d %d", slot,(redisCommand(source->context, "CLUSTER " "GETKEYSINSLOT %d %d"
, slot, pipeline))
3654 pipeline)(redisCommand(source->context, "CLUSTER " "GETKEYSINSLOT %d %d"
, slot, pipeline))
;
3655 success = (reply != NULL((void*)0));
3656 if (!success) return 0;
3657 if (reply->type == REDIS_REPLY_ERROR6) {
3658 success = 0;
3659 if (err != NULL((void*)0)) {
3660 *err = zmalloc((reply->len + 1) * sizeof(char));
3661 strcpy(*err, reply->str);
3662 CLUSTER_MANAGER_PRINT_REPLY_ERROR(source, *err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", source
->ip, source->port, *err);
;
3663 }
3664 goto next;
3665 }
3666 assert(reply->type == REDIS_REPLY_ARRAY)((reply->type == 2) ? (void) (0) : __assert_fail ("reply->type == REDIS_REPLY_ARRAY"
, "redis-cli.c", 3666, __extension__ __PRETTY_FUNCTION__))
;
3667 size_t count = reply->elements;
3668 if (count == 0) {
3669 freeReplyObject(reply);
3670 break;
3671 }
3672 if (verbose) dots = zmalloc((count+1) * sizeof(char));
3673 /* Calling MIGRATE command. */
3674 migrate_reply = clusterManagerMigrateKeysInReply(source, target,
3675 reply, 0, timeout,
3676 dots);
3677 if (migrate_reply == NULL((void*)0)) goto next;
3678 if (migrate_reply->type == REDIS_REPLY_ERROR6) {
3679 int is_busy = strstr(migrate_reply->str, "BUSYKEY") != NULL((void*)0);
3680 int not_served = 0;
3681 if (!is_busy) {
3682 /* Check if the slot is unassigned (not served) in the
3683 * source node's configuration. */
3684 char *get_owner_err = NULL((void*)0);
3685 clusterManagerNode *served_by =
3686 clusterManagerGetSlotOwner(source, slot, &get_owner_err);
3687 if (!served_by) {
3688 if (get_owner_err == NULL((void*)0)) not_served = 1;
3689 else {
3690 CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", source
->ip, source->port, get_owner_err);
3691 get_owner_err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", source
->ip, source->port, get_owner_err);
;
3692 zfree(get_owner_err);
3693 }
3694 }
3695 }
3696 /* Try to handle errors. */
3697 if (is_busy || not_served) {
3698 /* If the key's slot is not served, try to assign slot
3699 * to the target node. */
3700 if (do_fix && not_served) {
3701 clusterManagerLogWarn("*** Slot was not served, setting "clusterManagerLog(2,"*** Slot was not served, setting " "owner to node %s:%d.\n"
, target->ip, target->port)
3702 "owner to node %s:%d.\n",clusterManagerLog(2,"*** Slot was not served, setting " "owner to node %s:%d.\n"
, target->ip, target->port)
3703 target->ip, target->port)clusterManagerLog(2,"*** Slot was not served, setting " "owner to node %s:%d.\n"
, target->ip, target->port)
;
3704 clusterManagerSetSlot(source, target, slot, "node", NULL((void*)0));
3705 }
3706 /* If the key already exists in the target node (BUSYKEY),
3707 * check whether its value is the same in both nodes.
3708 * In case of equal values, retry migration with the
3709 * REPLACE option.
3710 * In case of different values:
3711 * - If the migration is requested by the fix command, stop
3712 * and warn the user.
3713 * - In other cases (ie. reshard), proceed only if the user
3714 * launched the command with the --cluster-replace option.*/
3715 if (is_busy) {
3716 clusterManagerLogWarn("\n*** Target key exists\n")clusterManagerLog(2,"\n*** Target key exists\n");
3717 if (!do_replace) {
3718 clusterManagerLogWarn("*** Checking key values on "clusterManagerLog(2,"*** Checking key values on " "both nodes...\n"
)
3719 "both nodes...\n")clusterManagerLog(2,"*** Checking key values on " "both nodes...\n"
)
;
3720 list *diffs = listCreate();
3721 success = clusterManagerCompareKeysValues(source,
3722 target, reply, diffs);
3723 if (!success) {
3724 clusterManagerLogErr("*** Value check failed!\n")clusterManagerLog(3,"*** Value check failed!\n");
3725 listRelease(diffs);
3726 goto next;
3727 }
3728 if (listLength(diffs)((diffs)->len) > 0) {
3729 success = 0;
3730 clusterManagerLogErr(clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3731 "*** Found %d key(s) in both source node and "clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3732 "target node having different values.\n"clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3733 " Source node: %s:%d\n"clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3734 " Target node: %s:%d\n"clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3735 " Keys(s):\n",clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3736 listLength(diffs),clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3737 source->ip, source->port,clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
3738 target->ip, target->port)clusterManagerLog(3,"*** Found %d key(s) in both source node and "
"target node having different values.\n" " Source node: %s:%d\n"
" Target node: %s:%d\n" " Keys(s):\n", ((diffs)->len
), source->ip, source->port, target->ip, target->
port)
;
3739 listIter dli;
3740 listNode *dln;
3741 listRewind(diffs, &dli);
3742 while((dln = listNext(&dli)) != NULL((void*)0)) {
3743 char *k = dln->value;
3744 clusterManagerLogErr(" - %s\n", k)clusterManagerLog(3," - %s\n", k);
3745 }
3746 clusterManagerLogErr("Please fix the above key(s) "clusterManagerLog(3,"Please fix the above key(s) " "manually and try again "
"or relaunch the command \n" "with --cluster-replace " "option to force key "
"overriding.\n")
3747 "manually and try again "clusterManagerLog(3,"Please fix the above key(s) " "manually and try again "
"or relaunch the command \n" "with --cluster-replace " "option to force key "
"overriding.\n")
3748 "or relaunch the command \n"clusterManagerLog(3,"Please fix the above key(s) " "manually and try again "
"or relaunch the command \n" "with --cluster-replace " "option to force key "
"overriding.\n")
3749 "with --cluster-replace "clusterManagerLog(3,"Please fix the above key(s) " "manually and try again "
"or relaunch the command \n" "with --cluster-replace " "option to force key "
"overriding.\n")
3750 "option to force key "clusterManagerLog(3,"Please fix the above key(s) " "manually and try again "
"or relaunch the command \n" "with --cluster-replace " "option to force key "
"overriding.\n")
3751 "overriding.\n")clusterManagerLog(3,"Please fix the above key(s) " "manually and try again "
"or relaunch the command \n" "with --cluster-replace " "option to force key "
"overriding.\n")
;
3752 listRelease(diffs);
3753 goto next;
3754 }
3755 listRelease(diffs);
3756 }
3757 clusterManagerLogWarn("*** Replacing target keys...\n")clusterManagerLog(2,"*** Replacing target keys...\n");
3758 }
3759 freeReplyObject(migrate_reply);
3760 migrate_reply = clusterManagerMigrateKeysInReply(source,
3761 target,
3762 reply,
3763 is_busy,
3764 timeout,
3765 NULL((void*)0));
3766 success = (migrate_reply != NULL((void*)0) &&
3767 migrate_reply->type != REDIS_REPLY_ERROR6);
3768 } else success = 0;
3769 if (!success) {
3770 if (migrate_reply != NULL((void*)0)) {
3771 if (err) {
3772 *err = zmalloc((migrate_reply->len + 1) * sizeof(char));
3773 strcpy(*err, migrate_reply->str);
3774 }
3775 printf("\n");
3776 CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", source
->ip, source->port, migrate_reply->str);
3777 migrate_reply->str)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", source
->ip, source->port, migrate_reply->str);
;
3778 }
3779 goto next;
3780 }
3781 }
3782 if (verbose) {
3783 printf("%s", dots);
3784 fflush(stdoutstdout);
3785 }
3786next:
3787 if (reply != NULL((void*)0)) freeReplyObject(reply);
3788 if (migrate_reply != NULL((void*)0)) freeReplyObject(migrate_reply);
3789 if (dots) zfree(dots);
3790 if (!success) break;
3791 }
3792 return success;
3793}
3794
3795/* Move slots between source and target nodes using MIGRATE.
3796 *
3797 * Options:
3798 * CLUSTER_MANAGER_OPT_VERBOSE -- Print a dot for every moved key.
3799 * CLUSTER_MANAGER_OPT_COLD -- Move keys without opening slots /
3800 * reconfiguring the nodes.
3801 * CLUSTER_MANAGER_OPT_UPDATE -- Update node->slots for source/target nodes.
3802 * CLUSTER_MANAGER_OPT_QUIET -- Don't print info messages.
3803*/
3804static int clusterManagerMoveSlot(clusterManagerNode *source,
3805 clusterManagerNode *target,
3806 int slot, int opts, char**err)
3807{
3808 if (!(opts & CLUSTER_MANAGER_OPT_QUIET1 << 6)) {
3809 printf("Moving slot %d from %s:%d to %s:%d: ", slot, source->ip,
3810 source->port, target->ip, target->port);
3811 fflush(stdoutstdout);
3812 }
3813 if (err != NULL((void*)0)) *err = NULL((void*)0);
3814 int pipeline = config.cluster_manager_command.pipeline,
3815 timeout = config.cluster_manager_command.timeout,
3816 print_dots = (opts & CLUSTER_MANAGER_OPT_VERBOSE1 << 7),
3817 option_cold = (opts & CLUSTER_MANAGER_OPT_COLD1 << 1),
3818 success = 1;
3819 if (!option_cold) {
3820 success = clusterManagerSetSlot(target, source, slot,
3821 "importing", err);
3822 if (!success) return 0;
3823 success = clusterManagerSetSlot(source, target, slot,
3824 "migrating", err);
3825 if (!success) return 0;
3826 }
3827 success = clusterManagerMigrateKeysInSlot(source, target, slot, timeout,
3828 pipeline, print_dots, err);
3829 if (!(opts & CLUSTER_MANAGER_OPT_QUIET1 << 6)) printf("\n");
3830 if (!success) return 0;
3831 /* Set the new node as the owner of the slot in all the known nodes. */
3832 if (!option_cold) {
3833 listIter li;
3834 listNode *ln;
3835 listRewind(cluster_manager.nodes, &li);
3836 while ((ln = listNext(&li)) != NULL((void*)0)) {
3837 clusterManagerNode *n = ln->value;
3838 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
3839 redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER "(redisCommand(n->context, "CLUSTER " "SETSLOT %d %s %s", slot
, "node", target->name))
3840 "SETSLOT %d %s %s",(redisCommand(n->context, "CLUSTER " "SETSLOT %d %s %s", slot
, "node", target->name))
3841 slot, "node",(redisCommand(n->context, "CLUSTER " "SETSLOT %d %s %s", slot
, "node", target->name))
3842 target->name)(redisCommand(n->context, "CLUSTER " "SETSLOT %d %s %s", slot
, "node", target->name))
;
3843 success = (r != NULL((void*)0));
3844 if (!success) return 0;
3845 if (r->type == REDIS_REPLY_ERROR6) {
3846 success = 0;
3847 if (err != NULL((void*)0)) {
3848 *err = zmalloc((r->len + 1) * sizeof(char));
3849 strcpy(*err, r->str);
3850 CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, *err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n
->ip, n->port, *err);
;
3851 }
3852 }
3853 freeReplyObject(r);
3854 if (!success) return 0;
3855 }
3856 }
3857 /* Update the node logical config */
3858 if (opts & CLUSTER_MANAGER_OPT_UPDATE1 << 2) {
3859 source->slots[slot] = 0;
3860 target->slots[slot] = 1;
3861 }
3862 return 1;
3863}
3864
3865/* Flush the dirty node configuration by calling replicate for slaves or
3866 * adding the slots defined in the masters. */
3867static int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err) {
3868 if (!node->dirty) return 0;
3869 redisReply *reply = NULL((void*)0);
3870 int is_err = 0, success = 1;
3871 if (err != NULL((void*)0)) *err = NULL((void*)0);
3872 if (node->replicate != NULL((void*)0)) {
3873 reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER REPLICATE %s",(redisCommand(node->context, "CLUSTER REPLICATE %s", node->
replicate))
3874 node->replicate)(redisCommand(node->context, "CLUSTER REPLICATE %s", node->
replicate))
;
3875 if (reply == NULL((void*)0) || (is_err = (reply->type == REDIS_REPLY_ERROR6))) {
3876 if (is_err && err != NULL((void*)0)) {
3877 *err = zmalloc((reply->len + 1) * sizeof(char));
3878 strcpy(*err, reply->str);
3879 }
3880 success = 0;
3881 /* If the cluster did not already joined it is possible that
3882 * the slave does not know the master node yet. So on errors
3883 * we return ASAP leaving the dirty flag set, to flush the
3884 * config later. */
3885 goto cleanup;
3886 }
3887 } else {
3888 int added = clusterManagerAddSlots(node, err);
3889 if (!added || *err != NULL((void*)0)) success = 0;
3890 }
3891 node->dirty = 0;
3892cleanup:
3893 if (reply != NULL((void*)0)) freeReplyObject(reply);
3894 return success;
3895}
3896
3897/* Wait until the cluster configuration is consistent. */
3898static void clusterManagerWaitForClusterJoin(void) {
3899 printf("Waiting for the cluster to join\n");
3900 int counter = 0,
3901 check_after = CLUSTER_JOIN_CHECK_AFTER20 +
3902 (int)(listLength(cluster_manager.nodes)((cluster_manager.nodes)->len) * 0.15f);
3903 while(!clusterManagerIsConfigConsistent()) {
3904 printf(".");
3905 fflush(stdoutstdout);
3906 sleep(1);
3907 if (++counter > check_after) {
3908 dict *status = clusterManagerGetLinkStatus();
3909 dictIterator *iter = NULL((void*)0);
3910 if (status != NULL((void*)0) && dictSize(status)((status)->ht[0].used+(status)->ht[1].used) > 0) {
3911 printf("\n");
3912 clusterManagerLogErr("Warning: %d node(s) may "clusterManagerLog(3,"Warning: %d node(s) may " "be unreachable\n"
, ((status)->ht[0].used+(status)->ht[1].used))
3913 "be unreachable\n", dictSize(status))clusterManagerLog(3,"Warning: %d node(s) may " "be unreachable\n"
, ((status)->ht[0].used+(status)->ht[1].used))
;
3914 iter = dictGetIterator(status);
3915 dictEntry *entry;
3916 while ((entry = dictNext(iter)) != NULL((void*)0)) {
3917 sdshisds nodeaddr = (sdshisds) dictGetKey(entry)((entry)->key);
3918 char *node_ip = NULL((void*)0);
3919 int node_port = 0, node_bus_port = 0;
3920 list *from = (list *) dictGetVal(entry)((entry)->v.val);
3921 if (parseClusterNodeAddress(nodeaddr, &node_ip,
3922 &node_port, &node_bus_port) && node_bus_port) {
3923 clusterManagerLogErr(" - The port %d of node %s may "clusterManagerLog(3," - The port %d of node %s may " "be unreachable from:\n"
, node_bus_port, node_ip)
3924 "be unreachable from:\n",clusterManagerLog(3," - The port %d of node %s may " "be unreachable from:\n"
, node_bus_port, node_ip)
3925 node_bus_port, node_ip)clusterManagerLog(3," - The port %d of node %s may " "be unreachable from:\n"
, node_bus_port, node_ip)
;
3926 } else {
3927 clusterManagerLogErr(" - Node %s may be unreachable "clusterManagerLog(3," - Node %s may be unreachable " "from:\n"
, nodeaddr)
3928 "from:\n", nodeaddr)clusterManagerLog(3," - Node %s may be unreachable " "from:\n"
, nodeaddr)
;
3929 }
3930 listIter li;
3931 listNode *ln;
3932 listRewind(from, &li);
3933 while ((ln = listNext(&li)) != NULL((void*)0)) {
3934 sdshisds from_addr = ln->value;
3935 clusterManagerLogErr(" %s\n", from_addr)clusterManagerLog(3," %s\n", from_addr);
3936 sdsfreehi_sdsfree(from_addr);
3937 }
3938 clusterManagerLogErr("Cluster bus ports must be reachable "clusterManagerLog(3,"Cluster bus ports must be reachable " "by every node.\nRemember that "
"cluster bus ports are different " "from standard instance ports.\n"
)
3939 "by every node.\nRemember that "clusterManagerLog(3,"Cluster bus ports must be reachable " "by every node.\nRemember that "
"cluster bus ports are different " "from standard instance ports.\n"
)
3940 "cluster bus ports are different "clusterManagerLog(3,"Cluster bus ports must be reachable " "by every node.\nRemember that "
"cluster bus ports are different " "from standard instance ports.\n"
)
3941 "from standard instance ports.\n")clusterManagerLog(3,"Cluster bus ports must be reachable " "by every node.\nRemember that "
"cluster bus ports are different " "from standard instance ports.\n"
)
;
3942 listEmpty(from);
3943 }
3944 }
3945 if (iter != NULL((void*)0)) dictReleaseIterator(iter);
3946 if (status != NULL((void*)0)) dictRelease(status);
3947 counter = 0;
3948 }
3949 }
3950 printf("\n");
3951}
3952
3953/* Load node's cluster configuration by calling "CLUSTER NODES" command.
3954 * Node's configuration (name, replicate, slots, ...) is then updated.
3955 * If CLUSTER_MANAGER_OPT_GETFRIENDS flag is set into 'opts' argument,
3956 * and node already knows other nodes, the node's friends list is populated
3957 * with the other nodes info. */
3958static int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,
3959 char **err)
3960{
3961 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER NODES")(redisCommand(node->context, "CLUSTER NODES"));
3962 int success = 1;
3963 *err = NULL((void*)0);
3964 if (!clusterManagerCheckRedisReply(node, reply, err)) {
3965 success = 0;
3966 goto cleanup;
3967 }
3968 int getfriends = (opts & CLUSTER_MANAGER_OPT_GETFRIENDS1 << 0);
3969 char *lines = reply->str, *p, *line;
3970 while ((p = strstr(lines, "\n")) != NULL((void*)0)) {
3971 *p = '\0';
3972 line = lines;
3973 lines = p + 1;
3974 char *name = NULL((void*)0), *addr = NULL((void*)0), *flags = NULL((void*)0), *master_id = NULL((void*)0),
3975 *ping_sent = NULL((void*)0), *ping_recv = NULL((void*)0), *config_epoch = NULL((void*)0),
3976 *link_status = NULL((void*)0);
3977 UNUSED(link_status)((void) link_status);
3978 int i = 0;
3979 while ((p = strchr(line, ' ')) != NULL((void*)0)) {
3980 *p = '\0';
3981 char *token = line;
3982 line = p + 1;
3983 switch(i++){
3984 case 0: name = token; break;
3985 case 1: addr = token; break;
3986 case 2: flags = token; break;
3987 case 3: master_id = token; break;
3988 case 4: ping_sent = token; break;
3989 case 5: ping_recv = token; break;
3990 case 6: config_epoch = token; break;
3991 case 7: link_status = token; break;
3992 }
3993 if (i == 8) break; // Slots
3994 }
3995 if (!flags) {
3996 success = 0;
3997 goto cleanup;
3998 }
3999 int myself = (strstr(flags, "myself") != NULL((void*)0));
4000 clusterManagerNode *currentNode = NULL((void*)0);
4001 if (myself) {
4002 node->flags |= CLUSTER_MANAGER_FLAG_MYSELF1 << 0;
4003 currentNode = node;
4004 clusterManagerNodeResetSlots(node);
4005 if (i == 8) {
4006 int remaining = strlen(line);
4007 while (remaining > 0) {
4008 p = strchr(line, ' ');
4009 if (p == NULL((void*)0)) p = line + remaining;
4010 remaining -= (p - line);
4011
4012 char *slotsdef = line;
4013 *p = '\0';
4014 if (remaining) {
4015 line = p + 1;
4016 remaining--;
4017 } else line = p;
4018 char *dash = NULL((void*)0);
4019 if (slotsdef[0] == '[') {
4020 slotsdef++;
4021 if ((p = strstr(slotsdef, "->-"))) { // Migrating
4022 *p = '\0';
4023 p += 3;
4024 char *closing_bracket = strchr(p, ']');
4025 if (closing_bracket) *closing_bracket = '\0';
4026 sdshisds slot = sdsnewhi_sdsnew(slotsdef);
4027 sdshisds dst = sdsnewhi_sdsnew(p);
4028 node->migrating_count += 2;
4029 node->migrating = zrealloc(node->migrating,
4030 (node->migrating_count * sizeof(sdshisds)));
4031 node->migrating[node->migrating_count - 2] =
4032 slot;
4033 node->migrating[node->migrating_count - 1] =
4034 dst;
4035 } else if ((p = strstr(slotsdef, "-<-"))) {//Importing
4036 *p = '\0';
4037 p += 3;
4038 char *closing_bracket = strchr(p, ']');
4039 if (closing_bracket) *closing_bracket = '\0';
4040 sdshisds slot = sdsnewhi_sdsnew(slotsdef);
4041 sdshisds src = sdsnewhi_sdsnew(p);
4042 node->importing_count += 2;
4043 node->importing = zrealloc(node->importing,
4044 (node->importing_count * sizeof(sdshisds)));
4045 node->importing[node->importing_count - 2] =
4046 slot;
4047 node->importing[node->importing_count - 1] =
4048 src;
4049 }
4050 } else if ((dash = strchr(slotsdef, '-')) != NULL((void*)0)) {
4051 p = dash;
4052 int start, stop;
4053 *p = '\0';
4054 start = atoi(slotsdef);
4055 stop = atoi(p + 1);
4056 node->slots_count += (stop - (start - 1));
4057 while (start <= stop) node->slots[start++] = 1;
4058 } else if (p > slotsdef) {
4059 node->slots[atoi(slotsdef)] = 1;
4060 node->slots_count++;
4061 }
4062 }
4063 }
4064 node->dirty = 0;
4065 } else if (!getfriends) {
4066 if (!(node->flags & CLUSTER_MANAGER_FLAG_MYSELF1 << 0)) continue;
4067 else break;
4068 } else {
4069 if (addr == NULL((void*)0)) {
4070 fprintf(stderrstderr, "Error: invalid CLUSTER NODES reply\n");
4071 success = 0;
4072 goto cleanup;
4073 }
4074 char *c = strrchr(addr, '@');
4075 if (c != NULL((void*)0)) *c = '\0';
4076 c = strrchr(addr, ':');
4077 if (c == NULL((void*)0)) {
4078 fprintf(stderrstderr, "Error: invalid CLUSTER NODES reply\n");
4079 success = 0;
4080 goto cleanup;
4081 }
4082 *c = '\0';
4083 int port = atoi(++c);
4084 currentNode = clusterManagerNewNode(sdsnewhi_sdsnew(addr), port);
4085 currentNode->flags |= CLUSTER_MANAGER_FLAG_FRIEND1 << 2;
4086 if (node->friends == NULL((void*)0)) node->friends = listCreate();
4087 listAddNodeTail(node->friends, currentNode);
4088 }
4089 if (name != NULL((void*)0)) {
4090 if (currentNode->name) sdsfreehi_sdsfree(currentNode->name);
4091 currentNode->name = sdsnewhi_sdsnew(name);
4092 }
4093 if (currentNode->flags_str != NULL((void*)0))
4094 freeClusterManagerNodeFlags(currentNode->flags_str);
4095 currentNode->flags_str = listCreate();
4096 int flag_len;
4097 while ((flag_len = strlen(flags)) > 0) {
4098 sdshisds flag = NULL((void*)0);
4099 char *fp = strchr(flags, ',');
4100 if (fp) {
4101 *fp = '\0';
4102 flag = sdsnewhi_sdsnew(flags);
4103 flags = fp + 1;
4104 } else {
4105 flag = sdsnewhi_sdsnew(flags);
4106 flags += flag_len;
4107 }
4108 if (strcmp(flag, "noaddr") == 0)
4109 currentNode->flags |= CLUSTER_MANAGER_FLAG_NOADDR1 << 3;
4110 else if (strcmp(flag, "disconnected") == 0)
4111 currentNode->flags |= CLUSTER_MANAGER_FLAG_DISCONNECT1 << 4;
4112 else if (strcmp(flag, "fail") == 0)
4113 currentNode->flags |= CLUSTER_MANAGER_FLAG_FAIL1 << 5;
4114 else if (strcmp(flag, "slave") == 0) {
4115 currentNode->flags |= CLUSTER_MANAGER_FLAG_SLAVE1 << 1;
4116 if (master_id != NULL((void*)0)) {
4117 if (currentNode->replicate) sdsfreehi_sdsfree(currentNode->replicate);
4118 currentNode->replicate = sdsnewhi_sdsnew(master_id);
4119 }
4120 }
4121 listAddNodeTail(currentNode->flags_str, flag);
4122 }
4123 if (config_epoch != NULL((void*)0))
4124 currentNode->current_epoch = atoll(config_epoch);
4125 if (ping_sent != NULL((void*)0)) currentNode->ping_sent = atoll(ping_sent);
4126 if (ping_recv != NULL((void*)0)) currentNode->ping_recv = atoll(ping_recv);
4127 if (!getfriends && myself) break;
4128 }
4129cleanup:
4130 if (reply) freeReplyObject(reply);
4131 return success;
4132}
4133
4134/* Retrieves info about the cluster using argument 'node' as the starting
4135 * point. All nodes will be loaded inside the cluster_manager.nodes list.
4136 * Warning: if something goes wrong, it will free the starting node before
4137 * returning 0. */
4138static int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts) {
4139 if (node->context == NULL((void*)0) && !clusterManagerNodeConnect(node)) {
4140 freeClusterManagerNode(node);
4141 return 0;
4142 }
4143 opts |= CLUSTER_MANAGER_OPT_GETFRIENDS1 << 0;
4144 char *e = NULL((void*)0);
4145 if (!clusterManagerNodeIsCluster(node, &e)) {
4146 clusterManagerPrintNotClusterNodeError(node, e);
4147 if (e) zfree(e);
4148 freeClusterManagerNode(node);
4149 return 0;
4150 }
4151 e = NULL((void*)0);
4152 if (!clusterManagerNodeLoadInfo(node, opts, &e)) {
4153 if (e) {
4154 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, e)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, e);
;
4155 zfree(e);
4156 }
4157 freeClusterManagerNode(node);
4158 return 0;
4159 }
4160 listIter li;
4161 listNode *ln;
4162 if (cluster_manager.nodes != NULL((void*)0)) {
4163 listRewind(cluster_manager.nodes, &li);
4164 while ((ln = listNext(&li)) != NULL((void*)0))
4165 freeClusterManagerNode((clusterManagerNode *) ln->value);
4166 listRelease(cluster_manager.nodes);
4167 }
4168 cluster_manager.nodes = listCreate();
4169 listAddNodeTail(cluster_manager.nodes, node);
4170 if (node->friends != NULL((void*)0)) {
4171 listRewind(node->friends, &li);
4172 while ((ln = listNext(&li)) != NULL((void*)0)) {
4173 clusterManagerNode *friend = ln->value;
4174 if (!friend->ip || !friend->port) goto invalid_friend;
4175 if (!friend->context && !clusterManagerNodeConnect(friend))
4176 goto invalid_friend;
4177 e = NULL((void*)0);
4178 if (clusterManagerNodeLoadInfo(friend, 0, &e)) {
4179 if (friend->flags & (CLUSTER_MANAGER_FLAG_NOADDR1 << 3 |
4180 CLUSTER_MANAGER_FLAG_DISCONNECT1 << 4 |
4181 CLUSTER_MANAGER_FLAG_FAIL1 << 5))
4182 {
4183 goto invalid_friend;
4184 }
4185 listAddNodeTail(cluster_manager.nodes, friend);
4186 } else {
4187 clusterManagerLogErr("[ERR] Unable to load info for "clusterManagerLog(3,"[ERR] Unable to load info for " "node %s:%d\n"
, friend->ip, friend->port)
4188 "node %s:%d\n",clusterManagerLog(3,"[ERR] Unable to load info for " "node %s:%d\n"
, friend->ip, friend->port)
4189 friend->ip, friend->port)clusterManagerLog(3,"[ERR] Unable to load info for " "node %s:%d\n"
, friend->ip, friend->port)
;
4190 goto invalid_friend;
4191 }
4192 continue;
4193invalid_friend:
4194 if (!(friend->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1))
4195 cluster_manager.unreachable_masters++;
4196 freeClusterManagerNode(friend);
4197 }
4198 listRelease(node->friends);
4199 node->friends = NULL((void*)0);
4200 }
4201 // Count replicas for each node
4202 listRewind(cluster_manager.nodes, &li);
4203 while ((ln = listNext(&li)) != NULL((void*)0)) {
4204 clusterManagerNode *n = ln->value;
4205 if (n->replicate != NULL((void*)0)) {
4206 clusterManagerNode *master = clusterManagerNodeByName(n->replicate);
4207 if (master == NULL((void*)0)) {
4208 clusterManagerLogWarn("*** WARNING: %s:%d claims to be "clusterManagerLog(2,"*** WARNING: %s:%d claims to be " "slave of unknown node ID %s.\n"
, n->ip, n->port, n->replicate)
4209 "slave of unknown node ID %s.\n",clusterManagerLog(2,"*** WARNING: %s:%d claims to be " "slave of unknown node ID %s.\n"
, n->ip, n->port, n->replicate)
4210 n->ip, n->port, n->replicate)clusterManagerLog(2,"*** WARNING: %s:%d claims to be " "slave of unknown node ID %s.\n"
, n->ip, n->port, n->replicate)
;
4211 } else master->replicas_count++;
4212 }
4213 }
4214 return 1;
4215}
4216
4217/* Compare functions used by various sorting operations. */
4218int clusterManagerSlotCompare(const void *slot1, const void *slot2) {
4219 const char **i1 = (const char **)slot1;
4220 const char **i2 = (const char **)slot2;
4221 return strcmp(*i1, *i2);
4222}
4223
4224int clusterManagerSlotCountCompareDesc(const void *n1, const void *n2) {
4225 clusterManagerNode *node1 = *((clusterManagerNode **) n1);
4226 clusterManagerNode *node2 = *((clusterManagerNode **) n2);
4227 return node2->slots_count - node1->slots_count;
4228}
4229
4230int clusterManagerCompareNodeBalance(const void *n1, const void *n2) {
4231 clusterManagerNode *node1 = *((clusterManagerNode **) n1);
4232 clusterManagerNode *node2 = *((clusterManagerNode **) n2);
4233 return node1->balance - node2->balance;
4234}
4235
4236static sdshisds clusterManagerGetConfigSignature(clusterManagerNode *node) {
4237 sdshisds signature = NULL((void*)0);
4238 int node_count = 0, i = 0, name_len = 0;
4239 char **node_configs = NULL((void*)0);
4240 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER NODES")(redisCommand(node->context, "CLUSTER NODES"));
4241 if (reply == NULL((void*)0) || reply->type == REDIS_REPLY_ERROR6)
4242 goto cleanup;
4243 char *lines = reply->str, *p, *line;
4244 while ((p = strstr(lines, "\n")) != NULL((void*)0)) {
4245 i = 0;
4246 *p = '\0';
4247 line = lines;
4248 lines = p + 1;
4249 char *nodename = NULL((void*)0);
4250 int tot_size = 0;
4251 while ((p = strchr(line, ' ')) != NULL((void*)0)) {
4252 *p = '\0';
4253 char *token = line;
4254 line = p + 1;
4255 if (i == 0) {
4256 nodename = token;
4257 tot_size = (p - token);
4258 name_len = tot_size++; // Make room for ':' in tot_size
4259 }
4260 if (++i == 8) break;
4261 }
4262 if (i != 8) continue;
4263 if (nodename == NULL((void*)0)) continue;
4264 int remaining = strlen(line);
4265 if (remaining == 0) continue;
4266 char **slots = NULL((void*)0);
4267 int c = 0;
4268 while (remaining > 0) {
4269 p = strchr(line, ' ');
4270 if (p == NULL((void*)0)) p = line + remaining;
4271 int size = (p - line);
4272 remaining -= size;
4273 tot_size += size;
4274 char *slotsdef = line;
4275 *p = '\0';
4276 if (remaining) {
4277 line = p + 1;
4278 remaining--;
4279 } else line = p;
4280 if (slotsdef[0] != '[') {
4281 c++;
4282 slots = zrealloc(slots, (c * sizeof(char *)));
4283 slots[c - 1] = slotsdef;
4284 }
4285 }
4286 if (c > 0) {
4287 if (c > 1)
4288 qsort(slots, c, sizeof(char *), clusterManagerSlotCompare);
4289 node_count++;
4290 node_configs =
4291 zrealloc(node_configs, (node_count * sizeof(char *)));
4292 /* Make room for '|' separators. */
4293 tot_size += (sizeof(char) * (c - 1));
4294 char *cfg = zmalloc((sizeof(char) * tot_size) + 1);
4295 memcpy(cfg, nodename, name_len);
4296 char *sp = cfg + name_len;
4297 *(sp++) = ':';
4298 for (i = 0; i < c; i++) {
4299 if (i > 0) *(sp++) = ',';
4300 int slen = strlen(slots[i]);
4301 memcpy(sp, slots[i], slen);
4302 sp += slen;
4303 }
4304 *(sp++) = '\0';
4305 node_configs[node_count - 1] = cfg;
4306 }
4307 zfree(slots);
4308 }
4309 if (node_count > 0) {
4310 if (node_count > 1) {
4311 qsort(node_configs, node_count, sizeof(char *),
4312 clusterManagerSlotCompare);
4313 }
4314 signature = sdsemptyhi_sdsempty();
4315 for (i = 0; i < node_count; i++) {
4316 if (i > 0) signature = sdscatprintfhi_sdscatprintf(signature, "%c", '|');
4317 signature = sdscatfmthi_sdscatfmt(signature, "%s", node_configs[i]);
4318 }
4319 }
4320cleanup:
4321 if (reply != NULL((void*)0)) freeReplyObject(reply);
4322 if (node_configs != NULL((void*)0)) {
4323 for (i = 0; i < node_count; i++) zfree(node_configs[i]);
4324 zfree(node_configs);
4325 }
4326 return signature;
4327}
4328
4329static int clusterManagerIsConfigConsistent(void) {
4330 if (cluster_manager.nodes == NULL((void*)0)) return 0;
4331 int consistent = (listLength(cluster_manager.nodes)((cluster_manager.nodes)->len) <= 1);
4332 // If the Cluster has only one node, it's always consistent
4333 if (consistent) return 1;
4334 sdshisds first_cfg = NULL((void*)0);
4335 listIter li;
4336 listNode *ln;
4337 listRewind(cluster_manager.nodes, &li);
4338 while ((ln = listNext(&li)) != NULL((void*)0)) {
4339 clusterManagerNode *node = ln->value;
4340 sdshisds cfg = clusterManagerGetConfigSignature(node);
4341 if (cfg == NULL((void*)0)) {
4342 consistent = 0;
4343 break;
4344 }
4345 if (first_cfg == NULL((void*)0)) first_cfg = cfg;
4346 else {
4347 consistent = !sdscmphi_sdscmp(first_cfg, cfg);
4348 sdsfreehi_sdsfree(cfg);
4349 if (!consistent) break;
4350 }
4351 }
4352 if (first_cfg != NULL((void*)0)) sdsfreehi_sdsfree(first_cfg);
4353 return consistent;
4354}
4355
4356static list *clusterManagerGetDisconnectedLinks(clusterManagerNode *node) {
4357 list *links = NULL((void*)0);
4358 redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER NODES")(redisCommand(node->context, "CLUSTER NODES"));
4359 if (!clusterManagerCheckRedisReply(node, reply, NULL((void*)0))) goto cleanup;
4360 links = listCreate();
4361 char *lines = reply->str, *p, *line;
4362 while ((p = strstr(lines, "\n")) != NULL((void*)0)) {
4363 int i = 0;
4364 *p = '\0';
4365 line = lines;
4366 lines = p + 1;
4367 char *nodename = NULL((void*)0), *addr = NULL((void*)0), *flags = NULL((void*)0), *link_status = NULL((void*)0);
4368 while ((p = strchr(line, ' ')) != NULL((void*)0)) {
4369 *p = '\0';
4370 char *token = line;
4371 line = p + 1;
4372 if (i == 0) nodename = token;
4373 else if (i == 1) addr = token;
4374 else if (i == 2) flags = token;
4375 else if (i == 7) link_status = token;
4376 else if (i == 8) break;
4377 i++;
4378 }
4379 if (i == 7) link_status = line;
4380 if (nodename == NULL((void*)0) || addr == NULL((void*)0) || flags == NULL((void*)0) ||
4381 link_status == NULL((void*)0)) continue;
4382 if (strstr(flags, "myself") != NULL((void*)0)) continue;
4383 int disconnected = ((strstr(flags, "disconnected") != NULL((void*)0)) ||
4384 (strstr(link_status, "disconnected")));
4385 int handshaking = (strstr(flags, "handshake") != NULL((void*)0));
4386 if (disconnected || handshaking) {
4387 clusterManagerLink *link = zmalloc(sizeof(*link));
4388 link->node_name = sdsnewhi_sdsnew(nodename);
4389 link->node_addr = sdsnewhi_sdsnew(addr);
4390 link->connected = 0;
4391 link->handshaking = handshaking;
4392 listAddNodeTail(links, link);
4393 }
4394 }
4395cleanup:
4396 if (reply != NULL((void*)0)) freeReplyObject(reply);
4397 return links;
4398}
4399
4400/* Check for disconnected cluster links. It returns a dict whose keys
4401 * are the unreachable node addresses and the values are lists of
4402 * node addresses that cannot reach the unreachable node. */
4403static dict *clusterManagerGetLinkStatus(void) {
4404 if (cluster_manager.nodes == NULL((void*)0)) return NULL((void*)0);
4405 dict *status = dictCreate(&clusterManagerLinkDictType, NULL((void*)0));
4406 listIter li;
4407 listNode *ln;
4408 listRewind(cluster_manager.nodes, &li);
4409 while ((ln = listNext(&li)) != NULL((void*)0)) {
4410 clusterManagerNode *node = ln->value;
4411 list *links = clusterManagerGetDisconnectedLinks(node);
4412 if (links) {
4413 listIter lli;
4414 listNode *lln;
4415 listRewind(links, &lli);
4416 while ((lln = listNext(&lli)) != NULL((void*)0)) {
4417 clusterManagerLink *link = lln->value;
4418 list *from = NULL((void*)0);
4419 dictEntry *entry = dictFind(status, link->node_addr);
4420 if (entry) from = dictGetVal(entry)((entry)->v.val);
4421 else {
4422 from = listCreate();
4423 dictAdd(status, sdsduphi_sdsdup(link->node_addr), from);
4424 }
4425 sdshisds myaddr = sdsemptyhi_sdsempty();
4426 myaddr = sdscatfmthi_sdscatfmt(myaddr, "%s:%u", node->ip, node->port);
4427 listAddNodeTail(from, myaddr);
4428 sdsfreehi_sdsfree(link->node_name);
4429 sdsfreehi_sdsfree(link->node_addr);
4430 zfree(link);
4431 }
4432 listRelease(links);
4433 }
4434 }
4435 return status;
4436}
4437
4438/* Add the error string to cluster_manager.errors and print it. */
4439static void clusterManagerOnError(sdshisds err) {
4440 if (cluster_manager.errors == NULL((void*)0))
4441 cluster_manager.errors = listCreate();
4442 listAddNodeTail(cluster_manager.errors, err);
4443 clusterManagerLogErr("%s\n", (char *) err)clusterManagerLog(3,"%s\n", (char *) err);
4444}
4445
4446/* Check the slots coverage of the cluster. The 'all_slots' argument must be
4447 * and array of 16384 bytes. Every covered slot will be set to 1 in the
4448 * 'all_slots' array. The function returns the total number if covered slots.*/
4449static int clusterManagerGetCoveredSlots(char *all_slots) {
4450 if (cluster_manager.nodes == NULL((void*)0)) return 0;
4451 listIter li;
4452 listNode *ln;
4453 listRewind(cluster_manager.nodes, &li);
4454 int totslots = 0, i;
4455 while ((ln = listNext(&li)) != NULL((void*)0)) {
4456 clusterManagerNode *node = ln->value;
4457 for (i = 0; i < CLUSTER_MANAGER_SLOTS16384; i++) {
4458 if (node->slots[i] && !all_slots[i]) {
4459 all_slots[i] = 1;
4460 totslots++;
4461 }
4462 }
4463 }
4464 return totslots;
4465}
4466
4467static void clusterManagerPrintSlotsList(list *slots) {
4468 clusterManagerNode n = {0};
4469 listIter li;
4470 listNode *ln;
4471 listRewind(slots, &li);
4472 while ((ln = listNext(&li)) != NULL((void*)0)) {
4473 int slot = atoi(ln->value);
4474 if (slot >= 0 && slot < CLUSTER_MANAGER_SLOTS16384)
4475 n.slots[slot] = 1;
4476 }
4477 sdshisds nodeslist = clusterManagerNodeSlotsString(&n);
4478 printf("%s\n", nodeslist);
4479 sdsfreehi_sdsfree(nodeslist);
4480}
4481
4482/* Return the node, among 'nodes' with the greatest number of keys
4483 * in the specified slot. */
4484static clusterManagerNode * clusterManagerGetNodeWithMostKeysInSlot(list *nodes,
4485 int slot,
4486 char **err)
4487{
4488 clusterManagerNode *node = NULL((void*)0);
4489 int numkeys = 0;
4490 listIter li;
4491 listNode *ln;
4492 listRewind(nodes, &li);
4493 if (err) *err = NULL((void*)0);
4494 while ((ln = listNext(&li)) != NULL((void*)0)) {
4495 clusterManagerNode *n = ln->value;
4496 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1 || n->replicate)
4497 continue;
4498 redisReply *r =
4499 CLUSTER_MANAGER_COMMAND(n, "CLUSTER COUNTKEYSINSLOT %d", slot)(redisCommand(n->context, "CLUSTER COUNTKEYSINSLOT %d", slot
))
;
4500 int success = clusterManagerCheckRedisReply(n, r, err);
4501 if (success) {
4502 if (r->integer > numkeys || node == NULL((void*)0)) {
4503 numkeys = r->integer;
4504 node = n;
4505 }
4506 }
4507 if (r != NULL((void*)0)) freeReplyObject(r);
4508 /* If the reply contains errors */
4509 if (!success) {
4510 if (err != NULL((void*)0) && *err != NULL((void*)0))
4511 CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", n
->ip, n->port, err);
;
4512 node = NULL((void*)0);
4513 break;
4514 }
4515 }
4516 return node;
4517}
4518
4519/* This function returns the master that has the least number of replicas
4520 * in the cluster. If there are multiple masters with the same smaller
4521 * number of replicas, one at random is returned. */
4522
4523static clusterManagerNode *clusterManagerNodeWithLeastReplicas() {
4524 clusterManagerNode *node = NULL((void*)0);
4525 int lowest_count = 0;
4526 listIter li;
4527 listNode *ln;
4528 listRewind(cluster_manager.nodes, &li);
4529 while ((ln = listNext(&li)) != NULL((void*)0)) {
4530 clusterManagerNode *n = ln->value;
4531 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
4532 if (node == NULL((void*)0) || n->replicas_count < lowest_count) {
4533 node = n;
4534 lowest_count = n->replicas_count;
4535 }
4536 }
4537 return node;
4538}
4539
4540/* This function returns a random master node, return NULL if none */
4541
4542static clusterManagerNode *clusterManagerNodeMasterRandom() {
4543 int master_count = 0;
4544 int idx;
4545 listIter li;
4546 listNode *ln;
4547 listRewind(cluster_manager.nodes, &li);
4548 while ((ln = listNext(&li)) != NULL((void*)0)) {
4549 clusterManagerNode *n = ln->value;
4550 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
4551 master_count++;
4552 }
4553
4554 srand(time(NULL((void*)0)));
4555 idx = rand() % master_count;
4556 listRewind(cluster_manager.nodes, &li);
4557 while ((ln = listNext(&li)) != NULL((void*)0)) {
4558 clusterManagerNode *n = ln->value;
4559 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
4560 if (!idx--) {
4561 return n;
4562 }
4563 }
4564 /* Can not be reached */
4565 return NULL((void*)0);
4566}
4567
4568static int clusterManagerFixSlotsCoverage(char *all_slots) {
4569 int force_fix = config.cluster_manager_command.flags &
4570 CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS1 << 10;
4571
4572 if (cluster_manager.unreachable_masters > 0 && !force_fix) {
4573 clusterManagerLogWarn("*** Fixing slots coverage with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\n", cluster_manager.unreachable_masters)clusterManagerLog(2,"*** Fixing slots coverage with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\n"
, cluster_manager.unreachable_masters)
;
4574 exit(1);
4575 }
4576
4577 int i, fixed = 0;
4578 list *none = NULL((void*)0), *single = NULL((void*)0), *multi = NULL((void*)0);
4579 clusterManagerLogInfo(">>> Fixing slots coverage...\n")clusterManagerLog(1,">>> Fixing slots coverage...\n"
)
;
4580 for (i = 0; i < CLUSTER_MANAGER_SLOTS16384; i++) {
4581 int covered = all_slots[i];
4582 if (!covered) {
4583 sdshisds slot = sdsfromlonglonghi_sdsfromlonglong((long long) i);
4584 list *slot_nodes = listCreate();
4585 sdshisds slot_nodes_str = sdsemptyhi_sdsempty();
4586 listIter li;
4587 listNode *ln;
4588 listRewind(cluster_manager.nodes, &li);
4589 while ((ln = listNext(&li)) != NULL((void*)0)) {
4590 clusterManagerNode *n = ln->value;
4591 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1 || n->replicate)
4592 continue;
4593 redisReply *reply = CLUSTER_MANAGER_COMMAND(n,(redisCommand(n->context, "CLUSTER GETKEYSINSLOT %d %d", i
, 1))
4594 "CLUSTER GETKEYSINSLOT %d %d", i, 1)(redisCommand(n->context, "CLUSTER GETKEYSINSLOT %d %d", i
, 1))
;
4595 if (!clusterManagerCheckRedisReply(n, reply, NULL((void*)0))) {
4596 fixed = -1;
4597 if (reply) freeReplyObject(reply);
4598 goto cleanup;
4599 }
4600 assert(reply->type == REDIS_REPLY_ARRAY)((reply->type == 2) ? (void) (0) : __assert_fail ("reply->type == REDIS_REPLY_ARRAY"
, "redis-cli.c", 4600, __extension__ __PRETTY_FUNCTION__))
;
4601 if (reply->elements > 0) {
4602 listAddNodeTail(slot_nodes, n);
4603 if (listLength(slot_nodes)((slot_nodes)->len) > 1)
4604 slot_nodes_str = sdscathi_sdscat(slot_nodes_str, ", ");
4605 slot_nodes_str = sdscatfmthi_sdscatfmt(slot_nodes_str,
4606 "%s:%u", n->ip, n->port);
4607 }
4608 freeReplyObject(reply);
4609 }
4610 sdsfreehi_sdsfree(slot_nodes_str);
4611 dictAdd(clusterManagerUncoveredSlots, slot, slot_nodes);
4612 }
4613 }
4614
4615 /* For every slot, take action depending on the actual condition:
4616 * 1) No node has keys for this slot.
4617 * 2) A single node has keys for this slot.
4618 * 3) Multiple nodes have keys for this slot. */
4619 none = listCreate();
4620 single = listCreate();
4621 multi = listCreate();
4622 dictIterator *iter = dictGetIterator(clusterManagerUncoveredSlots);
4623 dictEntry *entry;
4624 while ((entry = dictNext(iter)) != NULL((void*)0)) {
4625 sdshisds slot = (sdshisds) dictGetKey(entry)((entry)->key);
4626 list *nodes = (list *) dictGetVal(entry)((entry)->v.val);
4627 switch (listLength(nodes)((nodes)->len)){
4628 case 0: listAddNodeTail(none, slot); break;
4629 case 1: listAddNodeTail(single, slot); break;
4630 default: listAddNodeTail(multi, slot); break;
4631 }
4632 }
4633 dictReleaseIterator(iter);
4634
4635 /* we want explicit manual confirmation from users for all the fix cases */
4636 int ignore_force = 1;
4637
4638 /* Handle case "1": keys in no node. */
4639 if (listLength(none)((none)->len) > 0) {
4640 printf("The following uncovered slots have no keys "
4641 "across the cluster:\n");
4642 clusterManagerPrintSlotsList(none);
4643 if (confirmWithYes("Fix these slots by covering with a random node?",
4644 ignore_force)) {
4645 listIter li;
4646 listNode *ln;
4647 listRewind(none, &li);
4648 while ((ln = listNext(&li)) != NULL((void*)0)) {
4649 sdshisds slot = ln->value;
4650 int s = atoi(slot);
4651 clusterManagerNode *n = clusterManagerNodeMasterRandom();
4652 clusterManagerLogInfo(">>> Covering slot %s with %s:%d\n",clusterManagerLog(1,">>> Covering slot %s with %s:%d\n"
, slot, n->ip, n->port)
4653 slot, n->ip, n->port)clusterManagerLog(1,">>> Covering slot %s with %s:%d\n"
, slot, n->ip, n->port)
;
4654 if (!clusterManagerSetSlotOwner(n, s, 0)) {
4655 fixed = -1;
4656 goto cleanup;
4657 }
4658 /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
4659 * info into the node struct, in order to keep it synced */
4660 n->slots[s] = 1;
4661 fixed++;
4662 }
4663 }
4664 }
4665
4666 /* Handle case "2": keys only in one node. */
4667 if (listLength(single)((single)->len) > 0) {
4668 printf("The following uncovered slots have keys in just one node:\n");
4669 clusterManagerPrintSlotsList(single);
4670 if (confirmWithYes("Fix these slots by covering with those nodes?",
4671 ignore_force)) {
4672 listIter li;
4673 listNode *ln;
4674 listRewind(single, &li);
4675 while ((ln = listNext(&li)) != NULL((void*)0)) {
4676 sdshisds slot = ln->value;
4677 int s = atoi(slot);
4678 dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);
4679 assert(entry != NULL)((entry != ((void*)0)) ? (void) (0) : __assert_fail ("entry != NULL"
, "redis-cli.c", 4679, __extension__ __PRETTY_FUNCTION__))
;
4680 list *nodes = (list *) dictGetVal(entry)((entry)->v.val);
4681 listNode *fn = listFirst(nodes)((nodes)->head);
4682 assert(fn != NULL)((fn != ((void*)0)) ? (void) (0) : __assert_fail ("fn != NULL"
, "redis-cli.c", 4682, __extension__ __PRETTY_FUNCTION__))
;
4683 clusterManagerNode *n = fn->value;
4684 clusterManagerLogInfo(">>> Covering slot %s with %s:%d\n",clusterManagerLog(1,">>> Covering slot %s with %s:%d\n"
, slot, n->ip, n->port)
4685 slot, n->ip, n->port)clusterManagerLog(1,">>> Covering slot %s with %s:%d\n"
, slot, n->ip, n->port)
;
4686 if (!clusterManagerSetSlotOwner(n, s, 0)) {
4687 fixed = -1;
4688 goto cleanup;
4689 }
4690 /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
4691 * info into the node struct, in order to keep it synced */
4692 n->slots[atoi(slot)] = 1;
4693 fixed++;
4694 }
4695 }
4696 }
4697
4698 /* Handle case "3": keys in multiple nodes. */
4699 if (listLength(multi)((multi)->len) > 0) {
4700 printf("The following uncovered slots have keys in multiple nodes:\n");
4701 clusterManagerPrintSlotsList(multi);
4702 if (confirmWithYes("Fix these slots by moving keys "
4703 "into a single node?", ignore_force)) {
4704 listIter li;
4705 listNode *ln;
4706 listRewind(multi, &li);
4707 while ((ln = listNext(&li)) != NULL((void*)0)) {
4708 sdshisds slot = ln->value;
4709 dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);
4710 assert(entry != NULL)((entry != ((void*)0)) ? (void) (0) : __assert_fail ("entry != NULL"
, "redis-cli.c", 4710, __extension__ __PRETTY_FUNCTION__))
;
4711 list *nodes = (list *) dictGetVal(entry)((entry)->v.val);
4712 int s = atoi(slot);
4713 clusterManagerNode *target =
4714 clusterManagerGetNodeWithMostKeysInSlot(nodes, s, NULL((void*)0));
4715 if (target == NULL((void*)0)) {
4716 fixed = -1;
4717 goto cleanup;
4718 }
4719 clusterManagerLogInfo(">>> Covering slot %s moving keys "clusterManagerLog(1,">>> Covering slot %s moving keys "
"to %s:%d\n", slot, target->ip, target->port)
4720 "to %s:%d\n", slot,clusterManagerLog(1,">>> Covering slot %s moving keys "
"to %s:%d\n", slot, target->ip, target->port)
4721 target->ip, target->port)clusterManagerLog(1,">>> Covering slot %s moving keys "
"to %s:%d\n", slot, target->ip, target->port)
;
4722 if (!clusterManagerSetSlotOwner(target, s, 1)) {
4723 fixed = -1;
4724 goto cleanup;
4725 }
4726 /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
4727 * info into the node struct, in order to keep it synced */
4728 target->slots[atoi(slot)] = 1;
4729 listIter nli;
4730 listNode *nln;
4731 listRewind(nodes, &nli);
4732 while ((nln = listNext(&nli)) != NULL((void*)0)) {
4733 clusterManagerNode *src = nln->value;
4734 if (src == target) continue;
4735 /* Assign the slot to target node in the source node. */
4736 if (!clusterManagerSetSlot(src, target, s, "NODE", NULL((void*)0)))
4737 fixed = -1;
4738 if (fixed < 0) goto cleanup;
4739 /* Set the source node in 'importing' state
4740 * (even if we will actually migrate keys away)
4741 * in order to avoid receiving redirections
4742 * for MIGRATE. */
4743 if (!clusterManagerSetSlot(src, target, s,
4744 "IMPORTING", NULL((void*)0))) fixed = -1;
4745 if (fixed < 0) goto cleanup;
4746 int opts = CLUSTER_MANAGER_OPT_VERBOSE1 << 7 |
4747 CLUSTER_MANAGER_OPT_COLD1 << 1;
4748 if (!clusterManagerMoveSlot(src, target, s, opts, NULL((void*)0))) {
4749 fixed = -1;
4750 goto cleanup;
4751 }
4752 if (!clusterManagerClearSlotStatus(src, s))
4753 fixed = -1;
4754 if (fixed < 0) goto cleanup;
4755 }
4756 fixed++;
4757 }
4758 }
4759 }
4760cleanup:
4761 if (none) listRelease(none);
4762 if (single) listRelease(single);
4763 if (multi) listRelease(multi);
4764 return fixed;
4765}
4766
4767/* Slot 'slot' was found to be in importing or migrating state in one or
4768 * more nodes. This function fixes this condition by migrating keys where
4769 * it seems more sensible. */
4770static int clusterManagerFixOpenSlot(int slot) {
4771 int force_fix = config.cluster_manager_command.flags &
4772 CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS1 << 10;
4773
4774 if (cluster_manager.unreachable_masters > 0 && !force_fix) {
4775 clusterManagerLogWarn("*** Fixing open slots with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\n", cluster_manager.unreachable_masters)clusterManagerLog(2,"*** Fixing open slots with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\n"
, cluster_manager.unreachable_masters)
;
4776 exit(1);
4777 }
4778
4779 clusterManagerLogInfo(">>> Fixing open slot %d\n", slot)clusterManagerLog(1,">>> Fixing open slot %d\n", slot
)
;
4780 /* Try to obtain the current slot owner, according to the current
4781 * nodes configuration. */
4782 int success = 1;
4783 list *owners = listCreate(); /* List of nodes claiming some ownership.
4784 it could be stating in the configuration
4785 to have the node ownership, or just
4786 holding keys for such slot. */
4787 list *migrating = listCreate();
4788 list *importing = listCreate();
4789 sdshisds migrating_str = sdsemptyhi_sdsempty();
4790 sdshisds importing_str = sdsemptyhi_sdsempty();
4791 clusterManagerNode *owner = NULL((void*)0); /* The obvious slot owner if any. */
4792
4793 /* Iterate all the nodes, looking for potential owners of this slot. */
4794 listIter li;
4795 listNode *ln;
4796 listRewind(cluster_manager.nodes, &li);
4797 while ((ln = listNext(&li)) != NULL((void*)0)) {
4798 clusterManagerNode *n = ln->value;
4799 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
4800 if (n->slots[slot]) {
4801 listAddNodeTail(owners, n);
4802 } else {
4803 redisReply *r = CLUSTER_MANAGER_COMMAND(n,(redisCommand(n->context, "CLUSTER COUNTKEYSINSLOT %d", slot
))
4804 "CLUSTER COUNTKEYSINSLOT %d", slot)(redisCommand(n->context, "CLUSTER COUNTKEYSINSLOT %d", slot
))
;
4805 success = clusterManagerCheckRedisReply(n, r, NULL((void*)0));
4806 if (success && r->integer > 0) {
4807 clusterManagerLogWarn("*** Found keys about slot %d "clusterManagerLog(2,"*** Found keys about slot %d " "in non-owner node %s:%d!\n"
, slot, n->ip, n->port)
4808 "in non-owner node %s:%d!\n", slot,clusterManagerLog(2,"*** Found keys about slot %d " "in non-owner node %s:%d!\n"
, slot, n->ip, n->port)
4809 n->ip, n->port)clusterManagerLog(2,"*** Found keys about slot %d " "in non-owner node %s:%d!\n"
, slot, n->ip, n->port)
;
4810 listAddNodeTail(owners, n);
4811 }
4812 if (r) freeReplyObject(r);
4813 if (!success) goto cleanup;
4814 }
4815 }
4816
4817 /* If we have only a single potential owner for this slot,
4818 * set it as "owner". */
4819 if (listLength(owners)((owners)->len) == 1) owner = listFirst(owners)((owners)->head)->value;
4820
4821 /* Scan the list of nodes again, in order to populate the
4822 * list of nodes in importing or migrating state for
4823 * this slot. */
4824 listRewind(cluster_manager.nodes, &li);
4825 while ((ln = listNext(&li)) != NULL((void*)0)) {
4826 clusterManagerNode *n = ln->value;
4827 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
4828 int is_migrating = 0, is_importing = 0;
4829 if (n->migrating) {
4830 for (int i = 0; i < n->migrating_count; i += 2) {
4831 sdshisds migrating_slot = n->migrating[i];
4832 if (atoi(migrating_slot) == slot) {
4833 char *sep = (listLength(migrating)((migrating)->len) == 0 ? "" : ",");
4834 migrating_str = sdscatfmthi_sdscatfmt(migrating_str, "%s%s:%u",
4835 sep, n->ip, n->port);
4836 listAddNodeTail(migrating, n);
4837 is_migrating = 1;
4838 break;
4839 }
4840 }
4841 }
4842 if (!is_migrating && n->importing) {
4843 for (int i = 0; i < n->importing_count; i += 2) {
4844 sdshisds importing_slot = n->importing[i];
4845 if (atoi(importing_slot) == slot) {
4846 char *sep = (listLength(importing)((importing)->len) == 0 ? "" : ",");
4847 importing_str = sdscatfmthi_sdscatfmt(importing_str, "%s%s:%u",
4848 sep, n->ip, n->port);
4849 listAddNodeTail(importing, n);
4850 is_importing = 1;
4851 break;
4852 }
4853 }
4854 }
4855
4856 /* If the node is neither migrating nor importing and it's not
4857 * the owner, then is added to the importing list in case
4858 * it has keys in the slot. */
4859 if (!is_migrating && !is_importing && n != owner) {
4860 redisReply *r = CLUSTER_MANAGER_COMMAND(n,(redisCommand(n->context, "CLUSTER COUNTKEYSINSLOT %d", slot
))
4861 "CLUSTER COUNTKEYSINSLOT %d", slot)(redisCommand(n->context, "CLUSTER COUNTKEYSINSLOT %d", slot
))
;
4862 success = clusterManagerCheckRedisReply(n, r, NULL((void*)0));
4863 if (success && r->integer > 0) {
4864 clusterManagerLogWarn("*** Found keys about slot %d "clusterManagerLog(2,"*** Found keys about slot %d " "in node %s:%d!\n"
, slot, n->ip, n->port)
4865 "in node %s:%d!\n", slot, n->ip,clusterManagerLog(2,"*** Found keys about slot %d " "in node %s:%d!\n"
, slot, n->ip, n->port)
4866 n->port)clusterManagerLog(2,"*** Found keys about slot %d " "in node %s:%d!\n"
, slot, n->ip, n->port)
;
4867 char *sep = (listLength(importing)((importing)->len) == 0 ? "" : ",");
4868 importing_str = sdscatfmthi_sdscatfmt(importing_str, "%s%S:%u",
4869 sep, n->ip, n->port);
4870 listAddNodeTail(importing, n);
4871 }
4872 if (r) freeReplyObject(r);
4873 if (!success) goto cleanup;
4874 }
4875 }
4876 if (sdslenhi_sdslen(migrating_str) > 0)
4877 printf("Set as migrating in: %s\n", migrating_str);
4878 if (sdslenhi_sdslen(importing_str) > 0)
4879 printf("Set as importing in: %s\n", importing_str);
4880
4881 /* If there is no slot owner, set as owner the node with the biggest
4882 * number of keys, among the set of migrating / importing nodes. */
4883 if (owner == NULL((void*)0)) {
4884 clusterManagerLogInfo(">>> No single clear owner for the slot, "clusterManagerLog(1,">>> No single clear owner for the slot, "
"selecting an owner by # of keys...\n")
4885 "selecting an owner by # of keys...\n")clusterManagerLog(1,">>> No single clear owner for the slot, "
"selecting an owner by # of keys...\n")
;
4886 owner = clusterManagerGetNodeWithMostKeysInSlot(cluster_manager.nodes,
4887 slot, NULL((void*)0));
4888 // If we still don't have an owner, we can't fix it.
4889 if (owner == NULL((void*)0)) {
4890 clusterManagerLogErr("[ERR] Can't select a slot owner. "clusterManagerLog(3,"[ERR] Can't select a slot owner. " "Impossible to fix.\n"
)
4891 "Impossible to fix.\n")clusterManagerLog(3,"[ERR] Can't select a slot owner. " "Impossible to fix.\n"
)
;
4892 success = 0;
4893 goto cleanup;
4894 }
4895
4896 // Use ADDSLOTS to assign the slot.
4897 clusterManagerLogWarn("*** Configuring %s:%d as the slot owner\n",clusterManagerLog(2,"*** Configuring %s:%d as the slot owner\n"
, owner->ip, owner->port)
4898 owner->ip, owner->port)clusterManagerLog(2,"*** Configuring %s:%d as the slot owner\n"
, owner->ip, owner->port)
;
4899 success = clusterManagerClearSlotStatus(owner, slot);
4900 if (!success) goto cleanup;
4901 success = clusterManagerSetSlotOwner(owner, slot, 0);
4902 if (!success) goto cleanup;
4903 /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
4904 * info into the node struct, in order to keep it synced */
4905 owner->slots[slot] = 1;
4906 /* Make sure this information will propagate. Not strictly needed
4907 * since there is no past owner, so all the other nodes will accept
4908 * whatever epoch this node will claim the slot with. */
4909 success = clusterManagerBumpEpoch(owner);
4910 if (!success) goto cleanup;
4911 /* Remove the owner from the list of migrating/importing
4912 * nodes. */
4913 clusterManagerRemoveNodeFromList(migrating, owner);
4914 clusterManagerRemoveNodeFromList(importing, owner);
4915 }
4916
4917 /* If there are multiple owners of the slot, we need to fix it
4918 * so that a single node is the owner and all the other nodes
4919 * are in importing state. Later the fix can be handled by one
4920 * of the base cases above.
4921 *
4922 * Note that this case also covers multiple nodes having the slot
4923 * in migrating state, since migrating is a valid state only for
4924 * slot owners. */
4925 if (listLength(owners)((owners)->len) > 1) {
4926 /* Owner cannot be NULL at this point, since if there are more owners,
4927 * the owner has been set in the previous condition (owner == NULL). */
4928 assert(owner != NULL)((owner != ((void*)0)) ? (void) (0) : __assert_fail ("owner != NULL"
, "redis-cli.c", 4928, __extension__ __PRETTY_FUNCTION__))
;
4929 listRewind(owners, &li);
4930 while ((ln = listNext(&li)) != NULL((void*)0)) {
4931 clusterManagerNode *n = ln->value;
4932 if (n == owner) continue;
4933 success = clusterManagerDelSlot(n, slot, 1);
4934 if (!success) goto cleanup;
4935 n->slots[slot] = 0;
4936 /* Assign the slot to the owner in the node 'n' configuration.' */
4937 success = clusterManagerSetSlot(n, owner, slot, "node", NULL((void*)0));
4938 if (!success) goto cleanup;
4939 success = clusterManagerSetSlot(n, owner, slot, "importing", NULL((void*)0));
4940 if (!success) goto cleanup;
4941 /* Avoid duplicates. */
4942 clusterManagerRemoveNodeFromList(importing, n);
4943 listAddNodeTail(importing, n);
4944 /* Ensure that the node is not in the migrating list. */
4945 clusterManagerRemoveNodeFromList(migrating, n);
4946 }
4947 }
4948 int move_opts = CLUSTER_MANAGER_OPT_VERBOSE1 << 7;
4949
4950 /* Case 1: The slot is in migrating state in one node, and in
4951 * importing state in 1 node. That's trivial to address. */
4952 if (listLength(migrating)((migrating)->len) == 1 && listLength(importing)((importing)->len) == 1) {
4953 clusterManagerNode *src = listFirst(migrating)((migrating)->head)->value;
4954 clusterManagerNode *dst = listFirst(importing)((importing)->head)->value;
4955 clusterManagerLogInfo(">>> Case 1: Moving slot %d from "clusterManagerLog(1,">>> Case 1: Moving slot %d from "
"%s:%d to %s:%d\n", slot, src->ip, src->port, dst->
ip, dst->port)
4956 "%s:%d to %s:%d\n", slot,clusterManagerLog(1,">>> Case 1: Moving slot %d from "
"%s:%d to %s:%d\n", slot, src->ip, src->port, dst->
ip, dst->port)
4957 src->ip, src->port, dst->ip, dst->port)clusterManagerLog(1,">>> Case 1: Moving slot %d from "
"%s:%d to %s:%d\n", slot, src->ip, src->port, dst->
ip, dst->port)
;
4958 move_opts |= CLUSTER_MANAGER_OPT_UPDATE1 << 2;
4959 success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL((void*)0));
4960 }
4961
4962 /* Case 2: There are multiple nodes that claim the slot as importing,
4963 * they probably got keys about the slot after a restart so opened
4964 * the slot. In this case we just move all the keys to the owner
4965 * according to the configuration. */
4966 else if (listLength(migrating)((migrating)->len) == 0 && listLength(importing)((importing)->len) > 0) {
4967 clusterManagerLogInfo(">>> Case 2: Moving all the %d slot keys to its "clusterManagerLog(1,">>> Case 2: Moving all the %d slot keys to its "
"owner %s:%d\n", slot, owner->ip, owner->port)
4968 "owner %s:%d\n", slot, owner->ip, owner->port)clusterManagerLog(1,">>> Case 2: Moving all the %d slot keys to its "
"owner %s:%d\n", slot, owner->ip, owner->port)
;
4969 move_opts |= CLUSTER_MANAGER_OPT_COLD1 << 1;
4970 listRewind(importing, &li);
4971 while ((ln = listNext(&li)) != NULL((void*)0)) {
4972 clusterManagerNode *n = ln->value;
4973 if (n == owner) continue;
4974 success = clusterManagerMoveSlot(n, owner, slot, move_opts, NULL((void*)0));
4975 if (!success) goto cleanup;
4976 clusterManagerLogInfo(">>> Setting %d as STABLE in "clusterManagerLog(1,">>> Setting %d as STABLE in " "%s:%d\n"
, slot, n->ip, n->port)
4977 "%s:%d\n", slot, n->ip, n->port)clusterManagerLog(1,">>> Setting %d as STABLE in " "%s:%d\n"
, slot, n->ip, n->port)
;
4978 success = clusterManagerClearSlotStatus(n, slot);
4979 if (!success) goto cleanup;
4980 }
4981 /* Since the slot has been moved in "cold" mode, ensure that all the
4982 * other nodes update their own configuration about the slot itself. */
4983 listRewind(cluster_manager.nodes, &li);
4984 while ((ln = listNext(&li)) != NULL((void*)0)) {
4985 clusterManagerNode *n = ln->value;
4986 if (n == owner) continue;
4987 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
4988 success = clusterManagerSetSlot(n, owner, slot, "NODE", NULL((void*)0));
4989 if (!success) goto cleanup;
4990 }
4991 }
4992
4993 /* Case 3: The slot is in migrating state in one node but multiple
4994 * other nodes claim to be in importing state and don't have any key in
4995 * the slot. We search for the importing node having the same ID as
4996 * the destination node of the migrating node.
4997 * In that case we move the slot from the migrating node to this node and
4998 * we close the importing states on all the other importing nodes.
4999 * If no importing node has the same ID as the destination node of the
5000 * migrating node, the slot's state is closed on both the migrating node
5001 * and the importing nodes. */
5002 else if (listLength(migrating)((migrating)->len) == 1 && listLength(importing)((importing)->len) > 1) {
5003 int try_to_fix = 1;
5004 clusterManagerNode *src = listFirst(migrating)((migrating)->head)->value;
5005 clusterManagerNode *dst = NULL((void*)0);
5006 sdshisds target_id = NULL((void*)0);
5007 for (int i = 0; i < src->migrating_count; i += 2) {
5008 sdshisds migrating_slot = src->migrating[i];
5009 if (atoi(migrating_slot) == slot) {
5010 target_id = src->migrating[i + 1];
5011 break;
5012 }
5013 }
5014 assert(target_id != NULL)((target_id != ((void*)0)) ? (void) (0) : __assert_fail ("target_id != NULL"
, "redis-cli.c", 5014, __extension__ __PRETTY_FUNCTION__))
;
5015 listIter li;
5016 listNode *ln;
5017 listRewind(importing, &li);
5018 while ((ln = listNext(&li)) != NULL((void*)0)) {
5019 clusterManagerNode *n = ln->value;
5020 int count = clusterManagerCountKeysInSlot(n, slot);
5021 if (count > 0) {
5022 try_to_fix = 0;
5023 break;
5024 }
5025 if (strcmp(n->name, target_id) == 0) dst = n;
5026 }
5027 if (!try_to_fix) goto unhandled_case;
5028 if (dst != NULL((void*)0)) {
5029 clusterManagerLogInfo(">>> Case 3: Moving slot %d from %s:%d to "clusterManagerLog(1,">>> Case 3: Moving slot %d from %s:%d to "
"%s:%d and closing it on all the other " "importing nodes.\n"
, slot, src->ip, src->port, dst->ip, dst->port)
5030 "%s:%d and closing it on all the other "clusterManagerLog(1,">>> Case 3: Moving slot %d from %s:%d to "
"%s:%d and closing it on all the other " "importing nodes.\n"
, slot, src->ip, src->port, dst->ip, dst->port)
5031 "importing nodes.\n",clusterManagerLog(1,">>> Case 3: Moving slot %d from %s:%d to "
"%s:%d and closing it on all the other " "importing nodes.\n"
, slot, src->ip, src->port, dst->ip, dst->port)
5032 slot, src->ip, src->port,clusterManagerLog(1,">>> Case 3: Moving slot %d from %s:%d to "
"%s:%d and closing it on all the other " "importing nodes.\n"
, slot, src->ip, src->port, dst->ip, dst->port)
5033 dst->ip, dst->port)clusterManagerLog(1,">>> Case 3: Moving slot %d from %s:%d to "
"%s:%d and closing it on all the other " "importing nodes.\n"
, slot, src->ip, src->port, dst->ip, dst->port)
;
5034 /* Move the slot to the destination node. */
5035 success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL((void*)0));
5036 if (!success) goto cleanup;
5037 /* Close slot on all the other importing nodes. */
5038 listRewind(importing, &li);
5039 while ((ln = listNext(&li)) != NULL((void*)0)) {
5040 clusterManagerNode *n = ln->value;
5041 if (dst == n) continue;
5042 success = clusterManagerClearSlotStatus(n, slot);
5043 if (!success) goto cleanup;
5044 }
5045 } else {
5046 clusterManagerLogInfo(">>> Case 3: Closing slot %d on both "clusterManagerLog(1,">>> Case 3: Closing slot %d on both "
"migrating and importing nodes.\n", slot)
5047 "migrating and importing nodes.\n", slot)clusterManagerLog(1,">>> Case 3: Closing slot %d on both "
"migrating and importing nodes.\n", slot)
;
5048 /* Close the slot on both the migrating node and the importing
5049 * nodes. */
5050 success = clusterManagerClearSlotStatus(src, slot);
5051 if (!success) goto cleanup;
5052 listRewind(importing, &li);
5053 while ((ln = listNext(&li)) != NULL((void*)0)) {
5054 clusterManagerNode *n = ln->value;
5055 success = clusterManagerClearSlotStatus(n, slot);
5056 if (!success) goto cleanup;
5057 }
5058 }
5059 } else {
5060 int try_to_close_slot = (listLength(importing)((importing)->len) == 0 &&
5061 listLength(migrating)((migrating)->len) == 1);
5062 if (try_to_close_slot) {
5063 clusterManagerNode *n = listFirst(migrating)((migrating)->head)->value;
5064 if (!owner || owner != n) {
5065 redisReply *r = CLUSTER_MANAGER_COMMAND(n,(redisCommand(n->context, "CLUSTER GETKEYSINSLOT %d %d", slot
, 10))
5066 "CLUSTER GETKEYSINSLOT %d %d", slot, 10)(redisCommand(n->context, "CLUSTER GETKEYSINSLOT %d %d", slot
, 10))
;
5067 success = clusterManagerCheckRedisReply(n, r, NULL((void*)0));
5068 if (r) {
5069 if (success) try_to_close_slot = (r->elements == 0);
5070 freeReplyObject(r);
5071 }
5072 if (!success) goto cleanup;
5073 }
5074 }
5075 /* Case 4: There are no slots claiming to be in importing state, but
5076 * there is a migrating node that actually don't have any key or is the
5077 * slot owner. We can just close the slot, probably a reshard
5078 * interrupted in the middle. */
5079 if (try_to_close_slot) {
5080 clusterManagerNode *n = listFirst(migrating)((migrating)->head)->value;
5081 clusterManagerLogInfo(">>> Case 4: Closing slot %d on %s:%d\n",clusterManagerLog(1,">>> Case 4: Closing slot %d on %s:%d\n"
, slot, n->ip, n->port)
5082 slot, n->ip, n->port)clusterManagerLog(1,">>> Case 4: Closing slot %d on %s:%d\n"
, slot, n->ip, n->port)
;
5083 redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER SETSLOT %d %s",(redisCommand(n->context, "CLUSTER SETSLOT %d %s", slot, "STABLE"
))
5084 slot, "STABLE")(redisCommand(n->context, "CLUSTER SETSLOT %d %s", slot, "STABLE"
))
;
5085 success = clusterManagerCheckRedisReply(n, r, NULL((void*)0));
5086 if (r) freeReplyObject(r);
5087 if (!success) goto cleanup;
5088 } else {
5089unhandled_case:
5090 success = 0;
5091 clusterManagerLogErr("[ERR] Sorry, redis-cli can't fix this slot "clusterManagerLog(3,"[ERR] Sorry, redis-cli can't fix this slot "
"yet (work in progress). Slot is set as " "migrating in %s, as importing in %s, "
"owner is %s:%d\n", migrating_str, importing_str, owner->
ip, owner->port)
5092 "yet (work in progress). Slot is set as "clusterManagerLog(3,"[ERR] Sorry, redis-cli can't fix this slot "
"yet (work in progress). Slot is set as " "migrating in %s, as importing in %s, "
"owner is %s:%d\n", migrating_str, importing_str, owner->
ip, owner->port)
5093 "migrating in %s, as importing in %s, "clusterManagerLog(3,"[ERR] Sorry, redis-cli can't fix this slot "
"yet (work in progress). Slot is set as " "migrating in %s, as importing in %s, "
"owner is %s:%d\n", migrating_str, importing_str, owner->
ip, owner->port)
5094 "owner is %s:%d\n", migrating_str,clusterManagerLog(3,"[ERR] Sorry, redis-cli can't fix this slot "
"yet (work in progress). Slot is set as " "migrating in %s, as importing in %s, "
"owner is %s:%d\n", migrating_str, importing_str, owner->
ip, owner->port)
5095 importing_str, owner->ip, owner->port)clusterManagerLog(3,"[ERR] Sorry, redis-cli can't fix this slot "
"yet (work in progress). Slot is set as " "migrating in %s, as importing in %s, "
"owner is %s:%d\n", migrating_str, importing_str, owner->
ip, owner->port)
;
5096 }
5097 }
5098cleanup:
5099 listRelease(owners);
5100 listRelease(migrating);
5101 listRelease(importing);
5102 sdsfreehi_sdsfree(migrating_str);
5103 sdsfreehi_sdsfree(importing_str);
5104 return success;
5105}
5106
5107static int clusterManagerFixMultipleSlotOwners(int slot, list *owners) {
5108 clusterManagerLogInfo(">>> Fixing multiple owners for slot %d...\n", slot)clusterManagerLog(1,">>> Fixing multiple owners for slot %d...\n"
, slot)
;
5109 int success = 0;
5110 assert(listLength(owners) > 1)((((owners)->len) > 1) ? (void) (0) : __assert_fail ("listLength(owners) > 1"
, "redis-cli.c", 5110, __extension__ __PRETTY_FUNCTION__))
;
5111 clusterManagerNode *owner = clusterManagerGetNodeWithMostKeysInSlot(owners,
5112 slot,
5113 NULL((void*)0));
5114 if (!owner) owner = listFirst(owners)((owners)->head)->value;
5115 clusterManagerLogInfo(">>> Setting slot %d owner: %s:%d\n",clusterManagerLog(1,">>> Setting slot %d owner: %s:%d\n"
, slot, owner->ip, owner->port)
5116 slot, owner->ip, owner->port)clusterManagerLog(1,">>> Setting slot %d owner: %s:%d\n"
, slot, owner->ip, owner->port)
;
5117 /* Set the slot owner. */
5118 if (!clusterManagerSetSlotOwner(owner, slot, 0)) return 0;
5119 listIter li;
5120 listNode *ln;
5121 listRewind(cluster_manager.nodes, &li);
5122 /* Update configuration in all the other master nodes by assigning the slot
5123 * itself to the new owner, and by eventually migrating keys if the node
5124 * has keys for the slot. */
5125 while ((ln = listNext(&li)) != NULL((void*)0)) {
5126 clusterManagerNode *n = ln->value;
5127 if (n == owner) continue;
5128 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
5129 int count = clusterManagerCountKeysInSlot(n, slot);
5130 success = (count >= 0);
5131 if (!success) break;
5132 clusterManagerDelSlot(n, slot, 1);
5133 if (!clusterManagerSetSlot(n, owner, slot, "node", NULL((void*)0))) return 0;
5134 if (count > 0) {
5135 int opts = CLUSTER_MANAGER_OPT_VERBOSE1 << 7 |
5136 CLUSTER_MANAGER_OPT_COLD1 << 1;
5137 success = clusterManagerMoveSlot(n, owner, slot, opts, NULL((void*)0));
5138 if (!success) break;
5139 }
5140 }
5141 return success;
5142}
5143
5144static int clusterManagerCheckCluster(int quiet) {
5145 listNode *ln = listFirst(cluster_manager.nodes)((cluster_manager.nodes)->head);
5146 if (!ln) return 0;
5147 clusterManagerNode *node = ln->value;
5148 clusterManagerLogInfo(">>> Performing Cluster Check (using node %s:%d)\n",clusterManagerLog(1,">>> Performing Cluster Check (using node %s:%d)\n"
, node->ip, node->port)
5149 node->ip, node->port)clusterManagerLog(1,">>> Performing Cluster Check (using node %s:%d)\n"
, node->ip, node->port)
;
5150 int result = 1, consistent = 0;
5151 int do_fix = config.cluster_manager_command.flags &
5152 CLUSTER_MANAGER_CMD_FLAG_FIX1 << 0;
5153 if (!quiet) clusterManagerShowNodes();
5154 consistent = clusterManagerIsConfigConsistent();
5155 if (!consistent) {
5156 sdshisds err = sdsnewhi_sdsnew("[ERR] Nodes don't agree about configuration!");
5157 clusterManagerOnError(err);
5158 result = 0;
5159 } else {
5160 clusterManagerLogOk("[OK] All nodes agree about slots "clusterManagerLog(4,"[OK] All nodes agree about slots " "configuration.\n"
)
5161 "configuration.\n")clusterManagerLog(4,"[OK] All nodes agree about slots " "configuration.\n"
)
;
5162 }
5163 /* Check open slots */
5164 clusterManagerLogInfo(">>> Check for open slots...\n")clusterManagerLog(1,">>> Check for open slots...\n");
5165 listIter li;
5166 listRewind(cluster_manager.nodes, &li);
5167 int i;
5168 dict *open_slots = NULL((void*)0);
5169 while ((ln = listNext(&li)) != NULL((void*)0)) {
5170 clusterManagerNode *n = ln->value;
5171 if (n->migrating != NULL((void*)0)) {
5172 if (open_slots == NULL((void*)0))
5173 open_slots = dictCreate(&clusterManagerDictType, NULL((void*)0));
5174 sdshisds errstr = sdsemptyhi_sdsempty();
5175 errstr = sdscatprintfhi_sdscatprintf(errstr,
5176 "[WARNING] Node %s:%d has slots in "
5177 "migrating state ",
5178 n->ip,
5179 n->port);
5180 for (i = 0; i < n->migrating_count; i += 2) {
5181 sdshisds slot = n->migrating[i];
5182 dictReplace(open_slots, slot, sdsduphi_sdsdup(n->migrating[i + 1]));
5183 char *fmt = (i > 0 ? ",%S" : "%S");
5184 errstr = sdscatfmthi_sdscatfmt(errstr, fmt, slot);
5185 }
5186 errstr = sdscathi_sdscat(errstr, ".");
5187 clusterManagerOnError(errstr);
5188 }
5189 if (n->importing != NULL((void*)0)) {
5190 if (open_slots == NULL((void*)0))
5191 open_slots = dictCreate(&clusterManagerDictType, NULL((void*)0));
5192 sdshisds errstr = sdsemptyhi_sdsempty();
5193 errstr = sdscatprintfhi_sdscatprintf(errstr,
5194 "[WARNING] Node %s:%d has slots in "
5195 "importing state ",
5196 n->ip,
5197 n->port);
5198 for (i = 0; i < n->importing_count; i += 2) {
5199 sdshisds slot = n->importing[i];
5200 dictReplace(open_slots, slot, sdsduphi_sdsdup(n->importing[i + 1]));
5201 char *fmt = (i > 0 ? ",%S" : "%S");
5202 errstr = sdscatfmthi_sdscatfmt(errstr, fmt, slot);
5203 }
5204 errstr = sdscathi_sdscat(errstr, ".");
5205 clusterManagerOnError(errstr);
5206 }
5207 }
5208 if (open_slots != NULL((void*)0)) {
5209 result = 0;
5210 dictIterator *iter = dictGetIterator(open_slots);
5211 dictEntry *entry;
5212 sdshisds errstr = sdsnewhi_sdsnew("[WARNING] The following slots are open: ");
5213 i = 0;
5214 while ((entry = dictNext(iter)) != NULL((void*)0)) {
5215 sdshisds slot = (sdshisds) dictGetKey(entry)((entry)->key);
5216 char *fmt = (i++ > 0 ? ",%S" : "%S");
5217 errstr = sdscatfmthi_sdscatfmt(errstr, fmt, slot);
5218 }
5219 clusterManagerLogErr("%s.\n", (char *) errstr)clusterManagerLog(3,"%s.\n", (char *) errstr);
5220 sdsfreehi_sdsfree(errstr);
5221 if (do_fix) {
5222 /* Fix open slots. */
5223 dictReleaseIterator(iter);
5224 iter = dictGetIterator(open_slots);
5225 while ((entry = dictNext(iter)) != NULL((void*)0)) {
5226 sdshisds slot = (sdshisds) dictGetKey(entry)((entry)->key);
5227 result = clusterManagerFixOpenSlot(atoi(slot));
5228 if (!result) break;
5229 }
5230 }
5231 dictReleaseIterator(iter);
5232 dictRelease(open_slots);
5233 }
5234 clusterManagerLogInfo(">>> Check slots coverage...\n")clusterManagerLog(1,">>> Check slots coverage...\n");
5235 char slots[CLUSTER_MANAGER_SLOTS16384];
5236 memset(slots, 0, CLUSTER_MANAGER_SLOTS16384);
5237 int coverage = clusterManagerGetCoveredSlots(slots);
5238 if (coverage == CLUSTER_MANAGER_SLOTS16384) {
5239 clusterManagerLogOk("[OK] All %d slots covered.\n",clusterManagerLog(4,"[OK] All %d slots covered.\n", 16384)
5240 CLUSTER_MANAGER_SLOTS)clusterManagerLog(4,"[OK] All %d slots covered.\n", 16384);
5241 } else {
5242 sdshisds err = sdsemptyhi_sdsempty();
5243 err = sdscatprintfhi_sdscatprintf(err, "[ERR] Not all %d slots are "
5244 "covered by nodes.\n",
5245 CLUSTER_MANAGER_SLOTS16384);
5246 clusterManagerOnError(err);
5247 result = 0;
5248 if (do_fix/* && result*/) {
5249 dictType dtype = clusterManagerDictType;
5250 dtype.keyDestructor = dictSdsDestructor;
5251 dtype.valDestructor = dictListDestructor;
5252 clusterManagerUncoveredSlots = dictCreate(&dtype, NULL((void*)0));
5253 int fixed = clusterManagerFixSlotsCoverage(slots);
5254 if (fixed > 0) result = 1;
5255 }
5256 }
5257 int search_multiple_owners = config.cluster_manager_command.flags &
5258 CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS1 << 9;
5259 if (search_multiple_owners) {
5260 /* Check whether there are multiple owners, even when slots are
5261 * fully covered and there are no open slots. */
5262 clusterManagerLogInfo(">>> Check for multiple slot owners...\n")clusterManagerLog(1,">>> Check for multiple slot owners...\n"
)
;
5263 int slot = 0, slots_with_multiple_owners = 0;
5264 for (; slot < CLUSTER_MANAGER_SLOTS16384; slot++) {
5265 listIter li;
5266 listNode *ln;
5267 listRewind(cluster_manager.nodes, &li);
5268 list *owners = listCreate();
5269 while ((ln = listNext(&li)) != NULL((void*)0)) {
5270 clusterManagerNode *n = ln->value;
5271 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
5272 if (n->slots[slot]) listAddNodeTail(owners, n);
5273 else {
5274 /* Nodes having keys for the slot will be considered
5275 * owners too. */
5276 int count = clusterManagerCountKeysInSlot(n, slot);
5277 if (count > 0) listAddNodeTail(owners, n);
5278 }
5279 }
5280 if (listLength(owners)((owners)->len) > 1) {
5281 result = 0;
5282 clusterManagerLogErr("[WARNING] Slot %d has %d owners:\n",clusterManagerLog(3,"[WARNING] Slot %d has %d owners:\n", slot
, ((owners)->len))
5283 slot, listLength(owners))clusterManagerLog(3,"[WARNING] Slot %d has %d owners:\n", slot
, ((owners)->len))
;
5284 listRewind(owners, &li);
5285 while ((ln = listNext(&li)) != NULL((void*)0)) {
5286 clusterManagerNode *n = ln->value;
5287 clusterManagerLogErr(" %s:%d\n", n->ip, n->port)clusterManagerLog(3," %s:%d\n", n->ip, n->port);
5288 }
5289 slots_with_multiple_owners++;
5290 if (do_fix) {
5291 result = clusterManagerFixMultipleSlotOwners(slot, owners);
5292 if (!result) {
5293 clusterManagerLogErr("Failed to fix multiple owners "clusterManagerLog(3,"Failed to fix multiple owners " "for slot %d\n"
, slot)
5294 "for slot %d\n", slot)clusterManagerLog(3,"Failed to fix multiple owners " "for slot %d\n"
, slot)
;
5295 listRelease(owners);
5296 break;
5297 } else slots_with_multiple_owners--;
5298 }
5299 }
5300 listRelease(owners);
5301 }
5302 if (slots_with_multiple_owners == 0)
5303 clusterManagerLogOk("[OK] No multiple owners found.\n")clusterManagerLog(4,"[OK] No multiple owners found.\n");
5304 }
5305 return result;
5306}
5307
5308static clusterManagerNode *clusterNodeForResharding(char *id,
5309 clusterManagerNode *target,
5310 int *raise_err)
5311{
5312 clusterManagerNode *node = NULL((void*)0);
5313 const char *invalid_node_msg = "*** The specified node (%s) is not known "
5314 "or not a master, please retry.\n";
5315 node = clusterManagerNodeByName(id);
5316 *raise_err = 0;
5317 if (!node || node->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) {
5318 clusterManagerLogErr(invalid_node_msg, id)clusterManagerLog(3,invalid_node_msg, id);
5319 *raise_err = 1;
5320 return NULL((void*)0);
5321 } else if (target != NULL((void*)0)) {
5322 if (!strcmp(node->name, target->name)) {
5323 clusterManagerLogErr( "*** It is not possible to use "clusterManagerLog(3,"*** It is not possible to use " "the target node as "
"source node.\n")
5324 "the target node as "clusterManagerLog(3,"*** It is not possible to use " "the target node as "
"source node.\n")
5325 "source node.\n")clusterManagerLog(3,"*** It is not possible to use " "the target node as "
"source node.\n")
;
5326 return NULL((void*)0);
5327 }
5328 }
5329 return node;
5330}
5331
5332static list *clusterManagerComputeReshardTable(list *sources, int numslots) {
5333 list *moved = listCreate();
5334 int src_count = listLength(sources)((sources)->len), i = 0, tot_slots = 0, j;
5335 clusterManagerNode **sorted = zmalloc(src_count * sizeof(*sorted));
5336 listIter li;
5337 listNode *ln;
5338 listRewind(sources, &li);
5339 while ((ln = listNext(&li)) != NULL((void*)0)) {
5340 clusterManagerNode *node = ln->value;
5341 tot_slots += node->slots_count;
5342 sorted[i++] = node;
5343 }
5344 qsort(sorted, src_count, sizeof(clusterManagerNode *),
5345 clusterManagerSlotCountCompareDesc);
5346 for (i = 0; i < src_count; i++) {
5347 clusterManagerNode *node = sorted[i];
5348 float n = ((float) numslots / tot_slots * node->slots_count);
5349 if (i == 0) n = ceil(n);
5350 else n = floor(n);
5351 int max = (int) n, count = 0;
5352 for (j = 0; j < CLUSTER_MANAGER_SLOTS16384; j++) {
5353 int slot = node->slots[j];
5354 if (!slot) continue;
5355 if (count >= max || (int)listLength(moved)((moved)->len) >= numslots) break;
5356 clusterManagerReshardTableItem *item = zmalloc(sizeof(*item));
5357 item->source = node;
5358 item->slot = j;
5359 listAddNodeTail(moved, item);
5360 count++;
5361 }
5362 }
5363 zfree(sorted);
5364 return moved;
5365}
5366
5367static void clusterManagerShowReshardTable(list *table) {
5368 listIter li;
5369 listNode *ln;
5370 listRewind(table, &li);
5371 while ((ln = listNext(&li)) != NULL((void*)0)) {
5372 clusterManagerReshardTableItem *item = ln->value;
5373 clusterManagerNode *n = item->source;
5374 printf(" Moving slot %d from %s\n", item->slot, (char *) n->name);
5375 }
5376}
5377
5378static void clusterManagerReleaseReshardTable(list *table) {
5379 if (table != NULL((void*)0)) {
5380 listIter li;
5381 listNode *ln;
5382 listRewind(table, &li);
5383 while ((ln = listNext(&li)) != NULL((void*)0)) {
5384 clusterManagerReshardTableItem *item = ln->value;
5385 zfree(item);
5386 }
5387 listRelease(table);
5388 }
5389}
5390
5391static void clusterManagerLog(int level, const char* fmt, ...) {
5392 int use_colors =
5393 (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COLOR1 << 8);
5394 if (use_colors) {
5395 printf("\033[");
5396 switch (level) {
5397 case CLUSTER_MANAGER_LOG_LVL_INFO1: printf(LOG_COLOR_BOLD"29;1m"); break;
5398 case CLUSTER_MANAGER_LOG_LVL_WARN2: printf(LOG_COLOR_YELLOW"33;1m"); break;
5399 case CLUSTER_MANAGER_LOG_LVL_ERR3: printf(LOG_COLOR_RED"31;1m"); break;
5400 case CLUSTER_MANAGER_LOG_LVL_SUCCESS4: printf(LOG_COLOR_GREEN"32;1m"); break;
5401 default: printf(LOG_COLOR_RESET"0m"); break;
5402 }
5403 }
5404 va_list ap;
5405 va_start(ap, fmt)__builtin_va_start(ap, fmt);
5406 vprintf(fmt, ap);
5407 va_end(ap)__builtin_va_end(ap);
5408 if (use_colors) printf("\033[" LOG_COLOR_RESET"0m");
5409}
5410
5411static void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,
5412 int alloc_len)
5413{
5414 array->nodes = zcalloc(alloc_len * sizeof(clusterManagerNode*));
5415 array->alloc = array->nodes;
5416 array->len = alloc_len;
5417 array->count = 0;
5418}
5419
5420/* Reset array->nodes to the original array allocation and re-count non-NULL
5421 * nodes. */
5422static void clusterManagerNodeArrayReset(clusterManagerNodeArray *array) {
5423 if (array->nodes > array->alloc) {
5424 array->len = array->nodes - array->alloc;
5425 array->nodes = array->alloc;
5426 array->count = 0;
5427 int i = 0;
5428 for(; i < array->len; i++) {
5429 if (array->nodes[i] != NULL((void*)0)) array->count++;
5430 }
5431 }
5432}
5433
5434/* Shift array->nodes and store the shifted node into 'nodeptr'. */
5435static void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,
5436 clusterManagerNode **nodeptr)
5437{
5438 assert(array->nodes < (array->nodes + array->len))((array->nodes < (array->nodes + array->len)) ? (
void) (0) : __assert_fail ("array->nodes < (array->nodes + array->len)"
, "redis-cli.c", 5438, __extension__ __PRETTY_FUNCTION__))
;
5439 /* If the first node to be shifted is not NULL, decrement count. */
5440 if (*array->nodes != NULL((void*)0)) array->count--;
5441 /* Store the first node to be shifted into 'nodeptr'. */
5442 *nodeptr = *array->nodes;
5443 /* Shift the nodes array and decrement length. */
5444 array->nodes++;
5445 array->len--;
5446}
5447
5448static void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,
5449 clusterManagerNode *node)
5450{
5451 assert(array->nodes < (array->nodes + array->len))((array->nodes < (array->nodes + array->len)) ? (
void) (0) : __assert_fail ("array->nodes < (array->nodes + array->len)"
, "redis-cli.c", 5451, __extension__ __PRETTY_FUNCTION__))
;
5452 assert(node != NULL)((node != ((void*)0)) ? (void) (0) : __assert_fail ("node != NULL"
, "redis-cli.c", 5452, __extension__ __PRETTY_FUNCTION__))
;
5453 assert(array->count < array->len)((array->count < array->len) ? (void) (0) : __assert_fail
("array->count < array->len", "redis-cli.c", 5453, __extension__
__PRETTY_FUNCTION__))
;
5454 array->nodes[array->count++] = node;
5455}
5456
5457static void clusterManagerPrintNotEmptyNodeError(clusterManagerNode *node,
5458 char *err)
5459{
5460 char *msg;
5461 if (err) msg = err;
5462 else {
5463 msg = "is not empty. Either the node already knows other "
5464 "nodes (check with CLUSTER NODES) or contains some "
5465 "key in database 0.";
5466 }
5467 clusterManagerLogErr("[ERR] Node %s:%d %s\n", node->ip, node->port, msg)clusterManagerLog(3,"[ERR] Node %s:%d %s\n", node->ip, node
->port, msg)
;
5468}
5469
5470static void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,
5471 char *err)
5472{
5473 char *msg = (err ? err : "is not configured as a cluster node.");
5474 clusterManagerLogErr("[ERR] Node %s:%d %s\n", node->ip, node->port, msg)clusterManagerLog(3,"[ERR] Node %s:%d %s\n", node->ip, node
->port, msg)
;
5475}
5476
5477/* Execute redis-cli in Cluster Manager mode */
5478static void clusterManagerMode(clusterManagerCommandProc *proc) {
5479 int argc = config.cluster_manager_command.argc;
5480 char **argv = config.cluster_manager_command.argv;
5481 cluster_manager.nodes = NULL((void*)0);
5482 if (!proc(argc, argv)) goto cluster_manager_err;
5483 freeClusterManager();
5484 exit(0);
5485cluster_manager_err:
5486 freeClusterManager();
5487 exit(1);
5488}
5489
5490/* Cluster Manager Commands */
5491
5492static int clusterManagerCommandCreate(int argc, char **argv) {
5493 int i, j, success = 1;
5494 cluster_manager.nodes = listCreate();
5495 for (i = 0; i < argc; i++) {
5496 char *addr = argv[i];
5497 char *c = strrchr(addr, '@');
5498 if (c != NULL((void*)0)) *c = '\0';
5499 c = strrchr(addr, ':');
5500 if (c == NULL((void*)0)) {
5501 fprintf(stderrstderr, "Invalid address format: %s\n", addr);
5502 return 0;
5503 }
5504 *c = '\0';
5505 char *ip = addr;
5506 int port = atoi(++c);
5507 clusterManagerNode *node = clusterManagerNewNode(ip, port);
5508 if (!clusterManagerNodeConnect(node)) {
5509 freeClusterManagerNode(node);
5510 return 0;
5511 }
5512 char *err = NULL((void*)0);
5513 if (!clusterManagerNodeIsCluster(node, &err)) {
5514 clusterManagerPrintNotClusterNodeError(node, err);
5515 if (err) zfree(err);
5516 freeClusterManagerNode(node);
5517 return 0;
5518 }
5519 err = NULL((void*)0);
5520 if (!clusterManagerNodeLoadInfo(node, 0, &err)) {
5521 if (err) {
5522 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, err);
;
5523 zfree(err);
5524 }
5525 freeClusterManagerNode(node);
5526 return 0;
5527 }
5528 err = NULL((void*)0);
5529 if (!clusterManagerNodeIsEmpty(node, &err)) {
5530 clusterManagerPrintNotEmptyNodeError(node, err);
5531 if (err) zfree(err);
5532 freeClusterManagerNode(node);
5533 return 0;
5534 }
5535 listAddNodeTail(cluster_manager.nodes, node);
5536 }
5537 int node_len = cluster_manager.nodes->len;
5538 int replicas = config.cluster_manager_command.replicas;
5539 int masters_count = CLUSTER_MANAGER_MASTERS_COUNT(node_len, replicas)(node_len/(replicas + 1));
5540 if (masters_count < 3) {
5541 clusterManagerLogErr(clusterManagerLog(3,"*** ERROR: Invalid configuration for cluster creation.\n"
"*** Redis Cluster requires at least 3 master nodes.\n" "*** This is not possible with %d nodes and %d replicas per node."
, node_len, replicas)
5542 "*** ERROR: Invalid configuration for cluster creation.\n"clusterManagerLog(3,"*** ERROR: Invalid configuration for cluster creation.\n"
"*** Redis Cluster requires at least 3 master nodes.\n" "*** This is not possible with %d nodes and %d replicas per node."
, node_len, replicas)
5543 "*** Redis Cluster requires at least 3 master nodes.\n"clusterManagerLog(3,"*** ERROR: Invalid configuration for cluster creation.\n"
"*** Redis Cluster requires at least 3 master nodes.\n" "*** This is not possible with %d nodes and %d replicas per node."
, node_len, replicas)
5544 "*** This is not possible with %d nodes and %d replicas per node.",clusterManagerLog(3,"*** ERROR: Invalid configuration for cluster creation.\n"
"*** Redis Cluster requires at least 3 master nodes.\n" "*** This is not possible with %d nodes and %d replicas per node."
, node_len, replicas)
5545 node_len, replicas)clusterManagerLog(3,"*** ERROR: Invalid configuration for cluster creation.\n"
"*** Redis Cluster requires at least 3 master nodes.\n" "*** This is not possible with %d nodes and %d replicas per node."
, node_len, replicas)
;
5546 clusterManagerLogErr("\n*** At least %d nodes are required.\n",clusterManagerLog(3,"\n*** At least %d nodes are required.\n"
, 3 * (replicas + 1))
5547 3 * (replicas + 1))clusterManagerLog(3,"\n*** At least %d nodes are required.\n"
, 3 * (replicas + 1))
;
5548 return 0;
5549 }
5550 clusterManagerLogInfo(">>> Performing hash slots allocation "clusterManagerLog(1,">>> Performing hash slots allocation "
"on %d nodes...\n", node_len)
5551 "on %d nodes...\n", node_len)clusterManagerLog(1,">>> Performing hash slots allocation "
"on %d nodes...\n", node_len)
;
5552 int interleaved_len = 0, ip_count = 0;
5553 clusterManagerNode **interleaved = zcalloc(node_len*sizeof(**interleaved));
5554 char **ips = zcalloc(node_len * sizeof(char*));
5555 clusterManagerNodeArray *ip_nodes = zcalloc(node_len * sizeof(*ip_nodes));
5556 listIter li;
5557 listNode *ln;
5558 listRewind(cluster_manager.nodes, &li);
5559 while ((ln = listNext(&li)) != NULL((void*)0)) {
5560 clusterManagerNode *n = ln->value;
5561 int found = 0;
5562 for (i = 0; i < ip_count; i++) {
5563 char *ip = ips[i];
5564 if (!strcmp(ip, n->ip)) {
5565 found = 1;
5566 break;
5567 }
5568 }
5569 if (!found) {
5570 ips[ip_count++] = n->ip;
5571 }
5572 clusterManagerNodeArray *node_array = &(ip_nodes[i]);
5573 if (node_array->nodes == NULL((void*)0))
5574 clusterManagerNodeArrayInit(node_array, node_len);
5575 clusterManagerNodeArrayAdd(node_array, n);
5576 }
5577 while (interleaved_len < node_len) {
5578 for (i = 0; i < ip_count; i++) {
5579 clusterManagerNodeArray *node_array = &(ip_nodes[i]);
5580 if (node_array->count > 0) {
5581 clusterManagerNode *n = NULL((void*)0);
5582 clusterManagerNodeArrayShift(node_array, &n);
5583 interleaved[interleaved_len++] = n;
5584 }
5585 }
5586 }
5587 clusterManagerNode **masters = interleaved;
5588 interleaved += masters_count;
5589 interleaved_len -= masters_count;
5590 float slots_per_node = CLUSTER_MANAGER_SLOTS16384 / (float) masters_count;
5591 long first = 0;
5592 float cursor = 0.0f;
5593 for (i = 0; i < masters_count; i++) {
5594 clusterManagerNode *master = masters[i];
5595 long last = lround(cursor + slots_per_node - 1);
5596 if (last > CLUSTER_MANAGER_SLOTS16384 || i == (masters_count - 1))
5597 last = CLUSTER_MANAGER_SLOTS16384 - 1;
5598 if (last < first) last = first;
5599 printf("Master[%d] -> Slots %ld - %ld\n", i, first, last);
5600 master->slots_count = 0;
5601 for (j = first; j <= last; j++) {
5602 master->slots[j] = 1;
5603 master->slots_count++;
5604 }
5605 master->dirty = 1;
5606 first = last + 1;
5607 cursor += slots_per_node;
5608 }
5609
5610 /* Rotating the list sometimes helps to get better initial
5611 * anti-affinity before the optimizer runs. */
5612 clusterManagerNode *first_node = interleaved[0];
5613 for (i = 0; i < (interleaved_len - 1); i++)
5614 interleaved[i] = interleaved[i + 1];
5615 interleaved[interleaved_len - 1] = first_node;
5616 int assign_unused = 0, available_count = interleaved_len;
5617assign_replicas:
5618 for (i = 0; i < masters_count; i++) {
5619 clusterManagerNode *master = masters[i];
5620 int assigned_replicas = 0;
5621 while (assigned_replicas < replicas) {
5622 if (available_count == 0) break;
5623 clusterManagerNode *found = NULL((void*)0), *slave = NULL((void*)0);
5624 int firstNodeIdx = -1;
5625 for (j = 0; j < interleaved_len; j++) {
5626 clusterManagerNode *n = interleaved[j];
5627 if (n == NULL((void*)0)) continue;
5628 if (strcmp(n->ip, master->ip)) {
5629 found = n;
5630 interleaved[j] = NULL((void*)0);
5631 break;
5632 }
5633 if (firstNodeIdx < 0) firstNodeIdx = j;
5634 }
5635 if (found) slave = found;
5636 else if (firstNodeIdx >= 0) {
5637 slave = interleaved[firstNodeIdx];
5638 interleaved_len -= (interleaved - (interleaved + firstNodeIdx));
5639 interleaved += (firstNodeIdx + 1);
5640 }
5641 if (slave != NULL((void*)0)) {
5642 assigned_replicas++;
5643 available_count--;
5644 if (slave->replicate) sdsfreehi_sdsfree(slave->replicate);
5645 slave->replicate = sdsnewhi_sdsnew(master->name);
5646 slave->dirty = 1;
5647 } else break;
5648 printf("Adding replica %s:%d to %s:%d\n", slave->ip, slave->port,
5649 master->ip, master->port);
5650 if (assign_unused) break;
5651 }
5652 }
5653 if (!assign_unused && available_count > 0) {
5654 assign_unused = 1;
5655 printf("Adding extra replicas...\n");
5656 goto assign_replicas;
5657 }
5658 for (i = 0; i < ip_count; i++) {
5659 clusterManagerNodeArray *node_array = ip_nodes + i;
5660 clusterManagerNodeArrayReset(node_array);
5661 }
5662 clusterManagerOptimizeAntiAffinity(ip_nodes, ip_count);
5663 clusterManagerShowNodes();
5664 int ignore_force = 0;
5665 if (confirmWithYes("Can I set the above configuration?", ignore_force)) {
5666 listRewind(cluster_manager.nodes, &li);
5667 while ((ln = listNext(&li)) != NULL((void*)0)) {
5668 clusterManagerNode *node = ln->value;
5669 char *err = NULL((void*)0);
5670 int flushed = clusterManagerFlushNodeConfig(node, &err);
5671 if (!flushed && node->dirty && !node->replicate) {
5672 if (err != NULL((void*)0)) {
5673 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, err);
;
5674 zfree(err);
5675 }
5676 success = 0;
5677 goto cleanup;
5678 } else if (err != NULL((void*)0)) zfree(err);
5679 }
5680 clusterManagerLogInfo(">>> Nodes configuration updated\n")clusterManagerLog(1,">>> Nodes configuration updated\n"
)
;
5681 clusterManagerLogInfo(">>> Assign a different config epoch to "clusterManagerLog(1,">>> Assign a different config epoch to "
"each node\n")
5682 "each node\n")clusterManagerLog(1,">>> Assign a different config epoch to "
"each node\n")
;
5683 int config_epoch = 1;
5684 listRewind(cluster_manager.nodes, &li);
5685 while ((ln = listNext(&li)) != NULL((void*)0)) {
5686 clusterManagerNode *node = ln->value;
5687 redisReply *reply = NULL((void*)0);
5688 reply = CLUSTER_MANAGER_COMMAND(node,(redisCommand(node->context, "cluster set-config-epoch %d"
, config_epoch++))
5689 "cluster set-config-epoch %d",(redisCommand(node->context, "cluster set-config-epoch %d"
, config_epoch++))
5690 config_epoch++)(redisCommand(node->context, "cluster set-config-epoch %d"
, config_epoch++))
;
5691 if (reply != NULL((void*)0)) freeReplyObject(reply);
5692 }
5693 clusterManagerLogInfo(">>> Sending CLUSTER MEET messages to join "clusterManagerLog(1,">>> Sending CLUSTER MEET messages to join "
"the cluster\n")
5694 "the cluster\n")clusterManagerLog(1,">>> Sending CLUSTER MEET messages to join "
"the cluster\n")
;
5695 clusterManagerNode *first = NULL((void*)0);
5696 listRewind(cluster_manager.nodes, &li);
5697 while ((ln = listNext(&li)) != NULL((void*)0)) {
5698 clusterManagerNode *node = ln->value;
5699 if (first == NULL((void*)0)) {
5700 first = node;
5701 continue;
5702 }
5703 redisReply *reply = NULL((void*)0);
5704 reply = CLUSTER_MANAGER_COMMAND(node, "cluster meet %s %d",(redisCommand(node->context, "cluster meet %s %d", first->
ip, first->port))
5705 first->ip, first->port)(redisCommand(node->context, "cluster meet %s %d", first->
ip, first->port))
;
5706 int is_err = 0;
5707 if (reply != NULL((void*)0)) {
5708 if ((is_err = reply->type == REDIS_REPLY_ERROR6))
5709 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, reply->str)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, reply->str);
;
5710 freeReplyObject(reply);
5711 } else {
5712 is_err = 1;
5713 fprintf(stderrstderr, "Failed to send CLUSTER MEET command.\n");
5714 }
5715 if (is_err) {
5716 success = 0;
5717 goto cleanup;
5718 }
5719 }
5720 /* Give one second for the join to start, in order to avoid that
5721 * waiting for cluster join will find all the nodes agree about
5722 * the config as they are still empty with unassigned slots. */
5723 sleep(1);
5724 clusterManagerWaitForClusterJoin();
5725 /* Useful for the replicas */
5726 listRewind(cluster_manager.nodes, &li);
5727 while ((ln = listNext(&li)) != NULL((void*)0)) {
5728 clusterManagerNode *node = ln->value;
5729 if (!node->dirty) continue;
5730 char *err = NULL((void*)0);
5731 int flushed = clusterManagerFlushNodeConfig(node, &err);
5732 if (!flushed && !node->replicate) {
5733 if (err != NULL((void*)0)) {
5734 CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", node
->ip, node->port, err);
;
5735 zfree(err);
5736 }
5737 success = 0;
5738 goto cleanup;
5739 }
5740 }
5741 // Reset Nodes
5742 listRewind(cluster_manager.nodes, &li);
5743 clusterManagerNode *first_node = NULL((void*)0);
5744 while ((ln = listNext(&li)) != NULL((void*)0)) {
5745 clusterManagerNode *node = ln->value;
5746 if (!first_node) first_node = node;
5747 else freeClusterManagerNode(node);
5748 }
5749 listEmpty(cluster_manager.nodes);
5750 if (!clusterManagerLoadInfoFromNode(first_node, 0)) {
5751 success = 0;
5752 goto cleanup;
5753 }
5754 clusterManagerCheckCluster(0);
5755 }
5756cleanup:
5757 /* Free everything */
5758 zfree(masters);
5759 zfree(ips);
5760 for (i = 0; i < node_len; i++) {
5761 clusterManagerNodeArray *node_array = ip_nodes + i;
5762 CLUSTER_MANAGER_NODE_ARRAY_FREE(node_array)zfree(node_array->alloc);
5763 }
5764 zfree(ip_nodes);
5765 return success;
5766}
5767
5768static int clusterManagerCommandAddNode(int argc, char **argv) {
5769 int success = 1;
5770 redisReply *reply = NULL((void*)0);
5771 char *ref_ip = NULL((void*)0), *ip = NULL((void*)0);
5772 int ref_port = 0, port = 0;
5773 if (!getClusterHostFromCmdArgs(argc - 1, argv + 1, &ref_ip, &ref_port))
5774 goto invalid_args;
5775 if (!getClusterHostFromCmdArgs(1, argv, &ip, &port))
5776 goto invalid_args;
5777 clusterManagerLogInfo(">>> Adding node %s:%d to cluster %s:%d\n", ip, port,clusterManagerLog(1,">>> Adding node %s:%d to cluster %s:%d\n"
, ip, port, ref_ip, ref_port)
5778 ref_ip, ref_port)clusterManagerLog(1,">>> Adding node %s:%d to cluster %s:%d\n"
, ip, port, ref_ip, ref_port)
;
5779 // Check the existing cluster
5780 clusterManagerNode *refnode = clusterManagerNewNode(ref_ip, ref_port);
5781 if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
5782 if (!clusterManagerCheckCluster(0)) return 0;
5783
5784 /* If --cluster-master-id was specified, try to resolve it now so that we
5785 * abort before starting with the node configuration. */
5786 clusterManagerNode *master_node = NULL((void*)0);
5787 if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVE1 << 1) {
5788 char *master_id = config.cluster_manager_command.master_id;
5789 if (master_id != NULL((void*)0)) {
5790 master_node = clusterManagerNodeByName(master_id);
5791 if (master_node == NULL((void*)0)) {
5792 clusterManagerLogErr("[ERR] No such master ID %s\n", master_id)clusterManagerLog(3,"[ERR] No such master ID %s\n", master_id
)
;
5793 return 0;
5794 }
5795 } else {
5796 master_node = clusterManagerNodeWithLeastReplicas();
5797 assert(master_node != NULL)((master_node != ((void*)0)) ? (void) (0) : __assert_fail ("master_node != NULL"
, "redis-cli.c", 5797, __extension__ __PRETTY_FUNCTION__))
;
5798 printf("Automatically selected master %s:%d\n", master_node->ip,
5799 master_node->port);
5800 }
5801 }
5802
5803 // Add the new node
5804 clusterManagerNode *new_node = clusterManagerNewNode(ip, port);
5805 int added = 0;
5806 if (!clusterManagerNodeConnect(new_node)) {
5807 clusterManagerLogErr("[ERR] Sorry, can't connect to node %s:%d\n",clusterManagerLog(3,"[ERR] Sorry, can't connect to node %s:%d\n"
, ip, port)
5808 ip, port)clusterManagerLog(3,"[ERR] Sorry, can't connect to node %s:%d\n"
, ip, port)
;
5809 success = 0;
5810 goto cleanup;
5811 }
5812 char *err = NULL((void*)0);
5813 if (!(success = clusterManagerNodeIsCluster(new_node, &err))) {
5814 clusterManagerPrintNotClusterNodeError(new_node, err);
5815 if (err) zfree(err);
5816 goto cleanup;
5817 }
5818 if (!clusterManagerNodeLoadInfo(new_node, 0, &err)) {
5819 if (err) {
5820 CLUSTER_MANAGER_PRINT_REPLY_ERROR(new_node, err)clusterManagerLog(3,"Node %s:%d replied with error:\n%s\n", new_node
->ip, new_node->port, err);
;
5821 zfree(err);
5822 }
5823 success = 0;
5824 goto cleanup;
5825 }
5826 if (!(success = clusterManagerNodeIsEmpty(new_node, &err))) {
5827 clusterManagerPrintNotEmptyNodeError(new_node, err);
5828 if (err) zfree(err);
5829 goto cleanup;
5830 }
5831 clusterManagerNode *first = listFirst(cluster_manager.nodes)((cluster_manager.nodes)->head)->value;
5832 listAddNodeTail(cluster_manager.nodes, new_node);
5833 added = 1;
5834
5835 // Send CLUSTER MEET command to the new node
5836 clusterManagerLogInfo(">>> Send CLUSTER MEET to node %s:%d to make it "clusterManagerLog(1,">>> Send CLUSTER MEET to node %s:%d to make it "
"join the cluster.\n", ip, port)
5837 "join the cluster.\n", ip, port)clusterManagerLog(1,">>> Send CLUSTER MEET to node %s:%d to make it "
"join the cluster.\n", ip, port)
;
5838 reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER MEET %s %d",(redisCommand(new_node->context, "CLUSTER MEET %s %d", first
->ip, first->port))
5839 first->ip, first->port)(redisCommand(new_node->context, "CLUSTER MEET %s %d", first
->ip, first->port))
;
5840 if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL((void*)0))))
5841 goto cleanup;
5842
5843 /* Additional configuration is needed if the node is added as a slave. */
5844 if (master_node) {
5845 sleep(1);
5846 clusterManagerWaitForClusterJoin();
5847 clusterManagerLogInfo(">>> Configure node as replica of %s:%d.\n",clusterManagerLog(1,">>> Configure node as replica of %s:%d.\n"
, master_node->ip, master_node->port)
5848 master_node->ip, master_node->port)clusterManagerLog(1,">>> Configure node as replica of %s:%d.\n"
, master_node->ip, master_node->port)
;
5849 freeReplyObject(reply);
5850 reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER REPLICATE %s",(redisCommand(new_node->context, "CLUSTER REPLICATE %s", master_node
->name))
5851 master_node->name)(redisCommand(new_node->context, "CLUSTER REPLICATE %s", master_node
->name))
;
5852 if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL((void*)0))))
5853 goto cleanup;
5854 }
5855 clusterManagerLogOk("[OK] New node added correctly.\n")clusterManagerLog(4,"[OK] New node added correctly.\n");
5856cleanup:
5857 if (!added && new_node) freeClusterManagerNode(new_node);
5858 if (reply) freeReplyObject(reply);
5859 return success;
5860invalid_args:
5861 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
5862 return 0;
5863}
5864
5865static int clusterManagerCommandDeleteNode(int argc, char **argv) {
5866 UNUSED(argc)((void) argc);
5867 int success = 1;
5868 int port = 0;
5869 char *ip = NULL((void*)0);
5870 if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
5871 char *node_id = argv[1];
5872 clusterManagerLogInfo(">>> Removing node %s from cluster %s:%d\n",clusterManagerLog(1,">>> Removing node %s from cluster %s:%d\n"
, node_id, ip, port)
5873 node_id, ip, port)clusterManagerLog(1,">>> Removing node %s from cluster %s:%d\n"
, node_id, ip, port)
;
5874 clusterManagerNode *ref_node = clusterManagerNewNode(ip, port);
5875 clusterManagerNode *node = NULL((void*)0);
5876
5877 // Load cluster information
5878 if (!clusterManagerLoadInfoFromNode(ref_node, 0)) return 0;
5879
5880 // Check if the node exists and is not empty
5881 node = clusterManagerNodeByName(node_id);
5882 if (node == NULL((void*)0)) {
5883 clusterManagerLogErr("[ERR] No such node ID %s\n", node_id)clusterManagerLog(3,"[ERR] No such node ID %s\n", node_id);
5884 return 0;
5885 }
5886 if (node->slots_count != 0) {
5887 clusterManagerLogErr("[ERR] Node %s:%d is not empty! Reshard data "clusterManagerLog(3,"[ERR] Node %s:%d is not empty! Reshard data "
"away and try again.\n", node->ip, node->port)
5888 "away and try again.\n", node->ip, node->port)clusterManagerLog(3,"[ERR] Node %s:%d is not empty! Reshard data "
"away and try again.\n", node->ip, node->port)
;
5889 return 0;
5890 }
5891
5892 // Send CLUSTER FORGET to all the nodes but the node to remove
5893 clusterManagerLogInfo(">>> Sending CLUSTER FORGET messages to the "clusterManagerLog(1,">>> Sending CLUSTER FORGET messages to the "
"cluster...\n")
5894 "cluster...\n")clusterManagerLog(1,">>> Sending CLUSTER FORGET messages to the "
"cluster...\n")
;
5895 listIter li;
5896 listNode *ln;
5897 listRewind(cluster_manager.nodes, &li);
5898 while ((ln = listNext(&li)) != NULL((void*)0)) {
5899 clusterManagerNode *n = ln->value;
5900 if (n == node) continue;
5901 if (n->replicate && !strcasecmp(n->replicate, node_id)) {
5902 // Reconfigure the slave to replicate with some other node
5903 clusterManagerNode *master = clusterManagerNodeWithLeastReplicas();
5904 assert(master != NULL)((master != ((void*)0)) ? (void) (0) : __assert_fail ("master != NULL"
, "redis-cli.c", 5904, __extension__ __PRETTY_FUNCTION__))
;
5905 clusterManagerLogInfo(">>> %s:%d as replica of %s:%d\n",clusterManagerLog(1,">>> %s:%d as replica of %s:%d\n"
, n->ip, n->port, master->ip, master->port)
5906 n->ip, n->port, master->ip, master->port)clusterManagerLog(1,">>> %s:%d as replica of %s:%d\n"
, n->ip, n->port, master->ip, master->port)
;
5907 redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER REPLICATE %s",(redisCommand(n->context, "CLUSTER REPLICATE %s", master->
name))
5908 master->name)(redisCommand(n->context, "CLUSTER REPLICATE %s", master->
name))
;
5909 success = clusterManagerCheckRedisReply(n, r, NULL((void*)0));
5910 if (r) freeReplyObject(r);
5911 if (!success) return 0;
5912 }
5913 redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER FORGET %s",(redisCommand(n->context, "CLUSTER FORGET %s", node_id))
5914 node_id)(redisCommand(n->context, "CLUSTER FORGET %s", node_id));
5915 success = clusterManagerCheckRedisReply(n, r, NULL((void*)0));
5916 if (r) freeReplyObject(r);
5917 if (!success) return 0;
5918 }
5919
5920 /* Finally send CLUSTER RESET to the node. */
5921 clusterManagerLogInfo(">>> Sending CLUSTER RESET SOFT to the "clusterManagerLog(1,">>> Sending CLUSTER RESET SOFT to the "
"deleted node.\n")
5922 "deleted node.\n")clusterManagerLog(1,">>> Sending CLUSTER RESET SOFT to the "
"deleted node.\n")
;
5923 redisReply *r = redisCommand(node->context, "CLUSTER RESET %s", "SOFT");
5924 success = clusterManagerCheckRedisReply(node, r, NULL((void*)0));
5925 if (r) freeReplyObject(r);
5926 return success;
5927invalid_args:
5928 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
5929 return 0;
5930}
5931
5932static int clusterManagerCommandInfo(int argc, char **argv) {
5933 int port = 0;
5934 char *ip = NULL((void*)0);
5935 if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5936 clusterManagerNode *node = clusterManagerNewNode(ip, port);
5937 if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5938 clusterManagerShowClusterInfo();
5939 return 1;
5940invalid_args:
5941 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
5942 return 0;
5943}
5944
5945static int clusterManagerCommandCheck(int argc, char **argv) {
5946 int port = 0;
5947 char *ip = NULL((void*)0);
5948 if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5949 clusterManagerNode *node = clusterManagerNewNode(ip, port);
5950 if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5951 clusterManagerShowClusterInfo();
5952 return clusterManagerCheckCluster(0);
5953invalid_args:
5954 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
5955 return 0;
5956}
5957
5958static int clusterManagerCommandFix(int argc, char **argv) {
5959 config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_FIX1 << 0;
5960 return clusterManagerCommandCheck(argc, argv);
5961}
5962
5963static int clusterManagerCommandReshard(int argc, char **argv) {
5964 int port = 0;
5965 char *ip = NULL((void*)0);
5966 if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5967 clusterManagerNode *node = clusterManagerNewNode(ip, port);
5968 if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5969 clusterManagerCheckCluster(0);
5970 if (cluster_manager.errors && listLength(cluster_manager.errors)((cluster_manager.errors)->len) > 0) {
5971 fflush(stdoutstdout);
5972 fprintf(stderrstderr,
5973 "*** Please fix your cluster problems before resharding\n");
5974 return 0;
5975 }
5976 int slots = config.cluster_manager_command.slots;
5977 if (!slots) {
5978 while (slots <= 0 || slots > CLUSTER_MANAGER_SLOTS16384) {
5979 printf("How many slots do you want to move (from 1 to %d)? ",
5980 CLUSTER_MANAGER_SLOTS16384);
5981 fflush(stdoutstdout);
5982 char buf[6];
5983 int nread = read(fileno(stdinstdin),buf,6);
5984 if (nread <= 0) continue;
5985 int last_idx = nread - 1;
5986 if (buf[last_idx] != '\n') {
5987 int ch;
5988 while ((ch = getchar()) != '\n' && ch != EOF(-1)) {}
5989 }
5990 buf[last_idx] = '\0';
5991 slots = atoi(buf);
5992 }
5993 }
5994 char buf[255];
5995 char *to = config.cluster_manager_command.to,
5996 *from = config.cluster_manager_command.from;
5997 while (to == NULL((void*)0)) {
5998 printf("What is the receiving node ID? ");
5999 fflush(stdoutstdout);
6000 int nread = read(fileno(stdinstdin),buf,255);
6001 if (nread <= 0) continue;
6002 int last_idx = nread - 1;
6003 if (buf[last_idx] != '\n') {
6004 int ch;
6005 while ((ch = getchar()) != '\n' && ch != EOF(-1)) {}
6006 }
6007 buf[last_idx] = '\0';
6008 if (strlen(buf) > 0) to = buf;
6009 }
6010 int raise_err = 0;
6011 clusterManagerNode *target = clusterNodeForResharding(to, NULL((void*)0), &raise_err);
6012 if (target == NULL((void*)0)) return 0;
6013 list *sources = listCreate();
6014 list *table = NULL((void*)0);
6015 int all = 0, result = 1;
6016 if (from == NULL((void*)0)) {
6017 printf("Please enter all the source node IDs.\n");
6018 printf(" Type 'all' to use all the nodes as source nodes for "
6019 "the hash slots.\n");
6020 printf(" Type 'done' once you entered all the source nodes IDs.\n");
6021 while (1) {
6022 printf("Source node #%lu: ", listLength(sources)((sources)->len) + 1);
6023 fflush(stdoutstdout);
6024 int nread = read(fileno(stdinstdin),buf,255);
6025 if (nread <= 0) continue;
6026 int last_idx = nread - 1;
6027 if (buf[last_idx] != '\n') {
6028 int ch;
6029 while ((ch = getchar()) != '\n' && ch != EOF(-1)) {}
6030 }
6031 buf[last_idx] = '\0';
6032 if (!strcmp(buf, "done")) break;
6033 else if (!strcmp(buf, "all")) {
6034 all = 1;
6035 break;
6036 } else {
6037 clusterManagerNode *src =
6038 clusterNodeForResharding(buf, target, &raise_err);
6039 if (src != NULL((void*)0)) listAddNodeTail(sources, src);
6040 else if (raise_err) {
6041 result = 0;
6042 goto cleanup;
6043 }
6044 }
6045 }
6046 } else {
6047 char *p;
6048 while((p = strchr(from, ',')) != NULL((void*)0)) {
6049 *p = '\0';
6050 if (!strcmp(from, "all")) {
6051 all = 1;
6052 break;
6053 } else {
6054 clusterManagerNode *src =
6055 clusterNodeForResharding(from, target, &raise_err);
6056 if (src != NULL((void*)0)) listAddNodeTail(sources, src);
6057 else if (raise_err) {
6058 result = 0;
6059 goto cleanup;
6060 }
6061 }
6062 from = p + 1;
6063 }
6064 /* Check if there's still another source to process. */
6065 if (!all && strlen(from) > 0) {
6066 if (!strcmp(from, "all")) all = 1;
6067 if (!all) {
6068 clusterManagerNode *src =
6069 clusterNodeForResharding(from, target, &raise_err);
6070 if (src != NULL((void*)0)) listAddNodeTail(sources, src);
6071 else if (raise_err) {
6072 result = 0;
6073 goto cleanup;
6074 }
6075 }
6076 }
6077 }
6078 listIter li;
6079 listNode *ln;
6080 if (all) {
6081 listEmpty(sources);
6082 listRewind(cluster_manager.nodes, &li);
6083 while ((ln = listNext(&li)) != NULL((void*)0)) {
6084 clusterManagerNode *n = ln->value;
6085 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1 || n->replicate)
6086 continue;
6087 if (!sdscmphi_sdscmp(n->name, target->name)) continue;
6088 listAddNodeTail(sources, n);
6089 }
6090 }
6091 if (listLength(sources)((sources)->len) == 0) {
6092 fprintf(stderrstderr, "*** No source nodes given, operation aborted.\n");
6093 result = 0;
6094 goto cleanup;
6095 }
6096 printf("\nReady to move %d slots.\n", slots);
6097 printf(" Source nodes:\n");
6098 listRewind(sources, &li);
6099 while ((ln = listNext(&li)) != NULL((void*)0)) {
6100 clusterManagerNode *src = ln->value;
6101 sdshisds info = clusterManagerNodeInfo(src, 4);
6102 printf("%s\n", info);
6103 sdsfreehi_sdsfree(info);
6104 }
6105 printf(" Destination node:\n");
6106 sdshisds info = clusterManagerNodeInfo(target, 4);
6107 printf("%s\n", info);
6108 sdsfreehi_sdsfree(info);
6109 table = clusterManagerComputeReshardTable(sources, slots);
6110 printf(" Resharding plan:\n");
6111 clusterManagerShowReshardTable(table);
6112 if (!(config.cluster_manager_command.flags &
6113 CLUSTER_MANAGER_CMD_FLAG_YES1 << 2))
6114 {
6115 printf("Do you want to proceed with the proposed "
6116 "reshard plan (yes/no)? ");
6117 fflush(stdoutstdout);
6118 char buf[4];
6119 int nread = read(fileno(stdinstdin),buf,4);
6120 buf[3] = '\0';
6121 if (nread <= 0 || strcmp("yes", buf) != 0) {
6122 result = 0;
6123 goto cleanup;
6124 }
6125 }
6126 int opts = CLUSTER_MANAGER_OPT_VERBOSE1 << 7;
6127 listRewind(table, &li);
6128 while ((ln = listNext(&li)) != NULL((void*)0)) {
6129 clusterManagerReshardTableItem *item = ln->value;
6130 char *err = NULL((void*)0);
6131 result = clusterManagerMoveSlot(item->source, target, item->slot,
6132 opts, &err);
6133 if (!result) {
6134 if (err != NULL((void*)0)) {
6135 //clusterManagerLogErr("\n%s\n", err);
6136 zfree(err);
6137 }
6138 goto cleanup;
6139 }
6140 }
6141cleanup:
6142 listRelease(sources);
6143 clusterManagerReleaseReshardTable(table);
6144 return result;
6145invalid_args:
6146 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
6147 return 0;
6148}
6149
6150static int clusterManagerCommandRebalance(int argc, char **argv) {
6151 int port = 0;
6152 char *ip = NULL((void*)0);
6153 clusterManagerNode **weightedNodes = NULL((void*)0);
6154 list *involved = NULL((void*)0);
6155 if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
6156 clusterManagerNode *node = clusterManagerNewNode(ip, port);
6157 if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
6158 int result = 1, i;
6159 if (config.cluster_manager_command.weight != NULL((void*)0)) {
6160 for (i = 0; i < config.cluster_manager_command.weight_argc; i++) {
6161 char *name = config.cluster_manager_command.weight[i];
6162 char *p = strchr(name, '=');
6163 if (p == NULL((void*)0)) {
6164 result = 0;
6165 goto cleanup;
6166 }
6167 *p = '\0';
6168 float w = atof(++p);
6169 clusterManagerNode *n = clusterManagerNodeByAbbreviatedName(name);
6170 if (n == NULL((void*)0)) {
6171 clusterManagerLogErr("*** No such master node %s\n", name)clusterManagerLog(3,"*** No such master node %s\n", name);
6172 result = 0;
6173 goto cleanup;
6174 }
6175 n->weight = w;
6176 }
6177 }
6178 float total_weight = 0;
6179 int nodes_involved = 0;
6180 int use_empty = config.cluster_manager_command.flags &
6181 CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER1 << 4;
6182 involved = listCreate();
6183 listIter li;
6184 listNode *ln;
6185 listRewind(cluster_manager.nodes, &li);
6186 /* Compute the total cluster weight. */
6187 while ((ln = listNext(&li)) != NULL((void*)0)) {
6188 clusterManagerNode *n = ln->value;
6189 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1 || n->replicate)
6190 continue;
6191 if (!use_empty && n->slots_count == 0) {
6192 n->weight = 0;
6193 continue;
6194 }
6195 total_weight += n->weight;
6196 nodes_involved++;
6197 listAddNodeTail(involved, n);
6198 }
6199 weightedNodes = zmalloc(nodes_involved * sizeof(clusterManagerNode *));
6200 if (weightedNodes == NULL((void*)0)) goto cleanup;
6201 /* Check cluster, only proceed if it looks sane. */
6202 clusterManagerCheckCluster(1);
6203 if (cluster_manager.errors && listLength(cluster_manager.errors)((cluster_manager.errors)->len) > 0) {
6204 clusterManagerLogErr("*** Please fix your cluster problems "clusterManagerLog(3,"*** Please fix your cluster problems " "before rebalancing\n"
)
6205 "before rebalancing\n")clusterManagerLog(3,"*** Please fix your cluster problems " "before rebalancing\n"
)
;
6206 result = 0;
6207 goto cleanup;
6208 }
6209 /* Calculate the slots balance for each node. It's the number of
6210 * slots the node should lose (if positive) or gain (if negative)
6211 * in order to be balanced. */
6212 int threshold_reached = 0, total_balance = 0;
6213 float threshold = config.cluster_manager_command.threshold;
6214 i = 0;
6215 listRewind(involved, &li);
6216 while ((ln = listNext(&li)) != NULL((void*)0)) {
6217 clusterManagerNode *n = ln->value;
6218 weightedNodes[i++] = n;
6219 int expected = (int) (((float)CLUSTER_MANAGER_SLOTS16384 / total_weight) *
6220 n->weight);
6221 n->balance = n->slots_count - expected;
6222 total_balance += n->balance;
6223 /* Compute the percentage of difference between the
6224 * expected number of slots and the real one, to see
6225 * if it's over the threshold specified by the user. */
6226 int over_threshold = 0;
6227 if (threshold > 0) {
6228 if (n->slots_count > 0) {
6229 float err_perc = fabs((100-(100.0*expected/n->slots_count)));
6230 if (err_perc > threshold) over_threshold = 1;
6231 } else if (expected > 1) {
6232 over_threshold = 1;
6233 }
6234 }
6235 if (over_threshold) threshold_reached = 1;
6236 }
6237 if (!threshold_reached) {
6238 clusterManagerLogWarn("*** No rebalancing needed! "clusterManagerLog(2,"*** No rebalancing needed! " "All nodes are within the %.2f%% threshold.\n"
, config.cluster_manager_command.threshold)
6239 "All nodes are within the %.2f%% threshold.\n",clusterManagerLog(2,"*** No rebalancing needed! " "All nodes are within the %.2f%% threshold.\n"
, config.cluster_manager_command.threshold)
6240 config.cluster_manager_command.threshold)clusterManagerLog(2,"*** No rebalancing needed! " "All nodes are within the %.2f%% threshold.\n"
, config.cluster_manager_command.threshold)
;
6241 goto cleanup;
6242 }
6243 /* Because of rounding, it is possible that the balance of all nodes
6244 * summed does not give 0. Make sure that nodes that have to provide
6245 * slots are always matched by nodes receiving slots. */
6246 while (total_balance > 0) {
6247 listRewind(involved, &li);
6248 while ((ln = listNext(&li)) != NULL((void*)0)) {
6249 clusterManagerNode *n = ln->value;
6250 if (n->balance <= 0 && total_balance > 0) {
6251 n->balance--;
6252 total_balance--;
6253 }
6254 }
6255 }
6256 /* Sort nodes by their slots balance. */
6257 qsort(weightedNodes, nodes_involved, sizeof(clusterManagerNode *),
6258 clusterManagerCompareNodeBalance);
6259 clusterManagerLogInfo(">>> Rebalancing across %d nodes. "clusterManagerLog(1,">>> Rebalancing across %d nodes. "
"Total weight = %.2f\n", nodes_involved, total_weight)
6260 "Total weight = %.2f\n",clusterManagerLog(1,">>> Rebalancing across %d nodes. "
"Total weight = %.2f\n", nodes_involved, total_weight)
6261 nodes_involved, total_weight)clusterManagerLog(1,">>> Rebalancing across %d nodes. "
"Total weight = %.2f\n", nodes_involved, total_weight)
;
6262 if (config.verbose) {
6263 for (i = 0; i < nodes_involved; i++) {
6264 clusterManagerNode *n = weightedNodes[i];
6265 printf("%s:%d balance is %d slots\n", n->ip, n->port, n->balance);
6266 }
6267 }
6268 /* Now we have at the start of the 'sn' array nodes that should get
6269 * slots, at the end nodes that must give slots.
6270 * We take two indexes, one at the start, and one at the end,
6271 * incrementing or decrementing the indexes accordingly til we
6272 * find nodes that need to get/provide slots. */
6273 int dst_idx = 0;
6274 int src_idx = nodes_involved - 1;
6275 int simulate = config.cluster_manager_command.flags &
6276 CLUSTER_MANAGER_CMD_FLAG_SIMULATE1 << 5;
6277 while (dst_idx < src_idx) {
6278 clusterManagerNode *dst = weightedNodes[dst_idx];
6279 clusterManagerNode *src = weightedNodes[src_idx];
6280 int db = abs(dst->balance);
6281 int sb = abs(src->balance);
6282 int numslots = (db < sb ? db : sb);
6283 if (numslots > 0) {
6284 printf("Moving %d slots from %s:%d to %s:%d\n", numslots,
6285 src->ip,
6286 src->port,
6287 dst->ip,
6288 dst->port);
6289 /* Actually move the slots. */
6290 list *lsrc = listCreate(), *table = NULL((void*)0);
6291 listAddNodeTail(lsrc, src);
6292 table = clusterManagerComputeReshardTable(lsrc, numslots);
6293 listRelease(lsrc);
6294 int table_len = (int) listLength(table)((table)->len);
6295 if (!table || table_len != numslots) {
6296 clusterManagerLogErr("*** Assertion failed: Reshard table "clusterManagerLog(3,"*** Assertion failed: Reshard table " "!= number of slots"
)
6297 "!= number of slots")clusterManagerLog(3,"*** Assertion failed: Reshard table " "!= number of slots"
)
;
6298 result = 0;
6299 goto end_move;
6300 }
6301 if (simulate) {
6302 for (i = 0; i < table_len; i++) printf("#");
6303 } else {
6304 int opts = CLUSTER_MANAGER_OPT_QUIET1 << 6 |
6305 CLUSTER_MANAGER_OPT_UPDATE1 << 2;
6306 listRewind(table, &li);
6307 while ((ln = listNext(&li)) != NULL((void*)0)) {
6308 clusterManagerReshardTableItem *item = ln->value;
6309 result = clusterManagerMoveSlot(item->source,
6310 dst,
6311 item->slot,
6312 opts, NULL((void*)0));
6313 if (!result) goto end_move;
6314 printf("#");
6315 fflush(stdoutstdout);
6316 }
6317
6318 }
6319 printf("\n");
6320end_move:
6321 clusterManagerReleaseReshardTable(table);
6322 if (!result) goto cleanup;
6323 }
6324 /* Update nodes balance. */
6325 dst->balance += numslots;
6326 src->balance -= numslots;
6327 if (dst->balance == 0) dst_idx++;
6328 if (src->balance == 0) src_idx --;
6329 }
6330cleanup:
6331 if (involved != NULL((void*)0)) listRelease(involved);
6332 if (weightedNodes != NULL((void*)0)) zfree(weightedNodes);
6333 return result;
6334invalid_args:
6335 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
6336 return 0;
6337}
6338
6339static int clusterManagerCommandSetTimeout(int argc, char **argv) {
6340 UNUSED(argc)((void) argc);
6341 int port = 0;
6342 char *ip = NULL((void*)0);
6343 if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
6344 int timeout = atoi(argv[1]);
6345 if (timeout < 100) {
6346 fprintf(stderrstderr, "Setting a node timeout of less than 100 "
6347 "milliseconds is a bad idea.\n");
6348 return 0;
6349 }
6350 // Load cluster information
6351 clusterManagerNode *node = clusterManagerNewNode(ip, port);
6352 if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
6353 int ok_count = 0, err_count = 0;
6354
6355 clusterManagerLogInfo(">>> Reconfiguring node timeout in every "clusterManagerLog(1,">>> Reconfiguring node timeout in every "
"cluster node...\n")
6356 "cluster node...\n")clusterManagerLog(1,">>> Reconfiguring node timeout in every "
"cluster node...\n")
;
6357 listIter li;
6358 listNode *ln;
6359 listRewind(cluster_manager.nodes, &li);
6360 while ((ln = listNext(&li)) != NULL((void*)0)) {
6361 clusterManagerNode *n = ln->value;
6362 char *err = NULL((void*)0);
6363 redisReply *reply = CLUSTER_MANAGER_COMMAND(n, "CONFIG %s %s %d",(redisCommand(n->context, "CONFIG %s %s %d", "SET", "cluster-node-timeout"
, timeout))
6364 "SET",(redisCommand(n->context, "CONFIG %s %s %d", "SET", "cluster-node-timeout"
, timeout))
6365 "cluster-node-timeout",(redisCommand(n->context, "CONFIG %s %s %d", "SET", "cluster-node-timeout"
, timeout))
6366 timeout)(redisCommand(n->context, "CONFIG %s %s %d", "SET", "cluster-node-timeout"
, timeout))
;
6367 if (reply == NULL((void*)0)) goto reply_err;
6368 int ok = clusterManagerCheckRedisReply(n, reply, &err);
6369 freeReplyObject(reply);
6370 if (!ok) goto reply_err;
6371 reply = CLUSTER_MANAGER_COMMAND(n, "CONFIG %s", "REWRITE")(redisCommand(n->context, "CONFIG %s", "REWRITE"));
6372 if (reply == NULL((void*)0)) goto reply_err;
6373 ok = clusterManagerCheckRedisReply(n, reply, &err);
6374 freeReplyObject(reply);
6375 if (!ok) goto reply_err;
6376 clusterManagerLogWarn("*** New timeout set for %s:%d\n", n->ip,clusterManagerLog(2,"*** New timeout set for %s:%d\n", n->
ip, n->port)
6377 n->port)clusterManagerLog(2,"*** New timeout set for %s:%d\n", n->
ip, n->port)
;
6378 ok_count++;
6379 continue;
6380reply_err:;
6381 int need_free = 0;
6382 if (err == NULL((void*)0)) err = "";
6383 else need_free = 1;
6384 clusterManagerLogErr("ERR setting node-timeot for %s:%d: %s\n", n->ip,clusterManagerLog(3,"ERR setting node-timeot for %s:%d: %s\n"
, n->ip, n->port, err)
6385 n->port, err)clusterManagerLog(3,"ERR setting node-timeot for %s:%d: %s\n"
, n->ip, n->port, err)
;
6386 if (need_free) zfree(err);
6387 err_count++;
6388 }
6389 clusterManagerLogInfo(">>> New node timeout set. %d OK, %d ERR.\n",clusterManagerLog(1,">>> New node timeout set. %d OK, %d ERR.\n"
, ok_count, err_count)
6390 ok_count, err_count)clusterManagerLog(1,">>> New node timeout set. %d OK, %d ERR.\n"
, ok_count, err_count)
;
6391 return 1;
6392invalid_args:
6393 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
6394 return 0;
6395}
6396
6397static int clusterManagerCommandImport(int argc, char **argv) {
6398 int success = 1;
6399 int port = 0, src_port = 0;
6400 char *ip = NULL((void*)0), *src_ip = NULL((void*)0);
6401 char *invalid_args_msg = NULL((void*)0);
6402 sdshisds cmdfmt = NULL((void*)0);
6403 if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) {
6404 invalid_args_msg = CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
;
6405 goto invalid_args;
6406 }
6407 if (config.cluster_manager_command.from == NULL((void*)0)) {
6408 invalid_args_msg = "[ERR] Option '--cluster-from' is required for "
6409 "subcommand 'import'.\n";
6410 goto invalid_args;
6411 }
6412 char *src_host[] = {config.cluster_manager_command.from};
6413 if (!getClusterHostFromCmdArgs(1, src_host, &src_ip, &src_port)) {
6414 invalid_args_msg = "[ERR] Invalid --cluster-from host. You need to "
6415 "pass a valid address (ie. 120.0.0.1:7000).\n";
6416 goto invalid_args;
6417 }
6418 clusterManagerLogInfo(">>> Importing data from %s:%d to cluster %s:%d\n",clusterManagerLog(1,">>> Importing data from %s:%d to cluster %s:%d\n"
, src_ip, src_port, ip, port)
6419 src_ip, src_port, ip, port)clusterManagerLog(1,">>> Importing data from %s:%d to cluster %s:%d\n"
, src_ip, src_port, ip, port)
;
6420
6421 clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
6422 if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
6423 if (!clusterManagerCheckCluster(0)) return 0;
6424 char *reply_err = NULL((void*)0);
6425 redisReply *src_reply = NULL((void*)0);
6426 // Connect to the source node.
6427 redisContext *src_ctx = redisConnect(src_ip, src_port);
6428 if (src_ctx->err) {
6429 success = 0;
6430 fprintf(stderrstderr,"Could not connect to Redis at %s:%d: %s.\n", src_ip,
6431 src_port, src_ctx->errstr);
6432 goto cleanup;
6433 }
6434 // Auth for the source node.
6435 char *from_user = config.cluster_manager_command.from_user;
6436 char *from_pass = config.cluster_manager_command.from_pass;
6437 if (cliAuth(src_ctx, from_user, from_pass) == REDIS_ERR-1) {
6438 success = 0;
6439 goto cleanup;
6440 }
6441
6442 src_reply = reconnectingRedisCommand(src_ctx, "INFO");
6443 if (!src_reply || src_reply->type == REDIS_REPLY_ERROR6) {
6444 if (src_reply && src_reply->str) reply_err = src_reply->str;
6445 success = 0;
6446 goto cleanup;
6447 }
6448 if (getLongInfoField(src_reply->str, "cluster_enabled")) {
6449 clusterManagerLogErr("[ERR] The source node should not be a "clusterManagerLog(3,"[ERR] The source node should not be a " "cluster node.\n"
)
6450 "cluster node.\n")clusterManagerLog(3,"[ERR] The source node should not be a " "cluster node.\n"
)
;
6451 success = 0;
6452 goto cleanup;
6453 }
6454 freeReplyObject(src_reply);
6455 src_reply = reconnectingRedisCommand(src_ctx, "DBSIZE");
6456 if (!src_reply || src_reply->type == REDIS_REPLY_ERROR6) {
6457 if (src_reply && src_reply->str) reply_err = src_reply->str;
6458 success = 0;
6459 goto cleanup;
6460 }
6461 int size = src_reply->integer, i;
6462 clusterManagerLogWarn("*** Importing %d keys from DB 0\n", size)clusterManagerLog(2,"*** Importing %d keys from DB 0\n", size
)
;
6463
6464 // Build a slot -> node map
6465 clusterManagerNode *slots_map[CLUSTER_MANAGER_SLOTS16384];
6466 memset(slots_map, 0, sizeof(slots_map));
6467 listIter li;
6468 listNode *ln;
6469 for (i = 0; i < CLUSTER_MANAGER_SLOTS16384; i++) {
6470 listRewind(cluster_manager.nodes, &li);
6471 while ((ln = listNext(&li)) != NULL((void*)0)) {
6472 clusterManagerNode *n = ln->value;
6473 if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE1 << 1) continue;
6474 if (n->slots_count == 0) continue;
6475 if (n->slots[i]) {
6476 slots_map[i] = n;
6477 break;
6478 }
6479 }
6480 }
6481 cmdfmt = sdsnewhi_sdsnew("MIGRATE %s %d %s %d %d");
6482 if (config.auth) {
6483 if (config.user) {
6484 cmdfmt = sdscatfmthi_sdscatfmt(cmdfmt," AUTH2 %s %s", config.user, config.auth);
6485 } else {
6486 cmdfmt = sdscatfmthi_sdscatfmt(cmdfmt," AUTH %s", config.auth);
6487 }
6488 }
6489
6490 if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COPY1 << 7)
6491 strcat(cmdfmt, " %s");
6492 if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_REPLACE1 << 6)
6493 strcat(cmdfmt, " %s");
6494
6495 /* Use SCAN to iterate over the keys, migrating to the
6496 * right node as needed. */
6497 int cursor = -999, timeout = config.cluster_manager_command.timeout;
6498 while (cursor != 0) {
6499 if (cursor < 0) cursor = 0;
6500 freeReplyObject(src_reply);
6501 src_reply = reconnectingRedisCommand(src_ctx, "SCAN %d COUNT %d",
6502 cursor, 1000);
6503 if (!src_reply || src_reply->type == REDIS_REPLY_ERROR6) {
6504 if (src_reply && src_reply->str) reply_err = src_reply->str;
6505 success = 0;
6506 goto cleanup;
6507 }
6508 assert(src_reply->type == REDIS_REPLY_ARRAY)((src_reply->type == 2) ? (void) (0) : __assert_fail ("src_reply->type == REDIS_REPLY_ARRAY"
, "redis-cli.c", 6508, __extension__ __PRETTY_FUNCTION__))
;
6509 assert(src_reply->elements >= 2)((src_reply->elements >= 2) ? (void) (0) : __assert_fail
("src_reply->elements >= 2", "redis-cli.c", 6509, __extension__
__PRETTY_FUNCTION__))
;
6510 assert(src_reply->element[1]->type == REDIS_REPLY_ARRAY)((src_reply->element[1]->type == 2) ? (void) (0) : __assert_fail
("src_reply->element[1]->type == REDIS_REPLY_ARRAY", "redis-cli.c"
, 6510, __extension__ __PRETTY_FUNCTION__))
;
6511 if (src_reply->element[0]->type == REDIS_REPLY_STRING1)
6512 cursor = atoi(src_reply->element[0]->str);
6513 else if (src_reply->element[0]->type == REDIS_REPLY_INTEGER3)
6514 cursor = src_reply->element[0]->integer;
6515 int keycount = src_reply->element[1]->elements;
6516 for (i = 0; i < keycount; i++) {
6517 redisReply *kr = src_reply->element[1]->element[i];
6518 assert(kr->type == REDIS_REPLY_STRING)((kr->type == 1) ? (void) (0) : __assert_fail ("kr->type == REDIS_REPLY_STRING"
, "redis-cli.c", 6518, __extension__ __PRETTY_FUNCTION__))
;
6519 char *key = kr->str;
6520 uint16_t slot = clusterManagerKeyHashSlot(key, kr->len);
6521 clusterManagerNode *target = slots_map[slot];
6522 printf("Migrating %s to %s:%d: ", key, target->ip, target->port);
6523 redisReply *r = reconnectingRedisCommand(src_ctx, cmdfmt,
6524 target->ip, target->port,
6525 key, 0, timeout,
6526 "COPY", "REPLACE");
6527 if (!r || r->type == REDIS_REPLY_ERROR6) {
6528 if (r && r->str) {
6529 clusterManagerLogErr("Source %s:%d replied with "clusterManagerLog(3,"Source %s:%d replied with " "error:\n%s\n"
, src_ip, src_port, r->str)
6530 "error:\n%s\n", src_ip, src_port,clusterManagerLog(3,"Source %s:%d replied with " "error:\n%s\n"
, src_ip, src_port, r->str)
6531 r->str)clusterManagerLog(3,"Source %s:%d replied with " "error:\n%s\n"
, src_ip, src_port, r->str)
;
6532 }
6533 success = 0;
6534 }
6535 freeReplyObject(r);
6536 if (!success) goto cleanup;
6537 clusterManagerLogOk("OK\n")clusterManagerLog(4,"OK\n");
6538 }
6539 }
6540cleanup:
6541 if (reply_err)
6542 clusterManagerLogErr("Source %s:%d replied with error:\n%s\n",clusterManagerLog(3,"Source %s:%d replied with error:\n%s\n",
src_ip, src_port, reply_err)
6543 src_ip, src_port, reply_err)clusterManagerLog(3,"Source %s:%d replied with error:\n%s\n",
src_ip, src_port, reply_err)
;
6544 if (src_ctx) redisFree(src_ctx);
6545 if (src_reply) freeReplyObject(src_reply);
6546 if (cmdfmt) sdsfreehi_sdsfree(cmdfmt);
6547 return success;
6548invalid_args:
6549 fprintf(stderrstderr, "%s", invalid_args_msg);
6550 return 0;
6551}
6552
6553static int clusterManagerCommandCall(int argc, char **argv) {
6554 int port = 0, i;
6555 char *ip = NULL((void*)0);
6556 if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
6557 clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
6558 if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
6559 argc--;
6560 argv++;
6561 size_t *argvlen = zmalloc(argc*sizeof(size_t));
6562 clusterManagerLogInfo(">>> Calling")clusterManagerLog(1,">>> Calling");
6563 for (i = 0; i < argc; i++) {
6564 argvlen[i] = strlen(argv[i]);
6565 printf(" %s", argv[i]);
6566 }
6567 printf("\n");
6568 listIter li;
6569 listNode *ln;
6570 listRewind(cluster_manager.nodes, &li);
6571 while ((ln = listNext(&li)) != NULL((void*)0)) {
6572 clusterManagerNode *n = ln->value;
6573 if ((config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY1 << 11)
6574 && (n->replicate != NULL((void*)0))) continue; // continue if node is slave
6575 if ((config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY1 << 12)
6576 && (n->replicate == NULL((void*)0))) continue; // continue if node is master
6577 if (!n->context && !clusterManagerNodeConnect(n)) continue;
6578 redisReply *reply = NULL((void*)0);
6579 redisAppendCommandArgv(n->context, argc, (const char **) argv, argvlen);
6580 int status = redisGetReply(n->context, (void **)(&reply));
6581 if (status != REDIS_OK0 || reply == NULL((void*)0) )
6582 printf("%s:%d: Failed!\n", n->ip, n->port);
6583 else {
6584 sdshisds formatted_reply = cliFormatReplyRaw(reply);
6585 printf("%s:%d: %s\n", n->ip, n->port, (char *) formatted_reply);
6586 sdsfreehi_sdsfree(formatted_reply);
6587 }
6588 if (reply != NULL((void*)0)) freeReplyObject(reply);
6589 }
6590 zfree(argvlen);
6591 return 1;
6592invalid_args:
6593 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
6594 return 0;
6595}
6596
6597static int clusterManagerCommandBackup(int argc, char **argv) {
6598 UNUSED(argc)((void) argc);
6599 int success = 1, port = 0;
6600 char *ip = NULL((void*)0);
6601 if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
6602 clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
6603 if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
6604 int no_issues = clusterManagerCheckCluster(0);
6605 int cluster_errors_count = (no_issues ? 0 :
6606 listLength(cluster_manager.errors)((cluster_manager.errors)->len));
6607 config.cluster_manager_command.backup_dir = argv[1];
6608 /* TODO: check if backup_dir is a valid directory. */
6609 sdshisds json = sdsnewhi_sdsnew("[\n");
6610 int first_node = 0;
6611 listIter li;
6612 listNode *ln;
6613 listRewind(cluster_manager.nodes, &li);
6614 while ((ln = listNext(&li)) != NULL((void*)0)) {
6615 if (!first_node) first_node = 1;
6616 else json = sdscathi_sdscat(json, ",\n");
6617 clusterManagerNode *node = ln->value;
6618 sdshisds node_json = clusterManagerNodeGetJSON(node, cluster_errors_count);
6619 json = sdscathi_sdscat(json, node_json);
6620 sdsfreehi_sdsfree(node_json);
6621 if (node->replicate)
6622 continue;
6623 clusterManagerLogInfo(">>> Node %s:%d -> Saving RDB...\n",clusterManagerLog(1,">>> Node %s:%d -> Saving RDB...\n"
, node->ip, node->port)
6624 node->ip, node->port)clusterManagerLog(1,">>> Node %s:%d -> Saving RDB...\n"
, node->ip, node->port)
;
6625 fflush(stdoutstdout);
6626 getRDB(node);
6627 }
6628 json = sdscathi_sdscat(json, "\n]");
6629 sdshisds jsonpath = sdsnewhi_sdsnew(config.cluster_manager_command.backup_dir);
6630 if (jsonpath[sdslenhi_sdslen(jsonpath) - 1] != '/')
6631 jsonpath = sdscathi_sdscat(jsonpath, "/");
6632 jsonpath = sdscathi_sdscat(jsonpath, "nodes.json");
6633 fflush(stdoutstdout);
6634 clusterManagerLogInfo("Saving cluster configuration to: %s\n", jsonpath)clusterManagerLog(1,"Saving cluster configuration to: %s\n", jsonpath
)
;
6635 FILE *out = fopen(jsonpath, "w+");
6636 if (!out) {
6637 clusterManagerLogErr("Could not save nodes to: %s\n", jsonpath)clusterManagerLog(3,"Could not save nodes to: %s\n", jsonpath
)
;
6638 success = 0;
6639 goto cleanup;
6640 }
6641 fputs(json, out);
6642 fclose(out);
6643cleanup:
6644 sdsfreehi_sdsfree(json);
6645 sdsfreehi_sdsfree(jsonpath);
6646 if (success) {
6647 if (!no_issues) {
6648 clusterManagerLogWarn("*** Cluster seems to have some problems, "clusterManagerLog(2,"*** Cluster seems to have some problems, "
"please be aware of it if you're going " "to restore this backup.\n"
)
6649 "please be aware of it if you're going "clusterManagerLog(2,"*** Cluster seems to have some problems, "
"please be aware of it if you're going " "to restore this backup.\n"
)
6650 "to restore this backup.\n")clusterManagerLog(2,"*** Cluster seems to have some problems, "
"please be aware of it if you're going " "to restore this backup.\n"
)
;
6651 }
6652 clusterManagerLogOk("[OK] Backup created into: %s\n",clusterManagerLog(4,"[OK] Backup created into: %s\n", config.
cluster_manager_command.backup_dir)
6653 config.cluster_manager_command.backup_dir)clusterManagerLog(4,"[OK] Backup created into: %s\n", config.
cluster_manager_command.backup_dir)
;
6654 } else clusterManagerLogOk("[ERR] Failed to back cluster!\n")clusterManagerLog(4,"[ERR] Failed to back cluster!\n");
6655 return success;
6656invalid_args:
6657 fprintf(stderrstderr, CLUSTER_MANAGER_INVALID_HOST_ARG"[ERR] Invalid arguments: you need to pass either a valid " "address (ie. 120.0.0.1:7000) or space separated IP "
"and port (ie. 120.0.0.1 7000)\n"
);
6658 return 0;
6659}
6660
6661static int clusterManagerCommandHelp(int argc, char **argv) {
6662 UNUSED(argc)((void) argc);
6663 UNUSED(argv)((void) argv);
6664 int commands_count = sizeof(clusterManagerCommands) /
6665 sizeof(clusterManagerCommandDef);
6666 int i = 0, j;
6667 fprintf(stderrstderr, "Cluster Manager Commands:\n");
6668 int padding = 15;
6669 for (; i < commands_count; i++) {
6670 clusterManagerCommandDef *def = &(clusterManagerCommands[i]);
6671 int namelen = strlen(def->name), padlen = padding - namelen;
6672 fprintf(stderrstderr, " %s", def->name);
6673 for (j = 0; j < padlen; j++) fprintf(stderrstderr, " ");
6674 fprintf(stderrstderr, "%s\n", (def->args ? def->args : ""));
6675 if (def->options != NULL((void*)0)) {
6676 int optslen = strlen(def->options);
6677 char *p = def->options, *eos = p + optslen;
6678 char *comma = NULL((void*)0);
6679 while ((comma = strchr(p, ',')) != NULL((void*)0)) {
6680 int deflen = (int)(comma - p);
6681 char buf[255];
6682 memcpy(buf, p, deflen);
6683 buf[deflen] = '\0';
6684 for (j = 0; j < padding; j++) fprintf(stderrstderr, " ");
6685 fprintf(stderrstderr, " --cluster-%s\n", buf);
6686 p = comma + 1;
6687 if (p >= eos) break;
6688 }
6689 if (p < eos) {
6690 for (j = 0; j < padding; j++) fprintf(stderrstderr, " ");
6691 fprintf(stderrstderr, " --cluster-%s\n", p);
6692 }
6693 }
6694 }
6695 fprintf(stderrstderr, "\nFor check, fix, reshard, del-node, set-timeout you "
6696 "can specify the host and port of any working node in "
6697 "the cluster.\n");
6698
6699 int options_count = sizeof(clusterManagerOptions) /
6700 sizeof(clusterManagerOptionDef);
6701 i = 0;
6702 fprintf(stderrstderr, "\nCluster Manager Options:\n");
6703 for (; i < options_count; i++) {
6704 clusterManagerOptionDef *def = &(clusterManagerOptions[i]);
6705 int namelen = strlen(def->name), padlen = padding - namelen;
6706 fprintf(stderrstderr, " %s", def->name);
6707 for (j = 0; j < padlen; j++) fprintf(stderrstderr, " ");
6708 fprintf(stderrstderr, "%s\n", def->desc);
6709 }
6710
6711 fprintf(stderrstderr, "\n");
6712 return 0;
6713}
6714
6715/*------------------------------------------------------------------------------
6716 * Latency and latency history modes
6717 *--------------------------------------------------------------------------- */
6718
6719static void latencyModePrint(long long min, long long max, double avg, long long count) {
6720 if (config.output == OUTPUT_STANDARD0) {
6721 printf("min: %lld, max: %lld, avg: %.2f (%lld samples)",
6722 min, max, avg, count);
6723 fflush(stdoutstdout);
6724 } else if (config.output == OUTPUT_CSV2) {
6725 printf("%lld,%lld,%.2f,%lld\n", min, max, avg, count);
6726 } else if (config.output == OUTPUT_RAW1) {
6727 printf("%lld %lld %.2f %lld\n", min, max, avg, count);
6728 }
6729}
6730
6731#define LATENCY_SAMPLE_RATE10 10 /* milliseconds. */
6732#define LATENCY_HISTORY_DEFAULT_INTERVAL15000 15000 /* milliseconds. */
6733static void latencyMode(void) {
6734 redisReply *reply;
6735 long long start, latency, min = 0, max = 0, tot = 0, count = 0;
6736 long long history_interval =
6737 config.interval ? config.interval/1000 :
6738 LATENCY_HISTORY_DEFAULT_INTERVAL15000;
6739 double avg;
6740 long long history_start = mstime();
6741
6742 /* Set a default for the interval in case of --latency option
6743 * with --raw, --csv or when it is redirected to non tty. */
6744 if (config.interval == 0) {
6745 config.interval = 1000;
6746 } else {
6747 config.interval /= 1000; /* We need to convert to milliseconds. */
6748 }
6749
6750 if (!context) exit(1);
6751 while(1) {
6752 start = mstime();
6753 reply = reconnectingRedisCommand(context,"PING");
6754 if (reply == NULL((void*)0)) {
6755 fprintf(stderrstderr,"\nI/O error\n");
6756 exit(1);
6757 }
6758 latency = mstime()-start;
6759 freeReplyObject(reply);
6760 count++;
6761 if (count == 1) {
6762 min = max = tot = latency;
6763 avg = (double) latency;
6764 } else {
6765 if (latency < min) min = latency;
6766 if (latency > max) max = latency;
6767 tot += latency;
6768 avg = (double) tot/count;
6769 }
6770
6771 if (config.output == OUTPUT_STANDARD0) {
6772 printf("\x1b[0G\x1b[2K"); /* Clear the line. */
6773 latencyModePrint(min,max,avg,count);
6774 } else {
6775 if (config.latency_history) {
6776 latencyModePrint(min,max,avg,count);
6777 } else if (mstime()-history_start > config.interval) {
6778 latencyModePrint(min,max,avg,count);
6779 exit(0);
6780 }
6781 }
6782
6783 if (config.latency_history && mstime()-history_start > history_interval)
6784 {
6785 printf(" -- %.2f seconds range\n", (float)(mstime()-history_start)/1000);
6786 history_start = mstime();
6787 min = max = tot = count = 0;
6788 }
6789 usleep(LATENCY_SAMPLE_RATE10 * 1000);
6790 }
6791}
6792
6793/*------------------------------------------------------------------------------
6794 * Latency distribution mode -- requires 256 colors xterm
6795 *--------------------------------------------------------------------------- */
6796
6797#define LATENCY_DIST_DEFAULT_INTERVAL1000 1000 /* milliseconds. */
6798
6799/* Structure to store samples distribution. */
6800struct distsamples {
6801 long long max; /* Max latency to fit into this interval (usec). */
6802 long long count; /* Number of samples in this interval. */
6803 int character; /* Associated character in visualization. */
6804};
6805
6806/* Helper function for latencyDistMode(). Performs the spectrum visualization
6807 * of the collected samples targeting an xterm 256 terminal.
6808 *
6809 * Takes an array of distsamples structures, ordered from smaller to bigger
6810 * 'max' value. Last sample max must be 0, to mean that it olds all the
6811 * samples greater than the previous one, and is also the stop sentinel.
6812 *
6813 * "tot' is the total number of samples in the different buckets, so it
6814 * is the SUM(samples[i].count) for i to 0 up to the max sample.
6815 *
6816 * As a side effect the function sets all the buckets count to 0. */
6817void showLatencyDistSamples(struct distsamples *samples, long long tot) {
6818 int j;
6819
6820 /* We convert samples into an index inside the palette
6821 * proportional to the percentage a given bucket represents.
6822 * This way intensity of the different parts of the spectrum
6823 * don't change relative to the number of requests, which avoids to
6824 * pollute the visualization with non-latency related info. */
6825 printf("\033[38;5;0m"); /* Set foreground color to black. */
6826 for (j = 0; ; j++) {
6827 int coloridx =
6828 ceil((float) samples[j].count / tot * (spectrum_palette_size-1));
6829 int color = spectrum_palette[coloridx];
6830 printf("\033[48;5;%dm%c", (int)color, samples[j].character);
6831 samples[j].count = 0;
6832 if (samples[j].max == 0) break; /* Last sample. */
6833 }
6834 printf("\033[0m\n");
6835 fflush(stdoutstdout);
6836}
6837
6838/* Show the legend: different buckets values and colors meaning, so
6839 * that the spectrum is more easily readable. */
6840void showLatencyDistLegend(void) {
6841 int j;
6842
6843 printf("---------------------------------------------\n");
6844 printf(". - * # .01 .125 .25 .5 milliseconds\n");
6845 printf("1,2,3,...,9 from 1 to 9 milliseconds\n");
6846 printf("A,B,C,D,E 10,20,30,40,50 milliseconds\n");
6847 printf("F,G,H,I,J .1,.2,.3,.4,.5 seconds\n");
6848 printf("K,L,M,N,O,P,Q,? 1,2,4,8,16,30,60,>60 seconds\n");
6849 printf("From 0 to 100%%: ");
6850 for (j = 0; j < spectrum_palette_size; j++) {
6851 printf("\033[48;5;%dm ", spectrum_palette[j]);
6852 }
6853 printf("\033[0m\n");
6854 printf("---------------------------------------------\n");
6855}
6856
6857static void latencyDistMode(void) {
6858 redisReply *reply;
6859 long long start, latency, count = 0;
6860 long long history_interval =
6861 config.interval ? config.interval/1000 :
6862 LATENCY_DIST_DEFAULT_INTERVAL1000;
6863 long long history_start = ustime();
6864 int j, outputs = 0;
6865
6866 struct distsamples samples[] = {
6867 /* We use a mostly logarithmic scale, with certain linear intervals
6868 * which are more interesting than others, like 1-10 milliseconds
6869 * range. */
6870 {10,0,'.'}, /* 0.01 ms */
6871 {125,0,'-'}, /* 0.125 ms */
6872 {250,0,'*'}, /* 0.25 ms */
6873 {500,0,'#'}, /* 0.5 ms */
6874 {1000,0,'1'}, /* 1 ms */
6875 {2000,0,'2'}, /* 2 ms */
6876 {3000,0,'3'}, /* 3 ms */
6877 {4000,0,'4'}, /* 4 ms */
6878 {5000,0,'5'}, /* 5 ms */
6879 {6000,0,'6'}, /* 6 ms */
6880 {7000,0,'7'}, /* 7 ms */
6881 {8000,0,'8'}, /* 8 ms */
6882 {9000,0,'9'}, /* 9 ms */
6883 {10000,0,'A'}, /* 10 ms */
6884 {20000,0,'B'}, /* 20 ms */
6885 {30000,0,'C'}, /* 30 ms */
6886 {40000,0,'D'}, /* 40 ms */
6887 {50000,0,'E'}, /* 50 ms */
6888 {100000,0,'F'}, /* 0.1 s */
6889 {200000,0,'G'}, /* 0.2 s */
6890 {300000,0,'H'}, /* 0.3 s */
6891 {400000,0,'I'}, /* 0.4 s */
6892 {500000,0,'J'}, /* 0.5 s */
6893 {1000000,0,'K'}, /* 1 s */
6894 {2000000,0,'L'}, /* 2 s */
6895 {4000000,0,'M'}, /* 4 s */
6896 {8000000,0,'N'}, /* 8 s */
6897 {16000000,0,'O'}, /* 16 s */
6898 {30000000,0,'P'}, /* 30 s */
6899 {60000000,0,'Q'}, /* 1 minute */
6900 {0,0,'?'}, /* > 1 minute */
6901 };
6902
6903 if (!context) exit(1);
6904 while(1) {
6905 start = ustime();
6906 reply = reconnectingRedisCommand(context,"PING");
6907 if (reply == NULL((void*)0)) {
6908 fprintf(stderrstderr,"\nI/O error\n");
6909 exit(1);
6910 }
6911 latency = ustime()-start;
6912 freeReplyObject(reply);
6913 count++;
6914
6915 /* Populate the relevant bucket. */
6916 for (j = 0; ; j++) {
6917 if (samples[j].max == 0 || latency <= samples[j].max) {
6918 samples[j].count++;
6919 break;
6920 }
6921 }
6922
6923 /* From time to time show the spectrum. */
6924 if (count && (ustime()-history_start)/1000 > history_interval) {
6925 if ((outputs++ % 20) == 0)
6926 showLatencyDistLegend();
6927 showLatencyDistSamples(samples,count);
6928 history_start = ustime();
6929 count = 0;
6930 }
6931 usleep(LATENCY_SAMPLE_RATE10 * 1000);
6932 }
6933}
6934
6935/*------------------------------------------------------------------------------
6936 * Slave mode
6937 *--------------------------------------------------------------------------- */
6938
6939#define RDB_EOF_MARK_SIZE40 40
6940
6941void sendReplconf(const char* arg1, const char* arg2) {
6942 printf("sending REPLCONF %s %s\n", arg1, arg2);
6943 redisReply *reply = redisCommand(context, "REPLCONF %s %s", arg1, arg2);
6944
6945 /* Handle any error conditions */
6946 if(reply == NULL((void*)0)) {
6947 fprintf(stderrstderr, "\nI/O error\n");
6948 exit(1);
6949 } else if(reply->type == REDIS_REPLY_ERROR6) {
6950 fprintf(stderrstderr, "REPLCONF %s error: %s\n", arg1, reply->str);
6951 /* non fatal, old versions may not support it */
6952 }
6953 freeReplyObject(reply);
6954}
6955
6956void sendCapa() {
6957 sendReplconf("capa", "eof");
6958}
6959
6960void sendRdbOnly(void) {
6961 sendReplconf("rdb-only", "1");
6962}
6963
6964/* Read raw bytes through a redisContext. The read operation is not greedy
6965 * and may not fill the buffer entirely.
6966 */
6967static ssize_t readConn(redisContext *c, char *buf, size_t len)
6968{
6969 return c->funcs->read(c, buf, len);
6970}
6971
6972/* Sends SYNC and reads the number of bytes in the payload. Used both by
6973 * slaveMode() and getRDB().
6974 * returns 0 in case an EOF marker is used. */
6975unsigned long long sendSync(redisContext *c, char *out_eof) {
6976 /* To start we need to send the SYNC command and return the payload.
6977 * The hiredis client lib does not understand this part of the protocol
6978 * and we don't want to mess with its buffers, so everything is performed
6979 * using direct low-level I/O. */
6980 char buf[4096], *p;
6981 ssize_t nread;
6982
6983 /* Send the SYNC command. */
6984 if (cliWriteConn(c, "SYNC\r\n", 6) != 6) {
6985 fprintf(stderrstderr,"Error writing to master\n");
6986 exit(1);
6987 }
6988
6989 /* Read $<payload>\r\n, making sure to read just up to "\n" */
6990 p = buf;
6991 while(1) {
6992 nread = readConn(c,p,1);
6993 if (nread <= 0) {
6994 fprintf(stderrstderr,"Error reading bulk length while SYNCing\n");
6995 exit(1);
6996 }
6997 if (*p == '\n' && p != buf) break;
6998 if (*p != '\n') p++;
6999 }
7000 *p = '\0';
7001 if (buf[0] == '-') {
7002 printf("SYNC with master failed: %s\n", buf);
7003 exit(1);
7004 }
7005 if (strncmp(buf+1,"EOF:",4) == 0 && strlen(buf+5) >= RDB_EOF_MARK_SIZE40) {
7006 memcpy(out_eof, buf+5, RDB_EOF_MARK_SIZE40);
7007 return 0;
7008 }
7009 return strtoull(buf+1,NULL((void*)0),10);
7010}
7011
7012static void slaveMode(void) {
7013 static char eofmark[RDB_EOF_MARK_SIZE40];
7014 static char lastbytes[RDB_EOF_MARK_SIZE40];
7015 static int usemark = 0;
7016 unsigned long long payload = sendSync(context,eofmark);
7017 char buf[1024];
7018 int original_output = config.output;
7019
7020 if (payload == 0) {
7021 payload = ULLONG_MAX(9223372036854775807LL*2ULL+1ULL);
7022 memset(lastbytes,0,RDB_EOF_MARK_SIZE40);
7023 usemark = 1;
7024 fprintf(stderrstderr,"SYNC with master, discarding "
7025 "bytes of bulk transfer until EOF marker...\n");
7026 } else {
7027 fprintf(stderrstderr,"SYNC with master, discarding %llu "
7028 "bytes of bulk transfer...\n", payload);
7029 }
7030
7031
7032 /* Discard the payload. */
7033 while(payload) {
7034 ssize_t nread;
7035
7036 nread = readConn(context,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
7037 if (nread <= 0) {
7038 fprintf(stderrstderr,"Error reading RDB payload while SYNCing\n");
7039 exit(1);
7040 }
7041 payload -= nread;
7042
7043 if (usemark) {
7044 /* Update the last bytes array, and check if it matches our delimiter.*/
7045 if (nread >= RDB_EOF_MARK_SIZE40) {
7046 memcpy(lastbytes,buf+nread-RDB_EOF_MARK_SIZE40,RDB_EOF_MARK_SIZE40);
7047 } else {
7048 int rem = RDB_EOF_MARK_SIZE40-nread;
7049 memmove(lastbytes,lastbytes+nread,rem);
7050 memcpy(lastbytes+rem,buf,nread);
7051 }
7052 if (memcmp(lastbytes,eofmark,RDB_EOF_MARK_SIZE40) == 0)
7053 break;
7054 }
7055 }
7056
7057 if (usemark) {
7058 unsigned long long offset = ULLONG_MAX(9223372036854775807LL*2ULL+1ULL) - payload;
7059 fprintf(stderrstderr,"SYNC done after %llu bytes. Logging commands from master.\n", offset);
7060 /* put the slave online */
7061 sleep(1);
7062 sendReplconf("ACK", "0");
7063 } else
7064 fprintf(stderrstderr,"SYNC done. Logging commands from master.\n");
7065
7066 /* Now we can use hiredis to read the incoming protocol. */
7067 config.output = OUTPUT_CSV2;
7068 while (cliReadReply(0) == REDIS_OK0);
7069 config.output = original_output;
7070}
7071
7072/*------------------------------------------------------------------------------
7073 * RDB transfer mode
7074 *--------------------------------------------------------------------------- */
7075
7076/* This function implements --rdb, so it uses the replication protocol in order
7077 * to fetch the RDB file from a remote server. */
7078static void getRDB(clusterManagerNode *node) {
7079 int fd;
7080 redisContext *s;
7081 char *filename;
7082 if (node != NULL((void*)0)) {
7083 assert(node->context)((node->context) ? (void) (0) : __assert_fail ("node->context"
, "redis-cli.c", 7083, __extension__ __PRETTY_FUNCTION__))
;
7084 s = node->context;
7085 filename = clusterManagerGetNodeRDBFilename(node);
7086 } else {
7087 s = context;
7088 filename = config.rdb_filename;
7089 }
7090 static char eofmark[RDB_EOF_MARK_SIZE40];
7091 static char lastbytes[RDB_EOF_MARK_SIZE40];
7092 static int usemark = 0;
7093 unsigned long long payload = sendSync(s, eofmark);
7094 char buf[4096];
7095
7096 if (payload == 0) {
7097 payload = ULLONG_MAX(9223372036854775807LL*2ULL+1ULL);
7098 memset(lastbytes,0,RDB_EOF_MARK_SIZE40);
7099 usemark = 1;
7100 fprintf(stderrstderr,"SYNC sent to master, writing bytes of bulk transfer "
7101 "until EOF marker to '%s'\n", filename);
7102 } else {
7103 fprintf(stderrstderr,"SYNC sent to master, writing %llu bytes to '%s'\n",
7104 payload, filename);
7105 }
7106
7107 /* Write to file. */
7108 if (!strcmp(filename,"-")) {
7109 fd = STDOUT_FILENO1;
7110 } else {
7111 fd = open(filename, O_CREAT0100|O_WRONLY01, 0644);
7112 if (fd == -1) {
7113 fprintf(stderrstderr, "Error opening '%s': %s\n", filename,
7114 strerror(errno(*__errno_location ())));
7115 exit(1);
7116 }
7117 }
7118
7119 while(payload) {
7120 ssize_t nread, nwritten;
7121
7122 nread = readConn(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
7123 if (nread <= 0) {
7124 fprintf(stderrstderr,"I/O Error reading RDB payload from socket\n");
7125 exit(1);
7126 }
7127 nwritten = write(fd, buf, nread);
7128 if (nwritten != nread) {
7129 fprintf(stderrstderr,"Error writing data to file: %s\n",
7130 (nwritten == -1) ? strerror(errno(*__errno_location ())) : "short write");
7131 exit(1);
7132 }
7133 payload -= nread;
7134
7135 if (usemark) {
7136 /* Update the last bytes array, and check if it matches our delimiter.*/
7137 if (nread >= RDB_EOF_MARK_SIZE40) {
7138 memcpy(lastbytes,buf+nread-RDB_EOF_MARK_SIZE40,RDB_EOF_MARK_SIZE40);
7139 } else {
7140 int rem = RDB_EOF_MARK_SIZE40-nread;
7141 memmove(lastbytes,lastbytes+nread,rem);
7142 memcpy(lastbytes+rem,buf,nread);
7143 }
7144 if (memcmp(lastbytes,eofmark,RDB_EOF_MARK_SIZE40) == 0)
7145 break;
7146 }
7147 }
7148 if (usemark) {
7149 payload = ULLONG_MAX(9223372036854775807LL*2ULL+1ULL) - payload - RDB_EOF_MARK_SIZE40;
7150 if (ftruncate(fd, payload) == -1)
7151 fprintf(stderrstderr,"ftruncate failed: %s.\n", strerror(errno(*__errno_location ())));
7152 fprintf(stderrstderr,"Transfer finished with success after %llu bytes\n", payload);
7153 } else {
7154 fprintf(stderrstderr,"Transfer finished with success.\n");
7155 }
7156 redisFree(s); /* Close the connection ASAP as fsync() may take time. */
7157 if (node)
7158 node->context = NULL((void*)0);
7159 fsync(fd);
7160 close(fd);
7161 if (node) {
7162 sdsfreehi_sdsfree(filename);
7163 return;
7164 }
7165 exit(0);
7166}
7167
7168/*------------------------------------------------------------------------------
7169 * Bulk import (pipe) mode
7170 *--------------------------------------------------------------------------- */
7171
7172#define PIPEMODE_WRITE_LOOP_MAX_BYTES(128*1024) (128*1024)
7173static void pipeMode(void) {
7174 long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
7175 char obuf[1024*16]; /* Output buffer */
7176 char aneterr[ANET_ERR_LEN256];
7177 redisReply *reply;
7178 int eof = 0; /* True once we consumed all the standard input. */
7179 int done = 0;
7180 char magic[20]; /* Special reply we recognize. */
7181 time_t last_read_time = time(NULL((void*)0));
7182
7183 srand(time(NULL((void*)0)));
7184
7185 /* Use non blocking I/O. */
7186 if (anetNonBlock(aneterr,context->fd) == ANET_ERR-1) {
7187 fprintf(stderrstderr, "Can't set the socket in non blocking mode: %s\n",
7188 aneterr);
7189 exit(1);
7190 }
7191
7192 context->flags &= ~REDIS_BLOCK0x1;
7193
7194 /* Transfer raw protocol and read replies from the server at the same
7195 * time. */
7196 while(!done) {
7197 int mask = AE_READABLE1;
7198
7199 if (!eof || obuf_len != 0) mask |= AE_WRITABLE2;
7200 mask = aeWait(context->fd,mask,1000);
7201
7202 /* Handle the readable state: we can read replies from the server. */
7203 if (mask & AE_READABLE1) {
7204 int read_error = 0;
7205
7206 do {
7207 if (!read_error && redisBufferRead(context) == REDIS_ERR-1) {
7208 read_error = 1;
7209 }
7210
7211 reply = NULL((void*)0);
7212 if (redisGetReply(context, (void **) &reply) == REDIS_ERR-1) {
7213 fprintf(stderrstderr, "Error reading replies from server\n");
7214 exit(1);
7215 }
7216 if (reply) {
7217 last_read_time = time(NULL((void*)0));
7218 if (reply->type == REDIS_REPLY_ERROR6) {
7219 fprintf(stderrstderr,"%s\n", reply->str);
7220 errors++;
7221 } else if (eof && reply->type == REDIS_REPLY_STRING1 &&
7222 reply->len == 20) {
7223 /* Check if this is the reply to our final ECHO
7224 * command. If so everything was received
7225 * from the server. */
7226 if (memcmp(reply->str,magic,20) == 0) {
7227 printf("Last reply received from server.\n");
7228 done = 1;
7229 replies--;
7230 }
7231 }
7232 replies++;
7233 freeReplyObject(reply);
7234 }
7235 } while(reply);
7236
7237 /* Abort on read errors. We abort here because it is important
7238 * to consume replies even after a read error: this way we can
7239 * show a potential problem to the user. */
7240 if (read_error) exit(1);
7241 }
7242
7243 /* Handle the writable state: we can send protocol to the server. */
7244 if (mask & AE_WRITABLE2) {
7245 ssize_t loop_nwritten = 0;
7246
7247 while(1) {
7248 /* Transfer current buffer to server. */
7249 if (obuf_len != 0) {
7250 ssize_t nwritten = cliWriteConn(context,obuf+obuf_pos,obuf_len);
7251
7252 if (nwritten == -1) {
7253 if (errno(*__errno_location ()) != EAGAIN11 && errno(*__errno_location ()) != EINTR4) {
7254 fprintf(stderrstderr, "Error writing to the server: %s\n",
7255 strerror(errno(*__errno_location ())));
7256 exit(1);
7257 } else {
7258 nwritten = 0;
7259 }
7260 }
7261 obuf_len -= nwritten;
7262 obuf_pos += nwritten;
7263 loop_nwritten += nwritten;
7264 if (obuf_len != 0) break; /* Can't accept more data. */
7265 }
7266 if (context->err) {
7267 fprintf(stderrstderr, "Server I/O Error: %s\n", context->errstr);
7268 exit(1);
7269 }
7270 /* If buffer is empty, load from stdin. */
7271 if (obuf_len == 0 && !eof) {
7272 ssize_t nread = read(STDIN_FILENO0,obuf,sizeof(obuf));
7273
7274 if (nread == 0) {
7275 /* The ECHO sequence starts with a "\r\n" so that if there
7276 * is garbage in the protocol we read from stdin, the ECHO
7277 * will likely still be properly formatted.
7278 * CRLF is ignored by Redis, so it has no effects. */
7279 char echo[] =
7280 "\r\n*2\r\n$4\r\nECHO\r\n$20\r\n01234567890123456789\r\n";
7281 int j;
7282
7283 eof = 1;
7284 /* Everything transferred, so we queue a special
7285 * ECHO command that we can match in the replies
7286 * to make sure everything was read from the server. */
7287 for (j = 0; j < 20; j++)
7288 magic[j] = rand() & 0xff;
7289 memcpy(echo+21,magic,20);
7290 memcpy(obuf,echo,sizeof(echo)-1);
7291 obuf_len = sizeof(echo)-1;
7292 obuf_pos = 0;
7293 printf("All data transferred. Waiting for the last reply...\n");
7294 } else if (nread == -1) {
7295 fprintf(stderrstderr, "Error reading from stdin: %s\n",
7296 strerror(errno(*__errno_location ())));
7297 exit(1);
7298 } else {
7299 obuf_len = nread;
7300 obuf_pos = 0;
7301 }
7302 }
7303 if ((obuf_len == 0 && eof) ||
7304 loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES(128*1024)) break;
7305 }
7306 }
7307
7308 /* Handle timeout, that is, we reached EOF, and we are not getting
7309 * replies from the server for a few seconds, nor the final ECHO is
7310 * received. */
7311 if (eof && config.pipe_timeout > 0 &&
7312 time(NULL((void*)0))-last_read_time > config.pipe_timeout)
7313 {
7314 fprintf(stderrstderr,"No replies for %d seconds: exiting.\n",
7315 config.pipe_timeout);
7316 errors++;
7317 break;
7318 }
7319 }
7320 printf("errors: %lld, replies: %lld\n", errors, replies);
7321 if (errors)
7322 exit(1);
7323 else
7324 exit(0);
7325}
7326
7327/*------------------------------------------------------------------------------
7328 * Find big keys
7329 *--------------------------------------------------------------------------- */
7330
7331static redisReply *sendScan(unsigned long long *it) {
7332 redisReply *reply;
7333
7334 if (config.pattern)
7335 reply = redisCommand(context,"SCAN %llu MATCH %s",
7336 *it,config.pattern);
7337 else
7338 reply = redisCommand(context,"SCAN %llu",*it);
7339
7340 /* Handle any error conditions */
7341 if(reply == NULL((void*)0)) {
7342 fprintf(stderrstderr, "\nI/O error\n");
7343 exit(1);
7344 } else if(reply->type == REDIS_REPLY_ERROR6) {
7345 fprintf(stderrstderr, "SCAN error: %s\n", reply->str);
7346 exit(1);
7347 } else if(reply->type != REDIS_REPLY_ARRAY2) {
7348 fprintf(stderrstderr, "Non ARRAY response from SCAN!\n");
7349 exit(1);
7350 } else if(reply->elements != 2) {
7351 fprintf(stderrstderr, "Invalid element count from SCAN!\n");
7352 exit(1);
7353 }
7354
7355 /* Validate our types are correct */
7356 assert(reply->element[0]->type == REDIS_REPLY_STRING)((reply->element[0]->type == 1) ? (void) (0) : __assert_fail
("reply->element[0]->type == REDIS_REPLY_STRING", "redis-cli.c"
, 7356, __extension__ __PRETTY_FUNCTION__))
;
7357 assert(reply->element[1]->type == REDIS_REPLY_ARRAY)((reply->element[1]->type == 2) ? (void) (0) : __assert_fail
("reply->element[1]->type == REDIS_REPLY_ARRAY", "redis-cli.c"
, 7357, __extension__ __PRETTY_FUNCTION__))
;
7358
7359 /* Update iterator */
7360 *it = strtoull(reply->element[0]->str, NULL((void*)0), 10);
7361
7362 return reply;
7363}
7364
7365static int getDbSize(void) {
7366 redisReply *reply;
7367 int size;
7368
7369 reply = redisCommand(context, "DBSIZE");
7370
7371 if(reply == NULL((void*)0) || reply->type != REDIS_REPLY_INTEGER3) {
7372 fprintf(stderrstderr, "Couldn't determine DBSIZE!\n");
7373 exit(1);
7374 }
7375
7376 /* Grab the number of keys and free our reply */
7377 size = reply->integer;
7378 freeReplyObject(reply);
7379
7380 return size;
7381}
7382
7383typedef struct {
7384 char *name;
7385 char *sizecmd;
7386 char *sizeunit;
7387 unsigned long long biggest;
7388 unsigned long long count;
7389 unsigned long long totalsize;
7390 sdshisds biggest_key;
7391} typeinfo;
7392
7393typeinfo type_string = { "string", "STRLEN", "bytes" };
7394typeinfo type_list = { "list", "LLEN", "items" };
7395typeinfo type_set = { "set", "SCARD", "members" };
7396typeinfo type_hash = { "hash", "HLEN", "fields" };
7397typeinfo type_zset = { "zset", "ZCARD", "members" };
7398typeinfo type_stream = { "stream", "XLEN", "entries" };
7399typeinfo type_other = { "other", NULL((void*)0), "?" };
7400
7401static typeinfo* typeinfo_add(dict *types, char* name, typeinfo* type_template) {
7402 typeinfo *info = zmalloc(sizeof(typeinfo));
7403 *info = *type_template;
7404 info->name = sdsnewhi_sdsnew(name);
7405 dictAdd(types, info->name, info);
7406 return info;
7407}
7408
7409void type_free(void* priv_data, void* val) {
7410 typeinfo *info = val;
7411 UNUSED(priv_data)((void) priv_data);
7412 if (info->biggest_key)
7413 sdsfreehi_sdsfree(info->biggest_key);
7414 sdsfreehi_sdsfree(info->name);
7415 zfree(info);
7416}
7417
7418static dictType typeinfoDictType = {
7419 dictSdsHash, /* hash function */
7420 NULL((void*)0), /* key dup */
7421 NULL((void*)0), /* val dup */
7422 dictSdsKeyCompare, /* key compare */
7423 NULL((void*)0), /* key destructor (owned by the value)*/
7424 type_free, /* val destructor */
7425 NULL((void*)0) /* allow to expand */
7426};
7427
7428static void getKeyTypes(dict *types_dict, redisReply *keys, typeinfo **types) {
7429 redisReply *reply;
7430 unsigned int i;
7431
7432 /* Pipeline TYPE commands */
7433 for(i=0;i<keys->elements;i++) {
7434 const char* argv[] = {"TYPE", keys->element[i]->str};
7435 size_t lens[] = {4, keys->element[i]->len};
7436 redisAppendCommandArgv(context, 2, argv, lens);
7437 }
7438
7439 /* Retrieve types */
7440 for(i=0;i<keys->elements;i++) {
7441 if(redisGetReply(context, (void**)&reply)!=REDIS_OK0) {
7442 fprintf(stderrstderr, "Error getting type for key '%s' (%d: %s)\n",
7443 keys->element[i]->str, context->err, context->errstr);
7444 exit(1);
7445 } else if(reply->type != REDIS_REPLY_STATUS5) {
7446 if(reply->type == REDIS_REPLY_ERROR6) {
7447 fprintf(stderrstderr, "TYPE returned an error: %s\n", reply->str);
7448 } else {
7449 fprintf(stderrstderr,
7450 "Invalid reply type (%d) for TYPE on key '%s'!\n",
7451 reply->type, keys->element[i]->str);
7452 }
7453 exit(1);
7454 }
7455
7456 sdshisds typereply = sdsnewhi_sdsnew(reply->str);
7457 dictEntry *de = dictFind(types_dict, typereply);
7458 sdsfreehi_sdsfree(typereply);
7459 typeinfo *type = NULL((void*)0);
7460 if (de)
7461 type = dictGetVal(de)((de)->v.val);
7462 else if (strcmp(reply->str, "none")) /* create new types for modules, (but not for deleted keys) */
7463 type = typeinfo_add(types_dict, reply->str, &type_other);
7464 types[i] = type;
7465 freeReplyObject(reply);
7466 }
7467}
7468
7469static void getKeySizes(redisReply *keys, typeinfo **types,
7470 unsigned long long *sizes, int memkeys,
7471 unsigned memkeys_samples)
7472{
7473 redisReply *reply;
7474 unsigned int i;
7475
7476 /* Pipeline size commands */
7477 for(i=0;i<keys->elements;i++) {
29
Assuming 'i' is < field 'elements'
30
Loop condition is true. Entering loop body
7478 /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */
7479 if(!types[i] || (!types[i]->sizecmd && !memkeys))
31
Array access (from variable 'types') results in a null pointer dereference
7480 continue;
7481
7482 if (!memkeys) {
7483 const char* argv[] = {types[i]->sizecmd, keys->element[i]->str};
7484 size_t lens[] = {strlen(types[i]->sizecmd), keys->element[i]->len};
7485 redisAppendCommandArgv(context, 2, argv, lens);
7486 } else if (memkeys_samples==0) {
7487 const char* argv[] = {"MEMORY", "USAGE", keys->element[i]->str};
7488 size_t lens[] = {6, 5, keys->element[i]->len};
7489 redisAppendCommandArgv(context, 3, argv, lens);
7490 } else {
7491 sdshisds samplesstr = sdsfromlonglonghi_sdsfromlonglong(memkeys_samples);
7492 const char* argv[] = {"MEMORY", "USAGE", keys->element[i]->str, "SAMPLES", samplesstr};
7493 size_t lens[] = {6, 5, keys->element[i]->len, 7, sdslenhi_sdslen(samplesstr)};
7494 redisAppendCommandArgv(context, 5, argv, lens);
7495 sdsfreehi_sdsfree(samplesstr);
7496 }
7497 }
7498
7499 /* Retrieve sizes */
7500 for(i=0;i<keys->elements;i++) {
7501 /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */
7502 if(!types[i] || (!types[i]->sizecmd && !memkeys)) {
7503 sizes[i] = 0;
7504 continue;
7505 }
7506
7507 /* Retrieve size */
7508 if(redisGetReply(context, (void**)&reply)!=REDIS_OK0) {
7509 fprintf(stderrstderr, "Error getting size for key '%s' (%d: %s)\n",
7510 keys->element[i]->str, context->err, context->errstr);
7511 exit(1);
7512 } else if(reply->type != REDIS_REPLY_INTEGER3) {
7513 /* Theoretically the key could have been removed and
7514 * added as a different type between TYPE and SIZE */
7515 fprintf(stderrstderr,
7516 "Warning: %s on '%s' failed (may have changed type)\n",
7517 !memkeys? types[i]->sizecmd: "MEMORY USAGE",
7518 keys->element[i]->str);
7519 sizes[i] = 0;
7520 } else {
7521 sizes[i] = reply->integer;
7522 }
7523
7524 freeReplyObject(reply);
7525 }
7526}
7527
7528static void findBigKeys(int memkeys, unsigned memkeys_samples) {
7529 unsigned long long sampled = 0, total_keys, totlen=0, *sizes=NULL((void*)0), it=0;
7530 redisReply *reply, *keys;
7531 unsigned int arrsize=0, i;
7532 dictIterator *di;
7533 dictEntry *de;
7534 typeinfo **types = NULL((void*)0);
24
'types' initialized to a null pointer value
7535 double pct;
7536
7537 dict *types_dict = dictCreate(&typeinfoDictType, NULL((void*)0));
7538 typeinfo_add(types_dict, "string", &type_string);
7539 typeinfo_add(types_dict, "list", &type_list);
7540 typeinfo_add(types_dict, "set", &type_set);
7541 typeinfo_add(types_dict, "hash", &type_hash);
7542 typeinfo_add(types_dict, "zset", &type_zset);
7543 typeinfo_add(types_dict, "stream", &type_stream);
7544
7545 /* Total keys pre scanning */
7546 total_keys = getDbSize();
7547
7548 /* Status message */
7549 printf("\n# Scanning the entire keyspace to find biggest keys as well as\n");
7550 printf("# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec\n");
7551 printf("# per 100 SCAN commands (not usually needed).\n\n");
7552
7553 /* SCAN loop */
7554 do {
7555 /* Calculate approximate percentage completion */
7556 pct = 100 * (double)sampled/total_keys;
7557
7558 /* Grab some keys and point to the keys array */
7559 reply = sendScan(&it);
7560 keys = reply->element[1];
7561
7562 /* Reallocate our type and size array if we need to */
7563 if(keys->elements > arrsize) {
25
Assuming 'arrsize' is >= field 'elements'
26
Taking false branch
7564 types = zrealloc(types, sizeof(typeinfo*)*keys->elements);
7565 sizes = zrealloc(sizes, sizeof(unsigned long long)*keys->elements);
7566
7567 if(!types || !sizes) {
7568 fprintf(stderrstderr, "Failed to allocate storage for keys!\n");
7569 exit(1);
7570 }
7571
7572 arrsize = keys->elements;
7573 }
7574
7575 /* Retrieve types and then sizes */
7576 getKeyTypes(types_dict, keys, types);
7577 getKeySizes(keys, types, sizes, memkeys, memkeys_samples);
27
Passing null pointer value via 2nd parameter 'types'
28
Calling 'getKeySizes'
7578
7579 /* Now update our stats */
7580 for(i=0;i<keys->elements;i++) {
7581 typeinfo *type = types[i];
7582 /* Skip keys that disappeared between SCAN and TYPE */
7583 if(!type)
7584 continue;
7585
7586 type->totalsize += sizes[i];
7587 type->count++;
7588 totlen += keys->element[i]->len;
7589 sampled++;
7590
7591 if(type->biggest<sizes[i]) {
7592 /* Keep track of biggest key name for this type */
7593 if (type->biggest_key)
7594 sdsfreehi_sdsfree(type->biggest_key);
7595 type->biggest_key = sdscatreprhi_sdscatrepr(sdsemptyhi_sdsempty(), keys->element[i]->str, keys->element[i]->len);
7596 if(!type->biggest_key) {
7597 fprintf(stderrstderr, "Failed to allocate memory for key!\n");
7598 exit(1);
7599 }
7600
7601 printf(
7602 "[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
7603 pct, type->name, type->biggest_key, sizes[i],
7604 !memkeys? type->sizeunit: "bytes");
7605
7606 /* Keep track of the biggest size for this type */
7607 type->biggest = sizes[i];
7608 }
7609
7610 /* Update overall progress */
7611 if(sampled % 1000000 == 0) {
7612 printf("[%05.2f%%] Sampled %llu keys so far\n", pct, sampled);
7613 }
7614 }
7615
7616 /* Sleep if we've been directed to do so */
7617 if(sampled && (sampled %100) == 0 && config.interval) {
7618 usleep(config.interval);
7619 }
7620
7621 freeReplyObject(reply);
7622 } while(it != 0);
7623
7624 if(types) zfree(types);
7625 if(sizes) zfree(sizes);
7626
7627 /* We're done */
7628 printf("\n-------- summary -------\n\n");
7629
7630 printf("Sampled %llu keys in the keyspace!\n", sampled);
7631 printf("Total key length in bytes is %llu (avg len %.2f)\n\n",
7632 totlen, totlen ? (double)totlen/sampled : 0);
7633
7634 /* Output the biggest keys we found, for types we did find */
7635 di = dictGetIterator(types_dict);
7636 while ((de = dictNext(di))) {
7637 typeinfo *type = dictGetVal(de)((de)->v.val);
7638 if(type->biggest_key) {
7639 printf("Biggest %6s found '%s' has %llu %s\n", type->name, type->biggest_key,
7640 type->biggest, !memkeys? type->sizeunit: "bytes");
7641 }
7642 }
7643 dictReleaseIterator(di);
7644
7645 printf("\n");
7646
7647 di = dictGetIterator(types_dict);
7648 while ((de = dictNext(di))) {
7649 typeinfo *type = dictGetVal(de)((de)->v.val);
7650 printf("%llu %ss with %llu %s (%05.2f%% of keys, avg size %.2f)\n",
7651 type->count, type->name, type->totalsize, !memkeys? type->sizeunit: "bytes",
7652 sampled ? 100 * (double)type->count/sampled : 0,
7653 type->count ? (double)type->totalsize/type->count : 0);
7654 }
7655 dictReleaseIterator(di);
7656
7657 dictRelease(types_dict);
7658
7659 /* Success! */
7660 exit(0);
7661}
7662
7663static void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {
7664 redisReply *reply;
7665 unsigned int i;
7666
7667 /* Pipeline OBJECT freq commands */
7668 for(i=0;i<keys->elements;i++) {
7669 const char* argv[] = {"OBJECT", "FREQ", keys->element[i]->str};
7670 size_t lens[] = {6, 4, keys->element[i]->len};
7671 redisAppendCommandArgv(context, 3, argv, lens);
7672 }
7673
7674 /* Retrieve freqs */
7675 for(i=0;i<keys->elements;i++) {
7676 if(redisGetReply(context, (void**)&reply)!=REDIS_OK0) {
7677 sdshisds keyname = sdscatreprhi_sdscatrepr(sdsemptyhi_sdsempty(), keys->element[i]->str, keys->element[i]->len);
7678 fprintf(stderrstderr, "Error getting freq for key '%s' (%d: %s)\n",
7679 keyname, context->err, context->errstr);
7680 sdsfreehi_sdsfree(keyname);
7681 exit(1);
7682 } else if(reply->type != REDIS_REPLY_INTEGER3) {
7683 if(reply->type == REDIS_REPLY_ERROR6) {
7684 fprintf(stderrstderr, "Error: %s\n", reply->str);
7685 exit(1);
7686 } else {
7687 sdshisds keyname = sdscatreprhi_sdscatrepr(sdsemptyhi_sdsempty(), keys->element[i]->str, keys->element[i]->len);
7688 fprintf(stderrstderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keyname);
7689 sdsfreehi_sdsfree(keyname);
7690 freqs[i] = 0;
7691 }
7692 } else {
7693 freqs[i] = reply->integer;
7694 }
7695 freeReplyObject(reply);
7696 }
7697}
7698
7699#define HOTKEYS_SAMPLE16 16
7700static void findHotKeys(void) {
7701 redisReply *keys, *reply;
7702 unsigned long long counters[HOTKEYS_SAMPLE16] = {0};
7703 sdshisds hotkeys[HOTKEYS_SAMPLE16] = {NULL((void*)0)};
7704 unsigned long long sampled = 0, total_keys, *freqs = NULL((void*)0), it = 0;
7705 unsigned int arrsize = 0, i, k;
7706 double pct;
7707
7708 /* Total keys pre scanning */
7709 total_keys = getDbSize();
7710
7711 /* Status message */
7712 printf("\n# Scanning the entire keyspace to find hot keys as well as\n");
7713 printf("# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec\n");
7714 printf("# per 100 SCAN commands (not usually needed).\n\n");
7715
7716 /* SCAN loop */
7717 do {
7718 /* Calculate approximate percentage completion */
7719 pct = 100 * (double)sampled/total_keys;
7720
7721 /* Grab some keys and point to the keys array */
7722 reply = sendScan(&it);
7723 keys = reply->element[1];
7724
7725 /* Reallocate our freqs array if we need to */
7726 if(keys->elements > arrsize) {
7727 freqs = zrealloc(freqs, sizeof(unsigned long long)*keys->elements);
7728
7729 if(!freqs) {
7730 fprintf(stderrstderr, "Failed to allocate storage for keys!\n");
7731 exit(1);
7732 }
7733
7734 arrsize = keys->elements;
7735 }
7736
7737 getKeyFreqs(keys, freqs);
7738
7739 /* Now update our stats */
7740 for(i=0;i<keys->elements;i++) {
7741 sampled++;
7742 /* Update overall progress */
7743 if(sampled % 1000000 == 0) {
7744 printf("[%05.2f%%] Sampled %llu keys so far\n", pct, sampled);
7745 }
7746
7747 /* Use eviction pool here */
7748 k = 0;
7749 while (k < HOTKEYS_SAMPLE16 && freqs[i] > counters[k]) k++;
7750 if (k == 0) continue;
7751 k--;
7752 if (k == 0 || counters[k] == 0) {
7753 sdsfreehi_sdsfree(hotkeys[k]);
7754 } else {
7755 sdsfreehi_sdsfree(hotkeys[0]);
7756 memmove(counters,counters+1,sizeof(counters[0])*k);
7757 memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);
7758 }
7759 counters[k] = freqs[i];
7760 hotkeys[k] = sdscatreprhi_sdscatrepr(sdsemptyhi_sdsempty(), keys->element[i]->str, keys->element[i]->len);
7761 printf(
7762 "[%05.2f%%] Hot key '%s' found so far with counter %llu\n",
7763 pct, hotkeys[k], freqs[i]);
7764 }
7765
7766 /* Sleep if we've been directed to do so */
7767 if(sampled && (sampled %100) == 0 && config.interval) {
7768 usleep(config.interval);
7769 }
7770
7771 freeReplyObject(reply);
7772 } while(it != 0);
7773
7774 if (freqs) zfree(freqs);
7775
7776 /* We're done */
7777 printf("\n-------- summary -------\n\n");
7778
7779 printf("Sampled %llu keys in the keyspace!\n", sampled);
7780
7781 for (i=1; i<= HOTKEYS_SAMPLE16; i++) {
7782 k = HOTKEYS_SAMPLE16 - i;
7783 if(counters[k]>0) {
7784 printf("hot key found with counter: %llu\tkeyname: %s\n", counters[k], hotkeys[k]);
7785 sdsfreehi_sdsfree(hotkeys[k]);
7786 }
7787 }
7788
7789 exit(0);
7790}
7791
7792/*------------------------------------------------------------------------------
7793 * Stats mode
7794 *--------------------------------------------------------------------------- */
7795
7796/* Return the specified INFO field from the INFO command output "info".
7797 * A new buffer is allocated for the result, that needs to be free'd.
7798 * If the field is not found NULL is returned. */
7799static char *getInfoField(char *info, char *field) {
7800 char *p = strstr(info,field);
7801 char *n1, *n2;
7802 char *result;
7803
7804 if (!p) return NULL((void*)0);
7805 p += strlen(field)+1;
7806 n1 = strchr(p,'\r');
7807 n2 = strchr(p,',');
7808 if (n2 && n2 < n1) n1 = n2;
7809 result = zmalloc(sizeof(char)*(n1-p)+1);
7810 memcpy(result,p,(n1-p));
7811 result[n1-p] = '\0';
7812 return result;
7813}
7814
7815/* Like the above function but automatically convert the result into
7816 * a long. On error (missing field) LONG_MIN is returned. */
7817static long getLongInfoField(char *info, char *field) {
7818 char *value = getInfoField(info,field);
7819 long l;
7820
7821 if (!value) return LONG_MIN(-9223372036854775807L -1L);
7822 l = strtol(value,NULL((void*)0),10);
7823 zfree(value);
7824 return l;
7825}
7826
7827/* Convert number of bytes into a human readable string of the form:
7828 * 100B, 2G, 100M, 4K, and so forth. */
7829void bytesToHuman(char *s, long long n) {
7830 double d;
7831
7832 if (n < 0) {
7833 *s = '-';
7834 s++;
7835 n = -n;
7836 }
7837 if (n < 1024) {
7838 /* Bytes */
7839 sprintf(s,"%lldB",n);
7840 return;
7841 } else if (n < (1024*1024)) {
7842 d = (double)n/(1024);
7843 sprintf(s,"%.2fK",d);
7844 } else if (n < (1024LL*1024*1024)) {
7845 d = (double)n/(1024*1024);
7846 sprintf(s,"%.2fM",d);
7847 } else if (n < (1024LL*1024*1024*1024)) {
7848 d = (double)n/(1024LL*1024*1024);
7849 sprintf(s,"%.2fG",d);
7850 }
7851}
7852
7853static void statMode(void) {
7854 redisReply *reply;
7855 long aux, requests = 0;
7856 int i = 0;
7857
7858 while(1) {
7859 char buf[64];
7860 int j;
7861
7862 reply = reconnectingRedisCommand(context,"INFO");
7863 if (reply->type == REDIS_REPLY_ERROR6) {
7864 printf("ERROR: %s\n", reply->str);
7865 exit(1);
7866 }
7867
7868 if ((i++ % 20) == 0) {
7869 printf(
7870"------- data ------ --------------------- load -------------------- - child -\n"
7871"keys mem clients blocked requests connections \n");
7872 }
7873
7874 /* Keys */
7875 aux = 0;
7876 for (j = 0; j < 20; j++) {
7877 long k;
7878
7879 sprintf(buf,"db%d:keys",j);
7880 k = getLongInfoField(reply->str,buf);
7881 if (k == LONG_MIN(-9223372036854775807L -1L)) continue;
7882 aux += k;
7883 }
7884 sprintf(buf,"%ld",aux);
7885 printf("%-11s",buf);
7886
7887 /* Used memory */
7888 aux = getLongInfoField(reply->str,"used_memory");
7889 bytesToHuman(buf,aux);
7890 printf("%-8s",buf);
7891
7892 /* Clients */
7893 aux = getLongInfoField(reply->str,"connected_clients");
7894 sprintf(buf,"%ld",aux);
7895 printf(" %-8s",buf);
7896
7897 /* Blocked (BLPOPPING) Clients */
7898 aux = getLongInfoField(reply->str,"blocked_clients");
7899 sprintf(buf,"%ld",aux);
7900 printf("%-8s",buf);
7901
7902 /* Requests */
7903 aux = getLongInfoField(reply->str,"total_commands_processed");
7904 sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
7905 printf("%-19s",buf);
7906 requests = aux;
7907
7908 /* Connections */
7909 aux = getLongInfoField(reply->str,"total_connections_received");
7910 sprintf(buf,"%ld",aux);
7911 printf(" %-12s",buf);
7912
7913 /* Children */
7914 aux = getLongInfoField(reply->str,"bgsave_in_progress");
7915 aux |= getLongInfoField(reply->str,"aof_rewrite_in_progress") << 1;
7916 aux |= getLongInfoField(reply->str,"loading") << 2;
7917 switch(aux) {
7918 case 0: break;
7919 case 1:
7920 printf("SAVE");
7921 break;
7922 case 2:
7923 printf("AOF");
7924 break;
7925 case 3:
7926 printf("SAVE+AOF");
7927 break;
7928 case 4:
7929 printf("LOAD");
7930 break;
7931 }
7932
7933 printf("\n");
7934 freeReplyObject(reply);
7935 usleep(config.interval);
7936 }
7937}
7938
7939/*------------------------------------------------------------------------------
7940 * Scan mode
7941 *--------------------------------------------------------------------------- */
7942
7943static void scanMode(void) {
7944 redisReply *reply;
7945 unsigned long long cur = 0;
7946
7947 do {
7948 if (config.pattern)
7949 reply = redisCommand(context,"SCAN %llu MATCH %s",
7950 cur,config.pattern);
7951 else
7952 reply = redisCommand(context,"SCAN %llu",cur);
7953 if (reply == NULL((void*)0)) {
7954 printf("I/O error\n");
7955 exit(1);
7956 } else if (reply->type == REDIS_REPLY_ERROR6) {
7957 printf("ERROR: %s\n", reply->str);
7958 exit(1);
7959 } else {
7960 unsigned int j;
7961
7962 cur = strtoull(reply->element[0]->str,NULL((void*)0),10);
7963 for (j = 0; j < reply->element[1]->elements; j++)
7964 printf("%s\n", reply->element[1]->element[j]->str);
7965 }
7966 freeReplyObject(reply);
7967 } while(cur != 0);
7968
7969 exit(0);
7970}
7971
7972/*------------------------------------------------------------------------------
7973 * LRU test mode
7974 *--------------------------------------------------------------------------- */
7975
7976/* Return an integer from min to max (both inclusive) using a power-law
7977 * distribution, depending on the value of alpha: the greater the alpha
7978 * the more bias towards lower values.
7979 *
7980 * With alpha = 6.2 the output follows the 80-20 rule where 20% of
7981 * the returned numbers will account for 80% of the frequency. */
7982long long powerLawRand(long long min, long long max, double alpha) {
7983 double pl, r;
7984
7985 max += 1;
7986 r = ((double)rand()) / RAND_MAX2147483647;
7987 pl = pow(
7988 ((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),
7989 (1.0/(alpha+1)));
7990 return (max-1-(long long)pl)+min;
7991}
7992
7993/* Generates a key name among a set of lru_test_sample_size keys, using
7994 * an 80-20 distribution. */
7995void LRUTestGenKey(char *buf, size_t buflen) {
7996 snprintf(buf, buflen, "lru:%lld",
7997 powerLawRand(1, config.lru_test_sample_size, 6.2));
7998}
7999
8000#define LRU_CYCLE_PERIOD1000 1000 /* 1000 milliseconds. */
8001#define LRU_CYCLE_PIPELINE_SIZE250 250
8002static void LRUTestMode(void) {
8003 redisReply *reply;
8004 char key[128];
8005 long long start_cycle;
8006 int j;
8007
8008 srand(time(NULL((void*)0))^getpid());
8009 while(1) {
8010 /* Perform cycles of 1 second with 50% writes and 50% reads.
8011 * We use pipelining batching writes / reads N times per cycle in order
8012 * to fill the target instance easily. */
8013 start_cycle = mstime();
8014 long long hits = 0, misses = 0;
8015 while(mstime() - start_cycle < LRU_CYCLE_PERIOD1000) {
8016 /* Write cycle. */
8017 for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE250; j++) {
8018 char val[6];
8019 val[5] = '\0';
8020 for (int i = 0; i < 5; i++) val[i] = 'A'+rand()%('z'-'A');
8021 LRUTestGenKey(key,sizeof(key));
8022 redisAppendCommand(context, "SET %s %s",key,val);
8023 }
8024 for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE250; j++)
8025 redisGetReply(context, (void**)&reply);
8026
8027 /* Read cycle. */
8028 for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE250; j++) {
8029 LRUTestGenKey(key,sizeof(key));
8030 redisAppendCommand(context, "GET %s",key);
8031 }
8032 for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE250; j++) {
8033 if (redisGetReply(context, (void**)&reply) == REDIS_OK0) {
8034 switch(reply->type) {
8035 case REDIS_REPLY_ERROR6:
8036 printf("%s\n", reply->str);
8037 break;
8038 case REDIS_REPLY_NIL4:
8039 misses++;
8040 break;
8041 default:
8042 hits++;
8043 break;
8044 }
8045 }
8046 }
8047
8048 if (context->err) {
8049 fprintf(stderrstderr,"I/O error during LRU test\n");
8050 exit(1);
8051 }
8052 }
8053 /* Print stats. */
8054 printf(
8055 "%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\n",
8056 hits+misses,
8057 hits, (double)hits/(hits+misses)*100,
8058 misses, (double)misses/(hits+misses)*100);
8059 }
8060 exit(0);
8061}
8062
8063/*------------------------------------------------------------------------------
8064 * Intrisic latency mode.
8065 *
8066 * Measure max latency of a running process that does not result from
8067 * syscalls. Basically this software should provide a hint about how much
8068 * time the kernel leaves the process without a chance to run.
8069 *--------------------------------------------------------------------------- */
8070
8071/* This is just some computation the compiler can't optimize out.
8072 * Should run in less than 100-200 microseconds even using very
8073 * slow hardware. Runs in less than 10 microseconds in modern HW. */
8074unsigned long compute_something_fast(void) {
8075 unsigned char s[256], i, j, t;
8076 int count = 1000, k;
8077 unsigned long output = 0;
8078
8079 for (k = 0; k < 256; k++) s[k] = k;
8080
8081 i = 0;
8082 j = 0;
8083 while(count--) {
8084 i++;
8085 j = j + s[i];
8086 t = s[i];
8087 s[i] = s[j];
8088 s[j] = t;
8089 output += s[(s[i]+s[j])&255];
8090 }
8091 return output;
8092}
8093
8094static void intrinsicLatencyModeStop(int s) {
8095 UNUSED(s)((void) s);
8096 force_cancel_loop = 1;
8097}
8098
8099static void intrinsicLatencyMode(void) {
8100 long long test_end, run_time, max_latency = 0, runs = 0;
8101
8102 run_time = config.intrinsic_latency_duration*1000000;
8103 test_end = ustime() + run_time;
8104 signal(SIGINT2, intrinsicLatencyModeStop);
8105
8106 while(1) {
8107 long long start, end, latency;
8108
8109 start = ustime();
8110 compute_something_fast();
8111 end = ustime();
8112 latency = end-start;
8113 runs++;
8114 if (latency <= 0) continue;
8115
8116 /* Reporting */
8117 if (latency > max_latency) {
8118 max_latency = latency;
8119 printf("Max latency so far: %lld microseconds.\n", max_latency);
8120 }
8121
8122 double avg_us = (double)run_time/runs;
8123 double avg_ns = avg_us * 1e3;
8124 if (force_cancel_loop || end > test_end) {
8125 printf("\n%lld total runs "
8126 "(avg latency: "
8127 "%.4f microseconds / %.2f nanoseconds per run).\n",
8128 runs, avg_us, avg_ns);
8129 printf("Worst run took %.0fx longer than the average latency.\n",
8130 max_latency / avg_us);
8131 exit(0);
8132 }
8133 }
8134}
8135
8136static sdshisds askPassword(const char *msg) {
8137 linenoiseMaskModeEnable();
8138 sdshisds auth = linenoise(msg);
8139 linenoiseMaskModeDisable();
8140 return auth;
8141}
8142
8143/*------------------------------------------------------------------------------
8144 * Program main()
8145 *--------------------------------------------------------------------------- */
8146
8147int main(int argc, char **argv) {
8148 int firstarg;
8149 struct timeval tv;
8150
8151 memset(&config.sslconfig, 0, sizeof(config.sslconfig));
8152 config.hostip = sdsnewhi_sdsnew("127.0.0.1");
8153 config.hostport = 6379;
8154 config.hostsocket = NULL((void*)0);
8155 config.repeat = 1;
8156 config.interval = 0;
8157 config.dbnum = 0;
8158 config.interactive = 0;
8159 config.shutdown = 0;
8160 config.monitor_mode = 0;
8161 config.pubsub_mode = 0;
8162 config.latency_mode = 0;
8163 config.latency_dist_mode = 0;
8164 config.latency_history = 0;
8165 config.lru_test_mode = 0;
8166 config.lru_test_sample_size = 0;
8167 config.cluster_mode = 0;
8168 config.slave_mode = 0;
8169 config.getrdb_mode = 0;
8170 config.stat_mode = 0;
8171 config.scan_mode = 0;
8172 config.intrinsic_latency_mode = 0;
8173 config.pattern = NULL((void*)0);
8174 config.rdb_filename = NULL((void*)0);
8175 config.pipe_mode = 0;
8176 config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT30;
8177 config.bigkeys = 0;
8178 config.hotkeys = 0;
8179 config.stdinarg = 0;
8180 config.auth = NULL((void*)0);
8181 config.askpass = 0;
8182 config.user = NULL((void*)0);
8183 config.eval = NULL((void*)0);
8184 config.eval_ldb = 0;
8185 config.eval_ldb_end = 0;
8186 config.eval_ldb_sync = 0;
8187 config.enable_ldb_on_eval = 0;
8188 config.last_cmd_type = -1;
8189 config.verbose = 0;
8190 config.set_errcode = 0;
8191 config.no_auth_warning = 0;
8192 config.in_multi = 0;
8193 config.cluster_manager_command.name = NULL((void*)0);
8194 config.cluster_manager_command.argc = 0;
8195 config.cluster_manager_command.argv = NULL((void*)0);
8196 config.cluster_manager_command.flags = 0;
8197 config.cluster_manager_command.replicas = 0;
8198 config.cluster_manager_command.from = NULL((void*)0);
8199 config.cluster_manager_command.to = NULL((void*)0);
8200 config.cluster_manager_command.from_user = NULL((void*)0);
8201 config.cluster_manager_command.from_pass = NULL((void*)0);
8202 config.cluster_manager_command.from_askpass = 0;
8203 config.cluster_manager_command.weight = NULL((void*)0);
8204 config.cluster_manager_command.weight_argc = 0;
8205 config.cluster_manager_command.slots = 0;
8206 config.cluster_manager_command.timeout = CLUSTER_MANAGER_MIGRATE_TIMEOUT60000;
8207 config.cluster_manager_command.pipeline = CLUSTER_MANAGER_MIGRATE_PIPELINE10;
8208 config.cluster_manager_command.threshold =
8209 CLUSTER_MANAGER_REBALANCE_THRESHOLD2;
8210 config.cluster_manager_command.backup_dir = NULL((void*)0);
8211 pref.hints = 1;
8212
8213 spectrum_palette = spectrum_palette_color;
8214 spectrum_palette_size = spectrum_palette_color_size;
8215
8216 if (!isatty(fileno(stdoutstdout)) && (getenv("FAKETTY") == NULL((void*)0))) {
1
Assuming the condition is false
8217 config.output = OUTPUT_RAW1;
8218 config.push_output = 0;
8219 } else {
8220 config.output = OUTPUT_STANDARD0;
8221 config.push_output = 1;
8222 }
8223 config.mb_delim = sdsnewhi_sdsnew("\n");
8224 config.cmd_delim = sdsnewhi_sdsnew("\n");
8225
8226 firstarg = parseOptions(argc,argv);
8227 argc -= firstarg;
8228 argv += firstarg;
8229
8230 parseEnv();
8231
8232 if (config.askpass) {
2
Assuming field 'askpass' is 0
3
Taking false branch
8233 config.auth = askPassword("Please input password: ");
8234 }
8235
8236 if (config.cluster_manager_command.from_askpass) {
4
Assuming field 'from_askpass' is 0
5
Taking false branch
8237 config.cluster_manager_command.from_pass = askPassword(
8238 "Please input import source node password: ");
8239 }
8240
8241#ifdef USE_OPENSSL
8242 if (config.tls) {
8243 cliSecureInit();
8244 }
8245#endif
8246
8247 gettimeofday(&tv, NULL((void*)0));
8248 init_genrand64(((long long) tv.tv_sec * 1000000 + tv.tv_usec) ^ getpid());
8249
8250 /* Cluster Manager mode */
8251 if (CLUSTER_MANAGER_MODE()(config.cluster_manager_command.name != ((void*)0))) {
6
Assuming field 'name' is equal to null
7
Taking false branch
8252 clusterManagerCommandProc *proc = validateClusterManagerCommand();
8253 if (!proc) {
8254 exit(1);
8255 }
8256 clusterManagerMode(proc);
8257 }
8258
8259 /* Latency mode */
8260 if (config.latency_mode) {
8
Assuming field 'latency_mode' is 0
9
Taking false branch
8261 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8262 latencyMode();
8263 }
8264
8265 /* Latency distribution mode */
8266 if (config.latency_dist_mode) {
10
Assuming field 'latency_dist_mode' is 0
11
Taking false branch
8267 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8268 latencyDistMode();
8269 }
8270
8271 /* Slave mode */
8272 if (config.slave_mode) {
12
Assuming field 'slave_mode' is 0
13
Taking false branch
8273 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8274 sendCapa();
8275 slaveMode();
8276 }
8277
8278 /* Get RDB mode. */
8279 if (config.getrdb_mode) {
14
Assuming field 'getrdb_mode' is 0
15
Taking false branch
8280 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8281 sendCapa();
8282 sendRdbOnly();
8283 getRDB(NULL((void*)0));
8284 }
8285
8286 /* Pipe mode */
8287 if (config.pipe_mode) {
16
Assuming field 'pipe_mode' is 0
17
Taking false branch
8288 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8289 pipeMode();
8290 }
8291
8292 /* Find big keys */
8293 if (config.bigkeys) {
18
Assuming field 'bigkeys' is 0
19
Taking false branch
8294 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8295 findBigKeys(0, 0);
8296 }
8297
8298 /* Find large keys */
8299 if (config.memkeys) {
20
Assuming field 'memkeys' is not equal to 0
21
Taking true branch
8300 if (cliConnect(0) == REDIS_ERR-1) exit(1);
22
Taking false branch
8301 findBigKeys(1, config.memkeys_samples);
23
Calling 'findBigKeys'
8302 }
8303
8304 /* Find hot keys */
8305 if (config.hotkeys) {
8306 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8307 findHotKeys();
8308 }
8309
8310 /* Stat mode */
8311 if (config.stat_mode) {
8312 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8313 if (config.interval == 0) config.interval = 1000000;
8314 statMode();
8315 }
8316
8317 /* Scan mode */
8318 if (config.scan_mode) {
8319 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8320 scanMode();
8321 }
8322
8323 /* LRU test mode */
8324 if (config.lru_test_mode) {
8325 if (cliConnect(0) == REDIS_ERR-1) exit(1);
8326 LRUTestMode();
8327 }
8328
8329 /* Intrinsic latency mode */
8330 if (config.intrinsic_latency_mode) intrinsicLatencyMode();
8331
8332 /* Start interactive mode when no command is provided */
8333 if (argc == 0 && !config.eval) {
8334 /* Ignore SIGPIPE in interactive mode to force a reconnect */
8335 signal(SIGPIPE13, SIG_IGN((__sighandler_t) 1));
8336
8337 /* Note that in repl mode we don't abort on connection error.
8338 * A new attempt will be performed for every command send. */
8339 cliConnect(0);
8340 repl();
8341 }
8342
8343 /* Otherwise, we have some arguments to execute */
8344 if (cliConnect(0) != REDIS_OK0) exit(1);
8345 if (config.eval) {
8346 return evalMode(argc,argv);
8347 } else {
8348 return noninteractive(argc,argv);
8349 }
8350}