diff --git a/deps/Makefile b/deps/Makefile index 5a95545de..e183ede9c 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -60,10 +60,15 @@ endif LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS) 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 @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 diff --git a/deps/hiredis/fmacros.h b/deps/hiredis/fmacros.h index 9e5fec0ce..6a41aa176 100644 --- a/deps/hiredis/fmacros.h +++ b/deps/hiredis/fmacros.h @@ -5,6 +5,10 @@ #define _BSD_SOURCE #endif +#if defined(_AIX) +#define _ALL_SOURCE +#endif + #if defined(__sun__) #define _POSIX_C_SOURCE 200112L #elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) diff --git a/deps/hiredis/net.h b/deps/hiredis/net.h index 5e742f577..3763ab089 100644 --- a/deps/hiredis/net.h +++ b/deps/hiredis/net.h @@ -35,7 +35,7 @@ #include "hiredis.h" -#if defined(__sun) +#if defined(__sun) || defined(_AIX) #define AF_LOCAL AF_UNIX #endif diff --git a/deps/hiredis/sds.c b/deps/hiredis/sds.c index 47b9823ea..4af9961ad 100644 --- a/deps/hiredis/sds.c +++ b/deps/hiredis/sds.c @@ -123,7 +123,7 @@ void sdsclear(sds s) { /* Enlarge the free space at the end of the sds string so that the caller * is sure that after calling this function can overwrite up to addlen * bytes after the end of the string, plus one more byte for nul term. - * + * * Note: this does not change the *length* of the sds string as returned * by sdslen(), but only the free buffer space we have. */ sds sdsMakeRoomFor(sds s, size_t addlen) { @@ -200,7 +200,10 @@ size_t sdsAllocSize(sds s) { void sdsIncrLen(sds s, int incr) { 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->free -= incr; 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. */ while(*f) { char next, *str; - int l; + unsigned int l; long long num; unsigned long long unum; diff --git a/deps/hiredis/sds.h b/deps/hiredis/sds.h index 9a604021c..37aaf7a28 100644 --- a/deps/hiredis/sds.h +++ b/deps/hiredis/sds.h @@ -39,8 +39,8 @@ typedef char *sds; struct sdshdr { - int len; - int free; + unsigned int len; + unsigned int free; char buf[]; }; diff --git a/redis.conf b/redis.conf index 33f28a5bb..ae774d6fe 100644 --- a/redis.conf +++ b/redis.conf @@ -68,7 +68,7 @@ tcp-backlog 511 # on a unix socket when not specified. # # unixsocket /tmp/redis.sock -# unixsocketperm 755 +# unixsocketperm 700 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/src/Makefile b/src/Makefile index 4ccc6d367..96af74afa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,7 +19,7 @@ DEPENDENCY_TARGETS=hiredis linenoise lua # Default settings STD=-std=c99 -pedantic -WARN=-Wall +WARN=-Wall -W OPT=$(OPTIMIZATION) PREFIX?=/usr/local @@ -58,17 +58,23 @@ ifeq ($(uname_S),SunOS) # SunOS INSTALL=cp -pf FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6 - FINAL_LIBS+= -ldl -lnsl -lsocket -lpthread + FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread else ifeq ($(uname_S),Darwin) # Darwin (nothing to do) +else +ifeq ($(uname_S),AIX) + # AIX + FINAL_LDFLAGS+= -Wl,-bexpall + FINAL_LIBS+= -pthread -lcrypt -lbsd + else # All the other OSes (notably Linux) FINAL_LDFLAGS+= -rdynamic FINAL_LIBS+= -pthread endif endif - +endif # Include paths to dependencies 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) @echo "" - @echo "Hint: To run 'make test' is a good idea ;)" + @echo "Hint: It's a good idea to run 'make test' ;)" @echo "" .PHONY: all diff --git a/src/ae.c b/src/ae.c index 164f8fdeb..63a1ab4eb 100644 --- a/src/ae.c +++ b/src/ae.c @@ -156,8 +156,9 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) { if (fd >= eventLoop->setsize) return; aeFileEvent *fe = &eventLoop->events[fd]; - if (fe->mask == AE_NONE) return; + + aeApiDelEvent(eventLoop, fd, mask); fe->mask = fe->mask & (~mask); if (fd == eventLoop->maxfd && fe->mask == AE_NONE) { /* Update the max fd */ @@ -167,7 +168,6 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) if (eventLoop->events[j].mask != AE_NONE) break; eventLoop->maxfd = j; } - aeApiDelEvent(eventLoop, fd, mask); } int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { diff --git a/src/anet.c b/src/anet.c index 87cc3ea25..76915326f 100644 --- a/src/anet.c +++ b/src/anet.c @@ -117,6 +117,8 @@ int anetKeepAlive(char *err, int fd, int interval) anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); return ANET_ERR; } +#else + ((void) interval); /* Avoid unused var warning for non Linux systems. */ #endif return ANET_OK; @@ -262,7 +264,8 @@ static int anetTcpGenericConnect(char *err, char *addr, int port, if (source_addr) { int bound = 0; /* 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)); goto end; } @@ -272,6 +275,7 @@ static int anetTcpGenericConnect(char *err, char *addr, int port, break; } } + freeaddrinfo(bservinfo); if (!bound) { anetSetError(err, "bind: %s", strerror(errno)); goto end; diff --git a/src/anet.h b/src/anet.h index c4659cd35..5191c4b69 100644 --- a/src/anet.h +++ b/src/anet.h @@ -39,10 +39,14 @@ #define ANET_NONE 0 #define ANET_IP_ONLY (1<<0) -#if defined(__sun) +#if defined(__sun) || defined(_AIX) #define AF_LOCAL AF_UNIX #endif +#ifdef _AIX +#undef ip_len +#endif + int anetTcpConnect(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); diff --git a/src/aof.c b/src/aof.c index a2ef2df93..5d46a21c4 100644 --- a/src/aof.c +++ b/src/aof.c @@ -95,6 +95,10 @@ void aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) { listNode *ln; aofrwblock *block; ssize_t nwritten; + REDIS_NOTUSED(el); + REDIS_NOTUSED(fd); + REDIS_NOTUSED(privdata); + REDIS_NOTUSED(mask); while(1) { ln = listFirst(server.aof_rewrite_buf_blocks); @@ -177,7 +181,7 @@ ssize_t aofRewriteBufferWrite(int fd) { if (block->used) { nwritten = write(fd,block->buf,block->used); - if (nwritten != block->used) { + if (nwritten != (ssize_t)block->used) { if (nwritten == 0) errno = EIO; return -1; } @@ -1128,6 +1132,9 @@ werr: * parent sends a '!' as well to acknowledge. */ void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) { char byte; + REDIS_NOTUSED(el); + REDIS_NOTUSED(privdata); + REDIS_NOTUSED(mask); if (read(fd,&byte,1) == 1 && byte == '!') { redisLog(REDIS_NOTICE,"AOF rewrite child asks to stop sending diffs."); diff --git a/src/bitops.c b/src/bitops.c index 28f772430..94c7f3537 100644 --- a/src/bitops.c +++ b/src/bitops.c @@ -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 * 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. */ -long redisBitpos(void *s, long count, int bit) { +long redisBitpos(void *s, unsigned long count, int bit) { unsigned long *l; unsigned char *c; unsigned long skipval, word = 0, one; 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 * all ones or all zeros respectively if we are lookig for zeros @@ -276,11 +276,12 @@ void getbitCommand(redisClient *c) { void bitopCommand(redisClient *c) { char *opname = c->argv[1]->ptr; robj *o, *targetkey = c->argv[2]; - long op, j, numkeys; + unsigned long op, j, numkeys; robj **objects; /* Array of source objects. */ unsigned char **src; /* Array of source strings pointers. */ - long *len, maxlen = 0; /* Array of length of src strings, and max len. */ - long minlen = 0; /* Min len among the input keys. */ + unsigned long *len, maxlen = 0; /* Array of length of src strings, + and max len. */ + unsigned long minlen = 0; /* Min len among the input keys. */ unsigned char *res = NULL; /* Resulting string. */ /* Parse the operation name. */ @@ -320,9 +321,10 @@ void bitopCommand(redisClient *c) { } /* Return an error if one of the keys is not a string. */ if (checkType(c,o,REDIS_STRING)) { - for (j = j-1; j >= 0; j--) { - if (objects[j]) - decrRefCount(objects[j]); + unsigned long i; + for (i = 0; i < j; i++) { + if (objects[i]) + decrRefCount(objects[i]); } zfree(src); zfree(len); @@ -340,7 +342,7 @@ void bitopCommand(redisClient *c) { if (maxlen) { res = (unsigned char*) sdsnewlen(NULL,maxlen); unsigned char output, byte; - long i; + unsigned long i; /* 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 diff --git a/src/cluster.c b/src/cluster.c index 0d908349c..07eaa637c 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -72,6 +72,7 @@ void resetManualFailover(void); void clusterCloseAllSlots(void); void clusterSetNodeAsMaster(clusterNode *n); void clusterDelNode(clusterNode *delnode); +sds representRedisNodeFlags(sds ci, uint16_t flags); /* ----------------------------------------------------------------------------- * Initialization @@ -163,9 +164,13 @@ int clusterLoadConfig(char *filename) { argv[j]); } } + sdsfreesplitres(argv,argc); continue; } + /* Regular config lines have at least eight fields */ + if (argc < 8) goto fmterr; + /* Create this node if it does not exist */ n = clusterLookupNode(argv[0]); if (!n) { @@ -266,11 +271,12 @@ int clusterLoadConfig(char *filename) { sdsfreesplitres(argv,argc); } + /* Config sanity check */ + if (server.cluster->myself == NULL) goto fmterr; + zfree(line); fclose(fp); - /* Config sanity check */ - redisAssert(server.cluster->myself != NULL); redisLog(REDIS_NOTICE,"Node configuration loaded, I'm %.40s", myself->name); /* Something that should never happen: currentEpoch smaller than @@ -284,7 +290,8 @@ int clusterLoadConfig(char *filename) { fmterr: redisLog(REDIS_WARNING, "Unrecoverable error: corrupted cluster config file."); - fclose(fp); + zfree(line); + if (fp) fclose(fp); exit(1); } @@ -321,7 +328,7 @@ int clusterSaveConfig(int do_fsync) { /* Pad the new payload if the existing file length is greater. */ 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); 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); while(count--) { - sds ci = sdsempty(); uint16_t flags = ntohs(g->flags); clusterNode *node; + sds ci; - if (flags == 0) ci = sdscat(ci,"noflags,"); - 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] = ' '; - + ci = representRedisNodeFlags(sdsempty(), flags); redisLog(REDIS_DEBUG,"GOSSIP %.40s %s:%d %s", g->nodename, g->ip, @@ -1914,7 +1912,7 @@ void clusterReadHandler(aeEventLoop *el, int fd, void *privdata, int mask) { ssize_t nread; clusterMsg *hdr; clusterLink *link = (clusterLink*) privdata; - int readlen, rcvbuflen; + unsigned int readlen, rcvbuflen; REDIS_NOTUSED(el); REDIS_NOTUSED(mask); @@ -3296,14 +3294,13 @@ int verifyClusterConfigWithData(void) { update_config++; /* Case A: slot is unassigned. Take responsability for it. */ if (server.cluster->slots[j] == NULL) { - redisLog(REDIS_WARNING, "I've keys about slot %d that is " - "unassigned. Taking responsability " - "for it.",j); + redisLog(REDIS_WARNING, "I have keys for unassigned slot %d. " + "Taking responsibility for it.",j); clusterAddSlot(myself,j); } else { - redisLog(REDIS_WARNING, "I've keys about slot %d that is " - "already assigned to a different node. " - "Setting it in importing state.",j); + redisLog(REDIS_WARNING, "I have keys for slot %d, but the slot is " + "assigned to another node. " + "Setting it to importing state.",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. * See clusterGenNodesDescription() top comment for more information. * @@ -3354,21 +3382,13 @@ sds clusterGenNodeDescription(clusterNode *node) { node->port); /* Flags */ - if (node->flags == 0) ci = sdscat(ci,"noflags,"); - 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] = ' '; + ci = representRedisNodeFlags(ci, node->flags); /* Slave of... or just "-" */ if (node->slaveof) - ci = sdscatprintf(ci,"%.40s ",node->slaveof->name); + ci = sdscatprintf(ci," %.40s ",node->slaveof->name); else - ci = sdscatprintf(ci,"- "); + ci = sdscatlen(ci," - ",3); /* Latency from the POV of this node, link status */ ci = sdscatprintf(ci,"%lld %lld %llu %s", @@ -3446,6 +3466,10 @@ sds clusterGenNodesDescription(int filter) { return ci; } +/* ----------------------------------------------------------------------------- + * CLUSTER command + * -------------------------------------------------------------------------- */ + int getSlotOrReply(redisClient *c, robj *o) { long long slot; @@ -3962,7 +3986,7 @@ void clusterCommand(redisClient *c) { "configEpoch set to %llu via CLUSTER SET-CONFIG-EPOCH", (unsigned long long) myself->configEpoch); - if (server.cluster->currentEpoch < epoch) + if (server.cluster->currentEpoch < (uint64_t)epoch) server.cluster->currentEpoch = epoch; /* No need to fsync the config here since in the unlucky event * of a failure to persist the config, the conflict resolution code diff --git a/src/config.c b/src/config.c index b0fc50b9d..2aff1351f 100644 --- a/src/config.c +++ b/src/config.c @@ -73,7 +73,7 @@ void appendServerSaveParams(time_t seconds, int changes) { server.saveparamslen++; } -void resetServerSaveParams() { +void resetServerSaveParams(void) { zfree(server.saveparams); server.saveparams = NULL; server.saveparamslen = 0; @@ -629,7 +629,7 @@ void configSetCommand(redisClient *c) { server.maxclients = orig_value; return; } - if (aeGetSetSize(server.el) < + if ((unsigned int) aeGetSetSize(server.el) < server.maxclients + REDIS_EVENTLOOP_FDSET_INCR) { if (aeResizeSetSize(server.el, diff --git a/src/config.h b/src/config.h index 1bc70a13e..57d07599a 100644 --- a/src/config.h +++ b/src/config.h @@ -187,9 +187,14 @@ void setproctitle(const char *fmt, ...); #if (__i386 || __amd64 || __powerpc__) && __GNUC__ #define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#if (GNUC_VERSION >= 40100) || defined(__clang__) +#if defined(__clang__) #define HAVE_ATOMIC #endif +#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ)) +#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6)) +#define HAVE_ATOMIC +#endif +#endif #endif #endif diff --git a/src/db.c b/src/db.c index c83ab2ee6..8eb1b89a7 100644 --- a/src/db.c +++ b/src/db.c @@ -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 * of every element on the Hash. */ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) { - int rv; int i, j; - char buf[REDIS_LONGSTR_SIZE]; list *keys = listCreate(); listNode *node, *nextnode; long count = 10; @@ -503,7 +501,7 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) { privdata[1] = o; do { cursor = dictScan(ht, cursor, scanCallback, privdata); - } while (cursor && listLength(keys) < count); + } while (cursor && listLength(keys) < (unsigned long)count); } else if (o->type == REDIS_SET) { int pos = 0; int64_t ll; @@ -577,9 +575,7 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) { /* Step 4: Reply to the client. */ addReplyMultiBulkLen(c, 2); - rv = snprintf(buf, sizeof(buf), "%lu", cursor); - redisAssert(rv < sizeof(buf)); - addReplyBulkCBuffer(c, buf, rv); + addReplyBulkLongLong(c,cursor); addReplyMultiBulkLen(c, listLength(keys)); while ((node = listFirst(keys)) != NULL) { @@ -707,6 +703,7 @@ void moveCommand(redisClient *c) { robj *o; redisDb *src, *dst; int srcid; + long long dbid; if (server.cluster_enabled) { addReplyError(c,"MOVE is not allowed in cluster mode"); @@ -716,7 +713,11 @@ void moveCommand(redisClient *c) { /* Obtain source and target DB pointers */ src = c->db; 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); 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 * correctly identify keys in the "STORE" option. */ 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); 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 * to be sure to process the *last* "STORE" option if multiple * ones are provided. This is same behavior as SORT. */ + found_store = 1; keys[num] = i+1; /* */ break; } } } - *numkeys = num; + *numkeys = num + found_store; return keys; } diff --git a/src/dict.c b/src/dict.c index b27920a44..17818b853 100644 --- a/src/dict.c +++ b/src/dict.c @@ -79,12 +79,6 @@ unsigned int dictIntHashFunction(unsigned int 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; void dictSetHashFunctionSeed(uint32_t seed) { @@ -668,9 +662,9 @@ dictEntry *dictGetRandomKey(dict *d) * statistics. However the function is much faster than dictGetRandomKey() * at producing N elements, and the elements are guaranteed to be non * 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 stored = 0; + unsigned int stored = 0; if (dictSize(d) < count) count = dictSize(d); while(stored < count) { diff --git a/src/dict.h b/src/dict.h index 905330f5d..b82e137f9 100644 --- a/src/dict.h +++ b/src/dict.h @@ -142,7 +142,7 @@ typedef void (dictScanFunction)(void *privdata, const dictEntry *de); #define dictGetDoubleVal(he) ((he)->v.d) #define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size) #define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used) -#define dictIsRehashing(ht) ((ht)->rehashidx != -1) +#define dictIsRehashing(d) ((d)->rehashidx != -1) /* API */ dict *dictCreate(dictType *type, void *privDataPtr); @@ -162,7 +162,7 @@ dictIterator *dictGetSafeIterator(dict *d); dictEntry *dictNext(dictIterator *iter); void dictReleaseIterator(dictIterator *iter); 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); unsigned int dictGenHashFunction(const void *key, int len); unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); diff --git a/src/fmacros.h b/src/fmacros.h index 44e378a68..e49735ce5 100644 --- a/src/fmacros.h +++ b/src/fmacros.h @@ -36,6 +36,10 @@ #define _GNU_SOURCE #endif +#if defined(_AIX) +#define _ALL_SOURCE +#endif + #if defined(__linux__) || defined(__OpenBSD__) #define _XOPEN_SOURCE 700 /* diff --git a/src/hyperloglog.c b/src/hyperloglog.c index 63052a789..005beb18f 100644 --- a/src/hyperloglog.c +++ b/src/hyperloglog.c @@ -1349,7 +1349,7 @@ void pfmergeCommand(redisClient *c) { * Something that is not easy to test from within the outside. */ #define HLL_TEST_CYCLES 1000 void pfselftestCommand(redisClient *c) { - int j, i; + unsigned int j, i; sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE); struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2; robj *o = NULL; @@ -1431,7 +1431,7 @@ void pfselftestCommand(redisClient *c) { if (j == 10) maxerr = 1; if (abserr < 0) abserr = -abserr; - if (abserr > maxerr) { + if (abserr > (int64_t)maxerr) { addReplyErrorFormat(c, "TESTFAILED Too big error. card:%llu abserr:%llu", (unsigned long long) checkpoint, diff --git a/src/intset.c b/src/intset.c index b61530e45..5d894e3cd 100644 --- a/src/intset.c +++ b/src/intset.c @@ -133,7 +133,7 @@ static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) { } while(max >= min) { - mid = (min+max)/2; + mid = ((unsigned int)min + (unsigned int)max) >> 1; cur = _intsetGet(is,mid); if (value > cur) { min = mid+1; diff --git a/src/latency.c b/src/latency.c index fdc88210e..b7845ca29 100644 --- a/src/latency.c +++ b/src/latency.c @@ -37,6 +37,7 @@ /* Dictionary type for latency events. */ int dictStringKeyCompare(void *privdata, const void *key1, const void *key2) { + REDIS_NOTUSED(privdata); return strcmp(key1,key2) == 0; } diff --git a/src/memtest.c b/src/memtest.c index cabfb5c89..18d821b10 100644 --- a/src/memtest.c +++ b/src/memtest.c @@ -237,6 +237,7 @@ void memtest_test(size_t megabytes, int passes) { memtest_progress_end(); memtest_compare_times(m,bytes,pass,4); } + free(m); } void memtest_non_destructive_invert(void *addr, size_t size) { diff --git a/src/multi.c b/src/multi.c index 3d78e1488..c82876456 100644 --- a/src/multi.c +++ b/src/multi.c @@ -72,7 +72,7 @@ void queueMultiCommand(redisClient *c) { void discardTransaction(redisClient *c) { freeClientMultiState(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); } diff --git a/src/networking.c b/src/networking.c index 9c100db19..fb49b7964 100644 --- a/src/networking.c +++ b/src/networking.c @@ -1051,7 +1051,7 @@ int processMultibulkBuffer(redisClient *c) { qblen = sdslen(c->querybuf); /* Hint the sds library about the amount of bytes this string is * going to contain. */ - if (qblen < ll+2) + if (qblen < (size_t)ll+2) c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen); } c->bulklen = ll; diff --git a/src/pubsub.c b/src/pubsub.c index 720cd5185..d6cfbdf3c 100644 --- a/src/pubsub.c +++ b/src/pubsub.c @@ -358,7 +358,7 @@ void pubsubCommand(redisClient *c) { list *l = dictFetchValue(server.pubsub_channels,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) { /* PUBSUB NUMPAT */ diff --git a/src/rand.c b/src/rand.c index 36cb417cf..09b0508f1 100644 --- a/src/rand.c +++ b/src/rand.c @@ -66,7 +66,7 @@ #define HI_BIT (1L << (2 * N - 1)) 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() { next(); @@ -77,7 +77,7 @@ void redisSrand48(int32_t seedval) { SEED(X0, LOW(seedval), HIGH(seedval)); } -static void next() { +static void next(void) { uint32_t p[2], q[2], r[2], carry0, carry1; MUL(a[0], x[0], p); diff --git a/src/rdb.c b/src/rdb.c index ce5f99c9a..afaef2681 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -688,7 +688,7 @@ int rdbSave(char *filename) { * loading code skips the check in this case. */ cksum = rdb.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 */ if (fflush(fp) == EOF) goto werr; @@ -766,7 +766,7 @@ int rdbSaveBackground(char *filename) { void rdbRemoveTempFile(pid_t childpid) { char tmpfile[256]; - snprintf(tmpfile,256,"temp-%d.rdb", (int) childpid); + snprintf(tmpfile,sizeof(tmpfile),"temp-%d.rdb", (int) childpid); unlink(tmpfile); } @@ -943,7 +943,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { /* Add pair to hash table */ ret = dictAdd((dict*)o->ptr, field, value); - redisAssert(ret == REDIS_OK); + redisAssert(ret == DICT_OK); } /* All pairs should be read by now */ diff --git a/src/redis-benchmark.c b/src/redis-benchmark.c index 5ab2625d2..2e67f1021 100644 --- a/src/redis-benchmark.c +++ b/src/redis-benchmark.c @@ -77,6 +77,7 @@ static struct config { int dbnum; sds dbnumstr; char *tests; + char *auth; } config; typedef struct _client { @@ -213,7 +214,7 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) { freeReplyObject(reply); if (c->selectlen) { - int j; + size_t j; /* This is the OK from SELECT. Just discard the SELECT * from the buffer. */ @@ -325,6 +326,13 @@ static client createClient(char *cmd, size_t len, client from) { * the example client buffer. */ 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 * buffer with the SELECT command, that will be discarded the first * 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++) c->obuf = sdscatlen(c->obuf,cmd,len); } + c->written = 0; c->pending = config.pipeline; c->randptr = NULL; @@ -359,7 +368,7 @@ static client createClient(char *cmd, size_t len, client from) { c->randfree = 0; c->randptr = zmalloc(sizeof(char*)*c->randlen); /* 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); /* Adjust for the different select prefix length. */ 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) { 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) { createClient(NULL,0,c); @@ -489,6 +489,9 @@ int parseOptions(int argc, const char **argv) { } else if (!strcmp(argv[i],"-s")) { if (lastarg) goto invalid; 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")) { if (lastarg) goto invalid; config.datasize = atoi(argv[++i]); @@ -550,6 +553,7 @@ usage: " -h Server hostname (default 127.0.0.1)\n" " -p Server port (default 6379)\n" " -s Server socket (overrides host and port)\n" +" -a Password for Redis Auth\n" " -c Number of parallel connections (default 50)\n" " -n Total number of requests (default 10000)\n" " -d 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); if (config.liveclients == 0) { - fprintf(stderr,"All clients disconnected... aborting."); + fprintf(stderr,"All clients disconnected... aborting.\n"); exit(1); } @@ -651,6 +655,7 @@ int main(int argc, const char **argv) { config.hostsocket = NULL; config.tests = NULL; config.dbnum = 0; + config.auth = NULL; i = parseOptions(argc,argv); argc -= i; diff --git a/src/redis-check-dump.c b/src/redis-check-dump.c index a4f103fdd..546462001 100644 --- a/src/redis-check-dump.c +++ b/src/redis-check-dump.c @@ -138,8 +138,10 @@ typedef struct { * at runtime to avoid strange compiler optimizations. */ 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 */ -static char types[256][16]; +static char types[MAX_TYPES_NUM][MAX_TYPE_NAME_LEN]; /* Return true if 't' is a valid object type. */ int checkType(unsigned char t) { @@ -166,7 +168,7 @@ int readBytes(void *target, long num) { return 1; } -int processHeader() { +int processHeader(void) { char buf[10] = "_________"; int dump_version; @@ -335,6 +337,7 @@ char* loadStringObject() { if (len == REDIS_RDB_LENERR) return NULL; char *buf = malloc(sizeof(char) * (len+1)); + if (buf == NULL) return NULL; buf[len] = '\0'; if (!readBytes(buf, len)) { 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; entry entry; int dump_version = processHeader(); @@ -611,7 +614,7 @@ void process() { printf("RDB version >= 5 but no room for checksum.\n"); exit(1); } - positions[0].size -= 8;; + positions[0].size -= 8; } level = 1; diff --git a/src/redis-cli.c b/src/redis-cli.c index 907e62b74..3526319d5 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -94,10 +94,11 @@ static struct config { sds mb_delim; char prompt[128]; char *eval; + int last_cmd_type; } config; static volatile sig_atomic_t force_cancel_loop = 0; -static void usage(); +static void usage(void); static void slaveMode(void); char *redisGitSHA1(void); char *redisGitDirty(void); @@ -131,7 +132,7 @@ static void cliRefreshPrompt(void) { strchr(config.hostip,':') ? "[%s]:%d" : "%s:%d", config.hostip, config.hostport); /* 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]", config.dbnum); snprintf(config.prompt+len,sizeof(config.prompt)-len,"> "); @@ -157,7 +158,7 @@ typedef struct { static helpEntry *helpEntries; static int helpEntriesLen; -static sds cliVersion() { +static sds cliVersion(void) { sds version; version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION); @@ -171,7 +172,7 @@ static sds cliVersion() { return version; } -static void cliInitHelp() { +static void cliInitHelp(void) { int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp); int groupslen = sizeof(commandGroups)/sizeof(char*); int i, len, pos = 0; @@ -210,7 +211,7 @@ static void cliOutputCommandHelp(struct commandHelp *help, int group) { } /* Print generic help. */ -static void cliOutputGenericHelp() { +static void cliOutputGenericHelp(void) { sds version = cliVersion(); printf( "redis-cli %s\r\n" @@ -320,8 +321,10 @@ static int cliSelect() { reply = redisCommand(context,"SELECT %d",config.dbnum); if (reply != NULL) { + int result = REDIS_OK; + if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR; freeReplyObject(reply); - return REDIS_OK; + return result; } return REDIS_ERR; } @@ -365,7 +368,7 @@ static int cliConnect(int force) { return REDIS_OK; } -static void cliPrintContextError() { +static void cliPrintContextError(void) { if (context == NULL) return; fprintf(stderr,"Error: %s\n",context->errstr); } @@ -514,8 +517,11 @@ static int cliReadReply(int output_raw_strings) { int output = 1; if (redisGetReply(context,&_reply) != REDIS_OK) { - if (config.shutdown) + if (config.shutdown) { + redisFree(context); + context = NULL; return REDIS_OK; + } if (config.interactive) { /* Filter cases where we should reconnect */ if (context->err == REDIS_ERR_IO && errno == ECONNRESET) @@ -530,6 +536,8 @@ static int cliReadReply(int output_raw_strings) { reply = (redisReply*)_reply; + config.last_cmd_type = reply->type; + /* Check if we need to connect to a different node and reissue the * request. */ 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"); slaveMode(); config.slave_mode = 0; + free(argvlen); 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) { config.dbnum = atoi(argv[1]); cliRefreshPrompt(); + } else if (!strcasecmp(command,"auth") && argc == 2) { + cliSelect(); } } if (config.interval) usleep(config.interval); @@ -724,6 +735,8 @@ static int parseOptions(int argc, char **argv) { config.auth = argv[++i]; } else if (!strcmp(argv[i],"--raw")) { config.output = OUTPUT_RAW; + } else if (!strcmp(argv[i],"--no-raw")) { + config.output = OUTPUT_STANDARD; } else if (!strcmp(argv[i],"--csv")) { config.output = OUTPUT_CSV; } else if (!strcmp(argv[i],"--latency")) { @@ -795,7 +808,7 @@ static sds readArgFromStdin(void) { return arg; } -static void usage() { +static void usage(void) { sds version = cliVersion(); fprintf(stderr, "redis-cli %s\n" @@ -814,6 +827,7 @@ static void usage() { " -c Enable cluster mode (follow -ASK and -MOVED redirections).\n" " --raw Use raw formatting for replies (default when STDOUT is\n" " not a tty).\n" +" --no-raw Force formatted output even when STDOUT is not a tty.\n" " --csv Output in CSV format.\n" " --latency Enter a special mode continuously sampling latency.\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; } -#define LINE_BUFLEN 4096 -static void repl() { +static void repl(void) { sds historyfile = NULL; int history = 0; char *line; @@ -1406,7 +1419,7 @@ static int toIntType(char *key, char *type) { static void getKeyTypes(redisReply *keys, int *types) { redisReply *reply; - int i; + unsigned int i; /* Pipeline TYPE commands */ for(i=0;ielements;i++) { @@ -1435,7 +1448,7 @@ static void getKeySizes(redisReply *keys, int *types, { redisReply *reply; char *sizecmds[] = {"STRLEN","LLEN","SCARD","HLEN","ZCARD"}; - int i; + unsigned int i; /* Pipeline size commands */ for(i=0;ielements;i++) { @@ -1482,7 +1495,8 @@ static void findBigKeys(void) { char *typename[] = {"string","list","set","hash","zset"}; char *typeunit[] = {"bytes","items","members","fields","members"}; redisReply *reply, *keys; - int type, *types=NULL, arrsize=0, i; + unsigned int arrsize=0, i; + int type, *types=NULL; double pct; /* Total keys pre scanning */ @@ -1666,7 +1680,7 @@ void bytesToHuman(char *s, long long n) { } } -static void statMode() { +static void statMode(void) { redisReply *reply; long aux, requests = 0; int i = 0; @@ -1752,7 +1766,7 @@ static void statMode() { * Scan mode *--------------------------------------------------------------------------- */ -static void scanMode() { +static void scanMode(void) { redisReply *reply; unsigned long long cur = 0; @@ -1769,7 +1783,7 @@ static void scanMode() { printf("ERROR: %s\n", reply->str); exit(1); } else { - int j; + unsigned int j; cur = strtoull(reply->element[0]->str,NULL,10); 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); } + double avg_us = (double)run_time/runs; + double avg_ns = avg_us * 10e3; if (force_cancel_loop || end > test_end) { - printf("\n%lld total runs (avg %lld microseconds per run).\n", - runs, run_time/runs); - printf("Worst run took %.02fx times the average.\n", - (double) max_latency / (run_time/runs)); + printf("\n%lld total runs " + "(avg latency: " + "%.4f microseconds / %.2f nanoseconds per run).\n", + runs, avg_us, avg_ns); + printf("Worst run took %.0fx longer than the average latency.\n", + max_latency / avg_us); exit(0); } } @@ -1883,6 +1901,8 @@ int main(int argc, char **argv) { config.stdinarg = 0; config.auth = NULL; config.eval = NULL; + config.last_cmd_type = -1; + if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL)) config.output = OUTPUT_RAW; else diff --git a/src/redis.c b/src/redis.c index 5ddbc9250..e58ceb047 100644 --- a/src/redis.c +++ b/src/redis.c @@ -66,7 +66,6 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan; /* Global vars */ struct redisServer server; /* server global state */ -struct redisCommand *commandTable; /* Our command table. * @@ -267,7 +266,7 @@ struct redisCommand redisCommandTable[] = { {"readwrite",readwriteCommand,1,"rF",0,NULL,0,0,0,0,0}, {"dump",dumpCommand,2,"ar",0,NULL,1,1,1,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}, {"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,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 long long last_fast_cycle = 0; /* When last fast cycle ran. */ - unsigned int j, iteration = 0; - unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; + int j, iteration = 0; + int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; long long start = ustime(), timelimit; if (type == ACTIVE_EXPIRE_CYCLE_FAST) { @@ -1000,8 +999,8 @@ void databasesCron(void) { * cron loop iteration. */ static unsigned int resize_db = 0; static unsigned int rehash_db = 0; - unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; - unsigned int j; + int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; + int j; /* Don't test more DBs than we have. */ if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum; @@ -1376,7 +1375,7 @@ void createSharedObjects(void) { shared.maxstring = createStringObject("maxstring",9); } -void initServerConfig() { +void initServerConfig(void) { int j; getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE); @@ -1557,7 +1556,7 @@ void adjustOpenFilesLimit(void) { * to the higher value supported less than maxfiles. */ f = maxfiles; while(f > oldlimit) { - int decr_step = 16; + rlim_t decr_step = 16; limit.rlim_cur = f; limit.rlim_max = f; @@ -1696,7 +1695,7 @@ void resetServerStats(void) { server.ops_sec_last_sample_ops = 0; } -void initServer() { +void initServer(void) { int j; 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 * relative to the length of the user provided string, so no information * leak is possible in the following two lines of code. */ - int alen = strlen(a); - int blen = strlen(b); - int j; + unsigned int alen = strlen(a); + unsigned int blen = strlen(b); + unsigned int j; int diff = 0; /* 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)) { d = (double)n/(1024LL*1024*1024); 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 sections = 0; - if (section) { - allsections = strcasecmp(section,"all") == 0; - defsections = strcasecmp(section,"default") == 0; - } + if (section == NULL) section = "default"; + allsections = strcasecmp(section,"all") == 0; + defsections = strcasecmp(section,"default") == 0; getrusage(RUSAGE_SELF, &self_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", REDIS_VERSION, redisGitSHA1(), @@ -3361,7 +3368,7 @@ void version() { exit(0); } -void usage() { +void usage(void) { fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n"); fprintf(stderr," ./redis-server - (read config from stdin)\n"); fprintf(stderr," ./redis-server -v or --version\n"); @@ -3399,10 +3406,33 @@ void redisAsciiArt(void) { zfree(buf); } -static void sigtermHandler(int sig) { - REDIS_NOTUSED(sig); +static void sigShutdownHandler(int 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; } @@ -3413,8 +3443,9 @@ void setupSignalHandlers(void) { * Otherwise, sa_handler is used. */ sigemptyset(&act.sa_mask); act.sa_flags = 0; - act.sa_handler = sigtermHandler; + act.sa_handler = sigShutdownHandler; sigaction(SIGTERM, &act, NULL); + sigaction(SIGINT, &act, NULL); #ifdef HAVE_BACKTRACE sigemptyset(&act.sa_mask); @@ -3545,6 +3576,13 @@ int main(int argc, char **argv) { } 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); resetServerSaveParams(); loadServerConfig(configfile,options); diff --git a/src/redis.h b/src/redis.h index d2b00bff5..c352cd79a 100644 --- a/src/redis.h +++ b/src/redis.h @@ -626,6 +626,12 @@ typedef struct redisOpArray { 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 { /* General */ pid_t pid; /* Main process pid. */ @@ -808,12 +814,12 @@ struct redisServer { /* Replication script cache. */ dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */ 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. */ list *clients_waiting_acks; /* Clients waiting in WAIT command. */ int get_ack_from_slaves; /* If true we send REPLCONF GETACK. */ /* 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 */ int maxmemory_policy; /* Policy for key eviction */ int maxmemory_samples; /* Pricision of random sampling */ @@ -993,11 +999,10 @@ void freeClient(redisClient *c); void freeClientAsync(redisClient *c); void resetClient(redisClient *c); void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask); -void addReply(redisClient *c, robj *obj); void *addDeferredMultiBulkLength(redisClient *c); void setDeferredMultiBulkLength(redisClient *c, void *node, long length); -void addReplySds(redisClient *c, sds s); 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 acceptUnixHandler(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 addReplyBulkCBuffer(redisClient *c, void *p, size_t len); void addReplyBulkLongLong(redisClient *c, long long ll); -void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask); void addReply(redisClient *c, robj *obj); void addReplySds(redisClient *c, sds s); void addReplyError(redisClient *c, char *err); @@ -1210,7 +1214,7 @@ void redisLog(int level, const char *fmt, ...); #endif void redisLogRaw(int level, const char *msg); void redisLogFromHandler(int level, const char *msg); -void usage(); +void usage(void); void updateDictResizePolicy(void); int htNeedsResize(dict *dict); void oom(const char *msg); @@ -1270,7 +1274,7 @@ sds keyspaceEventsFlagsToString(int flags); /* Configuration */ void loadServerConfig(char *filename, char *options); void appendServerSaveParams(time_t seconds, int changes); -void resetServerSaveParams(); +void resetServerSaveParams(void); struct rewriteConfigState; /* Forward declaration to export API. */ void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force); int rewriteConfig(char *path); diff --git a/src/scripting.c b/src/scripting.c index ef00eede6..a0c88742f 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -212,7 +212,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { static robj **argv = NULL; static int argv_size = 0; 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 */ if (argc == 0) { @@ -910,6 +910,9 @@ void evalGenericCommand(redisClient *c, int evalsha) { if (numkeys > (c->argc - 3)) { addReplyError(c,"Number of keys can't be greater than number of args"); 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 diff --git a/src/sds.c b/src/sds.c index dc07d0d3a..4adcc06a8 100644 --- a/src/sds.c +++ b/src/sds.c @@ -200,7 +200,10 @@ size_t sdsAllocSize(sds s) { void sdsIncrLen(sds s, int incr) { 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->free -= incr; assert(sh->free >= 0); @@ -388,6 +391,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) { buf[buflen-2] = '\0'; va_copy(cpy,ap); vsnprintf(buf, buflen, fmt, cpy); + va_end(ap); if (buf[buflen-2] != '\0') { if (buf != staticbuf) zfree(buf); 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. */ while(*f) { char next, *str; - int l; + unsigned int l; long long num; unsigned long long unum; diff --git a/src/sds.h b/src/sds.h index 9a604021c..37aaf7a28 100644 --- a/src/sds.h +++ b/src/sds.h @@ -39,8 +39,8 @@ typedef char *sds; struct sdshdr { - int len; - int free; + unsigned int len; + unsigned int free; char buf[]; }; diff --git a/src/sentinel.c b/src/sentinel.c index 48e1de8dd..603cdd404 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -159,7 +159,7 @@ typedef struct sentinelRedisInstance { /* Master specific. */ dict *sentinels; /* Other sentinels monitoring the same master. */ 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. */ 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 dictInstancesValDestructor (void *privdata, void *obj) { + REDIS_NOTUSED(privdata); releaseSentinelRedisInstance(obj); } @@ -403,7 +404,7 @@ void initSentinelConfig(void) { /* Perform the Sentinel mode initialization. */ void initSentinel(void) { - int j; + unsigned int j; /* Remove usual Redis commands from the command table, then just add * the SENTINEL command. */ @@ -455,19 +456,19 @@ void sentinelIsRunning(void) { * EINVAL: Invalid port number. */ sentinelAddr *createSentinelAddr(char *hostname, int port) { - char buf[32]; + char ip[REDIS_IP_STR_LEN]; sentinelAddr *sa; if (port <= 0 || port > 65535) { errno = EINVAL; return NULL; } - if (anetResolve(NULL,hostname,buf,sizeof(buf)) == ANET_ERR) { + if (anetResolve(NULL,hostname,ip,sizeof(ip)) == ANET_ERR) { errno = ENOENT; return NULL; } sa = zmalloc(sizeof(*sa)); - sa->ip = sdsnew(buf); + sa->ip = sdsnew(ip); sa->port = port; return sa; } @@ -1634,6 +1635,7 @@ void sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) { } void sentinelDisconnectCallback(const redisAsyncContext *c, int status) { + REDIS_NOTUSED(status); sentinelDisconnectInstanceFromContext(c); } @@ -1998,6 +2000,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) { void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { sentinelRedisInstance *ri = c->data; redisReply *r; + REDIS_NOTUSED(privdata); if (ri) ri->pending_commands--; if (!reply || !ri) return; @@ -2012,6 +2015,8 @@ void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata * value of the command but its effects directly. */ void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { sentinelRedisInstance *ri = c->data; + REDIS_NOTUSED(reply); + REDIS_NOTUSED(privdata); 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) { sentinelRedisInstance *ri = c->data; redisReply *r; + REDIS_NOTUSED(privdata); if (ri) ri->pending_commands--; if (!reply || !ri) return; @@ -2057,6 +2063,7 @@ void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { sentinelRedisInstance *ri = c->data; redisReply *r; + REDIS_NOTUSED(privdata); if (ri) ri->pending_commands--; if (!reply || !ri) return; @@ -2166,6 +2173,7 @@ cleanup: void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) { sentinelRedisInstance *ri = c->data; redisReply *r; + REDIS_NOTUSED(privdata); if (!reply || !ri) return; r = reply; @@ -2559,7 +2567,7 @@ sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c, { sentinelRedisInstance *ri; - ri = dictFetchValue(sentinel.masters,c->argv[2]->ptr); + ri = dictFetchValue(sentinel.masters,name->ptr); if (!ri) { addReplyError(c,"No such master with that name"); return NULL; @@ -2682,7 +2690,7 @@ void sentinelCommand(redisClient *c) { /* SENTINEL MONITOR */ sentinelRedisInstance *ri; long quorum, port; - char buf[32]; + char ip[REDIS_IP_STR_LEN]; if (c->argc != 6) goto numargserr; 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 * to createSentinelRedisInstance(), otherwise we may trigger a * 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"); return; } @@ -2997,7 +3005,7 @@ void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) { void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) { dictIterator *di; dictEntry *de; - int quorum = 0, odown = 0; + unsigned int quorum = 0, odown = 0; if (master->flags & SRI_S_DOWN) { /* Is down for enough sentinels? */ @@ -3034,6 +3042,7 @@ void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) { void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) { sentinelRedisInstance *ri = c->data; redisReply *r; + REDIS_NOTUSED(privdata); if (ri) ri->pending_commands--; 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 * replied with a vote. */ sdsfree(ri->leader); - if (ri->leader_epoch != r->element[2]->integer) + if ((long long)ri->leader_epoch != r->element[2]->integer) redisLog(REDIS_WARNING, "%s voted for %s %llu", ri->name, r->element[1]->str, diff --git a/src/t_string.c b/src/t_string.c index 41e4b3b71..8a4d5f166 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -69,7 +69,7 @@ void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *ex if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK) return; if (milliseconds <= 0) { - addReplyError(c,"invalid expire time in SETEX"); + addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name); return; } if (unit == UNIT_SECONDS) milliseconds *= 1000; @@ -255,7 +255,7 @@ void getrangeCommand(redisClient *c) { if (end < 0) end = strlen+end; if (start < 0) start = 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 * nothing can be returned is: start > end. */ diff --git a/src/t_zset.c b/src/t_zset.c index 20ef62e27..d3c7214bd 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -205,8 +205,6 @@ int zslDelete(zskiplist *zsl, double score, robj *obj) { zslDeleteNode(zsl, x, update); zslFreeNode(x); return 1; - } else { - return 0; /* not found */ } return 0; /* not found */ } diff --git a/src/ziplist.c b/src/ziplist.c index d78f8f5da..4a0111105 100644 --- a/src/ziplist.c +++ b/src/ziplist.c @@ -576,19 +576,19 @@ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsig /* Insert item at "p". */ 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; int nextdiff = 0; unsigned char encoding = 0; long long value = 123456789; /* initialized to avoid warning. Using a value that is easy to see if for some reason we use it uninitialized. */ - zlentry entry, tail; + zlentry tail; /* Find out prevlen for the entry that is inserted. */ if (p[0] != ZIP_END) { - entry = zipEntry(p); - prevlen = entry.prevrawlen; + ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); } else { unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl); 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. */ unsigned char *ziplistIndex(unsigned char *zl, int index) { unsigned char *p; - zlentry entry; + unsigned int prevlensize, prevlen = 0; if (index < 0) { index = (-index)-1; p = ZIPLIST_ENTRY_TAIL(zl); if (p[0] != ZIP_END) { - entry = zipEntry(p); - while (entry.prevrawlen > 0 && index--) { - p -= entry.prevrawlen; - entry = zipEntry(p); + ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); + while (prevlen > 0 && index--) { + p -= prevlen; + ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); } } } else { @@ -722,7 +722,7 @@ unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) { /* Return pointer to previous entry in ziplist. */ 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 * 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)) { return NULL; } else { - entry = zipEntry(p); - assert(entry.prevrawlen > 0); - return p-entry.prevrawlen; + ZIP_DECODE_PREVLEN(p, prevlensize, prevlen); + assert(prevlen > 0); + return p-prevlen; } } diff --git a/tests/support/util.tcl b/tests/support/util.tcl index 7774dd99a..4b9caced8 100644 --- a/tests/support/util.tcl +++ b/tests/support/util.tcl @@ -353,7 +353,7 @@ proc colorstr {color str} { default {set colorcode {37}} } if {$colorcode ne {}} { - return "\033\[$b;${colorcode};40m$str\033\[0m" + return "\033\[$b;${colorcode};49m$str\033\[0m" } } else { return $str diff --git a/tests/unit/basic.tcl b/tests/unit/basic.tcl index 6f7fe292c..110376087 100644 --- a/tests/unit/basic.tcl +++ b/tests/unit/basic.tcl @@ -404,6 +404,12 @@ start_server {tags {"basic"}} { r move mykey 10 } {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} { r set a hello r set b world @@ -769,4 +775,9 @@ start_server {tags {"basic"}} { r keys * r keys * } {dlskeriewrioeuwqoirueioqwrueoqwrueqw} + + test {GETRANGE with huge ranges, Github issue #1844} { + r set foo bar + r getrange foo 0 4294967297 + } {bar} } diff --git a/tests/unit/pubsub.tcl b/tests/unit/pubsub.tcl index 18033bdf2..9c7a43bf0 100644 --- a/tests/unit/pubsub.tcl +++ b/tests/unit/pubsub.tcl @@ -196,6 +196,10 @@ start_server {tags {"pubsub"}} { $rd1 close } + test "NUMSUB returns numbers, not strings (#1561)" { + r pubsub numsub abc def + } {abc 0 def 0} + test "Mix SUBSCRIBE and PSUBSCRIBE" { set rd1 [redis_deferring_client] assert_equal {1} [subscribe $rd1 {foo.bar}] diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index 4190a0a49..d0c6f5d7a 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -358,6 +358,11 @@ start_server {tags {"scripting"}} { return redis.call("get", "key") } 0 } {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 diff --git a/tests/unit/sort.tcl b/tests/unit/sort.tcl index f48f88b5d..54b0cc7e2 100644 --- a/tests/unit/sort.tcl +++ b/tests/unit/sort.tcl @@ -95,6 +95,14 @@ start_server { 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" { assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC] } diff --git a/utils/generate-command-help.rb b/utils/generate-command-help.rb index 47fbc645c..068953198 100755 --- a/utils/generate-command-help.rb +++ b/utils/generate-command-help.rb @@ -50,7 +50,7 @@ def commands require "json" 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.use_ssl = true response = client.get url.path diff --git a/utils/install_server.sh b/utils/install_server.sh index 15b60a08e..98e047e3d 100755 --- a/utils/install_server.sh +++ b/utils/install_server.sh @@ -152,7 +152,7 @@ rm -f $TMP_FILE #we hard code the configs here to avoid issues with templates containing env vars #kinda lame but works! REDIS_INIT_HEADER=\ -"#/bin/sh\n +"#!/bin/sh\n #Configurations injected by install_server below....\n\n EXEC=$REDIS_EXECUTABLE\n CLIEXEC=$CLI_EXEC\n @@ -193,7 +193,7 @@ fi # warning if init info is not available. cat > ${TMP_FILE} <