Message ID | 20220830192713.19778-10-mwilck@suse.com (mailing list archive) |
---|---|
State | Not Applicable, archived |
Delegated to: | christophe varoqui |
Headers | show |
Series | Split libmultipath and libmpathutil | expand |
On Tue, Aug 30, 2022 at 09:27:10PM +0200, mwilck@suse.com wrote: > From: Martin Wilck <mwilck@suse.com> > > 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@suse.com> > Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com> Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com> > --- > .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@redhat.com https://listman.redhat.com/mailman/listinfo/dm-devel
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; }