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