On Tue, Aug 30, 2022 at 09:27:10PM +0200, mwi...@suse.com wrote:
> From: Martin Wilck <mwi...@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 <mwi...@suse.com>
> Reviewed-by: Benjamin Marzinski <bmarz...@redhat.com>
Reviewed-by: Benjamin Marzinski <bmarz...@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

Reply via email to