Unverified Commit adfaf548 authored by Salvatore Sanfilippo's avatar Salvatore Sanfilippo Committed by GitHub
Browse files

Merge branch 'unstable' into fixChildInfoPipeFdLeak

parents 440385de cfdc800a
......@@ -453,7 +453,8 @@ struct redisCommand sentinelcmds[] = {
{"role",sentinelRoleCommand,1,"l",0,NULL,0,0,0,0,0},
{"client",clientCommand,-2,"rs",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0},
{"auth",authCommand,2,"sltF",0,NULL,0,0,0,0,0}
{"auth",authCommand,2,"sltF",0,NULL,0,0,0,0,0},
{"hello",helloCommand,-2,"sF",0,NULL,0,0,0,0,0}
};
/* This function overwrites a few normal Redis config default with Sentinel
......@@ -885,17 +886,17 @@ void sentinelPendingScriptsCommand(client *c) {
listNode *ln;
listIter li;
addReplyMultiBulkLen(c,listLength(sentinel.scripts_queue));
addReplyArrayLen(c,listLength(sentinel.scripts_queue));
listRewind(sentinel.scripts_queue,&li);
while ((ln = listNext(&li)) != NULL) {
sentinelScriptJob *sj = ln->value;
int j = 0;
addReplyMultiBulkLen(c,10);
addReplyMapLen(c,5);
addReplyBulkCString(c,"argv");
while (sj->argv[j]) j++;
addReplyMultiBulkLen(c,j);
addReplyArrayLen(c,j);
j = 0;
while (sj->argv[j]) addReplyBulkCString(c,sj->argv[j++]);
......@@ -1960,7 +1961,7 @@ void sentinelSendAuthIfNeeded(sentinelRedisInstance *ri, redisAsyncContext *c) {
} else if (ri->flags & SRI_SLAVE) {
auth_pass = ri->master->auth_pass;
} else if (ri->flags & SRI_SENTINEL) {
if (server.requirepass) auth_pass = server.requirepass;
auth_pass = ACLDefaultUserFirstPassword();
}
if (auth_pass) {
......@@ -2741,7 +2742,7 @@ void addReplySentinelRedisInstance(client *c, sentinelRedisInstance *ri) {
void *mbl;
int fields = 0;
mbl = addDeferredMultiBulkLength(c);
mbl = addReplyDeferredLen(c);
addReplyBulkCString(c,"name");
addReplyBulkCString(c,ri->name);
......@@ -2922,7 +2923,7 @@ void addReplySentinelRedisInstance(client *c, sentinelRedisInstance *ri) {
fields++;
}
setDeferredMultiBulkLength(c,mbl,fields*2);
setDeferredMapLen(c,mbl,fields);
}
/* Output a number of instances contained inside a dictionary as
......@@ -2932,7 +2933,7 @@ void addReplyDictOfRedisInstances(client *c, dict *instances) {
dictEntry *de;
di = dictGetIterator(instances);
addReplyMultiBulkLen(c,dictSize(instances));
addReplyArrayLen(c,dictSize(instances));
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
......@@ -3062,7 +3063,7 @@ void sentinelCommand(client *c) {
/* Reply with a three-elements multi-bulk reply:
* down state, leader, vote epoch. */
addReplyMultiBulkLen(c,3);
addReplyArrayLen(c,3);
addReply(c, isdown ? shared.cone : shared.czero);
addReplyBulkCString(c, leader ? leader : "*");
addReplyLongLong(c, (long long)leader_epoch);
......@@ -3078,11 +3079,11 @@ void sentinelCommand(client *c) {
if (c->argc != 3) goto numargserr;
ri = sentinelGetMasterByName(c->argv[2]->ptr);
if (ri == NULL) {
addReply(c,shared.nullmultibulk);
addReplyNullArray(c);
} else {
sentinelAddr *addr = sentinelGetCurrentMasterAddress(ri);
addReplyMultiBulkLen(c,2);
addReplyArrayLen(c,2);
addReplyBulkCString(c,addr->ip);
addReplyBulkLongLong(c,addr->port);
}
......@@ -3232,7 +3233,7 @@ void sentinelCommand(client *c) {
* 3.) other master name
* ...
*/
addReplyMultiBulkLen(c,dictSize(masters_local) * 2);
addReplyArrayLen(c,dictSize(masters_local) * 2);
dictIterator *di;
dictEntry *de;
......@@ -3240,25 +3241,25 @@ void sentinelCommand(client *c) {
while ((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
addReplyBulkCBuffer(c,ri->name,strlen(ri->name));
addReplyMultiBulkLen(c,dictSize(ri->slaves) + 1); /* +1 for self */
addReplyMultiBulkLen(c,2);
addReplyArrayLen(c,dictSize(ri->slaves) + 1); /* +1 for self */
addReplyArrayLen(c,2);
addReplyLongLong(c, now - ri->info_refresh);
if (ri->info)
addReplyBulkCBuffer(c,ri->info,sdslen(ri->info));
else
addReply(c,shared.nullbulk);
addReplyNull(c);
dictIterator *sdi;
dictEntry *sde;
sdi = dictGetIterator(ri->slaves);
while ((sde = dictNext(sdi)) != NULL) {
sentinelRedisInstance *sri = dictGetVal(sde);
addReplyMultiBulkLen(c,2);
addReplyArrayLen(c,2);
addReplyLongLong(c, now - sri->info_refresh);
if (sri->info)
addReplyBulkCBuffer(c,sri->info,sdslen(sri->info));
else
addReply(c,shared.nullbulk);
addReplyNull(c);
}
dictReleaseIterator(sdi);
}
......@@ -3282,7 +3283,7 @@ void sentinelCommand(client *c) {
serverLog(LL_WARNING,"Failure simulation: this Sentinel "
"will crash after promoting the selected replica to master");
} else if (!strcasecmp(c->argv[j]->ptr,"help")) {
addReplyMultiBulkLen(c,2);
addReplyArrayLen(c,2);
addReplyBulkCString(c,"crash-after-election");
addReplyBulkCString(c,"crash-after-promotion");
} else {
......@@ -3382,9 +3383,9 @@ void sentinelRoleCommand(client *c) {
dictIterator *di;
dictEntry *de;
addReplyMultiBulkLen(c,2);
addReplyArrayLen(c,2);
addReplyBulkCBuffer(c,"sentinel",8);
addReplyMultiBulkLen(c,dictSize(sentinel.masters));
addReplyArrayLen(c,dictSize(sentinel.masters));
di = dictGetIterator(sentinel.masters);
while((de = dictNext(di)) != NULL) {
......
This diff is collapsed.
......@@ -707,11 +707,51 @@ typedef struct readyList {
robj *key;
} readyList;
/* This structure represents a Redis user. This is useful for ACLs, the
* user is associated to the connection after the connection is authenticated.
* If there is no associated user, the connection uses the default user. */
#define USER_MAX_COMMAND_BIT 1024 /* The first *not valid* bit that
would overflow. So check for >= */
#define USER_FLAG_ENABLED (1<<0) /* The user is active. */
#define USER_FLAG_ALLKEYS (1<<1) /* The user can mention any key. */
#define USER_FLAG_ALLCOMMANDS (1<<2) /* The user can run all commands. */
#define USER_FLAG_NOPASS (1<<3) /* The user requires no password, any
provided password will work. For the
default user, this also means that
no AUTH is needed, and every
connection is immediately
authenticated. */
typedef struct user {
sds name; /* The username as an SDS string. */
uint64_t flags; /* See USER_FLAG_* */
/* The bit in allowed_commands is set if this user has the right to
* execute this command. In commands having subcommands, if this bit is
* set, then all the subcommands are also available.
*
* If the bit for a given command is NOT set and the command has
* subcommands, Redis will also check allowed_subcommands in order to
* understand if the command can be executed. */
uint64_t allowed_commands[USER_MAX_COMMAND_BIT/64];
/* This array points, for each command ID (corresponding to the command
* bit set in allowed_commands), to an array of SDS strings, terminated by
* a NULL pointer, with all the sub commands that can be executed for
* this command. When no subcommands matching is used, the field is just
* set to NULL to avoid allocating USER_MAX_COMMAND_BIT pointers. */
sds **allowed_subcommands;
list *passwords; /* A list of SDS valid passwords for this user. */
list *patterns; /* A list of allowed key patterns. If this field is NULL
the user cannot mention any key in a command, unless
the flag ALLKEYS is set in the user. */
} user;
/* With multiplexing we need to take per-client state.
* Clients are taken in a linked list. */
typedef struct client {
uint64_t id; /* Client incremental unique ID. */
int fd; /* Client socket. */
int resp; /* RESP protocol version. Can be 2 or 3. */
redisDb *db; /* Pointer to currently SELECTed DB. */
robj *name; /* As set by CLIENT SETNAME. */
sds querybuf; /* Buffer we use to accumulate client queries. */
......@@ -724,6 +764,9 @@ typedef struct client {
int argc; /* Num of arguments of current command. */
robj **argv; /* Arguments of current command. */
struct redisCommand *cmd, *lastcmd; /* Last command executed. */
user *user; /* User associated with this connection. If the
user is set to NULL the connection can do
anything (admin). */
int reqtype; /* Request protocol type: PROTO_REQ_* */
int multibulklen; /* Number of multi bulk arguments left to read. */
long bulklen; /* Length of bulk argument in multi bulk request. */
......@@ -735,7 +778,7 @@ typedef struct client {
time_t lastinteraction; /* Time of the last interaction, used for timeout */
time_t obuf_soft_limit_reached_time;
int flags; /* Client flags: CLIENT_* macros. */
int authenticated; /* When requirepass is non-NULL. */
int authenticated; /* Needed when the default user requires auth. */
int replstate; /* Replication state if this is a slave. */
int repl_put_online_on_ack; /* Install slave write handler on ACK. */
int repldbfd; /* Replication DB file descriptor. */
......@@ -780,9 +823,9 @@ struct moduleLoadQueueEntry {
};
struct sharedObjectsStruct {
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,
*colon, *nullbulk, *nullmultibulk, *queued,
*emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,
*colon, *queued, *null[4], *nullarray[4],
*emptyarray, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
*outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,
*masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr,
*busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
......@@ -945,7 +988,6 @@ struct redisServer {
int shutdown_asap; /* SHUTDOWN needed ASAP */
int activerehashing; /* Incremental rehash in serverCron() */
int active_defrag_running; /* Active defragmentation running (holds current scan aggressiveness) */
char *requirepass; /* Pass for AUTH command, or NULL */
char *pidfile; /* PID file path */
int arch_bits; /* 32 or 64 depending on sizeof(long) */
int cronloops; /* Number of times the cron function run */
......@@ -1304,6 +1346,11 @@ struct redisCommand {
int lastkey; /* The last argument that's a key */
int keystep; /* The step between first and last key */
long long microseconds, calls;
int id; /* Command ID. This is a progressive ID starting from 0 that
is assigned at runtime, and is used in order to check
ACLs. A connection is able to execute a given command if
the user associated to the connection has this command
bit set in the bitmap of allowed commands. */
};
struct redisFunctionSym {
......@@ -1424,15 +1471,23 @@ void freeClient(client *c);
void freeClientAsync(client *c);
void resetClient(client *c);
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
void *addDeferredMultiBulkLength(client *c);
void setDeferredMultiBulkLength(client *c, void *node, long length);
void *addReplyDeferredLen(client *c);
void setDeferredArrayLen(client *c, void *node, long length);
void setDeferredMapLen(client *c, void *node, long length);
void setDeferredSetLen(client *c, void *node, long length);
void setDeferredAttributeLen(client *c, void *node, long length);
void setDeferredPushLen(client *c, void *node, long length);
void processInputBuffer(client *c);
void processInputBufferAndReplicate(client *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);
void addReplyString(client *c, const char *s, size_t len);
void addReplyNull(client *c);
void addReplyNullArray(client *c);
void addReplyBool(client *c, int b);
void addReplyVerbatim(client *c, const char *s, size_t len, const char *ext);
void addReplyProto(client *c, const char *s, size_t len);
void addReplyBulk(client *c, robj *obj);
void addReplyBulkCString(client *c, const char *s);
void addReplyBulkCBuffer(client *c, const void *p, size_t len);
......@@ -1445,9 +1500,14 @@ void addReplyStatus(client *c, const char *status);
void addReplyDouble(client *c, double d);
void addReplyHumanLongDouble(client *c, long double d);
void addReplyLongLong(client *c, long long ll);
void addReplyMultiBulkLen(client *c, long length);
void addReplyArrayLen(client *c, long length);
void addReplyMapLen(client *c, long length);
void addReplySetLen(client *c, long length);
void addReplyAttributeLen(client *c, long length);
void addReplyPushLen(client *c, long length);
void addReplyHelp(client *c, const char **help);
void addReplySubcommandSyntaxError(client *c);
void addReplyLoadedModules(client *c);
void copyClientOutputBuffer(client *dst, client *src);
size_t sdsZmallocSize(sds s);
size_t getStringObjectSdsUsedMemory(robj *o);
......@@ -1634,6 +1694,20 @@ void closeChildInfoPipe(void);
void sendChildInfo(int process_type);
void receiveChildInfo(void);
/* acl.c -- Authentication related prototypes. */
extern user *DefaultUser;
void ACLInit(void);
/* Return values for ACLCheckUserCredentials(). */
#define ACL_OK 0
#define ACL_DENIED_CMD 1
#define ACL_DENIED_KEY 2
int ACLCheckUserCredentials(robj *username, robj *password);
unsigned long ACLGetCommandID(const char *cmdname);
user *ACLGetUserByName(const char *name, size_t namelen);
int ACLCheckCommandPerm(client *c);
int ACLSetUser(user *u, const char *op, ssize_t oplen);
sds ACLDefaultUserFirstPassword(void);
/* Sorted sets data type */
/* Input flags. */
......@@ -2082,6 +2156,7 @@ void dumpCommand(client *c);
void objectCommand(client *c);
void memoryCommand(client *c);
void clientCommand(client *c);
void helloCommand(client *c);
void evalCommand(client *c);
void evalShaCommand(client *c);
void scriptCommand(client *c);
......@@ -2123,6 +2198,7 @@ void xinfoCommand(client *c);
void xdelCommand(client *c);
void xtrimCommand(client *c);
void lolwutCommand(client *c);
void aclCommand(client *c);
#if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
......
......@@ -169,23 +169,23 @@ NULL
return;
listRewind(server.slowlog,&li);
totentries = addDeferredMultiBulkLength(c);
totentries = addReplyDeferredLen(c);
while(count-- && (ln = listNext(&li))) {
int j;
se = ln->value;
addReplyMultiBulkLen(c,6);
addReplyArrayLen(c,6);
addReplyLongLong(c,se->id);
addReplyLongLong(c,se->time);
addReplyLongLong(c,se->duration);
addReplyMultiBulkLen(c,se->argc);
addReplyArrayLen(c,se->argc);
for (j = 0; j < se->argc; j++)
addReplyBulk(c,se->argv[j]);
addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid));
addReplyBulkCBuffer(c,se->cname,sdslen(se->cname));
sent++;
}
setDeferredMultiBulkLength(c,totentries,sent);
setDeferredArrayLen(c,totentries,sent);
} else {
addReplySubcommandSyntaxError(c);
}
......
......@@ -505,7 +505,7 @@ void sortCommand(client *c) {
addReplyError(c,"One or more scores can't be converted into double");
} else if (storekey == NULL) {
/* STORE option not specified, sent the sorting result to client */
addReplyMultiBulkLen(c,outputlen);
addReplyArrayLen(c,outputlen);
for (j = start; j <= end; j++) {
listNode *ln;
listIter li;
......@@ -519,7 +519,7 @@ void sortCommand(client *c) {
if (sop->type == SORT_OP_GET) {
if (!val) {
addReply(c,shared.nullbulk);
addReplyNull(c);
} else {
addReplyBulk(c,val);
decrRefCount(val);
......
......@@ -641,7 +641,7 @@ static void addHashFieldToReply(client *c, robj *o, sds field) {
int ret;
if (o == NULL) {
addReply(c, shared.nullbulk);
addReplyNull(c);
return;
}
......@@ -652,7 +652,7 @@ static void addHashFieldToReply(client *c, robj *o, sds field) {
ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
if (ret < 0) {
addReply(c, shared.nullbulk);
addReplyNull(c);
} else {
if (vstr) {
addReplyBulkCBuffer(c, vstr, vlen);
......@@ -664,7 +664,7 @@ static void addHashFieldToReply(client *c, robj *o, sds field) {
} else if (o->encoding == OBJ_ENCODING_HT) {
sds value = hashTypeGetFromHashTable(o, field);
if (value == NULL)
addReply(c, shared.nullbulk);
addReplyNull(c);
else
addReplyBulkCBuffer(c, value, sdslen(value));
} else {
......@@ -675,7 +675,7 @@ static void addHashFieldToReply(client *c, robj *o, sds field) {
void hgetCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
checkType(c,o,OBJ_HASH)) return;
addHashFieldToReply(c, o, c->argv[2]->ptr);
......@@ -693,7 +693,7 @@ void hmgetCommand(client *c) {
return;
}
addReplyMultiBulkLen(c, c->argc-2);
addReplyArrayLen(c, c->argc-2);
for (i = 2; i < c->argc; i++) {
addHashFieldToReply(c, o, c->argv[i]->ptr);
}
......@@ -766,17 +766,19 @@ static void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int wh
void genericHgetallCommand(client *c, int flags) {
robj *o;
hashTypeIterator *hi;
int multiplier = 0;
int length, count = 0;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL
|| checkType(c,o,OBJ_HASH)) return;
if (flags & OBJ_HASH_KEY) multiplier++;
if (flags & OBJ_HASH_VALUE) multiplier++;
length = hashTypeLength(o) * multiplier;
addReplyMultiBulkLen(c, length);
/* We return a map if the user requested keys and values, like in the
* HGETALL case. Otherwise to use a flat array makes more sense. */
length = hashTypeLength(o);
if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {
addReplyMapLen(c, length);
} else {
addReplyArrayLen(c, length);
}
hi = hashTypeInitIterator(o);
while (hashTypeNext(hi) != C_ERR) {
......@@ -791,6 +793,9 @@ void genericHgetallCommand(client *c, int flags) {
}
hashTypeReleaseIterator(hi);
/* Make sure we returned the right number of elements. */
if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= 2;
serverAssert(count == length);
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -80,7 +80,7 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire,
if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
(flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))
{
addReply(c, abort_reply ? abort_reply : shared.nullbulk);
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
return;
}
setKey(c->db,key,val);
......@@ -157,7 +157,7 @@ void psetexCommand(client *c) {
int getGenericCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL)
return C_OK;
if (o->type != OBJ_STRING) {
......@@ -285,14 +285,14 @@ void getrangeCommand(client *c) {
void mgetCommand(client *c) {
int j;
addReplyMultiBulkLen(c,c->argc-1);
addReplyArrayLen(c,c->argc-1);
for (j = 1; j < c->argc; j++) {
robj *o = lookupKeyRead(c->db,c->argv[j]);
if (o == NULL) {
addReply(c,shared.nullbulk);
addReplyNull(c);
} else {
if (o->type != OBJ_STRING) {
addReply(c,shared.nullbulk);
addReplyNull(c);
} else {
addReplyBulk(c,o);
}
......
This diff is collapsed.
......@@ -275,7 +275,6 @@ start_server {tags {"repl"}} {
start_server {} {
test "Master stream is correctly processed while the replica has a script in -BUSY state" {
set slave [srv 0 client]
puts [srv 0 port]
$slave config set lua-time-limit 500
$slave slaveof $master_host $master_port
......
......@@ -2,14 +2,14 @@ start_server {tags {"auth"}} {
test {AUTH fails if there is no password configured server side} {
catch {r auth foo} err
set _ $err
} {ERR*no password*}
} {ERR*any password*}
}
start_server {tags {"auth"} overrides {requirepass foobar}} {
test {AUTH fails when a wrong password is given} {
catch {r auth wrong!} err
set _ $err
} {ERR*invalid password}
} {WRONGPASS*}
test {Arbitrary command gives an error when AUTH is required} {
catch {r set foo bar} err
......
......@@ -362,7 +362,7 @@ start_server {tags {"dump"}} {
r -1 lpush list a b c d
$second config set requirepass foobar2
catch {r -1 migrate $second_host $second_port list 9 5000 AUTH foobar} err
assert_match {*invalid password*} $err
assert_match {*WRONGPASS*} $err
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment