2009-03-22 10:30:00 +01:00
/* Redis CLI (command line interface)
*
* Copyright ( c ) 2006 - 2009 , Salvatore Sanfilippo < antirez at gmail dot com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
2009-03-27 22:00:27 +01:00
# include "fmacros.h"
2009-03-22 10:30:00 +01:00
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include "anet.h"
# include "sds.h"
# include "adlist.h"
# include "zmalloc.h"
# define REDIS_CMD_INLINE 1
# define REDIS_CMD_BULK 2
# define REDIS_NOTUSED(V) ((void) V)
static struct config {
char * hostip ;
int hostport ;
} config ;
struct redisCommand {
char * name ;
int arity ;
int flags ;
} ;
static struct redisCommand cmdTable [ ] = {
2009-03-24 13:37:32 +01:00
{ " get " , 2 , REDIS_CMD_INLINE } ,
{ " set " , 3 , REDIS_CMD_BULK } ,
{ " setnx " , 3 , REDIS_CMD_BULK } ,
{ " del " , 2 , REDIS_CMD_INLINE } ,
{ " exists " , 2 , REDIS_CMD_INLINE } ,
{ " incr " , 2 , REDIS_CMD_INLINE } ,
{ " decr " , 2 , REDIS_CMD_INLINE } ,
{ " rpush " , 3 , REDIS_CMD_BULK } ,
{ " lpush " , 3 , REDIS_CMD_BULK } ,
{ " rpop " , 2 , REDIS_CMD_INLINE } ,
{ " lpop " , 2 , REDIS_CMD_INLINE } ,
{ " llen " , 2 , REDIS_CMD_INLINE } ,
{ " lindex " , 3 , REDIS_CMD_INLINE } ,
{ " lset " , 4 , REDIS_CMD_BULK } ,
{ " lrange " , 4 , REDIS_CMD_INLINE } ,
{ " ltrim " , 4 , REDIS_CMD_INLINE } ,
{ " lrem " , 4 , REDIS_CMD_BULK } ,
{ " sadd " , 3 , REDIS_CMD_BULK } ,
{ " srem " , 3 , REDIS_CMD_BULK } ,
{ " sismember " , 3 , REDIS_CMD_BULK } ,
{ " scard " , 2 , REDIS_CMD_INLINE } ,
{ " sinter " , - 2 , REDIS_CMD_INLINE } ,
{ " sinterstore " , - 3 , REDIS_CMD_INLINE } ,
2009-04-19 10:02:36 +02:00
{ " sunion " , - 2 , REDIS_CMD_INLINE } ,
{ " sunionstore " , - 3 , REDIS_CMD_INLINE } ,
2009-03-24 13:37:32 +01:00
{ " smembers " , 2 , REDIS_CMD_INLINE } ,
{ " incrby " , 3 , REDIS_CMD_INLINE } ,
{ " decrby " , 3 , REDIS_CMD_INLINE } ,
{ " randomkey " , 1 , REDIS_CMD_INLINE } ,
{ " select " , 2 , REDIS_CMD_INLINE } ,
{ " move " , 3 , REDIS_CMD_INLINE } ,
{ " rename " , 3 , REDIS_CMD_INLINE } ,
{ " renamenx " , 3 , REDIS_CMD_INLINE } ,
{ " keys " , 2 , REDIS_CMD_INLINE } ,
{ " dbsize " , 1 , REDIS_CMD_INLINE } ,
{ " ping " , 1 , REDIS_CMD_INLINE } ,
{ " echo " , 2 , REDIS_CMD_BULK } ,
{ " save " , 1 , REDIS_CMD_INLINE } ,
{ " bgsave " , 1 , REDIS_CMD_INLINE } ,
{ " shutdown " , 1 , REDIS_CMD_INLINE } ,
{ " lastsave " , 1 , REDIS_CMD_INLINE } ,
{ " type " , 2 , REDIS_CMD_INLINE } ,
{ " flushdb " , 1 , REDIS_CMD_INLINE } ,
{ " flushall " , 1 , REDIS_CMD_INLINE } ,
{ " sort " , - 2 , REDIS_CMD_INLINE } ,
{ " info " , 1 , REDIS_CMD_INLINE } ,
{ " mget " , - 2 , REDIS_CMD_INLINE } ,
2009-04-01 13:22:35 +02:00
{ " expire " , 3 , REDIS_CMD_INLINE } ,
2009-03-22 10:30:00 +01:00
{ NULL , 0 , 0 }
} ;
2009-03-24 13:37:32 +01:00
static int cliReadReply ( int fd ) ;
2009-03-22 10:30:00 +01:00
static struct redisCommand * lookupCommand ( char * name ) {
int j = 0 ;
while ( cmdTable [ j ] . name ! = NULL ) {
if ( ! strcasecmp ( name , cmdTable [ j ] . name ) ) return & cmdTable [ j ] ;
j + + ;
}
return NULL ;
}
static int cliConnect ( void ) {
char err [ ANET_ERR_LEN ] ;
int fd ;
fd = anetTcpConnect ( err , config . hostip , config . hostport ) ;
if ( fd = = ANET_ERR ) {
fprintf ( stderr , " Connect: %s \n " , err ) ;
return - 1 ;
}
anetTcpNoDelay ( NULL , fd ) ;
return fd ;
}
static sds cliReadLine ( int fd ) {
sds line = sdsempty ( ) ;
while ( 1 ) {
char c ;
2009-03-23 12:43:16 +01:00
ssize_t ret ;
2009-03-22 10:30:00 +01:00
2009-03-23 12:43:16 +01:00
ret = read ( fd , & c , 1 ) ;
if ( ret = = - 1 ) {
2009-03-22 10:30:00 +01:00
sdsfree ( line ) ;
return NULL ;
2009-03-23 12:43:16 +01:00
} else if ( ( ret = = 0 ) | | ( c = = ' \n ' ) ) {
2009-03-22 10:30:00 +01:00
break ;
} else {
line = sdscatlen ( line , & c , 1 ) ;
}
}
return sdstrim ( line , " \r \n " ) ;
}
2009-03-24 13:37:32 +01:00
static int cliReadSingleLineReply ( int fd ) {
2009-03-22 10:30:00 +01:00
sds reply = cliReadLine ( fd ) ;
if ( reply = = NULL ) return 1 ;
printf ( " %s \n " , reply ) ;
return 0 ;
}
2009-03-24 13:37:32 +01:00
static int cliReadBulkReply ( int fd ) {
2009-03-22 10:30:00 +01:00
sds replylen = cliReadLine ( fd ) ;
char * reply , crlf [ 2 ] ;
2009-03-24 13:37:32 +01:00
int bulklen ;
2009-03-22 10:30:00 +01:00
if ( replylen = = NULL ) return 1 ;
bulklen = atoi ( replylen ) ;
2009-03-24 13:37:32 +01:00
if ( bulklen = = - 1 ) {
2009-03-22 10:30:00 +01:00
sdsfree ( replylen ) ;
printf ( " (nil) " ) ;
return 0 ;
}
reply = zmalloc ( bulklen ) ;
anetRead ( fd , reply , bulklen ) ;
anetRead ( fd , crlf , 2 ) ;
if ( bulklen & & fwrite ( reply , bulklen , 1 , stdout ) = = 0 ) {
zfree ( reply ) ;
return 1 ;
}
2009-03-24 13:37:32 +01:00
if ( isatty ( fileno ( stdout ) ) & & reply [ bulklen - 1 ] ! = ' \n ' )
2009-03-22 10:30:00 +01:00
printf ( " \n " ) ;
zfree ( reply ) ;
2009-03-24 13:37:32 +01:00
return 0 ;
2009-03-22 10:30:00 +01:00
}
static int cliReadMultiBulkReply ( int fd ) {
sds replylen = cliReadLine ( fd ) ;
int elements , c = 1 ;
if ( replylen = = NULL ) return 1 ;
2009-03-24 13:37:32 +01:00
elements = atoi ( replylen ) ;
if ( elements = = - 1 ) {
2009-03-22 10:30:00 +01:00
sdsfree ( replylen ) ;
printf ( " (nil) \n " ) ;
return 0 ;
}
2009-03-24 13:37:32 +01:00
if ( elements = = 0 ) {
printf ( " (empty list or set) \n " ) ;
}
2009-03-22 10:30:00 +01:00
while ( elements - - ) {
printf ( " %d. " , c ) ;
2009-03-24 13:37:32 +01:00
if ( cliReadReply ( fd ) ) return 1 ;
2009-03-22 10:30:00 +01:00
c + + ;
}
return 0 ;
}
2009-03-24 13:37:32 +01:00
static int cliReadReply ( int fd ) {
char type ;
if ( anetRead ( fd , & type , 1 ) < = 0 ) exit ( 1 ) ;
switch ( type ) {
case ' - ' :
printf ( " (error) " ) ;
cliReadSingleLineReply ( fd ) ;
return 1 ;
case ' + ' :
case ' : ' :
return cliReadSingleLineReply ( fd ) ;
case ' $ ' :
return cliReadBulkReply ( fd ) ;
case ' * ' :
return cliReadMultiBulkReply ( fd ) ;
default :
printf ( " protocol error, got '%c' as reply type byte \n " , type ) ;
return 1 ;
}
}
2009-03-22 10:30:00 +01:00
static int cliSendCommand ( int argc , char * * argv ) {
struct redisCommand * rc = lookupCommand ( argv [ 0 ] ) ;
int fd , j , retval = 0 ;
sds cmd = sdsempty ( ) ;
if ( ! rc ) {
fprintf ( stderr , " Unknown command '%s' \n " , argv [ 0 ] ) ;
return 1 ;
}
if ( ( rc - > arity > 0 & & argc ! = rc - > arity ) | |
2009-03-22 14:59:05 +01:00
( rc - > arity < 0 & & argc < - rc - > arity ) ) {
2009-03-22 10:30:00 +01:00
fprintf ( stderr , " Wrong number of arguments for '%s' \n " , rc - > name ) ;
return 1 ;
}
if ( ( fd = cliConnect ( ) ) = = - 1 ) return 1 ;
/* Build the command to send */
for ( j = 0 ; j < argc ; j + + ) {
if ( j ! = 0 ) cmd = sdscat ( cmd , " " ) ;
if ( j = = argc - 1 & & rc - > flags & REDIS_CMD_BULK ) {
cmd = sdscatprintf ( cmd , " %d " , sdslen ( argv [ j ] ) ) ;
} else {
cmd = sdscatlen ( cmd , argv [ j ] , sdslen ( argv [ j ] ) ) ;
}
}
cmd = sdscat ( cmd , " \r \n " ) ;
if ( rc - > flags & REDIS_CMD_BULK ) {
cmd = sdscatlen ( cmd , argv [ argc - 1 ] , sdslen ( argv [ argc - 1 ] ) ) ;
cmd = sdscat ( cmd , " \r \n " ) ;
}
anetWrite ( fd , cmd , sdslen ( cmd ) ) ;
2009-03-24 13:37:32 +01:00
retval = cliReadReply ( fd ) ;
2009-03-22 10:30:00 +01:00
if ( retval ) {
close ( fd ) ;
return retval ;
}
close ( fd ) ;
return 0 ;
}
static int parseOptions ( int argc , char * * argv ) {
int i ;
for ( i = 1 ; i < argc ; i + + ) {
int lastarg = i = = argc - 1 ;
if ( ! strcmp ( argv [ i ] , " -h " ) & & ! lastarg ) {
char * ip = zmalloc ( 32 ) ;
if ( anetResolve ( NULL , argv [ i + 1 ] , ip ) = = ANET_ERR ) {
printf ( " Can't resolve %s \n " , argv [ i ] ) ;
exit ( 1 ) ;
}
config . hostip = ip ;
i + + ;
} else if ( ! strcmp ( argv [ i ] , " -p " ) & & ! lastarg ) {
config . hostport = atoi ( argv [ i + 1 ] ) ;
i + + ;
} else {
break ;
}
}
return i ;
}
static sds readArgFromStdin ( void ) {
char buf [ 1024 ] ;
sds arg = sdsempty ( ) ;
while ( 1 ) {
int nread = read ( fileno ( stdin ) , buf , 1024 ) ;
if ( nread = = 0 ) break ;
else if ( nread = = - 1 ) {
perror ( " Reading from standard input " ) ;
exit ( 1 ) ;
}
arg = sdscatlen ( arg , buf , nread ) ;
}
return arg ;
}
int main ( int argc , char * * argv ) {
int firstarg , j ;
char * * argvcopy ;
config . hostip = " 127.0.0.1 " ;
config . hostport = 6379 ;
firstarg = parseOptions ( argc , argv ) ;
argc - = firstarg ;
argv + = firstarg ;
/* Turn the plain C strings into Sds strings */
argvcopy = zmalloc ( sizeof ( char * ) * argc + 1 ) ;
for ( j = 0 ; j < argc ; j + + )
argvcopy [ j ] = sdsnew ( argv [ j ] ) ;
/* Read the last argument from stdandard input */
if ( ! isatty ( fileno ( stdin ) ) ) {
sds lastarg = readArgFromStdin ( ) ;
argvcopy [ argc ] = lastarg ;
argc + + ;
}
if ( argc < 1 ) {
fprintf ( stderr , " usage: redis-cli [-h host] [-p port] cmd arg1 arg2 arg3 ... argN \n " ) ;
fprintf ( stderr , " usage: echo \" argN \" | redis-cli [-h host] [-p port] cmd arg1 arg2 ... arg(N-1) \n " ) ;
fprintf ( stderr , " \n If a pipe from standard input is detected this data is used as last argument. \n \n " ) ;
fprintf ( stderr , " example: cat /etc/passwd | redis-cli set my_passwd \n " ) ;
fprintf ( stderr , " example: redis-cli get my_passwd \n " ) ;
exit ( 1 ) ;
}
return cliSendCommand ( argc , argvcopy ) ;
}