multipath: add '-t' option to dump internal hwtable
[platform/upstream/multipath-tools.git] / multipath / main.c
1 /*
2  * Soft:        multipath device mapper target autoconfig
3  *
4  * Version:     $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
5  *
6  * Author:      Christophe Varoqui
7  *
8  *              This program is distributed in the hope that it will be useful,
9  *              but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *              See the GNU General Public License for more details.
12  *
13  *              This program is free software; you can redistribute it and/or
14  *              modify it under the terms of the GNU General Public License
15  *              as published by the Free Software Foundation; either version
16  *              2 of the License, or (at your option) any later version.
17  *
18  * Copyright (c) 2003, 2004, 2005 Christophe Varoqui
19  * Copyright (c) 2005 Benjamin Marzinski, Redhat
20  * Copyright (c) 2005 Kiyoshi Ueda, NEC
21  * Copyright (c) 2005 Patrick Caulfield, Redhat
22  * Copyright (c) 2005 Edward Goggin, EMC
23  */
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <ctype.h>
28
29 #include <checkers.h>
30 #include <prio.h>
31 #include <vector.h>
32 #include <memory.h>
33 #include <libdevmapper.h>
34 #include <devmapper.h>
35 #include <util.h>
36 #include <defaults.h>
37 #include <structs.h>
38 #include <structs_vec.h>
39 #include <dmparser.h>
40 #include <sysfs.h>
41 #include <config.h>
42 #include <blacklist.h>
43 #include <discovery.h>
44 #include <debug.h>
45 #include <switchgroup.h>
46 #include <print.h>
47 #include <alias.h>
48 #include <configure.h>
49 #include <pgpolicies.h>
50 #include <version.h>
51 #include <errno.h>
52 #include <sys/time.h>
53 #include <sys/resource.h>
54
55 int logsink;
56
57 static int
58 filter_pathvec (vector pathvec, char * refwwid)
59 {
60         int i;
61         struct path * pp;
62
63         if (!refwwid || !strlen(refwwid))
64                 return 0;
65
66         vector_foreach_slot (pathvec, pp, i) {
67                 if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
68                         condlog(3, "skip path %s : out of scope", pp->dev);
69                         free_path(pp);
70                         vector_del_slot(pathvec, i);
71                         i--;
72                 }
73         }
74         return 0;
75 }
76
77 static void
78 usage (char * progname)
79 {
80         fprintf (stderr, VERSION_STRING);
81         fprintf (stderr, "Usage:\n");
82         fprintf (stderr, "  %s [-d] [-r] [-v lvl] [-p pol] [-b fil] [dev]\n", progname);
83         fprintf (stderr, "  %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname);
84         fprintf (stderr, "  %s -F [-v lvl]\n", progname);
85         fprintf (stderr, "  %s -t\n", progname);
86         fprintf (stderr, "  %s -h\n", progname);
87         fprintf (stderr,
88                 "\n"
89                 "Where:\n"
90                 "  -h      print this usage text\n" \
91                 "  -l      show multipath topology (sysfs and DM info)\n" \
92                 "  -ll     show multipath topology (maximum info)\n" \
93                 "  -f      flush a multipath device map\n" \
94                 "  -F      flush all multipath device maps\n" \
95                 "  -d      dry run, do not create or update devmaps\n" \
96                 "  -t      dump internal hardware table\n" \
97                 "  -r      force devmap reload\n" \
98                 "  -p      policy failover|multibus|group_by_serial|group_by_prio\n" \
99                 "  -b fil  bindings file location\n" \
100                 "  -p pol  force all maps to specified path grouping policy :\n" \
101                 "          . failover            one path per priority group\n" \
102                 "          . multibus            all paths in one priority group\n" \
103                 "          . group_by_serial     one priority group per serial\n" \
104                 "          . group_by_prio       one priority group per priority lvl\n" \
105                 "          . group_by_node_name  one priority group per target node\n" \
106                 "  -v lvl  verbosity level\n" \
107                 "          . 0 no output\n" \
108                 "          . 1 print created devmap names only\n" \
109                 "          . 2 default verbosity\n" \
110                 "          . 3 print debug information\n" \
111                 "  dev     action limited to:\n" \
112                 "          . multipath named 'dev' (ex: mpath0) or\n" \
113                 "          . multipath whose wwid is 'dev' (ex: 60051..)\n" \
114                 "          . multipath including the path named 'dev' (ex: /dev/sda)\n" \
115                 "          . multipath including the path with maj:min 'dev' (ex: 8:0)\n" \
116                 );
117
118         exit(1);
119 }
120
121 static int
122 update_paths (struct multipath * mpp)
123 {
124         int i, j;
125         struct pathgroup * pgp;
126         struct path * pp;
127
128         if (!mpp->pg)
129                 return 0;
130
131         vector_foreach_slot (mpp->pg, pgp, i) {
132                 if (!pgp->paths)
133                         continue;
134
135                 vector_foreach_slot (pgp->paths, pp, j) {
136                         if (!strlen(pp->dev)) {
137                                 if (devt2devname(pp->dev, FILE_NAME_SIZE,
138                                                  pp->dev_t)) {
139                                         /*
140                                          * path is not in sysfs anymore
141                                          */
142                                         pp->state = PATH_DOWN;
143                                         continue;
144                                 }
145                                 pp->mpp = mpp;
146                                 pathinfo(pp, conf->hwtable, DI_ALL);
147                                 continue;
148                         }
149                         pp->mpp = mpp;
150                         if (pp->state == PATH_UNCHECKED ||
151                             pp->state == PATH_WILD)
152                                 pathinfo(pp, conf->hwtable, DI_CHECKER);
153
154                         if (pp->priority == PRIO_UNDEF)
155                                 pathinfo(pp, conf->hwtable, DI_PRIO);
156                 }
157         }
158         return 0;
159 }
160
161 static int
162 get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
163 {
164         int i;
165         struct multipath * mpp;
166         char params[PARAMS_SIZE], status[PARAMS_SIZE];
167
168         if (dm_get_maps(curmp))
169                 return 1;
170
171         vector_foreach_slot (curmp, mpp, i) {
172                 /*
173                  * discard out of scope maps
174                  */
175                 if (mpp->wwid && refwwid &&
176                     strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
177                         condlog(3, "skip map %s: out of scope", mpp->alias);
178                         free_multipath(mpp, KEEP_PATHS);
179                         vector_del_slot(curmp, i);
180                         i--;
181                         continue;
182                 }
183
184                 dm_get_map(mpp->alias, &mpp->size, params);
185                 condlog(3, "params = %s", params);
186                 dm_get_status(mpp->alias, status);
187                 condlog(3, "status = %s", status);
188
189                 disassemble_map(pathvec, params, mpp);
190
191                 /*
192                  * disassemble_map() can add new paths to pathvec.
193                  * If not in "fast list mode", we need to fetch information
194                  * about them
195                  */
196                 if (conf->list != 1)
197                         update_paths(mpp);
198
199                 if (conf->list > 1)
200                         mpp->bestpg = select_path_group(mpp);
201
202                 disassemble_status(status, mpp);
203
204                 if (conf->list)
205                         print_multipath_topology(mpp, conf->verbosity);
206
207                 if (!conf->dry_run)
208                         reinstate_paths(mpp);
209         }
210         return 0;
211 }
212
213
214 /*
215  * Return value:
216  *  -1: Retry
217  *   0: Success
218  *   1: Failure
219  */
220 static int
221 configure (void)
222 {
223         vector curmp = NULL;
224         vector pathvec = NULL;
225         struct vectors vecs;
226         int r = 1;
227         int di_flag = 0;
228         char * refwwid = NULL;
229         char * dev = NULL;
230
231         /*
232          * allocate core vectors to store paths and multipaths
233          */
234         curmp = vector_alloc();
235         pathvec = vector_alloc();
236
237         if (!curmp || !pathvec) {
238                 condlog(0, "can not allocate memory");
239                 goto out;
240         }
241         vecs.pathvec = pathvec;
242         vecs.mpvec = curmp;
243
244         /*
245          * dev is "/dev/" . "sysfs block dev"
246          */
247         if (conf->dev) {
248                 if (!strncmp(conf->dev, "/dev/", 5) &&
249                     strlen(conf->dev) > 5)
250                         dev = conf->dev + 5;
251                 else
252                         dev = conf->dev;
253         }
254
255         /*
256          * if we have a blacklisted device parameter, exit early
257          */
258         if (dev &&
259             (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0))
260                         goto out;
261
262         /*
263          * scope limiting must be translated into a wwid
264          * failing the translation is fatal (by policy)
265          */
266         if (conf->dev) {
267                 refwwid = get_refwwid(conf->dev, conf->dev_type, pathvec);
268
269                 if (!refwwid) {
270                         condlog(3, "scope is nul");
271                         goto out;
272                 }
273                 condlog(3, "scope limited to %s", refwwid);
274                 if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
275                                 refwwid) > 0)
276                         goto out;
277         }
278
279         /*
280          * get a path list
281          */
282         if (conf->dev)
283                 di_flag = DI_WWID;
284
285         if (conf->list > 1)
286                 /* extended path info '-ll' */
287                 di_flag |= DI_SYSFS | DI_CHECKER;
288         else if (conf->list)
289                 /* minimum path info '-l' */
290                 di_flag |= DI_SYSFS;
291         else
292                 /* maximum info */
293                 di_flag = DI_ALL;
294
295         if (path_discovery(pathvec, conf, di_flag))
296                 goto out;
297
298         if (conf->verbosity > 2)
299                 print_all_paths(pathvec, 1);
300
301         get_path_layout(pathvec, 0);
302
303         if (get_dm_mpvec(curmp, pathvec, refwwid))
304                 goto out;
305
306         filter_pathvec(pathvec, refwwid);
307
308         if (conf->list) {
309                 r = 0;
310                 goto out;
311         }
312
313         /*
314          * core logic entry point
315          */
316         r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload);
317
318 out:
319         if (refwwid)
320                 FREE(refwwid);
321
322         free_multipathvec(curmp, KEEP_PATHS);
323         free_pathvec(pathvec, FREE_PATHS);
324
325         return r;
326 }
327
328 static int
329 dump_config (void)
330 {
331         char * c;
332         char * reply;
333         unsigned int maxlen = 256;
334         int again = 1;
335
336         reply = MALLOC(maxlen);
337
338         while (again) {
339                 if (!reply)
340                         return 1;
341                 c = reply;
342                 c += snprint_defaults(c, reply + maxlen - c);
343                 again = ((c - reply) == maxlen);
344                 if (again) {
345                         reply = REALLOC(reply, maxlen *= 2);
346                         continue;
347                 }
348                 c += snprint_blacklist(c, reply + maxlen - c);
349                 again = ((c - reply) == maxlen);
350                 if (again) {
351                         reply = REALLOC(reply, maxlen *= 2);
352                         continue;
353                 }
354                 c += snprint_blacklist_except(c, reply + maxlen - c);
355                 again = ((c - reply) == maxlen);
356                 if (again) {
357                         reply = REALLOC(reply, maxlen *= 2);
358                         continue;
359                 }
360                 c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable);
361                 again = ((c - reply) == maxlen);
362                 if (again) {
363                         reply = REALLOC(reply, maxlen *= 2);
364                         continue;
365                 }
366                 c += snprint_mptable(c, reply + maxlen - c, conf->mptable);
367                 again = ((c - reply) == maxlen);
368                 if (again)
369                         reply = REALLOC(reply, maxlen *= 2);
370         }
371
372         printf("%s", reply);
373         FREE(reply);
374         return 0;
375 }
376
377 int
378 main (int argc, char *argv[])
379 {
380         int arg;
381         extern char *optarg;
382         extern int optind;
383         int i, r = 1;
384
385         if (getuid() != 0) {
386                 fprintf(stderr, "need to be root\n");
387                 exit(1);
388         }
389
390         if (dm_prereq())
391                 exit(1);
392
393         if (load_config(DEFAULT_CONFIGFILE))
394                 exit(1);
395
396         if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) {
397                 condlog(0, "multipath tools need sysfs mounted");
398                 exit(1);
399         }
400         while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:Brt")) != EOF ) {
401                 switch(arg) {
402                 case 1: printf("optarg : %s\n",optarg);
403                         break;
404                 case 'v':
405                         if (sizeof(optarg) > sizeof(char *) ||
406                             !isdigit(optarg[0]))
407                                 usage (argv[0]);
408
409                         conf->verbosity = atoi(optarg);
410                         break;
411                 case 'b':
412                         conf->bindings_file = strdup(optarg);
413                         break;
414                 case 'B':
415                         conf->bindings_read_only = 1;
416                         break;
417                 case 'd':
418                         conf->dry_run = 1;
419                         break;
420                 case 'f':
421                         conf->remove = FLUSH_ONE;
422                         break;
423                 case 'F':
424                         conf->remove = FLUSH_ALL;
425                         break;
426                 case 'l':
427                         conf->list = 1;
428                         conf->dry_run = 1;
429
430                         if (optarg && !strncmp(optarg, "l", 1))
431                                 conf->list++;
432
433                         break;
434                 case 'M':
435 #if _DEBUG_
436                         debug = atoi(optarg);
437 #endif
438                         break;
439                 case 'p':
440                         conf->pgpolicy_flag = get_pgpolicy_id(optarg);
441                         if (conf->pgpolicy_flag == -1) {
442                                 printf("'%s' is not a valid policy\n", optarg);
443                                 usage(argv[0]);
444                         }
445                         break;
446                 case 'r':
447                         conf->force_reload = 1;
448                         break;
449                 case 't':
450                         dump_config();
451                         goto out;
452                 case 'h':
453                         usage(argv[0]);
454                 case ':':
455                         fprintf(stderr, "Missing option arguement\n");
456                         usage(argv[0]);
457                 case '?':
458                         fprintf(stderr, "Unknown switch: %s\n", optarg);
459                         usage(argv[0]);
460                 default:
461                         usage(argv[0]);
462                 }
463         }
464         if (optind < argc) {
465                 conf->dev = MALLOC(FILE_NAME_SIZE);
466
467                 if (!conf->dev)
468                         goto out;
469
470                 strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
471
472                 if (filepresent(conf->dev))
473                         conf->dev_type = DEV_DEVNODE;
474                 else if (sscanf(conf->dev, "%d:%d", &i, &i) == 2)
475                         conf->dev_type = DEV_DEVT;
476                 else
477                         conf->dev_type = DEV_DEVMAP;
478
479         }
480         conf->daemon = 0;
481
482         if (conf->max_fds) {
483                 struct rlimit fd_limit;
484
485                 fd_limit.rlim_cur = conf->max_fds;
486                 fd_limit.rlim_max = conf->max_fds;
487                 if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
488                         condlog(0, "can't set open fds limit to %d : %s\n",
489                                 conf->max_fds, strerror(errno));
490         }
491
492         if (init_checkers()) {
493                 condlog(0, "failed to initialize checkers");
494                 exit(1);
495         }
496         if (init_prio()) {
497                 condlog(0, "failed to initialize prioritizers");
498                 exit(1);
499         }
500         dm_init();
501
502         if (conf->remove == FLUSH_ONE) {
503                 if (conf->dev_type == DEV_DEVMAP)
504                         r = dm_flush_map(conf->dev);
505                 else
506                         condlog(0, "must provide a map name to remove");
507
508                 goto out;
509         }
510         else if (conf->remove == FLUSH_ALL) {
511                 r = dm_flush_maps();
512                 goto out;
513         }
514         while ((r = configure()) < 0)
515                 condlog(3, "restart multipath configuration process");
516
517 out:
518         udev_wait(conf->cookie);
519
520         sysfs_cleanup();
521         dm_lib_release();
522         dm_lib_exit();
523
524         cleanup_prio();
525         cleanup_checkers();
526         /*
527          * Freeing config must be done after dm_lib_exit(), because
528          * the logging function (dm_write_log()), which is called there,
529          * references the config.
530          */
531         free_config(conf);
532         conf = NULL;
533
534 #ifdef _DEBUG_
535         dbg_free_final(NULL);
536 #endif
537         return r;
538 }