From 26794504899d3bffa279fd7a54674536d9e00efb Mon Sep 17 00:00:00 2001 From: John Sully Date: Tue, 21 May 2019 15:12:12 -0400 Subject: [PATCH] Implement MOTD feature in keydb-cli Former-commit-id: a6c56416fc1bf09f1ecbae45195290209aa93c89 --- src/Makefile | 2 +- src/redis-cli.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c197d0927..0d5801d45 100644 --- a/src/Makefile +++ b/src/Makefile @@ -264,7 +264,7 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME) # keydb-cli $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) - $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) + $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) -lcurl # keydb-benchmark $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) diff --git a/src/redis-cli.c b/src/redis-cli.c index ab3de2e73..50e2e8480 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */ @@ -6532,6 +6534,116 @@ static void intrinsicLatencyMode(void) { } } +/*------------------------------------------------------------------------------ + * Message of the day + *--------------------------------------------------------------------------- */ +#ifndef NO_MOTD +#include + +static const char *szMotdCachePath() +{ + static sds sdsMotdCachePath = NULL; + if (sdsMotdCachePath != NULL) + return sdsMotdCachePath; + + struct passwd *pw = getpwuid(getuid()); + if (pw == NULL) + return ""; + const char *homedir = pw->pw_dir; + sdsMotdCachePath = sdsnew(homedir); + sdsMotdCachePath = sdscat(sdsMotdCachePath, "/.keydb-cli-motd"); + return sdsMotdCachePath; +} +static size_t motd_write_callback(void *ptr, size_t size, size_t nmemb, sds *str) +{ + *str = sdscatlen(*str, ptr, size*nmemb); + return (size*nmemb); +} + +static char *fetchMOTDFromCache() +{ + struct stat attrib; + if (stat(szMotdCachePath(), &attrib) != 0) + return NULL; + time_t t = attrib.st_mtim.tv_sec; + time_t now = time(NULL); + if ((now - t) < 14400) + { + // If our cache was updated no more than 4 hours ago use it instead of fetching the MOTD + FILE *pf = fopen(szMotdCachePath(), "rb"); + if (pf == NULL) + return NULL; + fseek(pf, 0L, SEEK_END); + long cb = ftell(pf); + fseek(pf, 0L, SEEK_SET); // rewind + sds str = sdsnewlen(NULL, cb); + size_t cbRead = fread(str, 1, cb, pf); + fclose(pf); + if ((long)cbRead != cb) + { + sdsfree(str); + return NULL; + } + return str; + } + return NULL; +} + +static void setMOTDCache(const char *sz) +{ + FILE *pf = fopen(szMotdCachePath(), "wb"); + size_t celem = fwrite(sz, strlen(sz), 1, pf); + (void)celem; // best effort + fclose(pf); +} + +static char *fetchMOTD() +{ + sds str; + CURL *curl; + CURLcode res; + + /* First try and get the string from the cache */ + str = fetchMOTDFromCache(); + if (str != NULL) + return str; + + str = sdsnew(""); + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "http://api.keydb.dev/motd/motd.txt"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // follow redirects + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2); // take no more than two seconds + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, motd_write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &str); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if(res != CURLE_OK) + { + sdsfree(str); + str = NULL; + } + + /* always cleanup */ + curl_easy_cleanup(curl); + + if (str != NULL) + setMOTDCache(str); + } + return str; +} + +#else + +static char *fetchMOTD() +{ + return NULL; +} + +#endif + /*------------------------------------------------------------------------------ * Program main() *--------------------------------------------------------------------------- */ @@ -6700,6 +6812,15 @@ int main(int argc, char **argv) { /* Start interactive mode when no command is provided */ if (argc == 0 && !config.eval) { + /* Show the message of the day if we are interactive */ + if (config.output == OUTPUT_STANDARD) { + char *szMotd = fetchMOTD(); + if (szMotd != NULL) { + printf("Message of the day:\n %s\n", szMotd); + sdsfree(szMotd); + } + } + /* Ignore SIGPIPE in interactive mode to force a reconnect */ signal(SIGPIPE, SIG_IGN); @@ -6711,6 +6832,7 @@ int main(int argc, char **argv) { /* Otherwise, we have some arguments to execute */ if (cliConnect(0) != REDIS_OK) exit(1); + if (config.eval) { return evalMode(argc,argv); } else {