add wwids file cleanup options
[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 <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <ctype.h>
30
31 #include <checkers.h>
32 #include <prio.h>
33 #include <vector.h>
34 #include <memory.h>
35 #include <libdevmapper.h>
36 #include <devmapper.h>
37 #include <util.h>
38 #include <defaults.h>
39 #include <structs.h>
40 #include <structs_vec.h>
41 #include <dmparser.h>
42 #include <sysfs.h>
43 #include <config.h>
44 #include <blacklist.h>
45 #include <discovery.h>
46 #include <debug.h>
47 #include <switchgroup.h>
48 #include <print.h>
49 #include <alias.h>
50 #include <configure.h>
51 #include <pgpolicies.h>
52 #include <version.h>
53 #include <errno.h>
54 #include <sys/time.h>
55 #include <sys/resource.h>
56 #include <wwids.h>
57 #include "dev_t.h"
58
59 int logsink;
60
61 static int
62 filter_pathvec (vector pathvec, char * refwwid)
63 {
64         int i;
65         struct path * pp;
66
67         if (!refwwid || !strlen(refwwid))
68                 return 0;
69
70         vector_foreach_slot (pathvec, pp, i) {
71                 if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
72                         condlog(3, "skip path %s : out of scope", pp->dev);
73                         free_path(pp);
74                         vector_del_slot(pathvec, i);
75                         i--;
76                 }
77         }
78         return 0;
79 }
80
81 static void
82 usage (char * progname)
83 {
84         fprintf (stderr, VERSION_STRING);
85         fprintf (stderr, "Usage:\n");
86         fprintf (stderr, "  %s [-c|-w|-W] [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname);
87         fprintf (stderr, "  %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname);
88         fprintf (stderr, "  %s -F [-v lvl]\n", progname);
89         fprintf (stderr, "  %s -t\n", progname);
90         fprintf (stderr, "  %s -h\n", progname);
91         fprintf (stderr,
92                 "\n"
93                 "Where:\n"
94                 "  -h      print this usage text\n" \
95                 "  -l      show multipath topology (sysfs and DM info)\n" \
96                 "  -ll     show multipath topology (maximum info)\n" \
97                 "  -f      flush a multipath device map\n" \
98                 "  -F      flush all multipath device maps\n" \
99                 "  -c      check if a device should be a path in a multipath device\n" \
100                 "  -q      allow queue_if_no_path when multipathd is not running\n"\
101                 "  -d      dry run, do not create or update devmaps\n" \
102                 "  -t      dump internal hardware table\n" \
103                 "  -r      force devmap reload\n" \
104                 "  -B      treat the bindings file as read only\n" \
105                 "  -p      policy failover|multibus|group_by_serial|group_by_prio\n" \
106                 "  -b fil  bindings file location\n" \
107                 "  -w      remove a device from the wwids file\n" \
108                 "  -W      reset the wwids file include only the current devices\n" \
109                 "  -p pol  force all maps to specified path grouping policy :\n" \
110                 "          . failover            one path per priority group\n" \
111                 "          . multibus            all paths in one priority group\n" \
112                 "          . group_by_serial     one priority group per serial\n" \
113                 "          . group_by_prio       one priority group per priority lvl\n" \
114                 "          . group_by_node_name  one priority group per target node\n" \
115                 "  -v lvl  verbosity level\n" \
116                 "          . 0 no output\n" \
117                 "          . 1 print created devmap names only\n" \
118                 "          . 2 default verbosity\n" \
119                 "          . 3 print debug information\n" \
120                 "  dev     action limited to:\n" \
121                 "          . multipath named 'dev' (ex: mpath0) or\n" \
122                 "          . multipath whose wwid is 'dev' (ex: 60051..)\n" \
123                 "          . multipath including the path named 'dev' (ex: /dev/sda)\n" \
124                 "          . multipath including the path with maj:min 'dev' (ex: 8:0)\n" \
125                 );
126
127 }
128
129 static int
130 update_paths (struct multipath * mpp)
131 {
132         int i, j;
133         struct pathgroup * pgp;
134         struct path * pp;
135
136         if (!mpp->pg)
137                 return 0;
138
139         vector_foreach_slot (mpp->pg, pgp, i) {
140                 if (!pgp->paths)
141                         continue;
142
143                 vector_foreach_slot (pgp->paths, pp, j) {
144                         if (!strlen(pp->dev)) {
145                                 if (devt2devname(pp->dev, FILE_NAME_SIZE,
146                                                  pp->dev_t)) {
147                                         /*
148                                          * path is not in sysfs anymore
149                                          */
150                                         pp->chkrstate = pp->state = PATH_DOWN;
151                                         continue;
152                                 }
153                                 pp->mpp = mpp;
154                                 pathinfo(pp, conf->hwtable, DI_ALL);
155                                 continue;
156                         }
157                         pp->mpp = mpp;
158                         if (pp->state == PATH_UNCHECKED ||
159                             pp->state == PATH_WILD)
160                                 pathinfo(pp, conf->hwtable, DI_CHECKER);
161
162                         if (pp->priority == PRIO_UNDEF)
163                                 pathinfo(pp, conf->hwtable, DI_PRIO);
164                 }
165         }
166         return 0;
167 }
168
169 static int
170 get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
171 {
172         int i;
173         struct multipath * mpp;
174         char params[PARAMS_SIZE], status[PARAMS_SIZE];
175
176         if (dm_get_maps(curmp))
177                 return 1;
178
179         vector_foreach_slot (curmp, mpp, i) {
180                 /*
181                  * discard out of scope maps
182                  */
183                 if (mpp->wwid && refwwid &&
184                     strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
185                         condlog(3, "skip map %s: out of scope", mpp->alias);
186                         free_multipath(mpp, KEEP_PATHS);
187                         vector_del_slot(curmp, i);
188                         i--;
189                         continue;
190                 }
191
192                 dm_get_map(mpp->alias, &mpp->size, params);
193                 condlog(3, "params = %s", params);
194                 dm_get_status(mpp->alias, status);
195                 condlog(3, "status = %s", status);
196
197                 disassemble_map(pathvec, params, mpp);
198
199                 /*
200                  * disassemble_map() can add new paths to pathvec.
201                  * If not in "fast list mode", we need to fetch information
202                  * about them
203                  */
204                 if (conf->list != 1)
205                         update_paths(mpp);
206
207                 if (conf->list > 1)
208                         mpp->bestpg = select_path_group(mpp);
209
210                 disassemble_status(status, mpp);
211
212                 if (conf->list)
213                         print_multipath_topology(mpp, conf->verbosity);
214
215                 if (!conf->dry_run)
216                         reinstate_paths(mpp);
217         }
218         return 0;
219 }
220
221
222 /*
223  * Return value:
224  *  -1: Retry
225  *   0: Success
226  *   1: Failure
227  */
228 static int
229 configure (void)
230 {
231         vector curmp = NULL;
232         vector pathvec = NULL;
233         struct vectors vecs;
234         int r = 1;
235         int di_flag = 0;
236         char * refwwid = NULL;
237         char * dev = NULL;
238
239         /*
240          * allocate core vectors to store paths and multipaths
241          */
242         curmp = vector_alloc();
243         pathvec = vector_alloc();
244
245         if (!curmp || !pathvec) {
246                 condlog(0, "can not allocate memory");
247                 goto out;
248         }
249         vecs.pathvec = pathvec;
250         vecs.mpvec = curmp;
251
252         /*
253          * dev is "/dev/" . "sysfs block dev"
254          */
255         if (conf->dev) {
256                 if (!strncmp(conf->dev, "/dev/", 5) &&
257                     strlen(conf->dev) > 5)
258                         dev = conf->dev + 5;
259                 else
260                         dev = conf->dev;
261         }
262
263         /*
264          * if we have a blacklisted device parameter, exit early
265          */
266         if (dev && conf->dev_type == DEV_DEVNODE && conf->dry_run != 3 &&
267             (filter_devnode(conf->blist_devnode,
268                             conf->elist_devnode, dev) > 0)) {
269                 if (conf->dry_run == 2)
270                         printf("%s is not a valid multipath device path\n",
271                                conf->dev);
272                 goto out;
273         }
274         /*
275          * scope limiting must be translated into a wwid
276          * failing the translation is fatal (by policy)
277          */
278         if (conf->dev) {
279                 int failed = get_refwwid(conf->dev, conf->dev_type, pathvec,
280                                          &refwwid);
281                 if (!refwwid) {
282                         if (failed == 2 && conf->dry_run == 2)
283                                 printf("%s is not a valid multipath device path\n", conf->dev);
284                         else
285                                 condlog(3, "scope is nul");
286                         goto out;
287                 }
288                 if (conf->dry_run == 3) {
289                         r = remove_wwid(refwwid);
290                         if (r == 0)
291                                 printf("wwid '%s' removed\n", refwwid);
292                         else if (r == 1) {
293                                 printf("wwid '%s' not in wwids file\n",
294                                         refwwid);
295                                 r = 0;
296                         }
297                         goto out;
298                 }
299                 condlog(3, "scope limited to %s", refwwid);
300                 if (conf->dry_run == 2) {
301                         if (check_wwids_file(refwwid, 0) == 0){
302                                 printf("%s is a valid multipath device path\n", conf->dev);
303                                 r = 0;
304                         }
305                         else
306                                 printf("%s is not a valid multipath device path\n", conf->dev);
307                         goto out;
308                 }
309         }
310
311         /*
312          * get a path list
313          */
314         if (conf->dev)
315                 di_flag = DI_WWID;
316
317         if (conf->list > 1)
318                 /* extended path info '-ll' */
319                 di_flag |= DI_SYSFS | DI_CHECKER;
320         else if (conf->list)
321                 /* minimum path info '-l' */
322                 di_flag |= DI_SYSFS;
323         else
324                 /* maximum info */
325                 di_flag = DI_ALL;
326
327         if (path_discovery(pathvec, conf, di_flag))
328                 goto out;
329
330         if (conf->verbosity > 2)
331                 print_all_paths(pathvec, 1);
332
333         get_path_layout(pathvec, 0);
334
335         if (get_dm_mpvec(curmp, pathvec, refwwid))
336                 goto out;
337
338         filter_pathvec(pathvec, refwwid);
339
340         if (conf->list) {
341                 r = 0;
342                 goto out;
343         }
344
345         /*
346          * core logic entry point
347          */
348         r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload);
349
350 out:
351         if (refwwid)
352                 FREE(refwwid);
353
354         free_multipathvec(curmp, KEEP_PATHS);
355         free_pathvec(pathvec, FREE_PATHS);
356
357         return r;
358 }
359
360 static int
361 dump_config (void)
362 {
363         char * c;
364         char * reply;
365         unsigned int maxlen = 256;
366         int again = 1;
367
368         reply = MALLOC(maxlen);
369
370         while (again) {
371                 if (!reply)
372                         return 1;
373                 c = reply;
374                 c += snprint_defaults(c, reply + maxlen - c);
375                 again = ((c - reply) == maxlen);
376                 if (again) {
377                         reply = REALLOC(reply, maxlen *= 2);
378                         continue;
379                 }
380                 c += snprint_blacklist(c, reply + maxlen - c);
381                 again = ((c - reply) == maxlen);
382                 if (again) {
383                         reply = REALLOC(reply, maxlen *= 2);
384                         continue;
385                 }
386                 c += snprint_blacklist_except(c, reply + maxlen - c);
387                 again = ((c - reply) == maxlen);
388                 if (again) {
389                         reply = REALLOC(reply, maxlen *= 2);
390                         continue;
391                 }
392                 c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable);
393                 again = ((c - reply) == maxlen);
394                 if (again) {
395                         reply = REALLOC(reply, maxlen *= 2);
396                         continue;
397                 }
398                 c += snprint_mptable(c, reply + maxlen - c, conf->mptable);
399                 again = ((c - reply) == maxlen);
400                 if (again)
401                         reply = REALLOC(reply, maxlen *= 2);
402         }
403
404         printf("%s", reply);
405         FREE(reply);
406         return 0;
407 }
408
409 static int
410 get_dev_type(char *dev) {
411         struct stat buf;
412         int i;
413
414         if (stat(dev, &buf) == 0 && S_ISBLK(buf.st_mode)) {
415                 if (dm_is_dm_major(MAJOR(buf.st_rdev)))
416                         return DEV_DEVMAP;
417                 return DEV_DEVNODE;
418         }
419         else if (sscanf(dev, "%d:%d", &i, &i) == 2)
420                 return DEV_DEVT;
421         else
422                 return DEV_DEVMAP;
423 }
424
425 static void
426 convert_dev(char *dev)
427 {
428         char *ptr = strstr(dev, "cciss/");
429         if (ptr) {
430                 ptr += 5;
431                 *ptr = '!';
432         }
433 }
434
435 int
436 main (int argc, char *argv[])
437 {
438         int arg;
439         extern char *optarg;
440         extern int optind;
441         int r = 1;
442
443         if (getuid() != 0) {
444                 fprintf(stderr, "need to be root\n");
445                 exit(1);
446         }
447
448         if (dm_prereq())
449                 exit(1);
450
451         if (load_config(DEFAULT_CONFIGFILE))
452                 exit(1);
453
454         while ((arg = getopt(argc, argv, ":dchl::FfM:v:p:b:BrtqwW")) != EOF ) {
455                 switch(arg) {
456                 case 1: printf("optarg : %s\n",optarg);
457                         break;
458                 case 'v':
459                         if (sizeof(optarg) > sizeof(char *) ||
460                             !isdigit(optarg[0])) {
461                                 usage (argv[0]);
462                                 exit(1);
463                         }
464
465                         conf->verbosity = atoi(optarg);
466                         break;
467                 case 'b':
468                         conf->bindings_file = strdup(optarg);
469                         break;
470                 case 'B':
471                         conf->bindings_read_only = 1;
472                         break;
473                 case 'q':
474                         conf->allow_queueing = 1;
475                         break;
476                 case 'c':
477                         conf->dry_run = 2;
478                         break;
479                 case 'd':
480                         if (!conf->dry_run)
481                                 conf->dry_run = 1;
482                         break;
483                 case 'f':
484                         conf->remove = FLUSH_ONE;
485                         break;
486                 case 'F':
487                         conf->remove = FLUSH_ALL;
488                         break;
489                 case 'l':
490                         conf->list = 1;
491                         conf->dry_run = 1;
492
493                         if (optarg && !strncmp(optarg, "l", 1))
494                                 conf->list++;
495
496                         break;
497                 case 'M':
498 #if _DEBUG_
499                         debug = atoi(optarg);
500 #endif
501                         break;
502                 case 'p':
503                         conf->pgpolicy_flag = get_pgpolicy_id(optarg);
504                         if (conf->pgpolicy_flag == -1) {
505                                 printf("'%s' is not a valid policy\n", optarg);
506                                 usage(argv[0]);
507                                 exit(1);
508                         }
509                         break;
510                 case 'r':
511                         conf->force_reload = 1;
512                         break;
513                 case 't':
514                         r = dump_config();
515                         goto out;
516                 case 'h':
517                         usage(argv[0]);
518                         exit(0);
519                 case 'w':
520                         conf->dry_run = 3;
521                         break;
522                 case 'W':
523                         conf->dry_run = 4;
524                         break;
525                 case ':':
526                         fprintf(stderr, "Missing option argument\n");
527                         usage(argv[0]);
528                         exit(1);
529                 case '?':
530                         fprintf(stderr, "Unknown switch: %s\n", optarg);
531                         usage(argv[0]);
532                         exit(1);
533                 default:
534                         usage(argv[0]);
535                         exit(1);
536                 }
537         }
538         if (optind < argc) {
539                 conf->dev = MALLOC(FILE_NAME_SIZE);
540
541                 if (!conf->dev)
542                         goto out;
543
544                 strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
545                 conf->dev_type = get_dev_type(conf->dev);
546                 if (conf->dev_type == DEV_DEVNODE)
547                         convert_dev(conf->dev);
548         }
549         conf->daemon = 0;
550
551         if (conf->max_fds) {
552                 struct rlimit fd_limit;
553
554                 fd_limit.rlim_cur = conf->max_fds;
555                 fd_limit.rlim_max = conf->max_fds;
556                 if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
557                         condlog(0, "can't set open fds limit to %d : %s",
558                                 conf->max_fds, strerror(errno));
559         }
560
561         if (init_checkers()) {
562                 condlog(0, "failed to initialize checkers");
563                 exit(1);
564         }
565         if (init_prio()) {
566                 condlog(0, "failed to initialize prioritizers");
567                 exit(1);
568         }
569         dm_init();
570
571         if (conf->dry_run == 2 &&
572             (!conf->dev || conf->dev_type == DEV_DEVMAP)) {
573                 condlog(0, "the -c option requires a path to check");
574                 goto out;
575         }
576         if (conf->dry_run == 3 && !conf->dev) {
577                 condlog(0, "the -w option requires a device");
578                 goto out;
579         }
580         if (conf->dry_run == 4) {
581                 struct multipath * mpp;
582                 int i;
583                 vector curmp;
584
585                 curmp = vector_alloc();
586                 if (!curmp) {
587                         condlog(0, "can't allocate memory for mp list");
588                         goto out;
589                 }
590                 if (dm_get_maps(curmp) == 0)
591                         r = replace_wwids(curmp);
592                 if (r == 0)
593                         printf("successfully reset wwids\n");
594                 vector_foreach_slot_backwards(curmp, mpp, i) {
595                         vector_del_slot(curmp, i);
596                         free_multipath(mpp, KEEP_PATHS);
597                 }
598                 vector_free(curmp);
599                 goto out;
600         }
601         if (conf->remove == FLUSH_ONE) {
602                 if (conf->dev_type == DEV_DEVMAP) {
603                         r = dm_suspend_and_flush_map(conf->dev);
604                 } else
605                         condlog(0, "must provide a map name to remove");
606
607                 goto out;
608         }
609         else if (conf->remove == FLUSH_ALL) {
610                 r = dm_flush_maps();
611                 goto out;
612         }
613         while ((r = configure()) < 0)
614                 condlog(3, "restart multipath configuration process");
615
616 out:
617         udev_wait(conf->cookie);
618
619         dm_lib_release();
620         dm_lib_exit();
621
622         cleanup_prio();
623         cleanup_checkers();
624         /*
625          * Freeing config must be done after dm_lib_exit(), because
626          * the logging function (dm_write_log()), which is called there,
627          * references the config.
628          */
629         free_config(conf);
630         conf = NULL;
631
632 #ifdef _DEBUG_
633         dbg_free_final(NULL);
634 #endif
635         return r;
636 }