/* * 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. */ /* * Copyright (c) Valkey Contributors * All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ #include "server.h" #include "connection.h" #include "monotonic.h" #include "cluster.h" #include "cluster_slot_stats.h" #include "cluster_migrateslots.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 "tls.h" #include "sds.h" #include "module.h" #include "scripting_engine.h" #include "util.h" #include "eval.h" #include "trace/trace_commands.h" #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); void addReplyCommandInfo(client *c, struct serverCommand *cmd); /*============================ 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"; const char *logfmt_format = "pid=%d role=%s timestamp=\"%s\" level=%s message=\"%s\"\n"; 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, logfmt_format, (int)getpid(), roles[role_index], buf, verbose_level[level], safemsg); } else { fprintf(fp, logfmt_format, (int)getpid(), roles[role_index], buf, verbose_level[level], msg); } break; case LOG_FORMAT_JSON: { sds jsonmsg = escapeJsonString(sdsempty(), msg, strlen(msg)); fprintf(fp, "{\"pid\":%d,\"role\":\"%s\",\"timestamp\":\"%s\",\"level\":\"%s\",\"message\":%s}\n", (int)getpid(), roles[role_index], buf, verbose_level[level], jsonmsg); sdsfree(jsonmsg); 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 (