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