Skip to content

Commit

Permalink
Fixes, and add redisx-cli tool
Browse files Browse the repository at this point in the history
  • Loading branch information
attipaci committed Dec 12, 2024
1 parent 3b08f08 commit 7032e1e
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 15 deletions.
29 changes: 27 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ LDFLAGS += -pthread

# Build for distribution
.PHONY: distro
distro: shared $(DOC_TARGETS)
distro: shared tools $(DOC_TARGETS)

# Build everything...
.PHONY: all
Expand All @@ -48,6 +48,15 @@ shared: $(LIB)/libredisx.so
.PHONY: static
static: $(LIB)/libredisx.a

# Command-line tools
.PHONY: tools
ifdef STATICLINK
tools: static
else
tools: shared
endif
make -f tools.mk

# Run regression tests
.PHONY: test
test:
Expand Down Expand Up @@ -92,6 +101,11 @@ $(LIB)/libredisx.so.$(SO_VERSION): $(SOURCES)
# Static library
$(LIB)/libredisx.a: $(OBJECTS)

# redisx-cli
$(BIN)/redisx-cli: LDFLAGS += -lredisx
$(BIN)/redisx-cli: $(OBJ)/redisx-cli.o


README-redisx.md: README.md
LINE=`sed -n '/\# /{=;q;}' $<` && tail -n +$$((LINE+2)) $< > $@

Expand Down Expand Up @@ -125,6 +139,7 @@ pdf:
# See https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
prefix ?= /usr
exec_prefix ?= $(prefix)
bindir ?= $(exec_prefix)/bin
libdir ?= $(exec_prefix)/lib
includedir ?= $(prefix)/include
datarootdir ?= $(prefix)/share
Expand All @@ -138,7 +153,7 @@ INSTALL_PROGRAM ?= install
INSTALL_DATA ?= install -m 644

.PHONY: install
install: install-libs install-headers install-html
install: install-libs install-bin install-headers install-html

.PHONY: install-libs
install-libs:
Expand All @@ -150,6 +165,16 @@ else
@echo "WARNING! Skipping libs install: needs 'shared' and/or 'static'"
endif

.PHONY: install-bin
install-bin:
ifneq ($(wildcard $(BIN)/*),)
@echo "installing executables to $(DESTDIR)$(bindir)"
install -d $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) -D $(BIN)/* $(DESTDIR)$(bindir)/
else
@echo "WARNING! Skipping bins install: needs 'tools'"
endif

.PHONY: install-headers
install-headers:
@echo "installing headers to $(DESTDIR)$(includedir)"
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Last Updated: 10 December 2024
- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Building RedisX](#building-redisx)
- [Command-line interface (`redisx-cli`)](#redisx-cli)
- [Linking your application against RedisX](#linking)
- [Managing Redis server connections](#managing-redis-server-connections)
- [Simple Redis queries](#simple-redis-queries)
Expand Down Expand Up @@ -144,6 +145,25 @@ Or, to stage the installation (to `/usr`) under a 'build root':

-----------------------------------------------------------------------------

<a name="redisx-cli"></a>
## Command-line interface (`redisx-cli`)

The __RedisX__ library provides its own command-line tool, called `redisx-cli`. It works very similar to `redis-cli`,
expect that it prints replies in JSON format by default (but also supports `--raw` flag for string-type replies).

```bash
$ redisx-cli ping "Hello World"
```
will print:

```bash
"REPLY": "Hello world!"
```

provided it successfully connected to the Redis / Valkey server. (Otherwise it will print an error and a trace).

-----------------------------------------------------------------------------

<a name="linking"></a>
## Linking your application against RedisX

Expand Down
7 changes: 6 additions & 1 deletion build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ $(LIB)/lib%.so:
$(LIB)/%.a:
@make $(LIB)
ar -rc $@ $^
ranlib $@
ranlib $@

# Simple binaries
$(BIN)/%: $(OBJ)/%.o
@make $(BIN)
$(CC) -o $@ $^ $(LDFLAGS)

# Create sub-directories for build targets
dep $(OBJ) $(LIB) $(BIN) apidoc:
Expand Down
108 changes: 108 additions & 0 deletions src/redisx-cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @file
*
* @date Created on Dec 11, 2024
* @author Attila Kovacs
*/

#include <stdio.h>
#include <stdlib.h>
#include <popt.h>
#include <time.h>
#include <math.h>

#include "redisx.h"

int main(int argc, const char *argv[]) {

char *host = "127.0.0.1";
int port = 0;
double timeout = 0.0;
char *password = NULL;
char *user = NULL;
int repeat = 1;
double interval = 1.0;
int dbIndex = 0;
int protocol = -1;
int raw = 0;

struct poptOption options[] = { //
{"host", 'h', POPT_ARG_STRING, &host, 0, "Server hostname (default: 127.0.0.1).", NULL}, //
{"port", 'p', POPT_ARG_INT, &port, 0, "Server port (default: 6379).", NULL}, //
{"timeout", 't', POPT_ARG_DOUBLE, &timeout, 0, "Server connection timeout in seconds (decimals allowed).", NULL}, //
{"pass", 'a', POPT_ARG_STRING, &password, 0, "Password to use when connecting to the server.", NULL}, //
{"user", 'u', POPT_ARG_STRING, &user, 0, "Used to send ACL style 'AUTH username pass'. Needs -a.", NULL}, //
{"repeat", 'r', POPT_ARG_INT, &repeat, 0, "Execute specified command N times.", NULL}, //
{"interval", 'i', POPT_ARG_DOUBLE, &interval, 0, "When -r is used, waits <interval> seconds per command. " //
"It is possible to specify sub-second times like -i 0.1.", NULL}, //
{"db", 'n', POPT_ARG_INT, &dbIndex, 0, "Database number.", NULL}, //
{"resp2", '2', POPT_ARG_NONE, NULL, 2, "Start session in RESP2 protocol mode.", NULL}, //
{"resp3", '3', POPT_ARG_NONE, NULL, 3, "Start session in RESP3 protocol mode.", NULL}, //
{"raw", 0, POPT_ARG_NONE, &raw, 0, "Print raw strings", NULL}, //
POPT_AUTOHELP POPT_TABLEEND //
};

int rc;
char **cmdargs;
int i, nargs = 0;
Redis *redis;

poptContext optcon = poptGetContext("redisx-cli", argc, argv, options, 0);

while((rc = poptGetNextOpt(optcon)) != -1) {
if(rc < -1) {
fprintf(stderr, "ERROR! Bad syntax. Try running with --help to see command-line options.\n");
exit(1);
}

switch(rc) {
case '2': protocol = 2; break;
case '3': protocol = 3; break;
}
}

cmdargs = (char **) poptGetArgs(optcon);

if(!cmdargs) {
poptPrintHelp(optcon, stdout, 0);
return 1;
}

while(cmdargs[nargs]) nargs++;

redis = redisxInit(host);
if(port) redisxSetPort(redis, port);
if(user) redisxSetUser(redis, user);
if(password) redisxSetPassword(redis, password);
if(dbIndex > 0) redisxSelectDB(redis, dbIndex);
if(timeout > 0.0) redisxSetSocketTimeout(redis, (int) ceil(1000 * timeout));
if(protocol > 0) redisxSetProtocol(redis, protocol);

redisxConnect(redis, 0);
xSetDebug(1);

for(i = 0; i < repeat; i++) {
int status;
RESP *reply;

if(i > 0 && interval > 0.0) {
struct timespec sleeptime;
sleeptime.tv_sec = (int) interval;
sleeptime.tv_nsec = 1000000000 * (interval - sleeptime.tv_sec);
nanosleep(&sleeptime, NULL);
}

reply = redisxArrayRequest(redis, cmdargs, NULL, nargs, &status);

if(!status) {
if(redisxIsStringType(reply) && raw) puts((char *) reply->value);
else redisxPrintRESP("REPLY", reply);
}

redisxDestroyRESP(reply);
}

poptFreeContext(optcon);

return 0;
}
1 change: 0 additions & 1 deletion src/redisx-net.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,6 @@ static void rShutdownAsync() {
while(l != NULL) {
ServerLink *next = l->next;
redisxDestroy(l->redis);
free(l);
l = next;
}

Expand Down
30 changes: 20 additions & 10 deletions src/resp.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,10 @@ static XField *respArrayToXField(const char *name, const RESP **component, int n
f = xCreateMixed1DField(name, n, array);

for(i = 0; i < n; i++) {
XField *e = redisxRESP2XField(array[i].name, component[i]);
XField *e;
char idx[20];
sprintf(idx, ".%d", (i + 1));
e = redisxRESP2XField(idx, component[i]);
if(e) {
array[i] = *e;
free(e);
Expand All @@ -611,19 +614,25 @@ static XField *respArrayToXField(const char *name, const RESP **component, int n
if(eType == X_UNKNOWN) return xCreateMixed1DField(name, 0, NULL);

eSize = xElementSizeOf(eType);
array = (char *) calloc(1, n * eSize);

f = xCreate1DField(name, eType, n, array);
rSetRESPType(f, type);
if(!array) return x_trace_null(fn, "field array");
array = (char *) calloc(n, eSize);

for(i = 0; i < n; i++) {
XField *e = redisxRESP2XField("<element>", component[i]);
XField *e;
char idx[20];

sprintf(idx, ".%d", (i + 1));
e = redisxRESP2XField(idx, component[i]);
if(e) {
memcpy(&array[i * eSize], e, sizeof(XField));
memcpy(&array[i * eSize], e->value, eSize);
free(e);
}
}

f = xCreate1DField(name, eType, n, array);
rSetRESPType(f, type);
if(!array) return x_trace_null(fn, "field array");


}

return f;
Expand Down Expand Up @@ -654,7 +663,7 @@ static XField *respMap2XField(const char *name, const RedisMapEntry *map, int n)
sprintf(idx, ".%d", ++nNonString);
if(!nonstring)
nonstring = xCreateStruct();
xSetSubstruct(nonstring, xStringCopyOf(idx), sub);
xSetSubstruct(nonstring, idx, sub);
}
}

Expand Down Expand Up @@ -765,11 +774,12 @@ int redisxPrintRESP(const char *name, const RESP *resp) {
char *json = redisxRESP2JSON(name, resp);

if(json) {
printf("%s", json);
puts(json);
free(json);
}
else printf("\"%s\": null\n", name);

return X_SUCCESS;
}


2 changes: 1 addition & 1 deletion test/src/test-hello.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ int main() {

resp = redisxGetHelloData(redis);
json = redisxRESP2JSON("server_properties", resp);
printf("%s", json ? json : "<null>");
puts(json ? json : "<null>");
free(json);
redisxDestroyRESP(resp);

Expand Down
9 changes: 9 additions & 0 deletions tools.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
include config.mk

LDFLAGS += -L$(LIB) -lredisx -lpopt
LD_LIBRARY_PATH := $(LIB):$(LD_LIBRARY_PATH)

# Top level make targets...
all: $(BIN)/redisx-cli

include build.mk

0 comments on commit 7032e1e

Please sign in to comment.