diff --git a/src/server.c b/src/server.c new file mode 100644 index 000000000..3e83e5ebd --- /dev/null +++ b/src/server.c @@ -0,0 +1,7160 @@ +/* + * Copyright (c) 2009-2016, Redis Ltd. + * 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. + */ + +#include "server.h" +#include "monotonic.h" +#include "cluster.h" +#include "cluster_slot_stats.h" +#include "commandlog.h" +#include "bio.h" +#include "latency.h" +#include "mt19937-64.h" +#include "functions.h" +#include "hdr_histogram.h" +#include "syscheck.h" +#include "threads_mngr.h" +#include "fmtargs.h" +#include "io_threads.h" +#include "sds.h" +#include "module.h" +#include "scripting_engine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +#if defined(HAVE_SYSCTL_KIPC_SOMAXCONN) || defined(HAVE_SYSCTL_KERN_SOMAXCONN) +#include +#endif + +#ifdef __GNUC__ +#define GNUC_VERSION_STR STRINGIFY(__GNUC__) "." STRINGIFY(__GNUC_MINOR__) "." STRINGIFY(__GNUC_PATCHLEVEL__) +#else +#define GNUC_VERSION_STR "0.0.0" +#endif + +/* Our shared "common" objects */ + +struct sharedObjectsStruct shared; + +/* Global vars that are actually used as constants. The following double + * values are used for double on-disk serialization, and are initialized + * at runtime to avoid strange compiler optimizations. */ + +double R_Zero, R_PosInf, R_NegInf, R_Nan; + +/*================================= Globals ================================= */ + +/* Global vars */ +struct valkeyServer server; /* Server global state */ + +/*============================ Internal prototypes ========================== */ + +static inline int isShutdownInitiated(void); +int isReadyToShutdown(void); +int finishShutdown(void); +const char *replstateToString(int replstate); + +/*============================ Utility functions ============================ */ + +/* This macro tells if we are in the context of loading an AOF. */ +#define isAOFLoadingContext() ((server.current_client && server.current_client->id == CLIENT_ID_AOF) ? 1 : 0) + +/* We use a private localtime implementation which is fork-safe. The logging + * function of the server may be called from other threads. */ +void nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst); + +/* Formats the timezone offset into a string. daylight_active indicates whether dst is active (1) + * or not (0). */ +void formatTimezone(char *buf, size_t buflen, int timezone, int daylight_active) { + serverAssert(buflen >= 7); + serverAssert(timezone >= -50400 && timezone <= 43200); + // Adjust the timezone for daylight saving, if active + int total_offset = (-1) * timezone + 3600 * daylight_active; + int hours = abs(total_offset / 3600); + int minutes = abs(total_offset % 3600) / 60; + buf[0] = total_offset >= 0 ? '+' : '-'; + buf[1] = '0' + hours / 10; + buf[2] = '0' + hours % 10; + buf[3] = ':'; + buf[4] = '0' + minutes / 10; + buf[5] = '0' + minutes % 10; + buf[6] = '\0'; +} + +bool hasInvalidLogfmtChar(const char *msg) { + if (msg == NULL) return false; + + for (int i = 0; msg[i] != '\0'; i++) { + if (msg[i] == '"' || msg[i] == '\n' || msg[i] == '\r') { + return true; + } + } + return false; +} + +/* Modifies the input string by: + * replacing \r and \n with whitespace + * replacing " with ' + * + * Parameters: + * safemsg - A char pointer where the modified message will be stored + * safemsglen - size of safemsg + * msg - The original message */ +void filterInvalidLogfmtChar(char *safemsg, size_t safemsglen, const char *msg) { + serverAssert(safemsglen == LOG_MAX_LEN); + if (msg == NULL) return; + + size_t index = 0; + while (index < safemsglen - 1 && msg[index] != '\0') { + if (msg[index] == '"') { + safemsg[index] = '\''; + } else if (msg[index] == '\n' || msg[index] == '\r') { + safemsg[index] = ' '; + } else { + safemsg[index] = msg[index]; + } + index++; + } + safemsg[index] = '\0'; +} + +/* Low level logging. To use only for very big messages, otherwise + * serverLog() is to prefer. */ +void serverLogRaw(int level, const char *msg) { + const int syslogLevelMap[] = {LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING}; + const char *c = ".-*#"; + const char *verbose_level[] = {"debug", "info", "notice", "warning"}; + const char *roles[] = {"sentinel", "RDB/AOF", "replica", "primary"}; + const char *role_chars = "XCSM"; + FILE *fp; + char buf[64]; + int rawmode = (level & LL_RAW); + int log_to_stdout = server.logfile[0] == '\0'; + + level &= 0xff; /* clear flags */ + if (level < server.verbosity) return; + + /* We open and close the log file in every call to support log rotation. + * This allows external processes to move or truncate the log file without + * disrupting logging. */ + fp = log_to_stdout ? stdout : fopen(server.logfile, "a"); + if (!fp) return; + + if (rawmode) { + fprintf(fp, "%s", msg); + } else { + int off; + struct timeval tv; + pid_t pid = getpid(); + int daylight_active = atomic_load_explicit(&server.daylight_active, memory_order_relaxed); + + gettimeofday(&tv, NULL); + struct tm tm; + nolocks_localtime(&tm, tv.tv_sec, server.timezone, daylight_active); + switch (server.log_timestamp_format) { + case LOG_TIMESTAMP_LEGACY: + off = strftime(buf, sizeof(buf), "%d %b %Y %H:%M:%S.", &tm); + snprintf(buf + off, sizeof(buf) - off, "%03d", (int)tv.tv_usec / 1000); + break; + + case LOG_TIMESTAMP_ISO8601: + off = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.", &tm); + char tzbuf[7]; + formatTimezone(tzbuf, sizeof(tzbuf), server.timezone, server.daylight_active); + snprintf(buf + off, sizeof(buf) - off, "%03d%s", (int)tv.tv_usec / 1000, tzbuf); + break; + + case LOG_TIMESTAMP_MILLISECONDS: + snprintf(buf, sizeof(buf), "%lld", (long long)tv.tv_sec * 1000 + (long long)tv.tv_usec / 1000); + break; + } + int role_index; + if (server.sentinel_mode) { + role_index = 0; /* Sentinel. */ + } else if (pid != server.pid) { + role_index = 1; /* RDB / AOF writing child. */ + } else { + role_index = (server.primary_host ? 2 : 3); /* Replica or Primary. */ + } + switch (server.log_format) { + case LOG_FORMAT_LOGFMT: + if (hasInvalidLogfmtChar(msg)) { + char safemsg[LOG_MAX_LEN]; + filterInvalidLogfmtChar(safemsg, LOG_MAX_LEN, msg); + fprintf(fp, "pid=%d role=%s timestamp=\"%s\" level=%s message=\"%s\"\n", (int)getpid(), roles[role_index], + buf, verbose_level[level], safemsg); + } else { + fprintf(fp, "pid=%d role=%s timestamp=\"%s\" level=%s message=\"%s\"\n", (int)getpid(), roles[role_index], + buf, verbose_level[level], msg); + } + break; + + case LOG_FORMAT_LEGACY: + fprintf(fp, "%d:%c %s %c %s\n", (int)getpid(), role_chars[role_index], buf, c[level], msg); + break; + } + } + fflush(fp); + + if (!log_to_stdout) fclose(fp); + if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); +} + +/* Like serverLogRaw() but with printf-alike support. This is the function that + * is used across the code. The raw version is only used in order to dump + * the INFO output on crash. */ +void _serverLog(int level, const char *fmt, ...) { + va_list ap; + char msg[LOG_MAX_LEN]; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + serverLogRaw(level, msg); +} + +/* Low level logging from signal handler. Should be used with pre-formatted strings. + See serverLogFromHandler. */ +void serverLogRawFromHandler(int level, const char *msg) { + int fd; + int log_to_stdout = server.logfile[0] == '\0'; + char buf[64]; + + if ((level & 0xff) < server.verbosity || (log_to_stdout && server.daemonize)) return; + fd = log_to_stdout ? STDOUT_FILENO : open(server.logfile, O_APPEND | O_CREAT | O_WRONLY, 0644); + if (fd == -1) return; + if (level & LL_RAW) { + if (write(fd, msg, strlen(msg)) == -1) goto err; + } else { + ll2string(buf, sizeof(buf), getpid()); + if (write(fd, buf, strlen(buf)) == -1) goto err; + if (write(fd, ":signal-handler (", 17) == -1) goto err; + ll2string(buf, sizeof(buf), time(NULL)); + if (write(fd, buf, strlen(buf)) == -1) goto err; + if (write(fd, ") ", 2) == -1) goto err; + if (write(fd, msg, strlen(msg)) == -1) goto err; + if (write(fd, "\n", 1) == -1) goto err; + } +err: + if (!log_to_stdout) close(fd); +} + +/* An async-signal-safe version of serverLog. if LL_RAW is not included in level flags, + * The message format is: :signal-handler (