diff --git a/src/aof.c b/src/aof.c index 9ad85c53..89f17aba 100644 --- a/src/aof.c +++ b/src/aof.c @@ -962,8 +962,7 @@ int rewriteAppendOnlyFileBackground(void) { char tmpfile[256]; /* Child */ - if (server.ipfd > 0) close(server.ipfd); - if (server.sofd > 0) close(server.sofd); + closeListeningSockets(0); redisSetProcTitle("redis-aof-rewrite"); snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid()); if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) { diff --git a/src/rdb.c b/src/rdb.c index 3118e320..e27efd15 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -729,8 +729,7 @@ int rdbSaveBackground(char *filename) { int retval; /* Child */ - if (server.ipfd > 0) close(server.ipfd); - if (server.sofd > 0) close(server.sofd); + closeListeningSockets(0); redisSetProcTitle("redis-rdb-bgsave"); retval = rdbSave(filename); if (retval == REDIS_OK) { diff --git a/src/redis.c b/src/redis.c index f5bf01af..31082bec 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1188,7 +1188,7 @@ void initServerConfig() { server.bindaddr_count = 0; server.unixsocket = NULL; server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM; - server.ipfd = -1; + server.ipfd_count = 0; server.sofd = -1; server.dbnum = REDIS_DEFAULT_DBNUM; server.verbosity = REDIS_DEFAULT_VERBOSITY; @@ -1385,14 +1385,25 @@ void initServer() { server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR); server.db = zmalloc(sizeof(redisDb)*server.dbnum); + /* Open the TCP listening sockets. */ if (server.port != 0) { - server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr,server.bindaddr_count); - if (server.ipfd == ANET_ERR) { - redisLog(REDIS_WARNING, "Opening port %d: %s", - server.port, server.neterr); - exit(1); + /* Force binding of 0.0.0.0 if no bind address is specified, always + * entering the loop if j == 0. */ + if (server.bindaddr_count == 0) server.bindaddr[0] = NULL; + for (j = 0; j < server.bindaddr_count || j == 0; j++) { + server.ipfd[server.ipfd_count] = anetTcpServer(server.neterr,server.port,server.bindaddr[j]); + if (server.ipfd[server.ipfd_count] == ANET_ERR) { + redisLog(REDIS_WARNING, + "Creating Server TCP listening socket %s:%d: %s", + server.bindaddr[j] ? server.bindaddr[j] : "*", + server.port, server.neterr); + exit(1); + } + server.ipfd_count++; } } + + /* Open the listening Unix domain socket. */ if (server.unixsocket != NULL) { unlink(server.unixsocket); /* don't care if this fails */ server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm); @@ -1401,10 +1412,14 @@ void initServer() { exit(1); } } - if (server.ipfd < 0 && server.sofd < 0) { + + /* Abort if there are no listening sockets at all. */ + if (server.ipfd_count == 0 && server.sofd < 0) { redisLog(REDIS_WARNING, "Configured to not listen anywhere, exiting."); exit(1); } + + /* Create the Redis databases, and initialize other internal state. */ for (j = 0; j < server.dbnum; j++) { server.db[j].dict = dictCreate(&dbDictType,NULL); server.db[j].expires = dictCreate(&keyptrDictType,NULL); @@ -1447,15 +1462,28 @@ void initServer() { server.unixtime = time(NULL); server.lastbgsave_status = REDIS_OK; server.repl_good_slaves_count = 0; + + /* Create the serverCron() time event, that's our main way to process + * background operations. */ if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { redisPanic("Can't create the serverCron time event."); exit(1); } - if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, - acceptTcpHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.ipfd file event."); + + /* Create an event handler for accepting new connections in TCP and Unix + * domain sockets. */ + for (j = 0; j < server.ipfd_count; j++) { + if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, + acceptTcpHandler,NULL) == AE_ERR) + { + redisPanic( + "Unrecoverable error creating server.ipfd file event."); + } + } if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE, acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event."); + /* Open the AOF file if needed. */ if (server.aof_state == REDIS_AOF_ON) { server.aof_fd = open(server.aof_filename, O_WRONLY|O_APPEND|O_CREAT,0644); @@ -1860,6 +1888,21 @@ int processCommand(redisClient *c) { /*================================== Shutdown =============================== */ +/* Close listening sockets. Also unlink the unix domain socket if + * unlink_unix_socket is non-zero. */ +void closeListeningSockets(int unlink_unix_socket) { + int j; + + for (j = 0; j < server.ipfd_count; j++) close(server.ipfd[j]); + if (server.sofd != -1) close(server.sofd); + if (server.cluster_enabled) + for (j = 0; j < server.cfd_count; j++) close(server.cfd[j]); + if (unlink_unix_socket && server.unixsocket) { + redisLog(REDIS_NOTICE,"Removing the unix socket file."); + unlink(server.unixsocket); /* don't care if this fails */ + } +} + int prepareForShutdown(int flags) { int save = flags & REDIS_SHUTDOWN_SAVE; int nosave = flags & REDIS_SHUTDOWN_NOSAVE; @@ -1903,13 +1946,7 @@ int prepareForShutdown(int flags) { unlink(server.pidfile); } /* Close the listening sockets. Apparently this allows faster restarts. */ - if (server.ipfd != -1) close(server.ipfd); - if (server.sofd != -1) close(server.sofd); - if (server.unixsocket) { - redisLog(REDIS_NOTICE,"Removing the unix socket file."); - unlink(server.unixsocket); /* don't care if this fails */ - } - + closeListeningSockets(1); redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye..."); return REDIS_OK; } @@ -2845,7 +2882,7 @@ int main(int argc, char **argv) { linuxOvercommitMemoryWarning(); #endif loadDataFromDisk(); - if (server.ipfd > 0) + if (server.ipfd_count > 0) redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port); if (server.sofd > 0) redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket); diff --git a/src/redis.h b/src/redis.h index b4dc0932..4cf3c51d 100644 --- a/src/redis.h +++ b/src/redis.h @@ -581,7 +581,8 @@ struct redisServer { int bindaddr_count; /* Number of addresses in server.bindaddr[] */ char *unixsocket; /* UNIX socket path */ mode_t unixsocketperm; /* UNIX socket permission */ - int ipfd; /* TCP socket file descriptor */ + int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */ + int ipfd_count; /* Used slots in ipfd[] */ int sofd; /* Unix socket file descriptor */ list *clients; /* List of active clients */ list *clients_to_close; /* Clients to close asynchronously */ @@ -1079,6 +1080,7 @@ void oom(const char *msg); void populateCommandTable(void); void resetCommandTableStats(void); void adjustOpenFilesLimit(void); +void closeListeningSockets(int unlink_unix_socket); /* Set data type */ robj *setTypeCreate(robj *value);