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