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 507b1f4
Show file tree
Hide file tree
Showing 14 changed files with 348 additions and 58 deletions.
16 changes: 10 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,25 @@ jobs:
steps:
- name: Check out RedisX
uses: actions/checkout@v4

- name: Check out xchange
uses: actions/checkout@v4
with:
repository: Smithsonian/xchange
path: xchange


- name: Install build dependencies
run: sudo apt-get install libpopt-dev

- name: Build static library
run: make static

- name: Build xchange dependency
run: make -C xchange shared

- name: Build shared library
run: make shared



- name: Build tools
run: make tools

4 changes: 2 additions & 2 deletions .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:
env:
CC: gcc
steps:
- name: install doxygen
run: sudo apt-get install doxygen
- name: install build deps
run: sudo apt-get install doxygen libpopt-dev

- name: Check out RedisX
uses: actions/checkout@v4
Expand Down
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`,
except that it supports a subset of the options (so far...).

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

```bash
"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
5 changes: 3 additions & 2 deletions include/redisx.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ enum redisx_protocol {
*/
typedef struct RESP {
enum resp_type type; ///< RESP type; RESP_ARRAY, RESP_INT ...
int n; ///< Either the integer value of a RESP_INT or a RESP3_BOOLEAN response, or the
int n; ///< Either the integer value of a RESP_INT or a RESP3_BOOLEAN response, or the
///< dimension of the value field.
void *value; ///< Pointer to text (char *) content or to an array of components
///< (RESP**) or (RedisMapEntry *), or else a pointer to a `double`, depending
Expand Down Expand Up @@ -436,7 +436,8 @@ boolean redisxIsEqualRESP(const RESP *a, const RESP *b);
int redisxSplitText(RESP *resp, char **text);
XField *redisxRESP2XField(const char *name, const RESP *resp);
char *redisxRESP2JSON(const char *name, const RESP *resp);
int redisxPrintRESP(const char *name, const RESP *resp);
int redisxPrintRESP(const RESP *resp);
int redisxPrintJSON(const char *name, const RESP *resp);

RedisMapEntry *redisxGetMapEntry(const RESP *map, const RESP *key);
RedisMapEntry *redisxGetKeywordEntry(const RESP *map, const char *key);
Expand Down
113 changes: 113 additions & 0 deletions src/redisx-cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @date Created on Dec 11, 2024
* @author Attila Kovacs
*/

#define _POSIX_C_SOURCE 199309L ///< for nanosleep()

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

#include "redisx.h"

#define FORMAT_JSON 1
#define FORMAT_RAW 2

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 format = 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}, //
{"json", 0, POPT_ARG_NONE, NULL, 'j', "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;
case 'j': format = FORMAT_JSON; break;
case 'r': format = FORMAT_RAW; 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 = X_SUCCESS;
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(format == FORMAT_JSON) redisxPrintJSON("REPLY", reply);
else redisxPrintRESP(reply);
}

redisxDestroyRESP(reply);
}

poptFreeContext(optcon);

return 0;
}
4 changes: 2 additions & 2 deletions src/redisx-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ RESP *redisxReadReplyAsync(RedisClient *cl) {
}


for(i=0; i<resp->n; i++) {
for(i = 0; i < resp->n; i++) {
RESP *r = redisxReadReplyAsync(cl); // Always read RESP even if we don't have storage for it...
if(component) component[i] = r;
else redisxDestroyRESP(r);
Expand Down Expand Up @@ -970,7 +970,7 @@ RESP *redisxReadReplyAsync(RedisClient *cl) {
dict = (RedisMapEntry *) calloc(resp->n, sizeof(RedisMapEntry));
x_check_alloc(dict);

for(i=0; i<resp->n; i++) {
for(i = 0; i < resp->n; i++) {
RedisMapEntry *e = &dict[i];
e->key = redisxReadReplyAsync(cl);
e->value = redisxReadReplyAsync(cl);
Expand Down
5 changes: 2 additions & 3 deletions 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 Expand Up @@ -1127,7 +1126,7 @@ boolean redisxIsConnected(Redis *redis) {
*
*/
void *RedisPipelineListener(void *pRedis) {
static int counter, lastError;
static long counter, lastError;

Redis *redis = (Redis *) pRedis;
RedisPrivate *p;
Expand Down Expand Up @@ -1187,7 +1186,7 @@ void *RedisPipelineListener(void *pRedis) {

} // <-- End of listener loop...

xvprintf("Redis-X> Stopped processing pipeline responses (%d processed)...\n", counter);
xvprintf("Redis-X> Stopped processing pipeline responses (%ld processed)...\n", counter);

rConfigLock(redis);
// If we are the current listener thread, then mark the listener as disabled.
Expand Down
6 changes: 3 additions & 3 deletions src/redisx-sub.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ static void rNotifyConsumers(Redis *redis, char *pattern, char *channel, char *m
*
*/
void *RedisSubscriptionListener(void *pRedis) {
static int counter, lastError;
static long counter, lastError;

Redis *redis = (Redis *) pRedis;
RedisPrivate *p;
Expand Down Expand Up @@ -585,7 +585,7 @@ void *RedisSubscriptionListener(void *pRedis) {

// Check for NULL component (all except last -- the payload -- which may be NULL)
for(i=reply->n-1; --i >= 0; ) if(component[i] == NULL) {
fprintf(stderr, "WARNING! Redis-X : subscriber NULL in component %d.\n", (i+1));
fprintf(stderr, "WARNING! Redis-X : subscriber NULL in component %d\n", (i+1));
continue;
}

Expand Down Expand Up @@ -625,7 +625,7 @@ void *RedisSubscriptionListener(void *pRedis) {
rConfigUnlock(redis);
}

xvprintf("Redis-X> Stopped processing subscriptions (%d processed)...\n", counter);
xvprintf("Redis-X> Stopped processing subscriptions (%ld processed)...\n", counter);

redisxDestroyRESP(reply);

Expand Down
Loading

0 comments on commit 507b1f4

Please sign in to comment.