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