On Tue, Aug 30, 2022 at 09:27:10PM +0200, mwilck@xxxxxxxx wrote: > From: Martin Wilck <mwilck@xxxxxxxx> > > Add a new small program, multipathc, that acts as interactive > uxsock client for multipathd. multipathc is the only program > that has an interactive mode and can thus link to either libreadline > or libedit for command line history. All code depending on libreadline > is moved from uxclnt.c and cli.c to multipathc.c. > > This patch breaks multipathd's interactive mode. It will be restored > in the next patch. > > As multipathc doesn't link to libmultipath, it can link to libreadline > without license conflict. > > Signed-off-by: Martin Wilck <mwilck@xxxxxxxx> > Reviewed-by: Benjamin Marzinski <bmarzins@xxxxxxxxxx> Reviewed-by: Benjamin Marzinski <bmarzins@xxxxxxxxxx> > --- > .gitignore | 1 + > multipathd/Makefile | 24 ++-- > multipathd/cli.c | 130 ++------------------ > multipathd/cli.h | 7 +- > multipathd/multipathc.c | 259 ++++++++++++++++++++++++++++++++++++++++ > multipathd/uxclnt.c | 131 +------------------- > 6 files changed, 299 insertions(+), 253 deletions(-) > create mode 100644 multipathd/multipathc.c > > diff --git a/.gitignore b/.gitignore > index b88608c..821c3e6 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -13,6 +13,7 @@ cscope.out > kpartx/kpartx > multipath/multipath > multipathd/multipathd > +multipathd/multipathc > mpathpersist/mpathpersist > abi.tar.gz > abi > diff --git a/multipathd/Makefile b/multipathd/Makefile > index 7128510..19ab2e9 100644 > --- a/multipathd/Makefile > +++ b/multipathd/Makefile > @@ -27,12 +27,12 @@ LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \ > > > ifeq ($(READLINE),libedit) > -CPPFLAGS += -DUSE_LIBEDIT > -LIBDEPS += -ledit > +RL_CPPFLAGS = -DUSE_LIBEDIT > +RL_LIBDEPS += -ledit > endif > ifeq ($(READLINE),libreadline) > -CPPFLAGS += -DUSE_LIBREADLINE > -LIBDEPS += -lreadline > +RL_CPPFLAGS += -DUSE_LIBREADLINE > +RL_LIBDEPS += -lreadline > endif > > ifdef SYSTEMD > @@ -50,6 +50,8 @@ endif > OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \ > dmevents.o init_unwinder.o > > +CLI_OBJS = multipathc.o cli.o > + > ifeq ($(FPIN_SUPPORT),1) > OBJS += fpin_handlers.o > endif > @@ -57,18 +59,26 @@ endif > > > EXEC = multipathd > +CLI = multipathc > > -all : $(EXEC) > +all : $(EXEC) $(CLI) > > $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so > $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS) > > +multipathc.o: multipathc.c > + $(CC) $(CPPFLAGS) $(RL_CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $< > + > +$(CLI): $(CLI_OBJS) > + $(CC) $(CFLAGS) $(CLI_OBJS) $(LDFLAGS) -o $@ $(CLI_LIBDEPS) $(RL_LIBDEPS) > + > cli_handlers.o: cli_handlers.c > $(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $< > > install: > $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) > $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) > + $(INSTALL_PROGRAM) -m 755 $(CLI) $(DESTDIR)$(bindir) > ifdef SYSTEMD > $(INSTALL_PROGRAM) -d $(DESTDIR)$(unitdir) > $(INSTALL_PROGRAM) -m 644 $(EXEC).service $(DESTDIR)$(unitdir) > @@ -78,13 +88,13 @@ endif > $(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir) > > uninstall: > - $(RM) $(DESTDIR)$(bindir)/$(EXEC) > + $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(DESTDIR)$(bindir)/$(CLI) > $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8 > $(RM) $(DESTDIR)$(unitdir)/$(EXEC).service > $(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket > > clean: dep_clean > - $(RM) core *.o $(EXEC) > + $(RM) core *.o $(EXEC) $(CLI) > > include $(wildcard $(OBJS:.o=.d)) > > diff --git a/multipathd/cli.c b/multipathd/cli.c > index cc56950..d1bfeee 100644 > --- a/multipathd/cli.c > +++ b/multipathd/cli.c > @@ -11,12 +11,6 @@ > #include "parser.h" > #include "util.h" > #include "version.h" > -#ifdef USE_LIBEDIT > -#include <editline/readline.h> > -#endif > -#ifdef USE_LIBREADLINE > -#include <readline/readline.h> > -#endif > > #include "mpath_cmd.h" > #include "cli.h" > @@ -26,6 +20,16 @@ > static vector keys; > static vector handlers; > > +vector get_keys(void) > +{ > + return keys; > +} > + > +vector get_handlers(void) > +{ > + return handlers; > +} > + > static struct key * > alloc_key (void) > { > @@ -225,8 +229,7 @@ load_keys (void) > return 0; > } > > -static struct key * > -find_key (const char * str) > +struct key *find_key (const char * str) > { > int i; > int len, klen; > @@ -323,8 +326,7 @@ out: > return r; > } > > -static uint64_t > -fingerprint(const struct _vector *vec) > +uint64_t fingerprint(const struct _vector *vec) > { > int i; > uint64_t fp = 0; > @@ -458,111 +460,3 @@ void cli_exit(void) > free_keys(keys); > keys = NULL; > } > - > -#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > -static int > -key_match_fingerprint (struct key * kw, uint64_t fp) > -{ > - if (!fp) > - return 0; > - > - return ((fp & kw->code) == kw->code); > -} > - > -/* > - * This is the readline completion handler > - */ > -char * > -key_generator (const char * str, int state) > -{ > - static int index, len, has_param; > - static uint64_t rlfp; > - struct key * kw; > - int i; > - struct handler *h; > - vector v = NULL; > - > - if (!state) { > - index = 0; > - has_param = 0; > - rlfp = 0; > - len = strlen(str); > - int r = get_cmdvec(rl_line_buffer, &v); > - /* > - * If a word completion is in progress, we don't want > - * to take an exact keyword match in the fingerprint. > - * For ex "show map[tab]" would validate "map" and discard > - * "maps" as a valid candidate. > - */ > - if (v && len) > - vector_del_slot(v, VECTOR_SIZE(v) - 1); > - /* > - * Clean up the mess if we dropped the last slot of a 1-slot > - * vector > - */ > - if (v && !VECTOR_SIZE(v)) { > - vector_free(v); > - v = NULL; > - } > - /* > - * If last keyword takes a param, don't even try to guess > - */ > - if (r == EINVAL) { > - has_param = 1; > - return (strdup("(value)")); > - } > - /* > - * Compute a command fingerprint to find out possible completions. > - * Once done, the vector is useless. Free it. > - */ > - if (v) { > - rlfp = fingerprint(v); > - free_keys(v); > - } > - } > - /* > - * No more completions for parameter placeholder. > - * Brave souls might try to add parameter completion by walking paths and > - * multipaths vectors. > - */ > - if (has_param) > - return ((char *)NULL); > - /* > - * Loop through keywords for completion candidates > - */ > - vector_foreach_slot_after (keys, kw, index) { > - if (!strncmp(kw->str, str, len)) { > - /* > - * Discard keywords already in the command line > - */ > - if (key_match_fingerprint(kw, rlfp)) { > - struct key * curkw = find_key(str); > - if (!curkw || (curkw != kw)) > - continue; > - } > - /* > - * Discard keywords making syntax errors. > - * > - * nfp is the candidate fingerprint we try to > - * validate against all known command fingerprints. > - */ > - uint64_t nfp = rlfp | kw->code; > - vector_foreach_slot(handlers, h, i) { > - if (!rlfp || ((h->fingerprint & nfp) == nfp)) { > - /* > - * At least one full command is > - * possible with this keyword : > - * Consider it validated > - */ > - index++; > - return (strdup(kw->str)); > - } > - } > - } > - } > - /* > - * No more candidates > - */ > - return ((char *)NULL); > -} > -#endif > diff --git a/multipathd/cli.h b/multipathd/cli.h > index 2a0c102..cb5bbe2 100644 > --- a/multipathd/cli.h > +++ b/multipathd/cli.h > @@ -151,8 +151,9 @@ void free_keys (vector vec); > void free_handlers (void); > int cli_init (void); > void cli_exit(void); > -#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > -char *key_generator (const char * str, int state); > -#endif > +uint64_t fingerprint(const struct _vector *vec); > +vector get_keys(void); > +vector get_handlers(void); > +struct key *find_key (const char * str); > > #endif /* _CLI_H_ */ > diff --git a/multipathd/multipathc.c b/multipathd/multipathc.c > new file mode 100644 > index 0000000..a4f9023 > --- /dev/null > +++ b/multipathd/multipathc.c > @@ -0,0 +1,259 @@ > +/* > + * Copyright (c) 2022 SUSE LLC > + * SPDX-License-Identifier: GPL-3.0-or-later > + */ > +#include <string.h> > +#include <stdio.h> > +#include <stdbool.h> > +#include <unistd.h> > +#include <errno.h> > +#include <ctype.h> > + > +#include "mpath_cmd.h" > +#include "uxclnt.h" > +#include "vector.h" > +#include "uxsock.h" > +#include "util.h" > +#include "cli.h" > + > +#ifdef USE_LIBEDIT > +#include <editline/readline.h> > +#endif > +#ifdef USE_LIBREADLINE > +#include <readline/readline.h> > +#include <readline/history.h> > +#endif > +/* > + * Versions of libedit prior to 2016 were using a wrong > + * prototype for rl_completion_entry_function in readline.h. > + * Internally, libedit casts this to the correct type > + * (char *)(*)(const char *, int). > + * So we simply cast to the wrong prototype here. > + * See http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libedit/readline/readline.h.diff?r1=1.34&r2=1.35 > + * Unfortunately, this change isn't reflected in the libedit version. > + */ > +#ifdef BROKEN_RL_COMPLETION_FUNC > +#define RL_COMP_ENTRY_CAST(x) ((int (*)(const char *, int)) (x)) > +#else > +#define RL_COMP_ENTRY_CAST(x) (x) > +#endif > + > +#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > +static int > +key_match_fingerprint (struct key * kw, uint64_t fp) > +{ > + if (!fp) > + return 0; > + > + return ((fp & kw->code) == kw->code); > +} > + > +/* > + * This is the readline completion handler > + */ > +char * > +key_generator (const char * str, int state) > +{ > + static int index, len, has_param; > + static uint64_t rlfp; > + struct key * kw; > + int i; > + struct handler *h; > + vector v = NULL; > + const vector keys = get_keys(); > + const vector handlers = get_handlers(); > + > + if (!state) { > + index = 0; > + has_param = 0; > + rlfp = 0; > + len = strlen(str); > + int r = get_cmdvec(rl_line_buffer, &v); > + /* > + * If a word completion is in progress, we don't want > + * to take an exact keyword match in the fingerprint. > + * For ex "show map[tab]" would validate "map" and discard > + * "maps" as a valid candidate. > + */ > + if (v && len) > + vector_del_slot(v, VECTOR_SIZE(v) - 1); > + /* > + * Clean up the mess if we dropped the last slot of a 1-slot > + * vector > + */ > + if (v && !VECTOR_SIZE(v)) { > + vector_free(v); > + v = NULL; > + } > + /* > + * If last keyword takes a param, don't even try to guess > + */ > + if (r == EINVAL) { > + has_param = 1; > + return (strdup("(value)")); > + } > + /* > + * Compute a command fingerprint to find out possible completions. > + * Once done, the vector is useless. Free it. > + */ > + if (v) { > + rlfp = fingerprint(v); > + free_keys(v); > + } > + } > + /* > + * No more completions for parameter placeholder. > + * Brave souls might try to add parameter completion by walking paths and > + * multipaths vectors. > + */ > + if (has_param) > + return ((char *)NULL); > + /* > + * Loop through keywords for completion candidates > + */ > + vector_foreach_slot_after (keys, kw, index) { > + if (!strncmp(kw->str, str, len)) { > + /* > + * Discard keywords already in the command line > + */ > + if (key_match_fingerprint(kw, rlfp)) { > + struct key * curkw = find_key(str); > + if (!curkw || (curkw != kw)) > + continue; > + } > + /* > + * Discard keywords making syntax errors. > + * > + * nfp is the candidate fingerprint we try to > + * validate against all known command fingerprints. > + */ > + uint64_t nfp = rlfp | kw->code; > + vector_foreach_slot(handlers, h, i) { > + if (!rlfp || ((h->fingerprint & nfp) == nfp)) { > + /* > + * At least one full command is > + * possible with this keyword : > + * Consider it validated > + */ > + index++; > + return (strdup(kw->str)); > + } > + } > + } > + } > + /* > + * No more candidates > + */ > + return ((char *)NULL); > +} > +#endif > + > +static void print_reply(char *s) > +{ > + if (!s) > + return; > + > + if (isatty(1)) { > + printf("%s", s); > + return; > + } > + /* strip ANSI color markers */ > + while (*s != '\0') { > + if ((*s == 0x1b) && (*(s+1) == '[')) > + while ((*s++ != 'm') && (*s != '\0')) {}; > + putchar(*s++); > + } > +} > + > +static int need_quit(char *str, size_t len) > +{ > + char *ptr, *start; > + size_t trimed_len = len; > + > + for (ptr = str; trimed_len && isspace(*ptr); > + trimed_len--, ptr++) > + ; > + > + start = ptr; > + > + for (ptr = str + len - 1; trimed_len && isspace(*ptr); > + trimed_len--, ptr--) > + ; > + > + if ((trimed_len == 4 && !strncmp(start, "exit", 4)) || > + (trimed_len == 4 && !strncmp(start, "quit", 4))) > + return 1; > + > + return 0; > +} > + > +/* > + * process the client > + */ > +static void process(int fd, unsigned int timeout) > +{ > + > +#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > + rl_readline_name = "multipathd"; > + rl_completion_entry_function = RL_COMP_ENTRY_CAST(key_generator); > +#endif > + > + cli_init(); > + for(;;) > + { > + char *line __attribute__((cleanup(cleanup_charp))) = NULL; > + char *reply __attribute__((cleanup(cleanup_charp))) = NULL; > + ssize_t llen; > + int ret; > + > +#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > + line = readline("multipathd> "); > + if (!line) > + break; > + llen = strlen(line); > + if (!llen) > + continue; > +#else > + size_t lsize = 0; > + > + fputs("multipathd> ", stdout); > + errno = 0; > + llen = getline(&line, &lsize, stdin); > + if (llen == -1) { > + if (errno != 0) > + fprintf(stderr, "Error in getline: %m"); > + break; > + } > + if (!llen || !strcmp(line, "\n")) > + continue; > +#endif > + > + if (need_quit(line, llen)) > + break; > + > + if (send_packet(fd, line) != 0) > + break; > + ret = recv_packet(fd, &reply, timeout); > + if (ret != 0) > + break; > + > + print_reply(reply); > + > +#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > + if (line && *line) > + add_history(line); > +#endif > + } > +} > + > +int main (void) > +{ > + int fd = mpath_connect(); > + > + if (fd == -1) > + return 1; > + > + process(fd, DEFAULT_REPLY_TIMEOUT + 100); > + mpath_disconnect(fd); > + return 0; > +} > diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c > index deff565..c3ae5db 100644 > --- a/multipathd/uxclnt.c > +++ b/multipathd/uxclnt.c > @@ -5,133 +5,14 @@ > * Copyright (c) 2005 Benjamin Marzinski, Redhat > */ > #include <stdio.h> > +#include <string.h> > #include <stdlib.h> > -#include <unistd.h> > -#include <stdarg.h> > -#include <ctype.h> > -#include <fcntl.h> > #include <errno.h> > -#include <sys/ioctl.h> > -#include <sys/types.h> > -#include <sys/socket.h> > -#include <sys/un.h> > -#include <poll.h> > - > -#ifdef USE_LIBEDIT > -#include <editline/readline.h> > -#endif > -#ifdef USE_LIBREADLINE > -#include <readline/readline.h> > -#include <readline/history.h> > -#endif > > #include "mpath_cmd.h" > #include "uxsock.h" > -#include "defaults.h" > - > -#include "vector.h" > -#include "util.h" > -#include "cli.h" > #include "uxclnt.h" > > -static void print_reply(char *s) > -{ > - if (!s) > - return; > - > - if (isatty(1)) { > - printf("%s", s); > - return; > - } > - /* strip ANSI color markers */ > - while (*s != '\0') { > - if ((*s == 0x1b) && (*(s+1) == '[')) > - while ((*s++ != 'm') && (*s != '\0')) {}; > - putchar(*s++); > - } > -} > - > -static int need_quit(char *str, size_t len) > -{ > - char *ptr, *start; > - size_t trimed_len = len; > - > - for (ptr = str; trimed_len && isspace(*ptr); > - trimed_len--, ptr++) > - ; > - > - start = ptr; > - > - for (ptr = str + len - 1; trimed_len && isspace(*ptr); > - trimed_len--, ptr--) > - ; > - > - if ((trimed_len == 4 && !strncmp(start, "exit", 4)) || > - (trimed_len == 4 && !strncmp(start, "quit", 4))) > - return 1; > - > - return 0; > -} > - > -/* > - * process the client > - */ > -static void process(int fd, unsigned int timeout) > -{ > - > -#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > - rl_readline_name = "multipathd"; > - rl_completion_entry_function = key_generator; > -#endif > - > - cli_init(); > - for(;;) > - { > - char *line __attribute__((cleanup(cleanup_charp))) = NULL; > - char *reply __attribute__((cleanup(cleanup_charp))) = NULL; > - ssize_t llen; > - int ret; > - > -#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > - line = readline("multipathd> "); > - if (!line) > - break; > - llen = strlen(line); > - if (!llen) > - continue; > -#else > - size_t lsize = 0; > - > - fputs("multipathd> ", stdout); > - errno = 0; > - llen = getline(&line, &lsize, stdin); > - if (llen == -1) { > - if (errno != 0) > - fprintf(stderr, "Error in getline: %m"); > - break; > - } > - if (!llen || !strcmp(line, "\n")) > - continue; > -#endif > - > - if (need_quit(line, llen)) > - break; > - > - if (send_packet(fd, line) != 0) > - break; > - ret = recv_packet(fd, &reply, timeout); > - if (ret != 0) > - break; > - > - print_reply(reply); > - > -#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT) > - if (line && *line) > - add_history(line); > -#endif > - } > -} > - > static int process_req(int fd, char * inbuf, unsigned int timeout) > { > char *reply; > @@ -163,14 +44,14 @@ int uxclnt(char * inbuf, unsigned int timeout) > { > int fd, ret = 0; > > + if (!inbuf) > + return 1; > fd = mpath_connect(); > if (fd == -1) > - exit(1); > + return 1; > + > + ret = process_req(fd, inbuf, timeout); > > - if (inbuf) > - ret = process_req(fd, inbuf, timeout); > - else > - process(fd, timeout); > mpath_disconnect(fd); > return ret; > } > -- > 2.37.1 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/dm-devel