[multipathd] unix socket daemon control interface
authorroot <root@xa-s05.(none)>
Mon, 20 Jun 2005 07:44:49 +0000 (09:44 +0200)
committerroot <root@xa-s05.(none)>
Mon, 20 Jun 2005 07:44:49 +0000 (09:44 +0200)
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

libmultipath/Makefile
libmultipath/dmparser.c
libmultipath/util.c
libmultipath/util.h
multipathd/Makefile
multipathd/main.c
multipathd/uxclnt.c [new file with mode: 0644]
multipathd/uxclnt.h [new file with mode: 0644]
multipathd/uxlsnr.c [new file with mode: 0644]
multipathd/uxlsnr.h [new file with mode: 0644]

index 1dc3c51..1aa587a 100644 (file)
@@ -10,7 +10,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        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
 
index a801a0a..0d53fb1 100644 (file)
 
 #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)
 {
index 8a3f790..53d8c3e 100644 (file)
@@ -4,6 +4,9 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "debug.h"
+#include "memory.h"
+
 #define PARAMS_SIZE 255
 
 int
@@ -49,3 +52,42 @@ filepresent (char * run) {
        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;
+}
+
index f7f6fc3..51f052a 100644 (file)
@@ -4,6 +4,8 @@
 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)
index 2226d10..5af3f83 100644 (file)
@@ -26,9 +26,8 @@ LDFLAGS = -lpthread -ldevmapper -lsysfs
 #
 # 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 \
 
 
 #
index 5e76ff2..0288f90 100644 (file)
 #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"
 
@@ -561,6 +564,115 @@ uev_remove_path (char * devname, struct paths * allpaths)
        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)
 {
@@ -622,6 +734,14 @@ ueventloop (void * ap)
        return NULL;
 }
 
+static void *
+uxlsnrloop (void * ap)
+{
+       uxsock_listen(&uxsock_trigger, ap);
+
+       return NULL;
+}
+
 static void
 strvec_free (vector vec)
 {
@@ -1045,7 +1165,7 @@ set_oom_adj (int val)
 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;
 
@@ -1120,8 +1240,10 @@ child (void * param)
        
        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;
 }
@@ -1156,7 +1278,7 @@ main (int argc, char *argv[])
        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;
@@ -1168,6 +1290,9 @@ main (int argc, char *argv[])
 
                        conf->verbosity = atoi(optarg);
                        break;
+               case 'k':
+                       uxclnt(optarg);
+                       exit(0);
                default:
                        ;
                }
diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c
new file mode 100644 (file)
index 0000000..fc019dc
--- /dev/null
@@ -0,0 +1,69 @@
+#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;
+}
diff --git a/multipathd/uxclnt.h b/multipathd/uxclnt.h
new file mode 100644 (file)
index 0000000..0667a24
--- /dev/null
@@ -0,0 +1 @@
+int uxclnt(char * inbuf);
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
new file mode 100644 (file)
index 0000000..faaa2e9
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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;
+}
diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h
new file mode 100644 (file)
index 0000000..2afade1
--- /dev/null
@@ -0,0 +1,4 @@
+void * uxsock_listen(char * (*uxsock_trigger)
+                       (char *, void * trigger_data),
+                       void * trigger_data);
+