* POSSIBILITY OF SUCH DAMAGE.
*/
-#define REDIS_VERSION "0.07"
+#define REDIS_VERSION "0.08"
#include <stdio.h>
#include <stdlib.h>
#define REDIS_CLOSE 1 /* This client connection should be closed ASAP */
#define REDIS_SLAVE 2 /* This client is a slave server */
#define REDIS_MASTER 4 /* This client is a master server */
+#define REDIS_MONITOR 8 /* This client is a slave monitor, see MONITOR */
/* Server replication state */
#define REDIS_REPL_NONE 0 /* No active replication */
list *reply;
int sentlen;
time_t lastinteraction; /* time of the last interaction, used for timeout */
- int flags; /* REDIS_CLOSE | REDIS_SLAVE */
+ int flags; /* REDIS_CLOSE | REDIS_SLAVE | REDIS_MONITOR */
int slaveseldb; /* slave selected db, if this client is a slave */
} redisClient;
dict **dict;
long long dirty; /* changes to DB from the last save */
list *clients;
- list *slaves;
+ list *slaves, *monitors;
char neterr[ANET_ERR_LEN];
aeEventLoop *el;
int cronloops; /* number of times the cron function run */
int maxidletime;
int dbnum;
int daemonize;
+ char *pidfile;
int bgsaveinprogress;
struct saveparam *saveparams;
int saveparamslen;
static void incrRefCount(robj *o);
static int saveDbBackground(char *filename);
static robj *createStringObject(char *ptr, size_t len);
-static void replicationFeedSlaves(struct redisCommand *cmd, int dictid, robj **argv, int argc);
+static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc);
static int syncWithMaster(void);
static void pingCommand(redisClient *c);
static void lremCommand(redisClient *c);
static void infoCommand(redisClient *c);
static void mgetCommand(redisClient *c);
+static void monitorCommand(redisClient *c);
/*================================= Globals ================================= */
{"flushall",flushallCommand,1,REDIS_CMD_INLINE},
{"sort",sortCommand,-2,REDIS_CMD_INLINE},
{"info",infoCommand,1,REDIS_CMD_INLINE},
+ {"monitor",monitorCommand,1,REDIS_CMD_INLINE},
{NULL,NULL,0,0}
};
server.bindaddr = NULL;
server.glueoutputbuf = 1;
server.daemonize = 0;
+ server.pidfile = "/var/run/redis.pid";
server.dbfilename = "dump.rdb";
ResetServerSaveParams();
server.clients = listCreate();
server.slaves = listCreate();
+ server.monitors = listCreate();
server.objfreelist = listCreate();
createSharedObjects();
server.el = aeCreateEventLoop();
server.dict = zmalloc(sizeof(dict*)*server.dbnum);
- if (!server.dict || !server.clients || !server.slaves || !server.el || !server.objfreelist)
+ if (!server.dict || !server.clients || !server.slaves || !server.monitors || !server.el || !server.objfreelist)
oom("server initialization"); /* Fatal OOM */
server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
if (server.fd == -1) {
else {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
+ } else if (!strcmp(argv[0],"pidfile") && argc == 2) {
+ server.pidfile = zstrdup(argv[1]);
} else {
err = "Bad directive or wrong number of arguments"; goto loaderr;
}
assert(ln != NULL);
listDelNode(server.clients,ln);
if (c->flags & REDIS_SLAVE) {
- ln = listSearchKey(server.slaves,c);
+ list *l = (c->flags & REDIS_MONITOR) ? server.monitors : server.slaves;
+ ln = listSearchKey(l,c);
assert(ln != NULL);
- listDelNode(server.slaves,ln);
+ listDelNode(l,ln);
}
if (c->flags & REDIS_MASTER) {
server.master = NULL;
dirty = server.dirty;
cmd->proc(c);
if (server.dirty-dirty != 0 && listLength(server.slaves))
- replicationFeedSlaves(cmd,c->dictid,c->argv,c->argc);
+ replicationFeedSlaves(server.slaves,cmd,c->dictid,c->argv,c->argc);
+ if (listLength(server.monitors))
+ replicationFeedSlaves(server.monitors,cmd,c->dictid,c->argv,c->argc);
server.stat_numcommands++;
/* Prepare the client for the next command */
return 1;
}
-static void replicationFeedSlaves(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
- listNode *ln = server.slaves->head;
+static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int dictid, robj **argv, int argc) {
+ listNode *ln = slaves->head;
robj *outv[REDIS_MAX_ARGS*4]; /* enough room for args, spaces, newlines */
int outc = 0, j;
static void shutdownCommand(redisClient *c) {
redisLog(REDIS_WARNING,"User requested shutdown, saving DB...");
if (saveDb(server.dbfilename) == REDIS_OK) {
+ if (server.daemonize) {
+ unlink(server.pidfile);
+ }
redisLog(REDIS_WARNING,"Server exit now, bye bye...");
exit(1);
} else {
time_t start = time(NULL);
char sizebuf[32];
+ /* ignore SYNC if aleady slave or in monitor mode */
+ if (c->flags & REDIS_SLAVE) return;
+
redisLog(REDIS_NOTICE,"Slave ask for syncronization");
if (flushClientOutput(c) == REDIS_ERR || saveDb(server.dbfilename) != REDIS_OK)
goto closeconn;
return REDIS_OK;
}
+static void monitorCommand(redisClient *c) {
+ /* ignore MONITOR if aleady slave or in monitor mode */
+ if (c->flags & REDIS_SLAVE) return;
+
+ c->flags |= (REDIS_SLAVE|REDIS_MONITOR);
+ c->slaveseldb = 0;
+ if (!listAddNodeTail(server.monitors,c)) oom("listAddNodeTail");
+ addReply(c,shared.ok);
+}
+
/* =================================== Main! ================================ */
static void daemonize(void) {
if (fd > STDERR_FILENO) close(fd);
}
/* Try to write the pid file */
- fp = fopen("/var/run/redis.pid","w");
+ fp = fopen(server.pidfile,"w");
if (fp) {
fprintf(fp,"%d\n",getpid());
fclose(fp);
redisLog(REDIS_NOTICE,"DB loaded from disk");
if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,
acceptHandler, NULL, NULL) == AE_ERR) oom("creating file event");
- redisLog(REDIS_NOTICE,"The server is now ready to accept connections");
+ redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
aeMain(server.el);
aeDeleteEventLoop(server.el);
return 0;