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