multipath: add '-t' option to dump internal hwtable
[platform/upstream/multipath-tools.git] / multipath / main.c
index a20aad6..f455985 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Version:     $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
  *
- * Author:      Copyright (C) 2003 Christophe Varoqui
+ * Author:      Christophe Varoqui
  *
  *              This program is distributed in the hope that it will be useful,
  *              but WITHOUT ANY WARRANTY; without even the implied warranty of
  *              modify it under the terms of the GNU General Public License
  *              as published by the Free Software Foundation; either version
  *              2 of the License, or (at your option) any later version.
+ *
+ * Copyright (c) 2003, 2004, 2005 Christophe Varoqui
+ * Copyright (c) 2005 Benjamin Marzinski, Redhat
+ * Copyright (c) 2005 Kiyoshi Ueda, NEC
+ * Copyright (c) 2005 Patrick Caulfield, Redhat
+ * Copyright (c) 2005 Edward Goggin, EMC
  */
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <unistd.h>
-#include <string.h>
+#include <ctype.h>
 
-#include <parser.h>
+#include <checkers.h>
+#include <prio.h>
 #include <vector.h>
 #include <memory.h>
 #include <libdevmapper.h>
 #include <devmapper.h>
-#include <checkers.h>
-#include <path_state.h>
-#include <blacklist.h>
-#include <hwtable.h>
 #include <util.h>
 #include <defaults.h>
 #include <structs.h>
+#include <structs_vec.h>
 #include <dmparser.h>
-#include <cache.h>
+#include <sysfs.h>
 #include <config.h>
-#include <propsel.h>
+#include <blacklist.h>
 #include <discovery.h>
 #include <debug.h>
 #include <switchgroup.h>
-#include <sysfs/libsysfs.h>
 #include <print.h>
+#include <alias.h>
+#include <configure.h>
+#include <pgpolicies.h>
+#include <version.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 
-#include "main.h"
-#include "pgpolicies.h"
-#include "dict.h"
-
-/* for column aligned output */
-struct path_layout pl;
+int logsink;
 
-static char *
-get_refwwid (vector pathvec)
+static int
+filter_pathvec (vector pathvec, char * refwwid)
 {
+       int i;
        struct path * pp;
-       char buff[FILE_NAME_SIZE];
-       char * refwwid;
-
-       if (conf->dev_type == DEV_NONE)
-               return NULL;
-
-       if (conf->dev_type == DEV_DEVNODE) {
-               condlog(3, "limited scope = %s", conf->dev);
-               basename(conf->dev, buff);
-               pp = find_path_by_dev(pathvec, buff);
-               
-               if (!pp) {
-                       pp = alloc_path();
-
-                       if (!pp)
-                               return NULL;
 
-                       strncpy(pp->dev, buff, FILE_NAME_SIZE);
-
-                       if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
-                               return NULL;
-
-                       if (store_path(pathvec, pp)) {
-                               free_path(pp);
-                               return NULL;
-                       }
-               }
-
-               refwwid = MALLOC(WWID_SIZE);
-
-               if (!refwwid)
-                       return NULL;
-
-               memcpy(refwwid, pp->wwid, WWID_SIZE);
-               return refwwid;
-       }
-
-       if (conf->dev_type == DEV_DEVT) {
-               condlog(3, "limited scope = %s", conf->dev);
-               pp = find_path_by_devt(pathvec, conf->dev);
-               
-               if (!pp) {
-                       if (devt2devname(conf->dev, buff))
-                               return NULL;
-
-                       pp = alloc_path();
-
-                       if (!pp)
-                               return NULL;
+       if (!refwwid || !strlen(refwwid))
+               return 0;
 
-                       if(safe_sprintf(pp->dev, "%s", buff)) {
-                               fprintf(stderr, "pp->dev too small\n");
-                               exit(1);
-                       }
-                       if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
-                               return NULL;
-                       
-                       if (store_path(pathvec, pp)) {
-                               free_path(pp);
-                               return NULL;
-                       }
+       vector_foreach_slot (pathvec, pp, i) {
+               if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
+                       condlog(3, "skip path %s : out of scope", pp->dev);
+                       free_path(pp);
+                       vector_del_slot(pathvec, i);
+                       i--;
                }
-
-               refwwid = MALLOC(WWID_SIZE);
-
-               if (!refwwid)
-                       return NULL;
-               
-               memcpy(refwwid, pp->wwid, WWID_SIZE);
-               return refwwid;
        }
-       if (conf->dev_type == DEV_DEVMAP) {
-               condlog(3, "limited scope = %s", conf->dev);
-               /*
-                * may be an alias
-                */
-               refwwid = get_mpe_wwid(conf->dev);
-
-               if (refwwid)
-                       return refwwid;
-               
-               /*
-                * or directly a wwid
-                */
-               refwwid = MALLOC(WWID_SIZE);
-
-               if (!refwwid)
-                       return NULL;
-
-               strncpy(refwwid, conf->dev, WWID_SIZE);
-               return refwwid;
-       }
-       return NULL;
+       return 0;
 }
 
 static void
-print_path (struct path * pp, char * style)
+usage (char * progname)
 {
-       char buff[MAX_LINE_LEN];
-
-       snprint_path(&buff[0], MAX_LINE_LEN, style, pp, &pl);
-       printf("%s", buff);
-}
+       fprintf (stderr, VERSION_STRING);
+       fprintf (stderr, "Usage:\n");
+       fprintf (stderr, "  %s [-d] [-r] [-v lvl] [-p pol] [-b fil] [dev]\n", progname);
+       fprintf (stderr, "  %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname);
+       fprintf (stderr, "  %s -F [-v lvl]\n", progname);
+       fprintf (stderr, "  %s -t\n", progname);
+       fprintf (stderr, "  %s -h\n", progname);
+       fprintf (stderr,
+               "\n"
+               "Where:\n"
+               "  -h      print this usage text\n" \
+               "  -l      show multipath topology (sysfs and DM info)\n" \
+               "  -ll     show multipath topology (maximum info)\n" \
+               "  -f      flush a multipath device map\n" \
+               "  -F      flush all multipath device maps\n" \
+               "  -d      dry run, do not create or update devmaps\n" \
+               "  -t      dump internal hardware table\n" \
+               "  -r      force devmap reload\n" \
+               "  -p      policy failover|multibus|group_by_serial|group_by_prio\n" \
+               "  -b fil  bindings file location\n" \
+               "  -p pol  force all maps to specified path grouping policy :\n" \
+               "          . failover            one path per priority group\n" \
+               "          . multibus            all paths in one priority group\n" \
+               "          . group_by_serial     one priority group per serial\n" \
+               "          . group_by_prio       one priority group per priority lvl\n" \
+               "          . group_by_node_name  one priority group per target node\n" \
+               "  -v lvl  verbosity level\n" \
+               "          . 0 no output\n" \
+               "          . 1 print created devmap names only\n" \
+               "          . 2 default verbosity\n" \
+               "          . 3 print debug information\n" \
+               "  dev     action limited to:\n" \
+               "          . multipath named 'dev' (ex: mpath0) or\n" \
+               "          . multipath whose wwid is 'dev' (ex: 60051..)\n" \
+               "          . multipath including the path named 'dev' (ex: /dev/sda)\n" \
+               "          . multipath including the path with maj:min 'dev' (ex: 8:0)\n" \
+               );
 
-static void
-print_map (struct multipath * mpp)
-{
-       if (mpp->size && mpp->params)
-               printf("0 %llu %s %s\n",
-                        mpp->size, DEFAULT_TARGET, mpp->params);
-       return;
+       exit(1);
 }
 
-static void
-print_all_paths (vector pathvec)
+static int
+update_paths (struct multipath * mpp)
 {
-       int i;
+       int i, j;
+       struct pathgroup * pgp;
        struct path * pp;
 
-       vector_foreach_slot (pathvec, pp, i)
-               print_path(pp, PRINT_PATH_LONG);
-}
-
-static void
-print_mp (struct multipath * mpp)
-{
-       int j, i;
-       struct path * pp = NULL;
-       struct pathgroup * pgp = NULL;
-
-       if (mpp->action == ACT_NOTHING || conf->verbosity == 0)
-               return;
-
-       if (conf->verbosity > 1) {
-               switch (mpp->action) {
-               case ACT_RELOAD:
-                       printf("%s: ", ACT_RELOAD_STR);
-                       break;
-
-               case ACT_CREATE:
-                       printf("%s: ", ACT_CREATE_STR);
-                       break;
-
-               case ACT_SWITCHPG:
-                       printf("%s: ", ACT_SWITCHPG_STR);
-                       break;
-
-               default:
-                       break;
-               }
-       }
-
-       if (mpp->alias)
-               printf("%s", mpp->alias);
-
-       if (conf->verbosity == 1) {
-               printf("\n");
-               return;
-       }
-       if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE))
-               printf(" (%s)", mpp->wwid);
-
-       printf("\n");
-
-       if (mpp->size < (1 << 11))
-               printf("[size=%llu kB]", mpp->size >> 1);
-       else if (mpp->size < (1 << 21))
-               printf("[size=%llu MB]", mpp->size >> 11);
-       else if (mpp->size < (1 << 31))
-               printf("[size=%llu GB]", mpp->size >> 21);
-       else
-               printf("[size=%llu TB]", mpp->size >> 31);
-
-       if (mpp->features)
-               printf("[features=\"%s\"]", mpp->features);
-
-       if (mpp->hwhandler)
-               printf("[hwhandler=\"%s\"]", mpp->hwhandler);
+       if (!mpp->pg)
+               return 0;
 
-       fprintf(stdout, "\n");
+       vector_foreach_slot (mpp->pg, pgp, i) {
+               if (!pgp->paths)
+                       continue;
 
-       if (!mpp->pg)
-               return;
-
-       vector_foreach_slot (mpp->pg, pgp, j) {
-               printf("\\_ ");
-
-               if (mpp->selector) {
-                       printf("%s ", mpp->selector);
-#if 0
-                       /* align to path status info */
-                       for (i = pl.hbtl_len + pl.dev_len + pl.dev_t_len + 4;
-                            i > strlen(mpp->selector); i--)
-                               printf(" ");
-#endif
-               }
-               if (pgp->priority)
-                       printf("[prio=%i]", pgp->priority);
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       if (!strlen(pp->dev)) {
+                               if (devt2devname(pp->dev, FILE_NAME_SIZE,
+                                                pp->dev_t)) {
+                                       /*
+                                        * path is not in sysfs anymore
+                                        */
+                                       pp->state = PATH_DOWN;
+                                       continue;
+                               }
+                               pp->mpp = mpp;
+                               pathinfo(pp, conf->hwtable, DI_ALL);
+                               continue;
+                       }
+                       pp->mpp = mpp;
+                       if (pp->state == PATH_UNCHECKED ||
+                           pp->state == PATH_WILD)
+                               pathinfo(pp, conf->hwtable, DI_CHECKER);
 
-               switch (pgp->status) {
-               case PGSTATE_ENABLED:
-                       printf("[enabled]");
-                       break;
-               case PGSTATE_DISABLED:
-                       printf("[disabled]");
-                       break;
-               case PGSTATE_ACTIVE:
-                       printf("[active]");
-                       break;
-               default:
-                       break;
+                       if (pp->priority == PRIO_UNDEF)
+                               pathinfo(pp, conf->hwtable, DI_PRIO);
                }
-               printf("\n");
-
-               vector_foreach_slot (pgp->paths, pp, i)
-                       print_path(pp, PRINT_PATH_INDENT);
        }
-       printf("\n");
+       return 0;
 }
 
 static int
-filter_pathvec (vector pathvec, char * refwwid)
+get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
 {
        int i;
-       struct path * pp;
+       struct multipath * mpp;
+       char params[PARAMS_SIZE], status[PARAMS_SIZE];
 
-       if (!refwwid || !strlen(refwwid))
-               return 0;
+       if (dm_get_maps(curmp))
+               return 1;
 
-       vector_foreach_slot (pathvec, pp, i) {
-               if (memcmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
-                       condlog(3, "skip path %s : out of scope", pp->dev);
-                       free_path(pp);
-                       vector_del_slot(pathvec, i);
+       vector_foreach_slot (curmp, mpp, i) {
+               /*
+                * discard out of scope maps
+                */
+               if (mpp->wwid && refwwid &&
+                   strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
+                       condlog(3, "skip map %s: out of scope", mpp->alias);
+                       free_multipath(mpp, KEEP_PATHS);
+                       vector_del_slot(curmp, i);
                        i--;
+                       continue;
                }
-       }
-       return 0;
-}
 
-/*
- * Transforms the path group vector into a proper device map string
- */
-int
-assemble_map (struct multipath * mp)
-{
-       int i, j;
-       int shift, freechar;
-       int minio;
-       char * p;
-       struct pathgroup * pgp;
-       struct path * pp;
+               dm_get_map(mpp->alias, &mpp->size, params);
+               condlog(3, "params = %s", params);
+               dm_get_status(mpp->alias, status);
+               condlog(3, "status = %s", status);
 
-       p = mp->params;
-       freechar = sizeof(mp->params);
-       
-       shift = snprintf(p, freechar, "%s %s %i %i",
-                        mp->features, mp->hwhandler,
-                        VECTOR_SIZE(mp->pg), mp->nextpg);
+               disassemble_map(pathvec, params, mpp);
 
-       if (shift >= freechar) {
-               fprintf(stderr, "mp->params too small\n");
-               return 1;
-       }
-       p += shift;
-       freechar -= shift;
-       
-       vector_foreach_slot (mp->pg, pgp, i) {
-               pgp = VECTOR_SLOT(mp->pg, i);
-               shift = snprintf(p, freechar, " %s %i 1", mp->selector,
-                                VECTOR_SIZE(pgp->paths));
-               if (shift >= freechar) {
-                       fprintf(stderr, "mp->params too small\n");
-                       return 1;
-               }
-               p += shift;
-               freechar -= shift;
+               /*
+                * disassemble_map() can add new paths to pathvec.
+                * If not in "fast list mode", we need to fetch information
+                * about them
+                */
+               if (conf->list != 1)
+                       update_paths(mpp);
 
-               vector_foreach_slot (pgp->paths, pp, j) {
-                       minio = conf->minio;
-                       
-                       if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority)
-                               minio *= pp->priority;
-
-                       shift = snprintf(p, freechar, " %s %d",
-                                        pp->dev_t, minio);
-                       if (shift >= freechar) {
-                               fprintf(stderr, "mp->params too small\n");
-                               return 1;
-                       }
-                       p += shift;
-                       freechar -= shift;
-               }
-       }
-       if (freechar < 1) {
-               fprintf(stderr, "mp->params too small\n");
-               return 1;
-       }
-       snprintf(p, 1, "\n");
+               if (conf->list > 1)
+                       mpp->bestpg = select_path_group(mpp);
 
-       if (conf->verbosity > 2)
-               print_map(mp);
+               disassemble_status(status, mpp);
+
+               if (conf->list)
+                       print_multipath_topology(mpp, conf->verbosity);
 
+               if (!conf->dry_run)
+                       reinstate_paths(mpp);
+       }
        return 0;
 }
 
+
+/*
+ * Return value:
+ *  -1: Retry
+ *   0: Success
+ *   1: Failure
+ */
 static int
-setup_map (struct multipath * mpp)
+configure (void)
 {
-       struct path * pp;
-       int i;
+       vector curmp = NULL;
+       vector pathvec = NULL;
+       struct vectors vecs;
+       int r = 1;
+       int di_flag = 0;
+       char * refwwid = NULL;
+       char * dev = NULL;
 
        /*
-        * don't bother if devmap size is unknown
+        * allocate core vectors to store paths and multipaths
         */
-       if (mpp->size <= 0) {
-               condlog(3, "%s devmap size is unknown", mpp->alias);
-               return 1;
+       curmp = vector_alloc();
+       pathvec = vector_alloc();
+
+       if (!curmp || !pathvec) {
+               condlog(0, "can not allocate memory");
+               goto out;
        }
+       vecs.pathvec = pathvec;
+       vecs.mpvec = curmp;
 
        /*
-        * don't bother if a constituant path is claimed
-        * (not by the device mapper driver)
+        * dev is "/dev/" . "sysfs block dev"
         */
-       vector_foreach_slot (mpp->paths, pp, i) {
-               if (pp->claimed && pp->dmstate == PSTATE_UNDEF) {
-                       condlog(3, "%s claimed", pp->dev);
-                       return 1;
-               }
+       if (conf->dev) {
+               if (!strncmp(conf->dev, "/dev/", 5) &&
+                   strlen(conf->dev) > 5)
+                       dev = conf->dev + 5;
+               else
+                       dev = conf->dev;
        }
 
        /*
-        * properties selectors
+        * if we have a blacklisted device parameter, exit early
         */
-       select_pgpolicy(mpp);
-       select_selector(mpp);
-       select_features(mpp);
-       select_hwhandler(mpp);
-       select_rr_weight(mpp);
+       if (dev &&
+           (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0))
+                       goto out;
 
        /*
-        * apply selected grouping policy to valid paths
+        * scope limiting must be translated into a wwid
+        * failing the translation is fatal (by policy)
         */
-       switch (mpp->pgpolicy) {
-       case MULTIBUS:
-               one_group(mpp);
-               break;
-       case FAILOVER:
-               one_path_per_group(mpp);
-               break;
-       case GROUP_BY_SERIAL:
-               group_by_serial(mpp);
-               break;
-       case GROUP_BY_PRIO:
-               group_by_prio(mpp);
-               break;
-       case GROUP_BY_NODE_NAME:
-               group_by_node_name(mpp);
-               break;
-       default:
-               break;
-       }
+       if (conf->dev) {
+               refwwid = get_refwwid(conf->dev, conf->dev_type, pathvec);
 
-       if (mpp->pg == NULL) {
-               condlog(3, "pgpolicy failed to produce a pg vector");
-               return 1;
+               if (!refwwid) {
+                       condlog(3, "scope is nul");
+                       goto out;
+               }
+               condlog(3, "scope limited to %s", refwwid);
+               if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
+                               refwwid) > 0)
+                       goto out;
        }
 
        /*
-        * ponders each path group and determine highest prio pg
-        * to switch over (default to first)
-        */
-       select_path_group(mpp);
-
-       /*
-        * transform the mp->pg vector of vectors of paths
-        * into a mp->params strings to feed the device-mapper
+        * get a path list
         */
-       if (assemble_map(mpp)) {
-               condlog(3, "problem assembing map");
-               return 1;
-       }
-       return 0;
-}
-
-static int
-pathcount (struct multipath * mpp, int state)
-{
-       struct pathgroup *pgp;
-       struct path *pp;
-       int i, j;
-       int count = 0;
-
-       vector_foreach_slot (mpp->pg, pgp, i)
-               vector_foreach_slot (pgp->paths, pp, j)
-                       if (pp->state == state)
-                               count++;
-       return count;
-}
-
-static void
-compute_pgid(struct pathgroup * pgp)
-{
-       struct path * pp;
-       int i;
-
-       vector_foreach_slot (pgp->paths, pp, i)
-               pgp->id ^= (long)pp;
-}
-
-static int
-pgcmp (struct multipath * mpp, struct multipath * cmpp)
-{
-       int i, j;
-       struct pathgroup * pgp;
-       struct pathgroup * cpgp;
-       int r = 0;
-
-       vector_foreach_slot (mpp->pg, pgp, i) {
-               compute_pgid(pgp);
-
-               vector_foreach_slot (cmpp->pg, cpgp, j) {
-                       if (pgp->id == cpgp->id) {
-                               r = 0;
-                               break;
-                       }
-                       r++;
-               }
-               if (r)
-                       return r;
-       }
-       return r;
-}
+       if (conf->dev)
+               di_flag = DI_WWID;
 
-static void
-select_action (struct multipath * mpp, vector curmp)
-{
-       struct multipath * cmpp;
+       if (conf->list > 1)
+               /* extended path info '-ll' */
+               di_flag |= DI_SYSFS | DI_CHECKER;
+       else if (conf->list)
+               /* minimum path info '-l' */
+               di_flag |= DI_SYSFS;
+       else
+               /* maximum info */
+               di_flag = DI_ALL;
 
-       cmpp = find_mp(curmp, mpp->alias);
+       if (path_discovery(pathvec, conf, di_flag))
+               goto out;
 
-       if (!cmpp) {
-               cmpp = find_mp_by_wwid(curmp, mpp->wwid);
+       if (conf->verbosity > 2)
+               print_all_paths(pathvec, 1);
 
-               if (cmpp && !conf->dry_run) {
-                       condlog(2, "remove: %s (dup of %s)",
-                               cmpp->alias, mpp->alias);
-                       dm_flush_map(cmpp->alias, DEFAULT_TARGET);
-               }
-               mpp->action = ACT_CREATE;
-               return;
-       }
-       if (pathcount(mpp, PATH_UP) == 0) {
-               condlog(3, "no good path");
-               mpp->action = ACT_NOTHING;
-               return;
-       }
-       if (cmpp->size != mpp->size) {
-               condlog(3, "size different than current");
-               mpp->action = ACT_RELOAD;
-               return;
-       }
-       if (strncmp(cmpp->features, mpp->features,
-                   strlen(mpp->features))) {
-               condlog(3, "features different than current");
-               mpp->action =  ACT_RELOAD;
-               return;
-       }
-       if (strncmp(cmpp->hwhandler, mpp->hwhandler,
-                   strlen(mpp->hwhandler))) {
-               condlog(3, "hwhandler different than current");
-               mpp->action = ACT_RELOAD;
-               return;
-       }
-       if (strncmp(cmpp->selector, mpp->selector,
-                   strlen(mpp->selector))) {
-               condlog(3, "selector different than current");
-               mpp->action = ACT_RELOAD;
-               return;
-       }
-       if (VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
-               condlog(3, "different number of PG");
-               mpp->action = ACT_RELOAD;
-               return;
-       }
-       if (pgcmp(mpp, cmpp)) {
-               condlog(3, "different path group topology");
-               mpp->action = ACT_RELOAD;
-               return;
-       }
-       if (cmpp->nextpg != mpp->nextpg) {
-               condlog(3, "nextpg different than current");
-               mpp->action = ACT_SWITCHPG;
-               return;
-       }
-       mpp->action = ACT_NOTHING;
-       return;
-}
+       get_path_layout(pathvec, 0);
 
-static int
-reinstate_paths (struct multipath * mpp)
-{
-       int i, j;
-       struct pathgroup * pgp;
-       struct path * pp;
+       if (get_dm_mpvec(curmp, pathvec, refwwid))
+               goto out;
 
-       vector_foreach_slot (mpp->pg, pgp, i) {
-               vector_foreach_slot (pgp->paths, pp, j) {
-                       if (pp->state != PATH_UP &&
-                           (pgp->status == PGSTATE_DISABLED ||
-                            pgp->status == PGSTATE_ACTIVE))
-                               continue;
+       filter_pathvec(pathvec, refwwid);
 
-                       if (pp->dmstate == PSTATE_FAILED) {
-                               if (dm_reinstate(mpp->alias, pp->dev_t))
-                                       condlog(0, "error reinstating %s",
-                                               pp->dev);
-                       }
-               }
+       if (conf->list) {
+               r = 0;
+               goto out;
        }
-       return 0;
-}
-
-static int
-domap (struct multipath * mpp)
-{
-       int r = 0;
-
-       print_mp(mpp);
 
        /*
-        * last chance to quit before touching the devmaps
+        * core logic entry point
         */
-       if (conf->dry_run)
-               return 0;
-
-       switch (mpp->action) {
-       case ACT_NOTHING:
-               return 0;
-
-       case ACT_SWITCHPG:
-               dm_switchgroup(mpp->alias, mpp->nextpg);
-               /*
-                * we may have avoided reinstating paths because there where in
-                * active or disabled PG. Now that the topology has changed,
-                * retry.
-                */
-               reinstate_paths(mpp);
-               return 0;
-
-       case ACT_CREATE:
-               r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET,
-                             mpp->params, mpp->size, mpp->wwid);
-               break;
+       r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload);
 
-       case ACT_RELOAD:
-               r = dm_addmap(DM_DEVICE_RELOAD, mpp->alias, DEFAULT_TARGET,
-                             mpp->params, mpp->size, NULL);
-               break;
-
-       default:
-               break;
-       }
+out:
+       if (refwwid)
+               FREE(refwwid);
 
-       if (r) {
-               /*
-                * DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded
-                */
-               dm_simplecmd(DM_DEVICE_RESUME, mpp->alias);
-               dm_switchgroup(mpp->alias, mpp->nextpg);
-       }
+       free_multipathvec(curmp, KEEP_PATHS);
+       free_pathvec(pathvec, FREE_PATHS);
 
        return r;
 }
 
 static int
-coalesce_paths (vector curmp, vector pathvec)
+dump_config (void)
 {
-       int k, i;
-       char empty_buff[WWID_SIZE];
-       struct multipath * mpp;
-       struct path * pp1;
-       struct path * pp2;
+       char * c;
+       char * reply;
+       unsigned int maxlen = 256;
+       int again = 1;
 
-       memset(empty_buff, 0, WWID_SIZE);
+       reply = MALLOC(maxlen);
 
-       vector_foreach_slot (pathvec, pp1, k) {
-               /* skip this path for some reason */
-
-               /* 1. if path has no unique id or wwid blacklisted */
-               if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
-                   blacklist(conf->blist, pp1->wwid))
+       while (again) {
+               if (!reply)
+                       return 1;
+               c = reply;
+               c += snprint_defaults(c, reply + maxlen - c);
+               again = ((c - reply) == maxlen);
+               if (again) {
+                       reply = REALLOC(reply, maxlen *= 2);
                        continue;
-
-               /* 2. if path already coalesced */
-               if (pp1->mpp)
+               }
+               c += snprint_blacklist(c, reply + maxlen - c);
+               again = ((c - reply) == maxlen);
+               if (again) {
+                       reply = REALLOC(reply, maxlen *= 2);
                        continue;
-
-               /*
-                * at this point, we know we really got a new mp
-                */
-               mpp = alloc_multipath();
-
-               if (!mpp)
-                       return 1;
-
-               mpp->mpe = find_mpe(pp1->wwid);
-               mpp->hwe = pp1->hwe;
-               select_alias(mpp);
-
-               pp1->mpp = mpp;
-               strcpy(mpp->wwid, pp1->wwid);
-               mpp->size = pp1->size;
-               mpp->paths = vector_alloc();
-
-               if (pp1->priority < 0)
-                       mpp->action = ACT_NOTHING;
-
-               if (!mpp->paths)
-                       return 1;
-               
-               if (store_path(mpp->paths, pp1))
-                       return 1;
-
-               for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) {
-                       pp2 = VECTOR_SLOT(pathvec, i);
-
-                       if (strcmp(pp1->wwid, pp2->wwid))
-                               continue;
-                       
-                       pp2->mpp = mpp;
-
-                       if (pp2->size != mpp->size) {
-                               /*
-                                * ouch, avoid feeding that to the DM
-                                */
-                               condlog(3, "path size mismatch : discard %s",
-                                    mpp->wwid);
-                               mpp->action = ACT_NOTHING;
-                       }
-                       if (pp2->priority < 0)
-                               mpp->action = ACT_NOTHING;
-
-                       if (store_path(mpp->paths, pp2))
-                               return 1;
                }
-               if (setup_map(mpp)) {
-                       free_multipath(mpp, KEEP_PATHS);
+               c += snprint_blacklist_except(c, reply + maxlen - c);
+               again = ((c - reply) == maxlen);
+               if (again) {
+                       reply = REALLOC(reply, maxlen *= 2);
                        continue;
                }
-               condlog(3, "action preset to %i", mpp->action);
-
-               if (mpp->action == ACT_UNDEF)
-                       select_action(mpp, curmp);
-
-               condlog(3, "action set to %i", mpp->action);
-
-               domap(mpp);
-               free_multipath(mpp, KEEP_PATHS);
-       }
-       return 0;
-}
-
-static void
-usage (char * progname)
-{
-       fprintf (stderr, VERSION_STRING);
-       fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l|-ll|-f|-F]\n",
-               progname);
-       fprintf (stderr,
-               "\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \
-               "\t\t\t[device]\n" \
-               "\n" \
-               "\t-v level\tverbosty level\n" \
-               "\t   0\t\t\tno output\n" \
-               "\t   1\t\t\tprint created devmap names only\n" \
-               "\t   2\t\t\tdefault verbosity\n" \
-               "\t   3\t\t\tprint debug information\n" \
-               "\t-d\t\tdry run, do not create or update devmaps\n" \
-               "\t-l\t\tshow multipath topology (sysfs and DM info)\n" \
-               "\t-ll\t\tshow multipath topology (maximum info)\n" \
-               "\t-F\t\tflush a multipath device map\n" \
-               "\t-F\t\tflush all multipath device maps\n" \
-               "\t-p policy\tforce all maps to specified policy :\n" \
-               "\t   failover\t\t1 path per priority group\n" \
-               "\t   multibus\t\tall paths in 1 priority group\n" \
-               "\t   group_by_serial\t1 priority group per serial\n" \
-               "\t   group_by_prio\t1 priority group per priority lvl\n" \
-               "\t   group_by_node_name\t1 priority group per target node\n" \
-               "\n" \
-               "\tdevice\t\tlimit scope to the device's multipath\n" \
-               "\t\t\t(udev-style $DEVNAME reference, eg /dev/sdb\n" \
-               "\t\t\tor major:minor or a device map name)\n" \
-               );
-
-       exit(1);
-}
-
-static int
-update_paths (struct multipath * mpp)
-{
-       int i, j;
-       struct pathgroup * pgp;
-       struct path * pp;
-
-       vector_foreach_slot (mpp->pg, pgp, i) {
-               vector_foreach_slot (pgp->paths, pp, j) {
-                       if (!strlen(pp->dev)) {
-                               if (devt2devname(pp->dev_t, pp->dev)) {
-                                       /*
-                                        * path is not in sysfs anymore
-                                        */
-                                       pp->state = PATH_DOWN;
-                                       continue;
-                               }
-                               pathinfo(pp, conf->hwtable,
-                                        DI_SYSFS | DI_CHECKER | \
-                                        DI_SERIAL | DI_PRIO);
-                               continue;
-                       }
-                       if (pp->state == PATH_UNCHECKED)
-                               pathinfo(pp, conf->hwtable, DI_CHECKER);
-
-                       if (!pp->priority)
-                               pathinfo(pp, conf->hwtable, DI_PRIO);
+               c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable);
+               again = ((c - reply) == maxlen);
+               if (again) {
+                       reply = REALLOC(reply, maxlen *= 2);
+                       continue;
                }
+               c += snprint_mptable(c, reply + maxlen - c, conf->mptable);
+               again = ((c - reply) == maxlen);
+               if (again)
+                       reply = REALLOC(reply, maxlen *= 2);
        }
-       return 0;
-}
 
-static int
-get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
-{
-       int i;
-       struct multipath * mpp;
-       char * wwid;
-
-       if (dm_get_maps(curmp, DEFAULT_TARGET))
-               return 1;
-
-       vector_foreach_slot (curmp, mpp, i) {
-               wwid = get_mpe_wwid(mpp->alias);
-
-               if (wwid) {
-                       /* out of specified scope */
-                       if (refwwid && strncmp(wwid, refwwid, WWID_SIZE))
-                               continue;
-                       wwid = NULL;
-               }
-
-               condlog(3, "params = %s", mpp->params);
-               condlog(3, "status = %s", mpp->status);
-
-               /* will set mpp->wwid */
-               disassemble_map(pathvec, mpp->params, mpp);
-
-               /*
-                * disassemble_map may have added new paths to pathvec.
-                * If not in "fast list mode", we need to fetch information
-                * about them
-                */
-               if (conf->list != 1)
-                       update_paths(mpp);
-
-               if (conf->list > 1)
-                       select_path_group(mpp);
-
-               disassemble_status(mpp->status, mpp);
-
-               if (conf->list)
-                       print_mp(mpp);
-
-               if (!conf->dry_run)
-                       reinstate_paths(mpp);
-       }
+       printf("%s", reply);
+       FREE(reply);
        return 0;
 }
 
 int
 main (int argc, char *argv[])
 {
-       vector curmp = NULL;
-       vector pathvec = NULL;
-       int i;
-       int di_flag = 0;
        int arg;
        extern char *optarg;
        extern int optind;
-       char * refwwid = NULL;
+       int i, r = 1;
 
        if (getuid() != 0) {
                fprintf(stderr, "need to be root\n");
                exit(1);
        }
 
-       if (dm_prereq(DEFAULT_TARGET, 1, 0, 3))
+       if (dm_prereq())
                exit(1);
 
-       if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
-               condlog(0, "multipath tools need sysfs mounted");
-               exit(1);
-       }
        if (load_config(DEFAULT_CONFIGFILE))
                exit(1);
 
-       while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:")) != EOF ) {
+       if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) {
+               condlog(0, "multipath tools need sysfs mounted");
+               exit(1);
+       }
+       while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:Brt")) != EOF ) {
                switch(arg) {
                case 1: printf("optarg : %s\n",optarg);
                        break;
@@ -870,14 +408,20 @@ main (int argc, char *argv[])
 
                        conf->verbosity = atoi(optarg);
                        break;
+               case 'b':
+                       conf->bindings_file = strdup(optarg);
+                       break;
+               case 'B':
+                       conf->bindings_read_only = 1;
+                       break;
                case 'd':
                        conf->dry_run = 1;
                        break;
                case 'f':
-                       conf->remove = 1;
+                       conf->remove = FLUSH_ONE;
                        break;
                case 'F':
-                       conf->remove = 2;
+                       conf->remove = FLUSH_ALL;
                        break;
                case 'l':
                        conf->list = 1;
@@ -897,18 +441,26 @@ main (int argc, char *argv[])
                        if (conf->pgpolicy_flag == -1) {
                                printf("'%s' is not a valid policy\n", optarg);
                                usage(argv[0]);
-                       }                
+                       }
+                       break;
+               case 'r':
+                       conf->force_reload = 1;
                        break;
+               case 't':
+                       dump_config();
+                       goto out;
+               case 'h':
+                       usage(argv[0]);
                case ':':
                        fprintf(stderr, "Missing option arguement\n");
-                       usage(argv[0]);        
+                       usage(argv[0]);
                case '?':
                        fprintf(stderr, "Unknown switch: %s\n", optarg);
                        usage(argv[0]);
                default:
                        usage(argv[0]);
                }
-       }        
+       }
        if (optind < argc) {
                conf->dev = MALLOC(FILE_NAME_SIZE);
 
@@ -925,108 +477,62 @@ main (int argc, char *argv[])
                        conf->dev_type = DEV_DEVMAP;
 
        }
+       conf->daemon = 0;
 
-       if (conf->remove == FLUSH_ONE) {
-               if (conf->dev_type == DEV_DEVMAP)
-                       dm_flush_map(conf->dev, DEFAULT_TARGET);
-               else
-                       condlog(0, "must provide a map name to remove");
+       if (conf->max_fds) {
+               struct rlimit fd_limit;
 
-               goto out;
-       }
-       else if (conf->remove == FLUSH_ALL) {
-               dm_flush_maps(DEFAULT_TARGET);
-               goto out;
+               fd_limit.rlim_cur = conf->max_fds;
+               fd_limit.rlim_max = conf->max_fds;
+               if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
+                       condlog(0, "can't set open fds limit to %d : %s\n",
+                               conf->max_fds, strerror(errno));
        }
 
-       /*
-        * allocate core vectors to store paths and multipaths
-        */
-       curmp = vector_alloc();
-       pathvec = vector_alloc();
-
-       if (!curmp || !pathvec) {
-               condlog(0, "can not allocate memory");
-               goto out;
+       if (init_checkers()) {
+               condlog(0, "failed to initialize checkers");
+               exit(1);
        }
-
-       /*
-        * if we have a blacklisted device parameter, exit early
-        */
-       if (conf->dev && blacklist(conf->blist, conf->dev))
-               goto out;
-       
-       condlog(3, "load path identifiers cache");
-       cache_load(pathvec);
-
-       if (conf->verbosity > 2) {
-               fprintf(stdout, "#\n# all paths in cache :\n#\n");
-               print_all_paths(pathvec);
+       if (init_prio()) {
+               condlog(0, "failed to initialize prioritizers");
+               exit(1);
        }
+       dm_init();
 
-       /*
-        * get a path list
-        */
-       if (conf->dev)
-               di_flag = DI_WWID;
-
-       if (conf->list > 1)
-               /* extended path info '-ll' */
-               di_flag |= DI_SYSFS | DI_CHECKER;
-       else if (conf->list)
-               /* minimum path info '-l' */
-               di_flag |= DI_SYSFS;
-       else
-               /* maximum info */
-               di_flag = DI_ALL;
+       if (conf->remove == FLUSH_ONE) {
+               if (conf->dev_type == DEV_DEVMAP)
+                       r = dm_flush_map(conf->dev);
+               else
+                       condlog(0, "must provide a map name to remove");
 
-       if (path_discovery(pathvec, conf, di_flag))
                goto out;
-
-       if (conf->verbosity > 2) {
-               fprintf(stdout, "#\n# all paths :\n#\n");
-               print_all_paths(pathvec);
-       }
-
-       /*
-        * scope limiting must be translated into a wwid
-        * failing the translation is fatal (by policy)
-        */
-       if (conf->dev) {
-               refwwid = get_refwwid(pathvec);
-
-               if (!refwwid) {
-                       condlog(3, "scope is nul");
-                       goto out;
-               }
        }
-
-       get_path_layout(&pl, pathvec);
-
-       if (get_dm_mpvec(curmp, pathvec, refwwid))
+       else if (conf->remove == FLUSH_ALL) {
+               r = dm_flush_maps();
                goto out;
+       }
+       while ((r = configure()) < 0)
+               condlog(3, "restart multipath configuration process");
 
-       filter_pathvec(pathvec, refwwid);
+out:
+       udev_wait(conf->cookie);
 
-       if (conf->list)
-               goto out;
+       sysfs_cleanup();
+       dm_lib_release();
+       dm_lib_exit();
 
+       cleanup_prio();
+       cleanup_checkers();
        /*
-        * core logic entry point
+        * Freeing config must be done after dm_lib_exit(), because
+        * the logging function (dm_write_log()), which is called there,
+        * references the config.
         */
-       coalesce_paths(curmp, pathvec);
-
-out:
-       if (refwwid)
-               FREE(refwwid);
-
-       free_multipathvec(curmp, KEEP_PATHS);
-       free_pathvec(pathvec, FREE_PATHS);
        free_config(conf);
-       dm_lib_release();
-       dm_lib_exit();
+       conf = NULL;
+
 #ifdef _DEBUG_
        dbg_free_final(NULL);
 #endif
-       exit(0);
+       return r;
 }