/* mcsdb - LGPLv3 - Copyright 2011-2022 - pancake */ #include "mcsdb.h" #include #include static inline void strtolower(char *s) { int i = 0; for (i = 0; s[i]; i++) { s[i] = tolower (s[i]); } } static void handle_get(McSdb *ms, int fd, char *key, int smode) { ut64 exptime = 0LL; ut32 cas; int n = 0; char *s, *k, *K = key; if (!key) { net_printf (fd, "ERROR\r\n"); return; } do { k = strchr (K, ' '); if (k) *k = 0; s = mcsdb_get (ms, K, &exptime, &cas); if (s) { if (smode) { net_printf (fd, "VALUE %s %llu %d %d\r\n", K, exptime, (int)strlen (s), cas); } else { net_printf (fd, "VALUE %s %llu %d\r\n", K, exptime, (int)strlen (s)); } net_printf (fd, "%s\r\n", s); free (s); n++; } if (k) K = k + 1; } while (k); net_printf (fd, "END\r\n"); // no elements found } int protocol_handle(McSdb *ms, McSdbClient *c, char *buf) { struct rusage ru; int fd, ret, reply, stored = 1; char *b, *p, *q, *cmd = buf, *key = NULL; int flags = 0, bytes = 0; ut64 n = 0; ut32 cmdhash = 0; if (!c || !*buf) { // never happenz //net_printf (fd, "ERROR\r\n"); return 0; } fd = c->fd; //printf ("----> mode=%d buf=(%s)\n", c->mode, buf); if (c->mode == 1) { b = buf; b[c->len-1] = 0; switch (c->cmdhash) { case MCSDB_CMD_SET: mcsdb_set (ms, c->key, b, c->exptime, 0); break; case MCSDB_CMD_APPEND: mcsdb_append (ms, c->key, c->exptime, b); break; case MCSDB_CMD_ADD: stored = mcsdb_add (ms, c->key, c->exptime, b); break; case MCSDB_CMD_PREPEND: mcsdb_prepend (ms, c->key, c->exptime, b); break; case MCSDB_CMD_REPLACE: stored = mcsdb_replace (ms, c->key, c->exptime, b); break; default: break; } net_printf (fd, stored? "STORED\r\n": "NOT_STORED\r\n"); c->mode = 0; c->idx = c->next; c->cmdhash = 0; return 1; } p = strchr (buf, ' '); if (p) { *p = 0; key = p + 1; if ((p=strchr (key, ' '))) *p = 0; strncpy (c->key, key, sizeof (c->key)-1); // XXX overflow if (p) { *p = ' '; p++; } } strtolower (cmd); cmdhash = sdb_hash (cmd); switch (cmdhash) { case MCSDB_CMD_FLUSH_ALL: mcsdb_flush (ms); net_printf (fd, "OK\r\n"); break; case MCSDB_CMD_VERBOSITY: // not yet implemented net_printf (fd, "OK\r\n"); break; case MCSDB_CMD_QUIT: return -1; case MCSDB_CMD_GET: if (key) // ignore error handle_get (ms, fd, key, 0); break; case MCSDB_CMD_GETS: handle_get (ms, fd, key, 1); break; case MCSDB_CMD_TOUCH: /* key exptime [noreply] */ if (!key || !p) { net_printf (fd, "ERROR\r\n"); return 0; } sscanf (p, "%llu", &n); net_printf (fd, mcsdb_touch (ms, key, n)? "TOUCHED\r\n": "NOT_TOUCHED\r\n"); break; case MCSDB_CMD_INCR: case MCSDB_CMD_DECR: if (!key || !p) { net_printf (fd, "ERROR\r\n"); return 0; } n = strtoull (p, &q, 10); if (q) { p--; *p=0; if (cmdhash == MCSDB_CMD_INCR) { p = mcsdb_incr (ms, key, n); } else { p = mcsdb_decr (ms, key, n); } if (p) { net_printf (fd, "%s\r\n", p); free (p); } else net_printf (fd, "NOT_FOUND\r\n"); } else net_printf (fd, "ERROR\r\n"); break; case MCSDB_CMD_STATS: net_printf (fd, "STAT pid %d\r\n", getpid ()); net_printf (fd, "STAT version "MCSDB_VERSION"\r\n"); net_printf (fd, "STAT pointer_size %u\r\n", (int)sizeof (void*)*8); net_printf (fd, "STAT time %llu\r\n", sdb_now ()); net_printf (fd, "STAT uptime %llu\r\n", sdb_now ()-ms->time); #ifndef __wasi__ getrusage (0, &ru); net_printf (fd, "STAT rusage_user %u.%u\r\n", (ut32)ru.ru_utime.tv_sec, (ut32)ru.ru_utime.tv_usec); net_printf (fd, "STAT rusage_system %u.%u\r\n", (ut32)ru.ru_stime.tv_sec, (ut32)ru.ru_stime.tv_usec); #endif net_printf (fd, "STAT curr_connections %u\r\n", ms->nfds-1); net_printf (fd, "STAT total_connections %u\r\n", ms->tfds); net_printf (fd, "STAT cmd_get %llu\r\n", ms->gets); net_printf (fd, "STAT cmd_set %llu\r\n", ms->sets); net_printf (fd, "STAT get_hits %llu\r\n", ms->hits); net_printf (fd, "STAT get_misses %llu\r\n", ms->misses); net_printf (fd, "STAT evictions %llu\r\n", ms->evictions); net_printf (fd, "STAT bytes_read %llu\r\n", ms->bread); net_printf (fd, "STAT bytes_written %llu\r\n", ms->bwrite); // ?? net_printf (fd, "STAT limit_maxbytes 0\r\n"); net_printf (fd, "STAT threads 1\r\n"); net_printf (fd, "END\r\n"); break; case MCSDB_CMD_VERSION: net_printf (fd, "VERSION 0.1\r\n"); break; case MCSDB_CMD_DELETE: if (!key) return 0; p = strchr (key, ' '); if (p) { *p = 0; if (!memcmp (p+1, "noreply", 7)) { reply = 0; } else { reply = 1; } } else { reply = 1; } c->exptime = 0LL; sscanf (key, "%llu", &c->exptime); ret = mcsdb_remove (ms, key, c->exptime); if (reply) { if (ret) { net_printf (fd, "DELETED\r\n"); } else { net_printf (fd, "NOT_FOUND\r\n"); } } break; case MCSDB_CMD_ADD: case MCSDB_CMD_SET: case MCSDB_CMD_APPEND: case MCSDB_CMD_PREPEND: case MCSDB_CMD_REPLACE: if (!key || !p) {// || !((p=strchr (key, ' ')))) { net_printf (fd, "ERROR\r\n"); return 0; } ret = sscanf (p, "%d %llu %d", &flags, &c->exptime, &bytes); if (ret != 3) { net_printf (fd, "ERROR parsing (%s)\r\n", p+1); return 0; } if (bytes<1) { net_printf (fd, "CLIENT_ERROR invalid length\r\n"); net_printf (fd, "ERROR\r\n"); return 0; } c->mode = 1; // read N bytes //c->next = 0; c->idx = c->next; c->len = bytes+1; c->cmdhash = cmdhash; // continue on mode 1 return 1; case MCSDB_CMD_CAS: // check and set // cas [noreply]\r\n printf ("NOT YET IMPLEMENTED\n"); return 0; default: net_printf (fd, "ERROR\r\n"); } return 0; }