Unload prio and checkers libraries during shutdown.
[platform/upstream/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 <stdio.h>
26 #include <unistd.h>
27 #include <ctype.h>
28
29 #include <checkers.h>
30 #include <prio.h>
31 #include <vector.h>
32 #include <memory.h>
33 #include <libdevmapper.h>
34 #include <devmapper.h>
35 #include <util.h>
36 #include <defaults.h>
37 #include <structs.h>
38 #include <structs_vec.h>
39 #include <dmparser.h>
40 #include <sysfs.h>
41 #include <config.h>
42 #include <blacklist.h>
43 #include <discovery.h>
44 #include <debug.h>
45 #include <switchgroup.h>
46 #include <print.h>
47 #include <alias.h>
48 #include <configure.h>
49 #include <pgpolicies.h>
50 #include <version.h>
51
52 int logsink;
53
54 static int
55 filter_pathvec (vector pathvec, char * refwwid)
56 {
57         int i;
58         struct path * pp;
59
60         if (!refwwid || !strlen(refwwid))
61                 return 0;
62
63         vector_foreach_slot (pathvec, pp, i) {
64                 if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
65                         condlog(3, "skip path %s : out of scope", pp->dev);
66                         free_path(pp);
67                         vector_del_slot(pathvec, i);
68                         i--;
69                 }
70         }
71         return 0;
72 }
73
74 static void
75 usage (char * progname)
76 {
77         fprintf (stderr, VERSION_STRING);
78         fprintf (stderr, "Usage:\n");
79         fprintf (stderr, "  %s [-d] [-r] [-v lvl] [-p pol] [-b fil] [dev]\n", progname);
80         fprintf (stderr, "  %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname);
81         fprintf (stderr, "  %s -F [-v lvl]\n", progname);
82         fprintf (stderr, "  %s -h\n", progname);
83         fprintf (stderr,
84                 "\n"
85                 "Where:\n"
86                 "  -h      print this usage text\n" \
87                 "  -l      show multipath topology (sysfs and DM info)\n" \
88                 "  -ll     show multipath topology (maximum info)\n" \
89                 "  -f      flush a multipath device map\n" \
90                 "  -F      flush all multipath device maps\n" \
91                 "  -d      dry run, do not create or update devmaps\n" \
92                 "  -r      force devmap reload\n" \
93                 "  -p      policy failover|multibus|group_by_serial|group_by_prio\n" \
94                 "  -b fil  bindings file location\n" \
95                 "  -p pol  force all maps to specified path grouping policy :\n" \
96                 "          . failover            one path per priority group\n" \
97                 "          . multibus            all paths in one priority group\n" \
98                 "          . group_by_serial     one priority group per serial\n" \
99                 "          . group_by_prio       one priority group per priority lvl\n" \
100                 "          . group_by_node_name  one priority group per target node\n" \
101                 "  -v lvl  verbosity level\n" \
102                 "          . 0 no output\n" \
103                 "          . 1 print created devmap names only\n" \
104                 "          . 2 default verbosity\n" \
105                 "          . 3 print debug information\n" \
106                 "  dev     action limited to:\n" \
107                 "          . multipath named 'dev' (ex: mpath0) or\n" \
108                 "          . multipath whose wwid is 'dev' (ex: 60051..)\n" \
109                 "          . multipath including the path named 'dev' (ex: /dev/sda)\n" \
110                 "          . multipath including the path with maj:min 'dev' (ex: 8:0)\n" \
111                 );
112
113         exit(1);
114 }
115
116 static int
117 update_paths (struct multipath * mpp)
118 {
119         int i, j;
120         struct pathgroup * pgp;
121         struct path * pp;
122
123         if (!mpp->pg)
124                 return 0;
125
126         vector_foreach_slot (mpp->pg, pgp, i) {
127                 if (!pgp->paths)
128                         continue;
129
130                 vector_foreach_slot (pgp->paths, pp, j) {
131                         if (!strlen(pp->dev)) {
132                                 if (devt2devname(pp->dev, pp->dev_t)) {
133                                         /*
134                                          * path is not in sysfs anymore
135                                          */
136                                         pp->state = PATH_DOWN;
137                                         continue;
138                                 }
139                                 pp->mpp = mpp;
140                                 pathinfo(pp, conf->hwtable, DI_ALL);
141                                 continue;
142                         }
143                         pp->mpp = mpp;
144                         if (pp->state == PATH_UNCHECKED ||
145                             pp->state == PATH_WILD)
146                                 pathinfo(pp, conf->hwtable, DI_CHECKER);
147
148                         if (pp->priority == PRIO_UNDEF)
149                                 pathinfo(pp, conf->hwtable, DI_PRIO);
150                 }
151         }
152         return 0;
153 }
154
155 static int
156 get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
157 {
158         int i;
159         struct multipath * mpp;
160
161         if (dm_get_maps(curmp))
162                 return 1;
163
164         vector_foreach_slot (curmp, mpp, i) {
165                 /*
166                  * discard out of scope maps
167                  */
168                 if (mpp->wwid && refwwid &&
169                     strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
170                         condlog(3, "skip map %s: out of scope", mpp->alias);
171                         free_multipath(mpp, KEEP_PATHS);
172                         vector_del_slot(curmp, i);
173                         i--;
174                         continue;
175                 }
176
177                 condlog(3, "params = %s", mpp->params);
178                 condlog(3, "status = %s", mpp->status);
179
180                 disassemble_map(pathvec, mpp->params, mpp);
181
182                 /*
183                  * disassemble_map() can add new paths to pathvec.
184                  * If not in "fast list mode", we need to fetch information
185                  * about them
186                  */
187                 if (conf->list != 1)
188                         update_paths(mpp);
189
190                 if (conf->list > 1)
191                         mpp->bestpg = select_path_group(mpp);
192
193                 disassemble_status(mpp->status, mpp);
194
195                 if (conf->list)
196                         print_multipath_topology(mpp, conf->verbosity);
197
198                 if (!conf->dry_run)
199                         reinstate_paths(mpp);
200         }
201         return 0;
202 }
203
204
205 /*
206  * Return value:
207  *  -1: Retry
208  *   0: Success
209  *   1: Failure
210  */
211 static int
212 configure (void)
213 {
214         vector curmp = NULL;
215         vector pathvec = NULL;
216         struct vectors vecs;
217         int r = 1;
218         int di_flag = 0;
219         char * refwwid = NULL;
220         char * dev = NULL;
221
222         /*
223          * allocate core vectors to store paths and multipaths
224          */
225         curmp = vector_alloc();
226         pathvec = vector_alloc();
227
228         if (!curmp || !pathvec) {
229                 condlog(0, "can not allocate memory");
230                 goto out;
231         }
232         vecs.pathvec = pathvec;
233         vecs.mpvec = curmp;
234
235         /*
236          * dev is "/dev/" . "sysfs block dev"
237          */
238         if (conf->dev) {
239                 if (!strncmp(conf->dev, "/dev/", 5) &&
240                     strlen(conf->dev) > 5)
241                         dev = conf->dev + 5;
242                 else
243                         dev = conf->dev;
244         }
245         
246         /*
247          * if we have a blacklisted device parameter, exit early
248          */
249         if (dev && 
250             (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0))
251                         goto out;
252         
253         /*
254          * scope limiting must be translated into a wwid
255          * failing the translation is fatal (by policy)
256          */
257         if (conf->dev) {
258                 refwwid = get_refwwid(conf->dev, conf->dev_type, pathvec);
259
260                 if (!refwwid) {
261                         condlog(3, "scope is nul");
262                         goto out;
263                 }
264                 condlog(3, "scope limited to %s", refwwid);
265                 if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
266                                 refwwid) > 0)
267                         goto out;
268         }
269
270         /*
271          * get a path list
272          */
273         if (conf->dev)
274                 di_flag = DI_WWID;
275
276         if (conf->list > 1)
277                 /* extended path info '-ll' */
278                 di_flag |= DI_SYSFS | DI_CHECKER;
279         else if (conf->list)
280                 /* minimum path info '-l' */
281                 di_flag |= DI_SYSFS;
282         else
283                 /* maximum info */
284                 di_flag = DI_ALL;
285
286         if (path_discovery(pathvec, conf, di_flag))
287                 goto out;
288
289         if (conf->verbosity > 2)
290                 print_all_paths(pathvec, 1);
291
292         get_path_layout(pathvec, 0);
293
294         if (get_dm_mpvec(curmp, pathvec, refwwid))
295                 goto out;
296
297         filter_pathvec(pathvec, refwwid);
298
299         if (conf->list) {
300                 r = 0;
301                 goto out;
302         }
303
304         /*
305          * core logic entry point
306          */
307         r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload);
308
309 out:
310         if (refwwid)
311                 FREE(refwwid);
312
313         free_multipathvec(curmp, KEEP_PATHS);
314         free_pathvec(pathvec, FREE_PATHS);
315
316         return r;
317 }
318
319 int
320 main (int argc, char *argv[])
321 {
322         int arg;
323         extern char *optarg;
324         extern int optind;
325         int i, r = 1;
326
327         if (getuid() != 0) {
328                 fprintf(stderr, "need to be root\n");
329                 exit(1);
330         }
331
332         if (dm_prereq())
333                 exit(1);
334
335         if (load_config(DEFAULT_CONFIGFILE))
336                 exit(1);
337
338         if (init_checkers()) {
339                 condlog(0, "failed to initialize checkers");
340                 exit(1);
341         }
342         if (init_prio()) {
343                 condlog(0, "failed to initialize prioritizers");
344                 exit(1);
345         }
346         if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) {
347                 condlog(0, "multipath tools need sysfs mounted");
348                 exit(1);
349         }
350         while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:r")) != EOF ) {
351                 switch(arg) {
352                 case 1: printf("optarg : %s\n",optarg);
353                         break;
354                 case 'v':
355                         if (sizeof(optarg) > sizeof(char *) ||
356                             !isdigit(optarg[0]))
357                                 usage (argv[0]);
358
359                         conf->verbosity = atoi(optarg);
360                         break;
361                 case 'b':
362                         conf->bindings_file = optarg;
363                         break;
364                 case 'd':
365                         conf->dry_run = 1;
366                         break;
367                 case 'f':
368                         conf->remove = FLUSH_ONE;
369                         break;
370                 case 'F':
371                         conf->remove = FLUSH_ALL;
372                         break;
373                 case 'l':
374                         conf->list = 1;
375                         conf->dry_run = 1;
376
377                         if (optarg && !strncmp(optarg, "l", 1))
378                                 conf->list++;
379
380                         break;
381                 case 'M':
382 #if _DEBUG_
383                         debug = atoi(optarg);
384 #endif
385                         break;
386                 case 'p':
387                         conf->pgpolicy_flag = get_pgpolicy_id(optarg);
388                         if (conf->pgpolicy_flag == -1) {
389                                 printf("'%s' is not a valid policy\n", optarg);
390                                 usage(argv[0]);
391                         }                
392                         break;
393                 case 'r':
394                         conf->force_reload = 1;
395                         break;
396                 case 'h':
397                         usage(argv[0]);
398                 case ':':
399                         fprintf(stderr, "Missing option arguement\n");
400                         usage(argv[0]);        
401                 case '?':
402                         fprintf(stderr, "Unknown switch: %s\n", optarg);
403                         usage(argv[0]);
404                 default:
405                         usage(argv[0]);
406                 }
407         }        
408         if (optind < argc) {
409                 conf->dev = MALLOC(FILE_NAME_SIZE);
410
411                 if (!conf->dev)
412                         goto out;
413
414                 strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
415
416                 if (filepresent(conf->dev))
417                         conf->dev_type = DEV_DEVNODE;
418                 else if (sscanf(conf->dev, "%d:%d", &i, &i) == 2)
419                         conf->dev_type = DEV_DEVT;
420                 else
421                         conf->dev_type = DEV_DEVMAP;
422
423         }
424         dm_init();
425
426         if (conf->remove == FLUSH_ONE) {
427                 if (conf->dev_type == DEV_DEVMAP)
428                         r = dm_flush_map(conf->dev);
429                 else
430                         condlog(0, "must provide a map name to remove");
431
432                 goto out;
433         }
434         else if (conf->remove == FLUSH_ALL) {
435                 r = dm_flush_maps();
436                 goto out;
437         }
438         while ((r = configure()) < 0)
439                 condlog(3, "restart multipath configuration process");
440         
441 out:
442
443         sysfs_cleanup();
444         dm_lib_release();
445         dm_lib_exit();
446
447         cleanup_prio();
448         cleanup_checkers();
449         /*
450          * Freeing config must be done after dm_lib_exit(), because
451          * the logging function (dm_write_log()), which is called there,
452          * references the config.
453          */
454         free_config(conf);
455         conf = NULL;
456
457 #ifdef _DEBUG_
458         dbg_free_final(NULL);
459 #endif
460         return r;
461 }