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