Re: [PATCH v4 09/12] multipathc: add new interactive client program

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [DM Crypt]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Packaging]     [Fedora SELinux]     [Yosemite Discussion]     [KDE Users]     [Fedora Docs]

  Powered by Linux