Merge branch '1906-merge' into unstable

This commit is contained in:
antirez 2014-08-25 10:27:53 +02:00
commit 209f266cc5
47 changed files with 374 additions and 196 deletions

7
deps/Makefile vendored
View File

@ -60,10 +60,15 @@ endif
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS) LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS)
LUA_LDFLAGS+= $(LDFLAGS) LUA_LDFLAGS+= $(LDFLAGS)
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
# challenging to cross-compile lua (and redis). These defines make it easier
# to fit redis into cross-compilation environments, which typically set AR.
AR=ar
ARFLAGS=rcu
lua: .make-prerequisites lua: .make-prerequisites
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
cd lua/src && $(MAKE) all CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(LUA_LDFLAGS)" cd lua/src && $(MAKE) all CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(LUA_LDFLAGS)" AR="$(AR) $(ARFLAGS)"
.PHONY: lua .PHONY: lua

View File

@ -5,6 +5,10 @@
#define _BSD_SOURCE #define _BSD_SOURCE
#endif #endif
#if defined(_AIX)
#define _ALL_SOURCE
#endif
#if defined(__sun__) #if defined(__sun__)
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200112L
#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) #elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)

2
deps/hiredis/net.h vendored
View File

@ -35,7 +35,7 @@
#include "hiredis.h" #include "hiredis.h"
#if defined(__sun) #if defined(__sun) || defined(_AIX)
#define AF_LOCAL AF_UNIX #define AF_LOCAL AF_UNIX
#endif #endif

7
deps/hiredis/sds.c vendored
View File

@ -200,7 +200,10 @@ size_t sdsAllocSize(sds s) {
void sdsIncrLen(sds s, int incr) { void sdsIncrLen(sds s, int incr) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
assert(sh->free >= incr); if (incr >= 0)
assert(sh->free >= (unsigned int)incr);
else
assert(sh->len >= (unsigned int)(-incr));
sh->len += incr; sh->len += incr;
sh->free -= incr; sh->free -= incr;
assert(sh->free >= 0); assert(sh->free >= 0);
@ -457,7 +460,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
i = initlen; /* Position of the next byte to write to dest str. */ i = initlen; /* Position of the next byte to write to dest str. */
while(*f) { while(*f) {
char next, *str; char next, *str;
int l; unsigned int l;
long long num; long long num;
unsigned long long unum; unsigned long long unum;

4
deps/hiredis/sds.h vendored
View File

@ -39,8 +39,8 @@
typedef char *sds; typedef char *sds;
struct sdshdr { struct sdshdr {
int len; unsigned int len;
int free; unsigned int free;
char buf[]; char buf[];
}; };

View File

@ -68,7 +68,7 @@ tcp-backlog 511
# on a unix socket when not specified. # on a unix socket when not specified.
# #
# unixsocket /tmp/redis.sock # unixsocket /tmp/redis.sock
# unixsocketperm 755 # unixsocketperm 700
# Close the connection after a client is idle for N seconds (0 to disable) # Close the connection after a client is idle for N seconds (0 to disable)
timeout 0 timeout 0

View File

@ -19,7 +19,7 @@ DEPENDENCY_TARGETS=hiredis linenoise lua
# Default settings # Default settings
STD=-std=c99 -pedantic STD=-std=c99 -pedantic
WARN=-Wall WARN=-Wall -W
OPT=$(OPTIMIZATION) OPT=$(OPTIMIZATION)
PREFIX?=/usr/local PREFIX?=/usr/local
@ -58,17 +58,23 @@ ifeq ($(uname_S),SunOS)
# SunOS # SunOS
INSTALL=cp -pf INSTALL=cp -pf
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6 FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
FINAL_LIBS+= -ldl -lnsl -lsocket -lpthread FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread
else else
ifeq ($(uname_S),Darwin) ifeq ($(uname_S),Darwin)
# Darwin (nothing to do) # Darwin (nothing to do)
else
ifeq ($(uname_S),AIX)
# AIX
FINAL_LDFLAGS+= -Wl,-bexpall
FINAL_LIBS+= -pthread -lcrypt -lbsd
else else
# All the other OSes (notably Linux) # All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic FINAL_LDFLAGS+= -rdynamic
FINAL_LIBS+= -pthread FINAL_LIBS+= -pthread
endif endif
endif endif
endif
# Include paths to dependencies # Include paths to dependencies
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
@ -119,7 +125,7 @@ REDIS_CHECK_AOF_OBJ=redis-check-aof.o
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME) all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)
@echo "" @echo ""
@echo "Hint: To run 'make test' is a good idea ;)" @echo "Hint: It's a good idea to run 'make test' ;)"
@echo "" @echo ""
.PHONY: all .PHONY: all

View File

@ -156,8 +156,9 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{ {
if (fd >= eventLoop->setsize) return; if (fd >= eventLoop->setsize) return;
aeFileEvent *fe = &eventLoop->events[fd]; aeFileEvent *fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return; if (fe->mask == AE_NONE) return;
aeApiDelEvent(eventLoop, fd, mask);
fe->mask = fe->mask & (~mask); fe->mask = fe->mask & (~mask);
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) { if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
/* Update the max fd */ /* Update the max fd */
@ -167,7 +168,6 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
if (eventLoop->events[j].mask != AE_NONE) break; if (eventLoop->events[j].mask != AE_NONE) break;
eventLoop->maxfd = j; eventLoop->maxfd = j;
} }
aeApiDelEvent(eventLoop, fd, mask);
} }
int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {

View File

@ -117,6 +117,8 @@ int anetKeepAlive(char *err, int fd, int interval)
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
return ANET_ERR; return ANET_ERR;
} }
#else
((void) interval); /* Avoid unused var warning for non Linux systems. */
#endif #endif
return ANET_OK; return ANET_OK;
@ -262,7 +264,8 @@ static int anetTcpGenericConnect(char *err, char *addr, int port,
if (source_addr) { if (source_addr) {
int bound = 0; int bound = 0;
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) { if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0)
{
anetSetError(err, "%s", gai_strerror(rv)); anetSetError(err, "%s", gai_strerror(rv));
goto end; goto end;
} }
@ -272,6 +275,7 @@ static int anetTcpGenericConnect(char *err, char *addr, int port,
break; break;
} }
} }
freeaddrinfo(bservinfo);
if (!bound) { if (!bound) {
anetSetError(err, "bind: %s", strerror(errno)); anetSetError(err, "bind: %s", strerror(errno));
goto end; goto end;

View File

@ -39,10 +39,14 @@
#define ANET_NONE 0 #define ANET_NONE 0
#define ANET_IP_ONLY (1<<0) #define ANET_IP_ONLY (1<<0)
#if defined(__sun) #if defined(__sun) || defined(_AIX)
#define AF_LOCAL AF_UNIX #define AF_LOCAL AF_UNIX
#endif #endif
#ifdef _AIX
#undef ip_len
#endif
int anetTcpConnect(char *err, char *addr, int port); int anetTcpConnect(char *err, char *addr, int port);
int anetTcpNonBlockConnect(char *err, char *addr, int port); int anetTcpNonBlockConnect(char *err, char *addr, int port);
int anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr); int anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr);

View File

@ -95,6 +95,10 @@ void aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {
listNode *ln; listNode *ln;
aofrwblock *block; aofrwblock *block;
ssize_t nwritten; ssize_t nwritten;
REDIS_NOTUSED(el);
REDIS_NOTUSED(fd);
REDIS_NOTUSED(privdata);
REDIS_NOTUSED(mask);
while(1) { while(1) {
ln = listFirst(server.aof_rewrite_buf_blocks); ln = listFirst(server.aof_rewrite_buf_blocks);
@ -177,7 +181,7 @@ ssize_t aofRewriteBufferWrite(int fd) {
if (block->used) { if (block->used) {
nwritten = write(fd,block->buf,block->used); nwritten = write(fd,block->buf,block->used);
if (nwritten != block->used) { if (nwritten != (ssize_t)block->used) {
if (nwritten == 0) errno = EIO; if (nwritten == 0) errno = EIO;
return -1; return -1;
} }
@ -1128,6 +1132,9 @@ werr:
* parent sends a '!' as well to acknowledge. */ * parent sends a '!' as well to acknowledge. */
void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) { void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {
char byte; char byte;
REDIS_NOTUSED(el);
REDIS_NOTUSED(privdata);
REDIS_NOTUSED(mask);
if (read(fd,&byte,1) == 1 && byte == '!') { if (read(fd,&byte,1) == 1 && byte == '!') {
redisLog(REDIS_NOTICE,"AOF rewrite child asks to stop sending diffs."); redisLog(REDIS_NOTICE,"AOF rewrite child asks to stop sending diffs.");

View File

@ -107,12 +107,12 @@ size_t redisPopcount(void *s, long count) {
* no zero bit is found, it returns count*8 assuming the string is zero * no zero bit is found, it returns count*8 assuming the string is zero
* padded on the right. However if 'bit' is 1 it is possible that there is * padded on the right. However if 'bit' is 1 it is possible that there is
* not a single set bit in the bitmap. In this special case -1 is returned. */ * not a single set bit in the bitmap. In this special case -1 is returned. */
long redisBitpos(void *s, long count, int bit) { long redisBitpos(void *s, unsigned long count, int bit) {
unsigned long *l; unsigned long *l;
unsigned char *c; unsigned char *c;
unsigned long skipval, word = 0, one; unsigned long skipval, word = 0, one;
long pos = 0; /* Position of bit, to return to the caller. */ long pos = 0; /* Position of bit, to return to the caller. */
int j; unsigned long j;
/* Process whole words first, seeking for first word that is not /* Process whole words first, seeking for first word that is not
* all ones or all zeros respectively if we are lookig for zeros * all ones or all zeros respectively if we are lookig for zeros
@ -276,11 +276,12 @@ void getbitCommand(redisClient *c) {
void bitopCommand(redisClient *c) { void bitopCommand(redisClient *c) {
char *opname = c->argv[1]->ptr; char *opname = c->argv[1]->ptr;
robj *o, *targetkey = c->argv[2]; robj *o, *targetkey = c->argv[2];
long op, j, numkeys; unsigned long op, j, numkeys;
robj **objects; /* Array of source objects. */ robj **objects; /* Array of source objects. */
unsigned char **src; /* Array of source strings pointers. */ unsigned char **src; /* Array of source strings pointers. */
long *len, maxlen = 0; /* Array of length of src strings, and max len. */ unsigned long *len, maxlen = 0; /* Array of length of src strings,
long minlen = 0; /* Min len among the input keys. */ and max len. */
unsigned long minlen = 0; /* Min len among the input keys. */
unsigned char *res = NULL; /* Resulting string. */ unsigned char *res = NULL; /* Resulting string. */
/* Parse the operation name. */ /* Parse the operation name. */
@ -320,9 +321,10 @@ void bitopCommand(redisClient *c) {
} }
/* Return an error if one of the keys is not a string. */ /* Return an error if one of the keys is not a string. */
if (checkType(c,o,REDIS_STRING)) { if (checkType(c,o,REDIS_STRING)) {
for (j = j-1; j >= 0; j--) { unsigned long i;
if (objects[j]) for (i = 0; i < j; i++) {
decrRefCount(objects[j]); if (objects[i])
decrRefCount(objects[i]);
} }
zfree(src); zfree(src);
zfree(len); zfree(len);
@ -340,7 +342,7 @@ void bitopCommand(redisClient *c) {
if (maxlen) { if (maxlen) {
res = (unsigned char*) sdsnewlen(NULL,maxlen); res = (unsigned char*) sdsnewlen(NULL,maxlen);
unsigned char output, byte; unsigned char output, byte;
long i; unsigned long i;
/* Fast path: as far as we have data for all the input bitmaps we /* Fast path: as far as we have data for all the input bitmaps we
* can take a fast path that performs much better than the * can take a fast path that performs much better than the

View File

@ -72,6 +72,7 @@ void resetManualFailover(void);
void clusterCloseAllSlots(void); void clusterCloseAllSlots(void);
void clusterSetNodeAsMaster(clusterNode *n); void clusterSetNodeAsMaster(clusterNode *n);
void clusterDelNode(clusterNode *delnode); void clusterDelNode(clusterNode *delnode);
sds representRedisNodeFlags(sds ci, uint16_t flags);
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Initialization * Initialization
@ -163,9 +164,13 @@ int clusterLoadConfig(char *filename) {
argv[j]); argv[j]);
} }
} }
sdsfreesplitres(argv,argc);
continue; continue;
} }
/* Regular config lines have at least eight fields */
if (argc < 8) goto fmterr;
/* Create this node if it does not exist */ /* Create this node if it does not exist */
n = clusterLookupNode(argv[0]); n = clusterLookupNode(argv[0]);
if (!n) { if (!n) {
@ -266,11 +271,12 @@ int clusterLoadConfig(char *filename) {
sdsfreesplitres(argv,argc); sdsfreesplitres(argv,argc);
} }
/* Config sanity check */
if (server.cluster->myself == NULL) goto fmterr;
zfree(line); zfree(line);
fclose(fp); fclose(fp);
/* Config sanity check */
redisAssert(server.cluster->myself != NULL);
redisLog(REDIS_NOTICE,"Node configuration loaded, I'm %.40s", myself->name); redisLog(REDIS_NOTICE,"Node configuration loaded, I'm %.40s", myself->name);
/* Something that should never happen: currentEpoch smaller than /* Something that should never happen: currentEpoch smaller than
@ -284,7 +290,8 @@ int clusterLoadConfig(char *filename) {
fmterr: fmterr:
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"Unrecoverable error: corrupted cluster config file."); "Unrecoverable error: corrupted cluster config file.");
fclose(fp); zfree(line);
if (fp) fclose(fp);
exit(1); exit(1);
} }
@ -321,7 +328,7 @@ int clusterSaveConfig(int do_fsync) {
/* Pad the new payload if the existing file length is greater. */ /* Pad the new payload if the existing file length is greater. */
if (fstat(fd,&sb) != -1) { if (fstat(fd,&sb) != -1) {
if (sb.st_size > content_size) { if (sb.st_size > (off_t)content_size) {
ci = sdsgrowzero(ci,sb.st_size); ci = sdsgrowzero(ci,sb.st_size);
memset(ci+content_size,'\n',sb.st_size-content_size); memset(ci+content_size,'\n',sb.st_size-content_size);
} }
@ -1144,20 +1151,11 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender); clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender);
while(count--) { while(count--) {
sds ci = sdsempty();
uint16_t flags = ntohs(g->flags); uint16_t flags = ntohs(g->flags);
clusterNode *node; clusterNode *node;
sds ci;
if (flags == 0) ci = sdscat(ci,"noflags,"); ci = representRedisNodeFlags(sdsempty(), flags);
if (flags & REDIS_NODE_MYSELF) ci = sdscat(ci,"myself,");
if (flags & REDIS_NODE_MASTER) ci = sdscat(ci,"master,");
if (flags & REDIS_NODE_SLAVE) ci = sdscat(ci,"slave,");
if (flags & REDIS_NODE_PFAIL) ci = sdscat(ci,"fail?,");
if (flags & REDIS_NODE_FAIL) ci = sdscat(ci,"fail,");
if (flags & REDIS_NODE_HANDSHAKE) ci = sdscat(ci,"handshake,");
if (flags & REDIS_NODE_NOADDR) ci = sdscat(ci,"noaddr,");
if (ci[sdslen(ci)-1] == ',') ci[sdslen(ci)-1] = ' ';
redisLog(REDIS_DEBUG,"GOSSIP %.40s %s:%d %s", redisLog(REDIS_DEBUG,"GOSSIP %.40s %s:%d %s",
g->nodename, g->nodename,
g->ip, g->ip,
@ -1914,7 +1912,7 @@ void clusterReadHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
ssize_t nread; ssize_t nread;
clusterMsg *hdr; clusterMsg *hdr;
clusterLink *link = (clusterLink*) privdata; clusterLink *link = (clusterLink*) privdata;
int readlen, rcvbuflen; unsigned int readlen, rcvbuflen;
REDIS_NOTUSED(el); REDIS_NOTUSED(el);
REDIS_NOTUSED(mask); REDIS_NOTUSED(mask);
@ -3296,14 +3294,13 @@ int verifyClusterConfigWithData(void) {
update_config++; update_config++;
/* Case A: slot is unassigned. Take responsability for it. */ /* Case A: slot is unassigned. Take responsability for it. */
if (server.cluster->slots[j] == NULL) { if (server.cluster->slots[j] == NULL) {
redisLog(REDIS_WARNING, "I've keys about slot %d that is " redisLog(REDIS_WARNING, "I have keys for unassigned slot %d. "
"unassigned. Taking responsability " "Taking responsibility for it.",j);
"for it.",j);
clusterAddSlot(myself,j); clusterAddSlot(myself,j);
} else { } else {
redisLog(REDIS_WARNING, "I've keys about slot %d that is " redisLog(REDIS_WARNING, "I have keys for slot %d, but the slot is "
"already assigned to a different node. " "assigned to another node. "
"Setting it in importing state.",j); "Setting it to importing state.",j);
server.cluster->importing_slots_from[j] = server.cluster->slots[j]; server.cluster->importing_slots_from[j] = server.cluster->slots[j];
} }
} }
@ -3336,9 +3333,40 @@ void clusterSetMaster(clusterNode *n) {
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* CLUSTER command * Nodes to string representation functions.
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
struct redisNodeFlags {
uint16_t flag;
char *name;
};
static struct redisNodeFlags redisNodeFlagsTable[] = {
{REDIS_NODE_MYSELF, "myself,"},
{REDIS_NODE_MASTER, "master,"},
{REDIS_NODE_SLAVE, "slave,"},
{REDIS_NODE_PFAIL, "fail?,"},
{REDIS_NODE_FAIL, "fail,"},
{REDIS_NODE_HANDSHAKE, "handshake,"},
{REDIS_NODE_NOADDR, "noaddr,"}
};
/* Concatenate the comma separated list of node flags to the given SDS
* string 'ci'. */
sds representRedisNodeFlags(sds ci, uint16_t flags) {
if (flags == 0) {
ci = sdscat(ci,"noflags,");
} else {
int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);
for (i = 0; i < size; i++) {
struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;
if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);
}
}
sdsIncrLen(ci,-1); /* Remove trailing comma. */
return ci;
}
/* Generate a csv-alike representation of the specified cluster node. /* Generate a csv-alike representation of the specified cluster node.
* See clusterGenNodesDescription() top comment for more information. * See clusterGenNodesDescription() top comment for more information.
* *
@ -3354,21 +3382,13 @@ sds clusterGenNodeDescription(clusterNode *node) {
node->port); node->port);
/* Flags */ /* Flags */
if (node->flags == 0) ci = sdscat(ci,"noflags,"); ci = representRedisNodeFlags(ci, node->flags);
if (node->flags & REDIS_NODE_MYSELF) ci = sdscat(ci,"myself,");
if (node->flags & REDIS_NODE_MASTER) ci = sdscat(ci,"master,");
if (node->flags & REDIS_NODE_SLAVE) ci = sdscat(ci,"slave,");
if (node->flags & REDIS_NODE_PFAIL) ci = sdscat(ci,"fail?,");
if (node->flags & REDIS_NODE_FAIL) ci = sdscat(ci,"fail,");
if (node->flags & REDIS_NODE_HANDSHAKE) ci =sdscat(ci,"handshake,");
if (node->flags & REDIS_NODE_NOADDR) ci = sdscat(ci,"noaddr,");
if (ci[sdslen(ci)-1] == ',') ci[sdslen(ci)-1] = ' ';
/* Slave of... or just "-" */ /* Slave of... or just "-" */
if (node->slaveof) if (node->slaveof)
ci = sdscatprintf(ci," %.40s ",node->slaveof->name); ci = sdscatprintf(ci," %.40s ",node->slaveof->name);
else else
ci = sdscatprintf(ci,"- "); ci = sdscatlen(ci," - ",3);
/* Latency from the POV of this node, link status */ /* Latency from the POV of this node, link status */
ci = sdscatprintf(ci,"%lld %lld %llu %s", ci = sdscatprintf(ci,"%lld %lld %llu %s",
@ -3446,6 +3466,10 @@ sds clusterGenNodesDescription(int filter) {
return ci; return ci;
} }
/* -----------------------------------------------------------------------------
* CLUSTER command
* -------------------------------------------------------------------------- */
int getSlotOrReply(redisClient *c, robj *o) { int getSlotOrReply(redisClient *c, robj *o) {
long long slot; long long slot;
@ -3962,7 +3986,7 @@ void clusterCommand(redisClient *c) {
"configEpoch set to %llu via CLUSTER SET-CONFIG-EPOCH", "configEpoch set to %llu via CLUSTER SET-CONFIG-EPOCH",
(unsigned long long) myself->configEpoch); (unsigned long long) myself->configEpoch);
if (server.cluster->currentEpoch < epoch) if (server.cluster->currentEpoch < (uint64_t)epoch)
server.cluster->currentEpoch = epoch; server.cluster->currentEpoch = epoch;
/* No need to fsync the config here since in the unlucky event /* No need to fsync the config here since in the unlucky event
* of a failure to persist the config, the conflict resolution code * of a failure to persist the config, the conflict resolution code

View File

@ -73,7 +73,7 @@ void appendServerSaveParams(time_t seconds, int changes) {
server.saveparamslen++; server.saveparamslen++;
} }
void resetServerSaveParams() { void resetServerSaveParams(void) {
zfree(server.saveparams); zfree(server.saveparams);
server.saveparams = NULL; server.saveparams = NULL;
server.saveparamslen = 0; server.saveparamslen = 0;
@ -629,7 +629,7 @@ void configSetCommand(redisClient *c) {
server.maxclients = orig_value; server.maxclients = orig_value;
return; return;
} }
if (aeGetSetSize(server.el) < if ((unsigned int) aeGetSetSize(server.el) <
server.maxclients + REDIS_EVENTLOOP_FDSET_INCR) server.maxclients + REDIS_EVENTLOOP_FDSET_INCR)
{ {
if (aeResizeSetSize(server.el, if (aeResizeSetSize(server.el,

View File

@ -187,9 +187,14 @@ void setproctitle(const char *fmt, ...);
#if (__i386 || __amd64 || __powerpc__) && __GNUC__ #if (__i386 || __amd64 || __powerpc__) && __GNUC__
#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if (GNUC_VERSION >= 40100) || defined(__clang__) #if defined(__clang__)
#define HAVE_ATOMIC #define HAVE_ATOMIC
#endif #endif
#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))
#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6))
#define HAVE_ATOMIC
#endif
#endif
#endif #endif
#endif #endif

View File

@ -421,9 +421,7 @@ int parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor) {
* In the case of a Hash object the function returns both the field and value * In the case of a Hash object the function returns both the field and value
* of every element on the Hash. */ * of every element on the Hash. */
void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) { void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
int rv;
int i, j; int i, j;
char buf[REDIS_LONGSTR_SIZE];
list *keys = listCreate(); list *keys = listCreate();
listNode *node, *nextnode; listNode *node, *nextnode;
long count = 10; long count = 10;
@ -503,7 +501,7 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
privdata[1] = o; privdata[1] = o;
do { do {
cursor = dictScan(ht, cursor, scanCallback, privdata); cursor = dictScan(ht, cursor, scanCallback, privdata);
} while (cursor && listLength(keys) < count); } while (cursor && listLength(keys) < (unsigned long)count);
} else if (o->type == REDIS_SET) { } else if (o->type == REDIS_SET) {
int pos = 0; int pos = 0;
int64_t ll; int64_t ll;
@ -577,9 +575,7 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
/* Step 4: Reply to the client. */ /* Step 4: Reply to the client. */
addReplyMultiBulkLen(c, 2); addReplyMultiBulkLen(c, 2);
rv = snprintf(buf, sizeof(buf), "%lu", cursor); addReplyBulkLongLong(c,cursor);
redisAssert(rv < sizeof(buf));
addReplyBulkCBuffer(c, buf, rv);
addReplyMultiBulkLen(c, listLength(keys)); addReplyMultiBulkLen(c, listLength(keys));
while ((node = listFirst(keys)) != NULL) { while ((node = listFirst(keys)) != NULL) {
@ -707,6 +703,7 @@ void moveCommand(redisClient *c) {
robj *o; robj *o;
redisDb *src, *dst; redisDb *src, *dst;
int srcid; int srcid;
long long dbid;
if (server.cluster_enabled) { if (server.cluster_enabled) {
addReplyError(c,"MOVE is not allowed in cluster mode"); addReplyError(c,"MOVE is not allowed in cluster mode");
@ -716,7 +713,11 @@ void moveCommand(redisClient *c) {
/* Obtain source and target DB pointers */ /* Obtain source and target DB pointers */
src = c->db; src = c->db;
srcid = c->db->id; srcid = c->db->id;
if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
if (getLongLongFromObject(c->argv[2],&dbid) == REDIS_ERR ||
dbid < INT_MIN || dbid > INT_MAX ||
selectDb(c,dbid) == REDIS_ERR)
{
addReply(c,shared.outofrangeerr); addReply(c,shared.outofrangeerr);
return; return;
} }
@ -1076,7 +1077,7 @@ int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
* follow in SQL-alike style. Here we parse just the minimum in order to * follow in SQL-alike style. Here we parse just the minimum in order to
* correctly identify keys in the "STORE" option. */ * correctly identify keys in the "STORE" option. */
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) { int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int i, j, num, *keys; int i, j, num, *keys, found_store = 0;
REDIS_NOTUSED(cmd); REDIS_NOTUSED(cmd);
num = 0; num = 0;
@ -1107,12 +1108,13 @@ int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
/* Note: we don't increment "num" here and continue the loop /* Note: we don't increment "num" here and continue the loop
* to be sure to process the *last* "STORE" option if multiple * to be sure to process the *last* "STORE" option if multiple
* ones are provided. This is same behavior as SORT. */ * ones are provided. This is same behavior as SORT. */
found_store = 1;
keys[num] = i+1; /* <store-key> */ keys[num] = i+1; /* <store-key> */
break; break;
} }
} }
} }
*numkeys = num; *numkeys = num + found_store;
return keys; return keys;
} }

View File

@ -79,12 +79,6 @@ unsigned int dictIntHashFunction(unsigned int key)
return key; return key;
} }
/* Identity hash function for integer keys */
unsigned int dictIdentityHashFunction(unsigned int key)
{
return key;
}
static uint32_t dict_hash_function_seed = 5381; static uint32_t dict_hash_function_seed = 5381;
void dictSetHashFunctionSeed(uint32_t seed) { void dictSetHashFunctionSeed(uint32_t seed) {
@ -668,9 +662,9 @@ dictEntry *dictGetRandomKey(dict *d)
* statistics. However the function is much faster than dictGetRandomKey() * statistics. However the function is much faster than dictGetRandomKey()
* at producing N elements, and the elements are guaranteed to be non * at producing N elements, and the elements are guaranteed to be non
* repeating. */ * repeating. */
int dictGetRandomKeys(dict *d, dictEntry **des, int count) { unsigned int dictGetRandomKeys(dict *d, dictEntry **des, unsigned int count) {
int j; /* internal hash table id, 0 or 1. */ int j; /* internal hash table id, 0 or 1. */
int stored = 0; unsigned int stored = 0;
if (dictSize(d) < count) count = dictSize(d); if (dictSize(d) < count) count = dictSize(d);
while(stored < count) { while(stored < count) {

View File

@ -142,7 +142,7 @@ typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
#define dictGetDoubleVal(he) ((he)->v.d) #define dictGetDoubleVal(he) ((he)->v.d)
#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size) #define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)
#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used) #define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
#define dictIsRehashing(ht) ((ht)->rehashidx != -1) #define dictIsRehashing(d) ((d)->rehashidx != -1)
/* API */ /* API */
dict *dictCreate(dictType *type, void *privDataPtr); dict *dictCreate(dictType *type, void *privDataPtr);
@ -162,7 +162,7 @@ dictIterator *dictGetSafeIterator(dict *d);
dictEntry *dictNext(dictIterator *iter); dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter); void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d); dictEntry *dictGetRandomKey(dict *d);
int dictGetRandomKeys(dict *d, dictEntry **des, int count); unsigned int dictGetRandomKeys(dict *d, dictEntry **des, unsigned int count);
void dictPrintStats(dict *d); void dictPrintStats(dict *d);
unsigned int dictGenHashFunction(const void *key, int len); unsigned int dictGenHashFunction(const void *key, int len);
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);

View File

@ -36,6 +36,10 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#if defined(_AIX)
#define _ALL_SOURCE
#endif
#if defined(__linux__) || defined(__OpenBSD__) #if defined(__linux__) || defined(__OpenBSD__)
#define _XOPEN_SOURCE 700 #define _XOPEN_SOURCE 700
/* /*

View File

@ -1349,7 +1349,7 @@ void pfmergeCommand(redisClient *c) {
* Something that is not easy to test from within the outside. */ * Something that is not easy to test from within the outside. */
#define HLL_TEST_CYCLES 1000 #define HLL_TEST_CYCLES 1000
void pfselftestCommand(redisClient *c) { void pfselftestCommand(redisClient *c) {
int j, i; unsigned int j, i;
sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE); sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE);
struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2; struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2;
robj *o = NULL; robj *o = NULL;
@ -1431,7 +1431,7 @@ void pfselftestCommand(redisClient *c) {
if (j == 10) maxerr = 1; if (j == 10) maxerr = 1;
if (abserr < 0) abserr = -abserr; if (abserr < 0) abserr = -abserr;
if (abserr > maxerr) { if (abserr > (int64_t)maxerr) {
addReplyErrorFormat(c, addReplyErrorFormat(c,
"TESTFAILED Too big error. card:%llu abserr:%llu", "TESTFAILED Too big error. card:%llu abserr:%llu",
(unsigned long long) checkpoint, (unsigned long long) checkpoint,

View File

@ -133,7 +133,7 @@ static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
} }
while(max >= min) { while(max >= min) {
mid = (min+max)/2; mid = ((unsigned int)min + (unsigned int)max) >> 1;
cur = _intsetGet(is,mid); cur = _intsetGet(is,mid);
if (value > cur) { if (value > cur) {
min = mid+1; min = mid+1;

View File

@ -37,6 +37,7 @@
/* Dictionary type for latency events. */ /* Dictionary type for latency events. */
int dictStringKeyCompare(void *privdata, const void *key1, const void *key2) { int dictStringKeyCompare(void *privdata, const void *key1, const void *key2) {
REDIS_NOTUSED(privdata);
return strcmp(key1,key2) == 0; return strcmp(key1,key2) == 0;
} }

View File

@ -237,6 +237,7 @@ void memtest_test(size_t megabytes, int passes) {
memtest_progress_end(); memtest_progress_end();
memtest_compare_times(m,bytes,pass,4); memtest_compare_times(m,bytes,pass,4);
} }
free(m);
} }
void memtest_non_destructive_invert(void *addr, size_t size) { void memtest_non_destructive_invert(void *addr, size_t size) {

View File

@ -72,7 +72,7 @@ void queueMultiCommand(redisClient *c) {
void discardTransaction(redisClient *c) { void discardTransaction(redisClient *c) {
freeClientMultiState(c); freeClientMultiState(c);
initClientMultiState(c); initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);; c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);
unwatchAllKeys(c); unwatchAllKeys(c);
} }

View File

@ -1051,7 +1051,7 @@ int processMultibulkBuffer(redisClient *c) {
qblen = sdslen(c->querybuf); qblen = sdslen(c->querybuf);
/* Hint the sds library about the amount of bytes this string is /* Hint the sds library about the amount of bytes this string is
* going to contain. */ * going to contain. */
if (qblen < ll+2) if (qblen < (size_t)ll+2)
c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen); c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen);
} }
c->bulklen = ll; c->bulklen = ll;

View File

@ -358,7 +358,7 @@ void pubsubCommand(redisClient *c) {
list *l = dictFetchValue(server.pubsub_channels,c->argv[j]); list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);
addReplyBulk(c,c->argv[j]); addReplyBulk(c,c->argv[j]);
addReplyBulkLongLong(c,l ? listLength(l) : 0); addReplyLongLong(c,l ? listLength(l) : 0);
} }
} else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) { } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
/* PUBSUB NUMPAT */ /* PUBSUB NUMPAT */

View File

@ -66,7 +66,7 @@
#define HI_BIT (1L << (2 * N - 1)) #define HI_BIT (1L << (2 * N - 1))
static uint32_t x[3] = { X0, X1, X2 }, a[3] = { A0, A1, A2 }, c = C; static uint32_t x[3] = { X0, X1, X2 }, a[3] = { A0, A1, A2 }, c = C;
static void next(); static void next(void);
int32_t redisLrand48() { int32_t redisLrand48() {
next(); next();
@ -77,7 +77,7 @@ void redisSrand48(int32_t seedval) {
SEED(X0, LOW(seedval), HIGH(seedval)); SEED(X0, LOW(seedval), HIGH(seedval));
} }
static void next() { static void next(void) {
uint32_t p[2], q[2], r[2], carry0, carry1; uint32_t p[2], q[2], r[2], carry0, carry1;
MUL(a[0], x[0], p); MUL(a[0], x[0], p);

View File

@ -688,7 +688,7 @@ int rdbSave(char *filename) {
* loading code skips the check in this case. */ * loading code skips the check in this case. */
cksum = rdb.cksum; cksum = rdb.cksum;
memrev64ifbe(&cksum); memrev64ifbe(&cksum);
rioWrite(&rdb,&cksum,8); if (rioWrite(&rdb,&cksum,8) == 0) goto werr;
/* Make sure data will not remain on the OS's output buffers */ /* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr; if (fflush(fp) == EOF) goto werr;
@ -766,7 +766,7 @@ int rdbSaveBackground(char *filename) {
void rdbRemoveTempFile(pid_t childpid) { void rdbRemoveTempFile(pid_t childpid) {
char tmpfile[256]; char tmpfile[256];
snprintf(tmpfile,256,"temp-%d.rdb", (int) childpid); snprintf(tmpfile,sizeof(tmpfile),"temp-%d.rdb", (int) childpid);
unlink(tmpfile); unlink(tmpfile);
} }
@ -943,7 +943,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
/* Add pair to hash table */ /* Add pair to hash table */
ret = dictAdd((dict*)o->ptr, field, value); ret = dictAdd((dict*)o->ptr, field, value);
redisAssert(ret == REDIS_OK); redisAssert(ret == DICT_OK);
} }
/* All pairs should be read by now */ /* All pairs should be read by now */

View File

@ -77,6 +77,7 @@ static struct config {
int dbnum; int dbnum;
sds dbnumstr; sds dbnumstr;
char *tests; char *tests;
char *auth;
} config; } config;
typedef struct _client { typedef struct _client {
@ -213,7 +214,7 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
freeReplyObject(reply); freeReplyObject(reply);
if (c->selectlen) { if (c->selectlen) {
int j; size_t j;
/* This is the OK from SELECT. Just discard the SELECT /* This is the OK from SELECT. Just discard the SELECT
* from the buffer. */ * from the buffer. */
@ -325,6 +326,13 @@ static client createClient(char *cmd, size_t len, client from) {
* the example client buffer. */ * the example client buffer. */
c->obuf = sdsempty(); c->obuf = sdsempty();
if (config.auth) {
char *buf = NULL;
int len = redisFormatCommand(&buf, "AUTH %s", config.auth);
c->obuf = sdscatlen(c->obuf, buf, len);
free(buf);
}
/* If a DB number different than zero is selected, prefix our request /* If a DB number different than zero is selected, prefix our request
* buffer with the SELECT command, that will be discarded the first * buffer with the SELECT command, that will be discarded the first
* time the replies are received, so if the client is reused the * time the replies are received, so if the client is reused the
@ -346,6 +354,7 @@ static client createClient(char *cmd, size_t len, client from) {
for (j = 0; j < config.pipeline; j++) for (j = 0; j < config.pipeline; j++)
c->obuf = sdscatlen(c->obuf,cmd,len); c->obuf = sdscatlen(c->obuf,cmd,len);
} }
c->written = 0; c->written = 0;
c->pending = config.pipeline; c->pending = config.pipeline;
c->randptr = NULL; c->randptr = NULL;
@ -359,7 +368,7 @@ static client createClient(char *cmd, size_t len, client from) {
c->randfree = 0; c->randfree = 0;
c->randptr = zmalloc(sizeof(char*)*c->randlen); c->randptr = zmalloc(sizeof(char*)*c->randlen);
/* copy the offsets. */ /* copy the offsets. */
for (j = 0; j < c->randlen; j++) { for (j = 0; j < (int)c->randlen; j++) {
c->randptr[j] = c->obuf + (from->randptr[j]-from->obuf); c->randptr[j] = c->obuf + (from->randptr[j]-from->obuf);
/* Adjust for the different select prefix length. */ /* Adjust for the different select prefix length. */
c->randptr[j] += c->selectlen - from->selectlen; c->randptr[j] += c->selectlen - from->selectlen;
@ -389,15 +398,6 @@ static client createClient(char *cmd, size_t len, client from) {
static void createMissingClients(client c) { static void createMissingClients(client c) {
int n = 0; int n = 0;
char *buf = c->obuf;
size_t buflen = sdslen(c->obuf);
/* If we are cloning from a client with a SELECT prefix, skip it since the
* client will be created with the prefixed SELECT if needed. */
if (c->selectlen) {
buf += c->selectlen;
buflen -= c->selectlen;
}
while(config.liveclients < config.numclients) { while(config.liveclients < config.numclients) {
createClient(NULL,0,c); createClient(NULL,0,c);
@ -489,6 +489,9 @@ int parseOptions(int argc, const char **argv) {
} else if (!strcmp(argv[i],"-s")) { } else if (!strcmp(argv[i],"-s")) {
if (lastarg) goto invalid; if (lastarg) goto invalid;
config.hostsocket = strdup(argv[++i]); config.hostsocket = strdup(argv[++i]);
} else if (!strcmp(argv[i],"-a") ) {
if (lastarg) goto invalid;
config.auth = strdup(argv[++i]);
} else if (!strcmp(argv[i],"-d")) { } else if (!strcmp(argv[i],"-d")) {
if (lastarg) goto invalid; if (lastarg) goto invalid;
config.datasize = atoi(argv[++i]); config.datasize = atoi(argv[++i]);
@ -550,6 +553,7 @@ usage:
" -h <hostname> Server hostname (default 127.0.0.1)\n" " -h <hostname> Server hostname (default 127.0.0.1)\n"
" -p <port> Server port (default 6379)\n" " -p <port> Server port (default 6379)\n"
" -s <socket> Server socket (overrides host and port)\n" " -s <socket> Server socket (overrides host and port)\n"
" -a <password> Password for Redis Auth\n"
" -c <clients> Number of parallel connections (default 50)\n" " -c <clients> Number of parallel connections (default 50)\n"
" -n <requests> Total number of requests (default 10000)\n" " -n <requests> Total number of requests (default 10000)\n"
" -d <size> Data size of SET/GET value in bytes (default 2)\n" " -d <size> Data size of SET/GET value in bytes (default 2)\n"
@ -593,7 +597,7 @@ int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData
REDIS_NOTUSED(clientData); REDIS_NOTUSED(clientData);
if (config.liveclients == 0) { if (config.liveclients == 0) {
fprintf(stderr,"All clients disconnected... aborting."); fprintf(stderr,"All clients disconnected... aborting.\n");
exit(1); exit(1);
} }
@ -651,6 +655,7 @@ int main(int argc, const char **argv) {
config.hostsocket = NULL; config.hostsocket = NULL;
config.tests = NULL; config.tests = NULL;
config.dbnum = 0; config.dbnum = 0;
config.auth = NULL;
i = parseOptions(argc,argv); i = parseOptions(argc,argv);
argc -= i; argc -= i;

View File

@ -138,8 +138,10 @@ typedef struct {
* at runtime to avoid strange compiler optimizations. */ * at runtime to avoid strange compiler optimizations. */
static double R_Zero, R_PosInf, R_NegInf, R_Nan; static double R_Zero, R_PosInf, R_NegInf, R_Nan;
#define MAX_TYPES_NUM 256
#define MAX_TYPE_NAME_LEN 16
/* store string types for output */ /* store string types for output */
static char types[256][16]; static char types[MAX_TYPES_NUM][MAX_TYPE_NAME_LEN];
/* Return true if 't' is a valid object type. */ /* Return true if 't' is a valid object type. */
int checkType(unsigned char t) { int checkType(unsigned char t) {
@ -166,7 +168,7 @@ int readBytes(void *target, long num) {
return 1; return 1;
} }
int processHeader() { int processHeader(void) {
char buf[10] = "_________"; char buf[10] = "_________";
int dump_version; int dump_version;
@ -335,6 +337,7 @@ char* loadStringObject() {
if (len == REDIS_RDB_LENERR) return NULL; if (len == REDIS_RDB_LENERR) return NULL;
char *buf = malloc(sizeof(char) * (len+1)); char *buf = malloc(sizeof(char) * (len+1));
if (buf == NULL) return NULL;
buf[len] = '\0'; buf[len] = '\0';
if (!readBytes(buf, len)) { if (!readBytes(buf, len)) {
free(buf); free(buf);
@ -600,7 +603,7 @@ void printErrorStack(entry *e) {
} }
} }
void process() { void process(void) {
uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0; uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;
entry entry; entry entry;
int dump_version = processHeader(); int dump_version = processHeader();
@ -611,7 +614,7 @@ void process() {
printf("RDB version >= 5 but no room for checksum.\n"); printf("RDB version >= 5 but no room for checksum.\n");
exit(1); exit(1);
} }
positions[0].size -= 8;; positions[0].size -= 8;
} }
level = 1; level = 1;

View File

@ -94,10 +94,11 @@ static struct config {
sds mb_delim; sds mb_delim;
char prompt[128]; char prompt[128];
char *eval; char *eval;
int last_cmd_type;
} config; } config;
static volatile sig_atomic_t force_cancel_loop = 0; static volatile sig_atomic_t force_cancel_loop = 0;
static void usage(); static void usage(void);
static void slaveMode(void); static void slaveMode(void);
char *redisGitSHA1(void); char *redisGitSHA1(void);
char *redisGitDirty(void); char *redisGitDirty(void);
@ -131,7 +132,7 @@ static void cliRefreshPrompt(void) {
strchr(config.hostip,':') ? "[%s]:%d" : "%s:%d", strchr(config.hostip,':') ? "[%s]:%d" : "%s:%d",
config.hostip, config.hostport); config.hostip, config.hostport);
/* Add [dbnum] if needed */ /* Add [dbnum] if needed */
if (config.dbnum != 0) if (config.dbnum != 0 && config.last_cmd_type != REDIS_REPLY_ERROR)
len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]", len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]",
config.dbnum); config.dbnum);
snprintf(config.prompt+len,sizeof(config.prompt)-len,"> "); snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
@ -157,7 +158,7 @@ typedef struct {
static helpEntry *helpEntries; static helpEntry *helpEntries;
static int helpEntriesLen; static int helpEntriesLen;
static sds cliVersion() { static sds cliVersion(void) {
sds version; sds version;
version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION); version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
@ -171,7 +172,7 @@ static sds cliVersion() {
return version; return version;
} }
static void cliInitHelp() { static void cliInitHelp(void) {
int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp); int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
int groupslen = sizeof(commandGroups)/sizeof(char*); int groupslen = sizeof(commandGroups)/sizeof(char*);
int i, len, pos = 0; int i, len, pos = 0;
@ -210,7 +211,7 @@ static void cliOutputCommandHelp(struct commandHelp *help, int group) {
} }
/* Print generic help. */ /* Print generic help. */
static void cliOutputGenericHelp() { static void cliOutputGenericHelp(void) {
sds version = cliVersion(); sds version = cliVersion();
printf( printf(
"redis-cli %s\r\n" "redis-cli %s\r\n"
@ -320,8 +321,10 @@ static int cliSelect() {
reply = redisCommand(context,"SELECT %d",config.dbnum); reply = redisCommand(context,"SELECT %d",config.dbnum);
if (reply != NULL) { if (reply != NULL) {
int result = REDIS_OK;
if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;
freeReplyObject(reply); freeReplyObject(reply);
return REDIS_OK; return result;
} }
return REDIS_ERR; return REDIS_ERR;
} }
@ -365,7 +368,7 @@ static int cliConnect(int force) {
return REDIS_OK; return REDIS_OK;
} }
static void cliPrintContextError() { static void cliPrintContextError(void) {
if (context == NULL) return; if (context == NULL) return;
fprintf(stderr,"Error: %s\n",context->errstr); fprintf(stderr,"Error: %s\n",context->errstr);
} }
@ -514,8 +517,11 @@ static int cliReadReply(int output_raw_strings) {
int output = 1; int output = 1;
if (redisGetReply(context,&_reply) != REDIS_OK) { if (redisGetReply(context,&_reply) != REDIS_OK) {
if (config.shutdown) if (config.shutdown) {
redisFree(context);
context = NULL;
return REDIS_OK; return REDIS_OK;
}
if (config.interactive) { if (config.interactive) {
/* Filter cases where we should reconnect */ /* Filter cases where we should reconnect */
if (context->err == REDIS_ERR_IO && errno == ECONNRESET) if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
@ -530,6 +536,8 @@ static int cliReadReply(int output_raw_strings) {
reply = (redisReply*)_reply; reply = (redisReply*)_reply;
config.last_cmd_type = reply->type;
/* Check if we need to connect to a different node and reissue the /* Check if we need to connect to a different node and reissue the
* request. */ * request. */
if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR && if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR &&
@ -639,6 +647,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
printf("Entering slave output mode... (press Ctrl-C to quit)\n"); printf("Entering slave output mode... (press Ctrl-C to quit)\n");
slaveMode(); slaveMode();
config.slave_mode = 0; config.slave_mode = 0;
free(argvlen);
return REDIS_ERR; /* Error = slaveMode lost connection to master */ return REDIS_ERR; /* Error = slaveMode lost connection to master */
} }
@ -650,6 +659,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
if (!strcasecmp(command,"select") && argc == 2) { if (!strcasecmp(command,"select") && argc == 2) {
config.dbnum = atoi(argv[1]); config.dbnum = atoi(argv[1]);
cliRefreshPrompt(); cliRefreshPrompt();
} else if (!strcasecmp(command,"auth") && argc == 2) {
cliSelect();
} }
} }
if (config.interval) usleep(config.interval); if (config.interval) usleep(config.interval);
@ -724,6 +735,8 @@ static int parseOptions(int argc, char **argv) {
config.auth = argv[++i]; config.auth = argv[++i];
} else if (!strcmp(argv[i],"--raw")) { } else if (!strcmp(argv[i],"--raw")) {
config.output = OUTPUT_RAW; config.output = OUTPUT_RAW;
} else if (!strcmp(argv[i],"--no-raw")) {
config.output = OUTPUT_STANDARD;
} else if (!strcmp(argv[i],"--csv")) { } else if (!strcmp(argv[i],"--csv")) {
config.output = OUTPUT_CSV; config.output = OUTPUT_CSV;
} else if (!strcmp(argv[i],"--latency")) { } else if (!strcmp(argv[i],"--latency")) {
@ -795,7 +808,7 @@ static sds readArgFromStdin(void) {
return arg; return arg;
} }
static void usage() { static void usage(void) {
sds version = cliVersion(); sds version = cliVersion();
fprintf(stderr, fprintf(stderr,
"redis-cli %s\n" "redis-cli %s\n"
@ -814,6 +827,7 @@ static void usage() {
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n" " -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
" --raw Use raw formatting for replies (default when STDOUT is\n" " --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n" " not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
" --csv Output in CSV format.\n" " --csv Output in CSV format.\n"
" --latency Enter a special mode continuously sampling latency.\n" " --latency Enter a special mode continuously sampling latency.\n"
" --latency-history Like --latency but tracking latency changes over time.\n" " --latency-history Like --latency but tracking latency changes over time.\n"
@ -862,8 +876,7 @@ static char **convertToSds(int count, char** args) {
return sds; return sds;
} }
#define LINE_BUFLEN 4096 static void repl(void) {
static void repl() {
sds historyfile = NULL; sds historyfile = NULL;
int history = 0; int history = 0;
char *line; char *line;
@ -1406,7 +1419,7 @@ static int toIntType(char *key, char *type) {
static void getKeyTypes(redisReply *keys, int *types) { static void getKeyTypes(redisReply *keys, int *types) {
redisReply *reply; redisReply *reply;
int i; unsigned int i;
/* Pipeline TYPE commands */ /* Pipeline TYPE commands */
for(i=0;i<keys->elements;i++) { for(i=0;i<keys->elements;i++) {
@ -1435,7 +1448,7 @@ static void getKeySizes(redisReply *keys, int *types,
{ {
redisReply *reply; redisReply *reply;
char *sizecmds[] = {"STRLEN","LLEN","SCARD","HLEN","ZCARD"}; char *sizecmds[] = {"STRLEN","LLEN","SCARD","HLEN","ZCARD"};
int i; unsigned int i;
/* Pipeline size commands */ /* Pipeline size commands */
for(i=0;i<keys->elements;i++) { for(i=0;i<keys->elements;i++) {
@ -1482,7 +1495,8 @@ static void findBigKeys(void) {
char *typename[] = {"string","list","set","hash","zset"}; char *typename[] = {"string","list","set","hash","zset"};
char *typeunit[] = {"bytes","items","members","fields","members"}; char *typeunit[] = {"bytes","items","members","fields","members"};
redisReply *reply, *keys; redisReply *reply, *keys;
int type, *types=NULL, arrsize=0, i; unsigned int arrsize=0, i;
int type, *types=NULL;
double pct; double pct;
/* Total keys pre scanning */ /* Total keys pre scanning */
@ -1666,7 +1680,7 @@ void bytesToHuman(char *s, long long n) {
} }
} }
static void statMode() { static void statMode(void) {
redisReply *reply; redisReply *reply;
long aux, requests = 0; long aux, requests = 0;
int i = 0; int i = 0;
@ -1752,7 +1766,7 @@ static void statMode() {
* Scan mode * Scan mode
*--------------------------------------------------------------------------- */ *--------------------------------------------------------------------------- */
static void scanMode() { static void scanMode(void) {
redisReply *reply; redisReply *reply;
unsigned long long cur = 0; unsigned long long cur = 0;
@ -1769,7 +1783,7 @@ static void scanMode() {
printf("ERROR: %s\n", reply->str); printf("ERROR: %s\n", reply->str);
exit(1); exit(1);
} else { } else {
int j; unsigned int j;
cur = strtoull(reply->element[0]->str,NULL,10); cur = strtoull(reply->element[0]->str,NULL,10);
for (j = 0; j < reply->element[1]->elements; j++) for (j = 0; j < reply->element[1]->elements; j++)
@ -1840,11 +1854,15 @@ static void intrinsicLatencyMode(void) {
printf("Max latency so far: %lld microseconds.\n", max_latency); printf("Max latency so far: %lld microseconds.\n", max_latency);
} }
double avg_us = (double)run_time/runs;
double avg_ns = avg_us * 10e3;
if (force_cancel_loop || end > test_end) { if (force_cancel_loop || end > test_end) {
printf("\n%lld total runs (avg %lld microseconds per run).\n", printf("\n%lld total runs "
runs, run_time/runs); "(avg latency: "
printf("Worst run took %.02fx times the average.\n", "%.4f microseconds / %.2f nanoseconds per run).\n",
(double) max_latency / (run_time/runs)); runs, avg_us, avg_ns);
printf("Worst run took %.0fx longer than the average latency.\n",
max_latency / avg_us);
exit(0); exit(0);
} }
} }
@ -1883,6 +1901,8 @@ int main(int argc, char **argv) {
config.stdinarg = 0; config.stdinarg = 0;
config.auth = NULL; config.auth = NULL;
config.eval = NULL; config.eval = NULL;
config.last_cmd_type = -1;
if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL)) if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
config.output = OUTPUT_RAW; config.output = OUTPUT_RAW;
else else

View File

@ -66,7 +66,6 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
/* Global vars */ /* Global vars */
struct redisServer server; /* server global state */ struct redisServer server; /* server global state */
struct redisCommand *commandTable;
/* Our command table. /* Our command table.
* *
@ -267,7 +266,7 @@ struct redisCommand redisCommandTable[] = {
{"readwrite",readwriteCommand,1,"rF",0,NULL,0,0,0,0,0}, {"readwrite",readwriteCommand,1,"rF",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0}, {"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0},
{"object",objectCommand,3,"r",0,NULL,2,2,2,0,0}, {"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},
{"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,"ars",0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0}, {"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0}, {"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0}, {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
@ -760,8 +759,8 @@ void activeExpireCycle(int type) {
static int timelimit_exit = 0; /* Time limit hit in previous call? */ static int timelimit_exit = 0; /* Time limit hit in previous call? */
static long long last_fast_cycle = 0; /* When last fast cycle ran. */ static long long last_fast_cycle = 0; /* When last fast cycle ran. */
unsigned int j, iteration = 0; int j, iteration = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
long long start = ustime(), timelimit; long long start = ustime(), timelimit;
if (type == ACTIVE_EXPIRE_CYCLE_FAST) { if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
@ -1000,8 +999,8 @@ void databasesCron(void) {
* cron loop iteration. */ * cron loop iteration. */
static unsigned int resize_db = 0; static unsigned int resize_db = 0;
static unsigned int rehash_db = 0; static unsigned int rehash_db = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
unsigned int j; int j;
/* Don't test more DBs than we have. */ /* Don't test more DBs than we have. */
if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum; if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;
@ -1376,7 +1375,7 @@ void createSharedObjects(void) {
shared.maxstring = createStringObject("maxstring",9); shared.maxstring = createStringObject("maxstring",9);
} }
void initServerConfig() { void initServerConfig(void) {
int j; int j;
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE); getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
@ -1557,7 +1556,7 @@ void adjustOpenFilesLimit(void) {
* to the higher value supported less than maxfiles. */ * to the higher value supported less than maxfiles. */
f = maxfiles; f = maxfiles;
while(f > oldlimit) { while(f > oldlimit) {
int decr_step = 16; rlim_t decr_step = 16;
limit.rlim_cur = f; limit.rlim_cur = f;
limit.rlim_max = f; limit.rlim_max = f;
@ -1696,7 +1695,7 @@ void resetServerStats(void) {
server.ops_sec_last_sample_ops = 0; server.ops_sec_last_sample_ops = 0;
} }
void initServer() { void initServer(void) {
int j; int j;
signal(SIGHUP, SIG_IGN); signal(SIGHUP, SIG_IGN);
@ -2359,9 +2358,9 @@ int time_independent_strcmp(char *a, char *b) {
* a or b are fixed (our password) length, and the difference is only * a or b are fixed (our password) length, and the difference is only
* relative to the length of the user provided string, so no information * relative to the length of the user provided string, so no information
* leak is possible in the following two lines of code. */ * leak is possible in the following two lines of code. */
int alen = strlen(a); unsigned int alen = strlen(a);
int blen = strlen(b); unsigned int blen = strlen(b);
int j; unsigned int j;
int diff = 0; int diff = 0;
/* We can't compare strings longer than our static buffers. /* We can't compare strings longer than our static buffers.
@ -2547,6 +2546,15 @@ void bytesToHuman(char *s, unsigned long long n) {
} else if (n < (1024LL*1024*1024*1024)) { } else if (n < (1024LL*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024); d = (double)n/(1024LL*1024*1024);
sprintf(s,"%.2fG",d); sprintf(s,"%.2fG",d);
} else if (n < (1024LL*1024*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024*1024);
sprintf(s,"%.2fT",d);
} else if (n < (1024LL*1024*1024*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024*1024*1024);
sprintf(s,"%.2fP",d);
} else {
/* Let's hope we never need this */
sprintf(s,"%lluB",n);
} }
} }
@ -2562,10 +2570,9 @@ sds genRedisInfoString(char *section) {
int allsections = 0, defsections = 0; int allsections = 0, defsections = 0;
int sections = 0; int sections = 0;
if (section) { if (section == NULL) section = "default";
allsections = strcasecmp(section,"all") == 0; allsections = strcasecmp(section,"all") == 0;
defsections = strcasecmp(section,"default") == 0; defsections = strcasecmp(section,"default") == 0;
}
getrusage(RUSAGE_SELF, &self_ru); getrusage(RUSAGE_SELF, &self_ru);
getrusage(RUSAGE_CHILDREN, &c_ru); getrusage(RUSAGE_CHILDREN, &c_ru);
@ -3350,7 +3357,7 @@ void daemonize(void) {
} }
} }
void version() { void version(void) {
printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d build=%llx\n", printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d build=%llx\n",
REDIS_VERSION, REDIS_VERSION,
redisGitSHA1(), redisGitSHA1(),
@ -3361,7 +3368,7 @@ void version() {
exit(0); exit(0);
} }
void usage() { void usage(void) {
fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n"); fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n");
fprintf(stderr," ./redis-server - (read config from stdin)\n"); fprintf(stderr," ./redis-server - (read config from stdin)\n");
fprintf(stderr," ./redis-server -v or --version\n"); fprintf(stderr," ./redis-server -v or --version\n");
@ -3399,10 +3406,33 @@ void redisAsciiArt(void) {
zfree(buf); zfree(buf);
} }
static void sigtermHandler(int sig) { static void sigShutdownHandler(int sig) {
REDIS_NOTUSED(sig); char *msg;
redisLogFromHandler(REDIS_WARNING,"Received SIGTERM, scheduling shutdown..."); switch (sig) {
case SIGINT:
msg = "Received SIGINT scheduling shutdown...";
break;
case SIGTERM:
msg = "Received SIGTERM scheduling shutdown...";
break;
default:
msg = "Received shutdown signal, scheduling shutdown...";
};
/* SIGINT is often delivered via Ctrl+C in an interactive session.
* If we receive the signal the second time, we interpret this as
* the user really wanting to quit ASAP without waiting to persist
* on disk. */
if (server.shutdown_asap && sig == SIGINT) {
redisLogFromHandler(REDIS_WARNING, "You insist... exiting now.");
rdbRemoveTempFile(getpid());
exit(1); /* Exit with an error since this was not a clean shutdown. */
} else if (server.loading) {
exit(0);
}
redisLogFromHandler(REDIS_WARNING, msg);
server.shutdown_asap = 1; server.shutdown_asap = 1;
} }
@ -3413,8 +3443,9 @@ void setupSignalHandlers(void) {
* Otherwise, sa_handler is used. */ * Otherwise, sa_handler is used. */
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
act.sa_flags = 0; act.sa_flags = 0;
act.sa_handler = sigtermHandler; act.sa_handler = sigShutdownHandler;
sigaction(SIGTERM, &act, NULL); sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
#ifdef HAVE_BACKTRACE #ifdef HAVE_BACKTRACE
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
@ -3545,6 +3576,13 @@ int main(int argc, char **argv) {
} }
j++; j++;
} }
if (server.sentinel_mode && configfile && *configfile == '-') {
redisLog(REDIS_WARNING,
"Sentinel config from STDIN not allowed.");
redisLog(REDIS_WARNING,
"Sentinel needs config file on disk to save state. Exiting...");
exit(1);
}
if (configfile) server.configfile = getAbsolutePath(configfile); if (configfile) server.configfile = getAbsolutePath(configfile);
resetServerSaveParams(); resetServerSaveParams();
loadServerConfig(configfile,options); loadServerConfig(configfile,options);

View File

@ -626,6 +626,12 @@ typedef struct redisOpArray {
struct clusterState; struct clusterState;
/* AIX defines hz to __hz, we don't use this define and in order to allow
* Redis build on AIX we need to undef it. */
#ifdef _AIX
#undef hz
#endif
struct redisServer { struct redisServer {
/* General */ /* General */
pid_t pid; /* Main process pid. */ pid_t pid; /* Main process pid. */
@ -808,12 +814,12 @@ struct redisServer {
/* Replication script cache. */ /* Replication script cache. */
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */ dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */ list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */
int repl_scriptcache_size; /* Max number of elements. */ unsigned int repl_scriptcache_size; /* Max number of elements. */
/* Synchronous replication. */ /* Synchronous replication. */
list *clients_waiting_acks; /* Clients waiting in WAIT command. */ list *clients_waiting_acks; /* Clients waiting in WAIT command. */
int get_ack_from_slaves; /* If true we send REPLCONF GETACK. */ int get_ack_from_slaves; /* If true we send REPLCONF GETACK. */
/* Limits */ /* Limits */
int maxclients; /* Max number of simultaneous clients */ unsigned int maxclients; /* Max number of simultaneous clients */
unsigned long long maxmemory; /* Max number of memory bytes to use */ unsigned long long maxmemory; /* Max number of memory bytes to use */
int maxmemory_policy; /* Policy for key eviction */ int maxmemory_policy; /* Policy for key eviction */
int maxmemory_samples; /* Pricision of random sampling */ int maxmemory_samples; /* Pricision of random sampling */
@ -993,11 +999,10 @@ void freeClient(redisClient *c);
void freeClientAsync(redisClient *c); void freeClientAsync(redisClient *c);
void resetClient(redisClient *c); void resetClient(redisClient *c);
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask); void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
void addReply(redisClient *c, robj *obj);
void *addDeferredMultiBulkLength(redisClient *c); void *addDeferredMultiBulkLength(redisClient *c);
void setDeferredMultiBulkLength(redisClient *c, void *node, long length); void setDeferredMultiBulkLength(redisClient *c, void *node, long length);
void addReplySds(redisClient *c, sds s);
void processInputBuffer(redisClient *c); void processInputBuffer(redisClient *c);
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask); void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask); void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask); void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);
@ -1005,7 +1010,6 @@ void addReplyBulk(redisClient *c, robj *obj);
void addReplyBulkCString(redisClient *c, char *s); void addReplyBulkCString(redisClient *c, char *s);
void addReplyBulkCBuffer(redisClient *c, void *p, size_t len); void addReplyBulkCBuffer(redisClient *c, void *p, size_t len);
void addReplyBulkLongLong(redisClient *c, long long ll); void addReplyBulkLongLong(redisClient *c, long long ll);
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void addReply(redisClient *c, robj *obj); void addReply(redisClient *c, robj *obj);
void addReplySds(redisClient *c, sds s); void addReplySds(redisClient *c, sds s);
void addReplyError(redisClient *c, char *err); void addReplyError(redisClient *c, char *err);
@ -1210,7 +1214,7 @@ void redisLog(int level, const char *fmt, ...);
#endif #endif
void redisLogRaw(int level, const char *msg); void redisLogRaw(int level, const char *msg);
void redisLogFromHandler(int level, const char *msg); void redisLogFromHandler(int level, const char *msg);
void usage(); void usage(void);
void updateDictResizePolicy(void); void updateDictResizePolicy(void);
int htNeedsResize(dict *dict); int htNeedsResize(dict *dict);
void oom(const char *msg); void oom(const char *msg);
@ -1270,7 +1274,7 @@ sds keyspaceEventsFlagsToString(int flags);
/* Configuration */ /* Configuration */
void loadServerConfig(char *filename, char *options); void loadServerConfig(char *filename, char *options);
void appendServerSaveParams(time_t seconds, int changes); void appendServerSaveParams(time_t seconds, int changes);
void resetServerSaveParams(); void resetServerSaveParams(void);
struct rewriteConfigState; /* Forward declaration to export API. */ struct rewriteConfigState; /* Forward declaration to export API. */
void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force); void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force);
int rewriteConfig(char *path); int rewriteConfig(char *path);

View File

@ -212,7 +212,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
static robj **argv = NULL; static robj **argv = NULL;
static int argv_size = 0; static int argv_size = 0;
static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE]; static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE];
static int cached_objects_len[LUA_CMD_OBJCACHE_SIZE]; static size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE];
/* Require at least one argument */ /* Require at least one argument */
if (argc == 0) { if (argc == 0) {
@ -910,6 +910,9 @@ void evalGenericCommand(redisClient *c, int evalsha) {
if (numkeys > (c->argc - 3)) { if (numkeys > (c->argc - 3)) {
addReplyError(c,"Number of keys can't be greater than number of args"); addReplyError(c,"Number of keys can't be greater than number of args");
return; return;
} else if (numkeys < 0) {
addReplyError(c,"Number of keys can't be negative");
return;
} }
/* We obtain the script SHA1, then check if this function is already /* We obtain the script SHA1, then check if this function is already

View File

@ -200,7 +200,10 @@ size_t sdsAllocSize(sds s) {
void sdsIncrLen(sds s, int incr) { void sdsIncrLen(sds s, int incr) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
assert(sh->free >= incr); if (incr >= 0)
assert(sh->free >= (unsigned int)incr);
else
assert(sh->len >= (unsigned int)(-incr));
sh->len += incr; sh->len += incr;
sh->free -= incr; sh->free -= incr;
assert(sh->free >= 0); assert(sh->free >= 0);
@ -388,6 +391,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
buf[buflen-2] = '\0'; buf[buflen-2] = '\0';
va_copy(cpy,ap); va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy); vsnprintf(buf, buflen, fmt, cpy);
va_end(ap);
if (buf[buflen-2] != '\0') { if (buf[buflen-2] != '\0') {
if (buf != staticbuf) zfree(buf); if (buf != staticbuf) zfree(buf);
buflen *= 2; buflen *= 2;
@ -457,7 +461,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
i = initlen; /* Position of the next byte to write to dest str. */ i = initlen; /* Position of the next byte to write to dest str. */
while(*f) { while(*f) {
char next, *str; char next, *str;
int l; unsigned int l;
long long num; long long num;
unsigned long long unum; unsigned long long unum;

View File

@ -39,8 +39,8 @@
typedef char *sds; typedef char *sds;
struct sdshdr { struct sdshdr {
int len; unsigned int len;
int free; unsigned int free;
char buf[]; char buf[];
}; };

View File

@ -159,7 +159,7 @@ typedef struct sentinelRedisInstance {
/* Master specific. */ /* Master specific. */
dict *sentinels; /* Other sentinels monitoring the same master. */ dict *sentinels; /* Other sentinels monitoring the same master. */
dict *slaves; /* Slaves for this master instance. */ dict *slaves; /* Slaves for this master instance. */
int quorum; /* Number of sentinels that need to agree on failure. */ unsigned int quorum;/* Number of sentinels that need to agree on failure. */
int parallel_syncs; /* How many slaves to reconfigure at same time. */ int parallel_syncs; /* How many slaves to reconfigure at same time. */
char *auth_pass; /* Password to use for AUTH against master & slaves. */ char *auth_pass; /* Password to use for AUTH against master & slaves. */
@ -345,6 +345,7 @@ int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
void releaseSentinelRedisInstance(sentinelRedisInstance *ri); void releaseSentinelRedisInstance(sentinelRedisInstance *ri);
void dictInstancesValDestructor (void *privdata, void *obj) { void dictInstancesValDestructor (void *privdata, void *obj) {
REDIS_NOTUSED(privdata);
releaseSentinelRedisInstance(obj); releaseSentinelRedisInstance(obj);
} }
@ -403,7 +404,7 @@ void initSentinelConfig(void) {
/* Perform the Sentinel mode initialization. */ /* Perform the Sentinel mode initialization. */
void initSentinel(void) { void initSentinel(void) {
int j; unsigned int j;
/* Remove usual Redis commands from the command table, then just add /* Remove usual Redis commands from the command table, then just add
* the SENTINEL command. */ * the SENTINEL command. */
@ -455,19 +456,19 @@ void sentinelIsRunning(void) {
* EINVAL: Invalid port number. * EINVAL: Invalid port number.
*/ */
sentinelAddr *createSentinelAddr(char *hostname, int port) { sentinelAddr *createSentinelAddr(char *hostname, int port) {
char buf[32]; char ip[REDIS_IP_STR_LEN];
sentinelAddr *sa; sentinelAddr *sa;
if (port <= 0 || port > 65535) { if (port <= 0 || port > 65535) {
errno = EINVAL; errno = EINVAL;
return NULL; return NULL;
} }
if (anetResolve(NULL,hostname,buf,sizeof(buf)) == ANET_ERR) { if (anetResolve(NULL,hostname,ip,sizeof(ip)) == ANET_ERR) {
errno = ENOENT; errno = ENOENT;
return NULL; return NULL;
} }
sa = zmalloc(sizeof(*sa)); sa = zmalloc(sizeof(*sa));
sa->ip = sdsnew(buf); sa->ip = sdsnew(ip);
sa->port = port; sa->port = port;
return sa; return sa;
} }
@ -1634,6 +1635,7 @@ void sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) {
} }
void sentinelDisconnectCallback(const redisAsyncContext *c, int status) { void sentinelDisconnectCallback(const redisAsyncContext *c, int status) {
REDIS_NOTUSED(status);
sentinelDisconnectInstanceFromContext(c); sentinelDisconnectInstanceFromContext(c);
} }
@ -1998,6 +2000,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data; sentinelRedisInstance *ri = c->data;
redisReply *r; redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--; if (ri) ri->pending_commands--;
if (!reply || !ri) return; if (!reply || !ri) return;
@ -2012,6 +2015,8 @@ void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata
* value of the command but its effects directly. */ * value of the command but its effects directly. */
void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data; sentinelRedisInstance *ri = c->data;
REDIS_NOTUSED(reply);
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--; if (ri) ri->pending_commands--;
} }
@ -2019,6 +2024,7 @@ void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privd
void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data; sentinelRedisInstance *ri = c->data;
redisReply *r; redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--; if (ri) ri->pending_commands--;
if (!reply || !ri) return; if (!reply || !ri) return;
@ -2057,6 +2063,7 @@ void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata
void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data; sentinelRedisInstance *ri = c->data;
redisReply *r; redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--; if (ri) ri->pending_commands--;
if (!reply || !ri) return; if (!reply || !ri) return;
@ -2166,6 +2173,7 @@ cleanup:
void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) { void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data; sentinelRedisInstance *ri = c->data;
redisReply *r; redisReply *r;
REDIS_NOTUSED(privdata);
if (!reply || !ri) return; if (!reply || !ri) return;
r = reply; r = reply;
@ -2559,7 +2567,7 @@ sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c,
{ {
sentinelRedisInstance *ri; sentinelRedisInstance *ri;
ri = dictFetchValue(sentinel.masters,c->argv[2]->ptr); ri = dictFetchValue(sentinel.masters,name->ptr);
if (!ri) { if (!ri) {
addReplyError(c,"No such master with that name"); addReplyError(c,"No such master with that name");
return NULL; return NULL;
@ -2682,7 +2690,7 @@ void sentinelCommand(redisClient *c) {
/* SENTINEL MONITOR <name> <ip> <port> <quorum> */ /* SENTINEL MONITOR <name> <ip> <port> <quorum> */
sentinelRedisInstance *ri; sentinelRedisInstance *ri;
long quorum, port; long quorum, port;
char buf[32]; char ip[REDIS_IP_STR_LEN];
if (c->argc != 6) goto numargserr; if (c->argc != 6) goto numargserr;
if (getLongFromObjectOrReply(c,c->argv[5],&quorum,"Invalid quorum") if (getLongFromObjectOrReply(c,c->argv[5],&quorum,"Invalid quorum")
@ -2692,7 +2700,7 @@ void sentinelCommand(redisClient *c) {
/* Make sure the IP field is actually a valid IP before passing it /* Make sure the IP field is actually a valid IP before passing it
* to createSentinelRedisInstance(), otherwise we may trigger a * to createSentinelRedisInstance(), otherwise we may trigger a
* DNS lookup at runtime. */ * DNS lookup at runtime. */
if (anetResolveIP(NULL,c->argv[3]->ptr,buf,sizeof(buf)) == ANET_ERR) { if (anetResolveIP(NULL,c->argv[3]->ptr,ip,sizeof(ip)) == ANET_ERR) {
addReplyError(c,"Invalid IP address specified"); addReplyError(c,"Invalid IP address specified");
return; return;
} }
@ -2997,7 +3005,7 @@ void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {
void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) { void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {
dictIterator *di; dictIterator *di;
dictEntry *de; dictEntry *de;
int quorum = 0, odown = 0; unsigned int quorum = 0, odown = 0;
if (master->flags & SRI_S_DOWN) { if (master->flags & SRI_S_DOWN) {
/* Is down for enough sentinels? */ /* Is down for enough sentinels? */
@ -3034,6 +3042,7 @@ void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {
void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) { void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data; sentinelRedisInstance *ri = c->data;
redisReply *r; redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--; if (ri) ri->pending_commands--;
if (!reply || !ri) return; if (!reply || !ri) return;
@ -3057,7 +3066,7 @@ void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *p
/* If the runid in the reply is not "*" the Sentinel actually /* If the runid in the reply is not "*" the Sentinel actually
* replied with a vote. */ * replied with a vote. */
sdsfree(ri->leader); sdsfree(ri->leader);
if (ri->leader_epoch != r->element[2]->integer) if ((long long)ri->leader_epoch != r->element[2]->integer)
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"%s voted for %s %llu", ri->name, "%s voted for %s %llu", ri->name,
r->element[1]->str, r->element[1]->str,

View File

@ -69,7 +69,7 @@ void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *ex
if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK) if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
return; return;
if (milliseconds <= 0) { if (milliseconds <= 0) {
addReplyError(c,"invalid expire time in SETEX"); addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
return; return;
} }
if (unit == UNIT_SECONDS) milliseconds *= 1000; if (unit == UNIT_SECONDS) milliseconds *= 1000;
@ -255,7 +255,7 @@ void getrangeCommand(redisClient *c) {
if (end < 0) end = strlen+end; if (end < 0) end = strlen+end;
if (start < 0) start = 0; if (start < 0) start = 0;
if (end < 0) end = 0; if (end < 0) end = 0;
if ((unsigned)end >= strlen) end = strlen-1; if ((size_t)end >= strlen) end = strlen-1;
/* Precondition: end >= 0 && end < strlen, so the only condition where /* Precondition: end >= 0 && end < strlen, so the only condition where
* nothing can be returned is: start > end. */ * nothing can be returned is: start > end. */

View File

@ -205,8 +205,6 @@ int zslDelete(zskiplist *zsl, double score, robj *obj) {
zslDeleteNode(zsl, x, update); zslDeleteNode(zsl, x, update);
zslFreeNode(x); zslFreeNode(x);
return 1; return 1;
} else {
return 0; /* not found */
} }
return 0; /* not found */ return 0; /* not found */
} }

View File

@ -576,19 +576,19 @@ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsig
/* Insert item at "p". */ /* Insert item at "p". */
static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) { static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0; size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;
unsigned int prevlensize, prevlen = 0;
size_t offset; size_t offset;
int nextdiff = 0; int nextdiff = 0;
unsigned char encoding = 0; unsigned char encoding = 0;
long long value = 123456789; /* initialized to avoid warning. Using a value long long value = 123456789; /* initialized to avoid warning. Using a value
that is easy to see if for some reason that is easy to see if for some reason
we use it uninitialized. */ we use it uninitialized. */
zlentry entry, tail; zlentry tail;
/* Find out prevlen for the entry that is inserted. */ /* Find out prevlen for the entry that is inserted. */
if (p[0] != ZIP_END) { if (p[0] != ZIP_END) {
entry = zipEntry(p); ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
prevlen = entry.prevrawlen;
} else { } else {
unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl); unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
if (ptail[0] != ZIP_END) { if (ptail[0] != ZIP_END) {
@ -676,15 +676,15 @@ unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int sle
* doesn't contain an element at the provided index, NULL is returned. */ * doesn't contain an element at the provided index, NULL is returned. */
unsigned char *ziplistIndex(unsigned char *zl, int index) { unsigned char *ziplistIndex(unsigned char *zl, int index) {
unsigned char *p; unsigned char *p;
zlentry entry; unsigned int prevlensize, prevlen = 0;
if (index < 0) { if (index < 0) {
index = (-index)-1; index = (-index)-1;
p = ZIPLIST_ENTRY_TAIL(zl); p = ZIPLIST_ENTRY_TAIL(zl);
if (p[0] != ZIP_END) { if (p[0] != ZIP_END) {
entry = zipEntry(p); ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
while (entry.prevrawlen > 0 && index--) { while (prevlen > 0 && index--) {
p -= entry.prevrawlen; p -= prevlen;
entry = zipEntry(p); ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
} }
} }
} else { } else {
@ -722,7 +722,7 @@ unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
/* Return pointer to previous entry in ziplist. */ /* Return pointer to previous entry in ziplist. */
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) { unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
zlentry entry; unsigned int prevlensize, prevlen = 0;
/* Iterating backwards from ZIP_END should return the tail. When "p" is /* Iterating backwards from ZIP_END should return the tail. When "p" is
* equal to the first element of the list, we're already at the head, * equal to the first element of the list, we're already at the head,
@ -733,9 +733,9 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
} else if (p == ZIPLIST_ENTRY_HEAD(zl)) { } else if (p == ZIPLIST_ENTRY_HEAD(zl)) {
return NULL; return NULL;
} else { } else {
entry = zipEntry(p); ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
assert(entry.prevrawlen > 0); assert(prevlen > 0);
return p-entry.prevrawlen; return p-prevlen;
} }
} }

View File

@ -353,7 +353,7 @@ proc colorstr {color str} {
default {set colorcode {37}} default {set colorcode {37}}
} }
if {$colorcode ne {}} { if {$colorcode ne {}} {
return "\033\[$b;${colorcode};40m$str\033\[0m" return "\033\[$b;${colorcode};49m$str\033\[0m"
} }
} else { } else {
return $str return $str

View File

@ -404,6 +404,12 @@ start_server {tags {"basic"}} {
r move mykey 10 r move mykey 10
} {0} } {0}
test {MOVE against non-integer DB (#1428)} {
r set mykey hello
catch {r move mykey notanumber} e
set e
} {*ERR*index out of range}
test {SET/GET keys in different DBs} { test {SET/GET keys in different DBs} {
r set a hello r set a hello
r set b world r set b world
@ -769,4 +775,9 @@ start_server {tags {"basic"}} {
r keys * r keys *
r keys * r keys *
} {dlskeriewrioeuwqoirueioqwrueoqwrueqw} } {dlskeriewrioeuwqoirueioqwrueoqwrueqw}
test {GETRANGE with huge ranges, Github issue #1844} {
r set foo bar
r getrange foo 0 4294967297
} {bar}
} }

View File

@ -196,6 +196,10 @@ start_server {tags {"pubsub"}} {
$rd1 close $rd1 close
} }
test "NUMSUB returns numbers, not strings (#1561)" {
r pubsub numsub abc def
} {abc 0 def 0}
test "Mix SUBSCRIBE and PSUBSCRIBE" { test "Mix SUBSCRIBE and PSUBSCRIBE" {
set rd1 [redis_deferring_client] set rd1 [redis_deferring_client]
assert_equal {1} [subscribe $rd1 {foo.bar}] assert_equal {1} [subscribe $rd1 {foo.bar}]

View File

@ -358,6 +358,11 @@ start_server {tags {"scripting"}} {
return redis.call("get", "key") return redis.call("get", "key")
} 0 } 0
} {12039611435714932082} } {12039611435714932082}
test {Verify negative arg count is error instead of crash (issue #1842)} {
catch { r eval { return "hello" } -12 } e
set e
} {ERR Number of keys can't be negative}
} }
# Start a new server since the last test in this stanza will kill the # Start a new server since the last test in this stanza will kill the

View File

@ -95,6 +95,14 @@ start_server {
assert_encoding ziplist sort-res assert_encoding ziplist sort-res
} }
test "SORT extracts STORE correctly" {
r command getkeys sort abc store def
} {abc def}
test "SORT extracts multiple STORE correctly" {
r command getkeys sort abc store invalid store stillbad store def
} {abc def}
test "SORT DESC" { test "SORT DESC" {
assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC] assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC]
} }

View File

@ -50,7 +50,7 @@ def commands
require "json" require "json"
require "uri" require "uri"
url = URI.parse "https://raw.github.com/antirez/redis-doc/master/commands.json" url = URI.parse "https://raw.githubusercontent.com/antirez/redis-doc/master/commands.json"
client = Net::HTTP.new url.host, url.port client = Net::HTTP.new url.host, url.port
client.use_ssl = true client.use_ssl = true
response = client.get url.path response = client.get url.path

View File

@ -152,7 +152,7 @@ rm -f $TMP_FILE
#we hard code the configs here to avoid issues with templates containing env vars #we hard code the configs here to avoid issues with templates containing env vars
#kinda lame but works! #kinda lame but works!
REDIS_INIT_HEADER=\ REDIS_INIT_HEADER=\
"#/bin/sh\n "#!/bin/sh\n
#Configurations injected by install_server below....\n\n #Configurations injected by install_server below....\n\n
EXEC=$REDIS_EXECUTABLE\n EXEC=$REDIS_EXECUTABLE\n
CLIEXEC=$CLI_EXEC\n CLIEXEC=$CLI_EXEC\n
@ -193,7 +193,7 @@ fi
# warning if init info is not available. # warning if init info is not available.
cat > ${TMP_FILE} <<EOT cat > ${TMP_FILE} <<EOT
#/bin/sh #!/bin/sh
#Configurations injected by install_server below.... #Configurations injected by install_server below....
EXEC=$REDIS_EXECUTABLE EXEC=$REDIS_EXECUTABLE