The client is wrapped in the daemon.
You can call it through :
- multipathd -k"$cmd" : one shot
- multipathd -k : interactive
The synthax is somewhat rude :/
- lp : list paths
- lm : list maps
- ap $path : add path checker, $path is in the "sdb" form
- rp $path : remove path checker
- am $map : add map event thread, $map is in the "dm-0" form
- rm $map : remove map event thread
hwtable.o blacklist.o util.o dmparser.o config.o \
structs.o cache.o discovery.o propsel.o dict.o \
pgpolicies.o debug.o regex.o defaults.o uevent.o \
- switchgroup.o
+ switchgroup.o uxsock.o
CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes
#define WORD_SIZE 64
static int
-get_word (char * sentence, char ** word)
-{
- char * p;
- int len;
- int skip = 0;
-
- while (*sentence == ' ') {
- sentence++;
- skip++;
- }
- if (*sentence == '\0')
- return 0;
-
- p = sentence;
-
- while (*p != ' ' && *p != '\0')
- p++;
-
- len = (int) (p - sentence);
-
- if (!word)
- return skip + len;
-
- *word = MALLOC(len + 1);
-
- if (!*word) {
- condlog(0, "get_word : oom\n");
- return 0;
- }
- strncpy(*word, sentence, len);
- condlog(4, "*word = %s, len = %i", *word, len);
-
- if (*p == '\0')
- return 0;
-
- return skip + len;
-}
-
-static int
merge_words (char ** dst, char * word, int space)
{
char * p;
#include <sys/stat.h>
#include <unistd.h>
+#include "debug.h"
+#include "memory.h"
+
#define PARAMS_SIZE 255
int
return 0;
}
+int
+get_word (char * sentence, char ** word)
+{
+ char * p;
+ int len;
+ int skip = 0;
+
+ while (*sentence == ' ') {
+ sentence++;
+ skip++;
+ }
+ if (*sentence == '\0')
+ return 0;
+
+ p = sentence;
+
+ while (*p != ' ' && *p != '\0')
+ p++;
+
+ len = (int) (p - sentence);
+
+ if (!word)
+ return skip + len;
+
+ *word = MALLOC(len + 1);
+
+ if (!*word) {
+ condlog(0, "get_word : oom\n");
+ return 0;
+ }
+ strncpy(*word, sentence, len);
+ condlog(4, "*word = %s, len = %i", *word, len);
+
+ if (*p == '\0')
+ return 0;
+
+ return skip + len;
+}
+
int strcmp_chomp(char *, char *);
void basename (char * src, char * dst);
int filepresent (char * run);
+int get_word (char * sentence, char ** word);
+
#define safe_sprintf(var, format, args...) \
snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
#
# object files
#
-OBJS = main.o copy.o log.o log_pthread.o pidfile.o \
- $(MULTIPATHLIB)-glibc.a \
- $(CHECKERSLIB)-glibc.a \
+OBJS = main.o copy.o log.o log_pthread.o pidfile.o uxlsnr.o uxclnt.o \
+ $(MULTIPATHLIB)-glibc.a $(CHECKERSLIB)-glibc.a \
#
#include "copy.h"
#include "clone_platform.h"
#include "pidfile.h"
+#include "uxlsnr.h"
+#include "uxclnt.h"
#define FILE_NAME_SIZE 256
#define CMDSIZE 160
+#define MAX_REPLY_LEN 1000
#define CALLOUT_DIR "/var/cache/multipathd"
return 0;
}
+static char *
+show_paths (struct paths * allpaths)
+{
+ int i, j, k;
+ struct path * pp;
+ char * c;
+ char * reply;
+
+ reply = MALLOC(MAX_REPLY_LEN);
+
+ if (!reply)
+ return NULL;
+
+ c = reply;
+ c += sprintf(c, "\n");
+
+ vector_foreach_slot(allpaths->pathvec, pp, i) {
+ c += sprintf(c, "%10s: ", pp->dev);
+ c += sprintf(c, "state %i, ", pp->state);
+
+ j = pp->tick;
+ k = pp->checkint - pp->tick;
+ c += sprintf(c, "%3i/%3i ", j, pp->checkint);
+
+ while (j-- > 0)
+ c += sprintf(c, "X");
+
+
+ while (k-- > 0)
+ c += sprintf(c, ".");
+
+ c += sprintf(c, "\n");
+ }
+
+ return reply;
+}
+
+static char *
+show_maps (struct paths * allpaths)
+{
+ int i, j, k;
+ struct multipath * mpp;
+ char * c;
+ char * reply;
+
+ reply = MALLOC(MAX_REPLY_LEN);
+
+ if (!reply)
+ return NULL;
+
+ c = reply;
+ c += sprintf(c, "\n");
+
+ vector_foreach_slot(allpaths->mpvec, mpp, i) {
+ c += sprintf(c, "%20s: ", mpp->alias);
+
+ j = mpp->failback_tick;
+ k = mpp->pgfailback - mpp->failback_tick;
+ c += sprintf(c, "%3i/%3i ", j, mpp->pgfailback);
+
+ while (j-- > 0)
+ c += sprintf(c, "X");
+
+
+ while (k-- > 0)
+ c += sprintf(c, ".");
+
+ c += sprintf(c, "\n");
+ }
+
+ return reply;
+}
+
+char *
+uxsock_trigger (char * str, void * trigger_data)
+{
+ struct paths * allpaths;
+ char * reply = NULL;
+
+ allpaths = (struct paths *)trigger_data;
+
+ lock(allpaths->lock);
+
+ if (*str == 'l' && *(str + 1) == 'p')
+ reply = show_paths(allpaths);
+
+ else if (*str == 'l' && *(str + 1) == 'm')
+ reply = show_maps(allpaths);
+
+ else if (*str == 'r' && *(str + 1) == 'p')
+ uev_remove_path(str + 3, allpaths);
+
+ else if (*str == 'a' && *(str + 1) == 'p')
+ uev_add_path(str + 3, allpaths);
+
+ else if (*str == 'r' && *(str + 1) == 'm')
+ uev_remove_map(str + 3, allpaths);
+
+ else if (*str == 'a' && *(str + 1) == 'm')
+ uev_add_map(str + 3, allpaths);
+
+ if (!reply)
+ asprintf(&reply, "ok\n");
+
+ unlock(allpaths->lock);
+
+ return reply;
+}
+
int
uev_trigger (struct uevent * uev, void * trigger_data)
{
return NULL;
}
+static void *
+uxlsnrloop (void * ap)
+{
+ uxsock_listen(&uxsock_trigger, ap);
+
+ return NULL;
+}
+
static void
strvec_free (vector vec)
{
static int
child (void * param)
{
- pthread_t check_thr, uevent_thr;
+ pthread_t check_thr, uevent_thr, uxlsnr_thr;
pthread_attr_t attr;
struct paths * allpaths;
pthread_create(&check_thr, &attr, checkerloop, allpaths);
pthread_create(&uevent_thr, &attr, ueventloop, allpaths);
+ pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, allpaths);
pthread_join(check_thr, NULL);
pthread_join(uevent_thr, NULL);
+ pthread_join(uxlsnr_thr, NULL);
return 0;
}
if (!conf)
exit(1);
- while ((arg = getopt(argc, argv, ":dv:")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":dv:k::")) != EOF ) {
switch(arg) {
case 'd':
logsink = 0;
conf->verbosity = atoi(optarg);
break;
+ case 'k':
+ uxclnt(optarg);
+ exit(0);
default:
;
}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+
+#include <uxsock.h>
+
+/*
+ * process the client
+ */
+static void process(int fd)
+{
+ char line[1000];
+ char *reply;
+
+ while (fgets(line, sizeof(line), stdin)) {
+ size_t len = strlen(line);
+
+ if (line[len-1] == '\n') {
+ line[len-1] = 0;
+ len--;
+ }
+
+ if (send_packet(fd, line, strlen(line)) != 0) break;
+ if (recv_packet(fd, &reply, &len) != 0) break;
+
+ printf("%*.*s\n", (int)len, (int)len, reply);
+ free(reply);
+ }
+}
+
+static void process_req(int fd, char * inbuf)
+{
+ char *reply;
+ size_t len;
+
+ send_packet(fd, inbuf, strlen(inbuf));
+ recv_packet(fd, &reply, &len);
+
+ printf("%*.*s\n", (int)len, (int)len, reply);
+ free(reply);
+}
+
+/*
+ * entry point
+ */
+int uxclnt(char * inbuf)
+{
+ int fd;
+
+ fd = ux_socket_connect(SOCKET_NAME);
+ if (fd == -1) {
+ perror("ux_socket_connect");
+ exit(1);
+ }
+
+ if (inbuf)
+ process_req(fd, inbuf);
+ else
+ process(fd);
+
+ return 0;
+}
--- /dev/null
+int uxclnt(char * inbuf);
--- /dev/null
+/*
+ * A simple domain socket listener
+ * Original author : tridge@samba.org, January 2002
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+
+#include <memory.h>
+#include <debug.h>
+#include <vector.h>
+#include <structs.h>
+#include <uxsock.h>
+
+#define SLEEP_TIME 5000
+
+struct client {
+ int fd;
+ struct client *next, *prev;
+};
+
+static struct client *clients;
+static unsigned num_clients;
+
+/*
+ * handle a new client joining
+ */
+static void new_client(int ux_sock)
+{
+ struct client *c;
+ struct sockaddr addr;
+ socklen_t len = sizeof(addr);
+ int fd;
+
+ fd = accept(ux_sock, &addr, &len);
+
+ if (fd == -1)
+ return;
+
+ /* put it in our linked list */
+ c = (struct client *)MALLOC(sizeof(*c));
+ memset(c, 0, sizeof(*c));
+ c->fd = fd;
+ c->next = clients;
+ clients = c;
+ num_clients++;
+}
+
+/*
+ * kill off a dead client
+ */
+static void dead_client(struct client *c)
+{
+ close(c->fd);
+ if (c->prev) c->prev->next = c->next;
+ if (c->next) c->next->prev = c->prev;
+ if (c == clients) clients = c->next;
+ FREE(c);
+ num_clients--;
+}
+
+/*
+ * entry point
+ */
+void * uxsock_listen(char * (*uxsock_trigger)(char *, void * trigger_data),
+ void * trigger_data)
+{
+ int ux_sock;
+ size_t len;
+ char *inbuf;
+ struct pollfd *polls = NULL;
+ char *reply;
+
+ ux_sock = ux_socket_listen(SOCKET_NAME);
+
+ if (ux_sock == -1) {
+ condlog(0, "ux_socket_listen error");
+ exit(1);
+ }
+
+ while (1) {
+ struct client *c;
+ int i, poll_count;
+
+ /* setup for a poll */
+ polls = REALLOC(polls, (1+num_clients) * sizeof(*polls));
+ polls[0].fd = ux_sock;
+ polls[0].events = POLLIN;
+
+ /* setup the clients */
+ for (i=1, c = clients; c; i++, c = c->next) {
+ polls[i].fd = c->fd;
+ polls[i].events = POLLIN;
+ }
+
+ /* most of our life is spent in this call */
+ poll_count = poll(polls, i, SLEEP_TIME);
+
+ if (poll_count == -1) {
+ /* something went badly wrong! */
+ condlog(0, "poll");
+ exit(1);
+ }
+
+ if (poll_count == 0)
+ continue;
+
+ /* see if a client wants to speak to us */
+ for (i=1, c = clients; c; i++) {
+ struct client *next = c->next;
+
+ if (polls[i].revents & POLLIN) {
+ if (recv_packet(c->fd, &inbuf, &len) != 0) {
+ dead_client(c);
+ } else {
+ condlog(4, "Got request [%*.*s]",
+ (int)len, (int)len, inbuf);
+ reply = uxsock_trigger(inbuf,
+ trigger_data);
+
+ if (reply) {
+ if (send_packet(c->fd, reply,
+ strlen(reply)) != 0) {
+ dead_client(c);
+ }
+ FREE(reply);
+ }
+ FREE(inbuf);
+ }
+ }
+ c = next;
+ }
+
+ /* see if we got a new client */
+ if (polls[0].revents & POLLIN) {
+ new_client(ux_sock);
+ }
+ }
+
+ return NULL;
+}
--- /dev/null
+void * uxsock_listen(char * (*uxsock_trigger)
+ (char *, void * trigger_data),
+ void * trigger_data);
+