4940e8cfdd3f84cf32c86298981a3675fe02aeeb
[multipath-tools/.git] / libmultipath / dmparser.c
1 /*
2  * Copyright (c) 2004, 2005 Christophe Varoqui
3  * Copyright (c) 2005 Stefan Bader, IBM
4  * Copyright (c) 2005 Edward Goggin, EMC
5  */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "checkers.h"
11 #include "vector.h"
12 #include "memory.h"
13 #include "structs.h"
14 #include "util.h"
15 #include "debug.h"
16
17 #define WORD_SIZE 64
18
19 static int
20 merge_words (char ** dst, char * word, int space)
21 {
22         char * p = *dst;
23         int len;
24
25         len = strlen(*dst) + strlen(word) + space;
26         *dst = REALLOC(*dst, len + 1);
27
28         if (!*dst) {
29                 free(p);
30                 return 1;
31         }
32
33         p = *dst;
34
35         while (*p != '\0')
36                 p++;
37
38         while (space) {
39                 *p = ' ';
40                 p++;
41                 space--;
42         }
43         strncpy(p, word, strlen(word) + 1);
44
45         return 0;
46 }
47
48 /*
49  * Transforms the path group vector into a proper device map string
50  */
51 int
52 assemble_map (struct multipath * mp, char * params, int len)
53 {
54         int i, j;
55         int shift, freechar;
56         int minio;
57         int nr_priority_groups, initial_pg_nr;
58         char * p, * f;
59         char no_path_retry[] = "queue_if_no_path";
60         char retain_hwhandler[] = "retain_attached_hw_handler";
61         struct pathgroup * pgp;
62         struct path * pp;
63
64         minio = mp->minio;
65         p = params;
66         freechar = len;
67
68         nr_priority_groups = VECTOR_SIZE(mp->pg);
69         initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0);
70
71         f = STRDUP(mp->features);
72
73         /*
74          * We have to set 'queue_if_no_path' here even
75          * to avoid path failures during map reload.
76          */
77         if (mp->no_path_retry == NO_PATH_RETRY_UNDEF ||
78             mp->no_path_retry == NO_PATH_RETRY_FAIL) {
79                 /* remove queue_if_no_path settings */
80                 condlog(3, "%s: remove queue_if_no_path from '%s'",
81                         mp->alias, mp->features);
82                 remove_feature(&f, no_path_retry);
83         } else {
84                 add_feature(&f, no_path_retry);
85         }
86         if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)
87                 add_feature(&f, retain_hwhandler);
88
89         shift = snprintf(p, freechar, "%s %s %i %i",
90                          f, mp->hwhandler,
91                          nr_priority_groups, initial_pg_nr);
92
93         FREE(f);
94
95         if (shift >= freechar) {
96                 condlog(0, "%s: params too small", mp->alias);
97                 return 1;
98         }
99         p += shift;
100         freechar -= shift;
101
102         vector_foreach_slot (mp->pg, pgp, i) {
103                 pgp = VECTOR_SLOT(mp->pg, i);
104                 shift = snprintf(p, freechar, " %s %i 1", mp->selector,
105                                  VECTOR_SIZE(pgp->paths));
106                 if (shift >= freechar) {
107                         condlog(0, "%s: params too small", mp->alias);
108                         return 1;
109                 }
110                 p += shift;
111                 freechar -= shift;
112
113                 vector_foreach_slot (pgp->paths, pp, j) {
114                         int tmp_minio = minio;
115
116                         if (mp->rr_weight == RR_WEIGHT_PRIO
117                             && pp->priority > 0)
118                                 tmp_minio = minio * pp->priority;
119                         if (!strlen(pp->dev_t) ) {
120                                 condlog(0, "dev_t not set for '%s'", pp->dev);
121                                 return 1;
122                         }
123                         shift = snprintf(p, freechar, " %s %d",
124                                          pp->dev_t, tmp_minio);
125                         if (shift >= freechar) {
126                                 condlog(0, "%s: params too small", mp->alias);
127                                 return 1;
128                         }
129                         p += shift;
130                         freechar -= shift;
131                 }
132         }
133         if (freechar < 1) {
134                 condlog(0, "%s: params too small", mp->alias);
135                 return 1;
136         }
137         snprintf(p, 1, "\n");
138
139         condlog(3, "%s: assembled map [%s]", mp->alias, params);
140         return 0;
141 }
142
143 int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
144                     int is_daemon)
145 {
146         char * word;
147         char * p;
148         int i, j, k;
149         int num_features = 0;
150         int num_hwhandler = 0;
151         int num_pg = 0;
152         int num_pg_args = 0;
153         int num_paths = 0;
154         int num_paths_args = 0;
155         int def_minio = 0;
156         int no_path_retry = NO_PATH_RETRY_UNDEF;
157         struct path * pp;
158         struct pathgroup * pgp;
159
160         p = params;
161
162         condlog(3, "%s: disassemble map [%s]", mpp->alias, params);
163
164         /*
165          * features
166          */
167         p += get_word(p, &mpp->features);
168
169         if (!mpp->features)
170                 return 1;
171
172         num_features = atoi(mpp->features);
173         no_path_retry = mpp->no_path_retry;
174         mpp->no_path_retry = NO_PATH_RETRY_UNDEF;
175
176         for (i = 0; i < num_features; i++) {
177                 p += get_word(p, &word);
178
179                 if (!word)
180                         return 1;
181
182                 if (merge_words(&mpp->features, word, 1)) {
183                         FREE(word);
184                         return 1;
185                 }
186                 setup_feature(mpp, word);
187
188                 FREE(word);
189         }
190
191         /*
192          * Reset no_path_retry.
193          * - if not set from features
194          * - if queue_if_no_path is set from features but
195          *   no_path_retry > 0 is selected.
196          */
197         if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF ||
198              mpp->no_path_retry == NO_PATH_RETRY_QUEUE) &&
199             mpp->no_path_retry != no_path_retry)
200                 mpp->no_path_retry = no_path_retry;
201
202         /*
203          * hwhandler
204          */
205         p += get_word(p, &mpp->hwhandler);
206
207         if (!mpp->hwhandler)
208                 return 1;
209
210         num_hwhandler = atoi(mpp->hwhandler);
211
212         for (i = 0; i < num_hwhandler; i++) {
213                 p += get_word(p, &word);
214
215                 if (!word)
216                         return 1;
217
218                 if (merge_words(&mpp->hwhandler, word, 1)) {
219                         FREE(word);
220                         return 1;
221                 }
222                 FREE(word);
223         }
224
225         /*
226          * nb of path groups
227          */
228         p += get_word(p, &word);
229
230         if (!word)
231                 return 1;
232
233         num_pg = atoi(word);
234         FREE(word);
235
236         if (num_pg > 0) {
237                 if (!mpp->pg) {
238                         mpp->pg = vector_alloc();
239                         if (!mpp->pg)
240                                 return 1;
241                 }
242         } else {
243                 free_pgvec(mpp->pg, KEEP_PATHS);
244                 mpp->pg = NULL;
245         }
246
247         /*
248          * first pg to try
249          */
250         p += get_word(p, &word);
251
252         if (!word)
253                 goto out;
254
255         mpp->nextpg = atoi(word);
256         FREE(word);
257
258         for (i = 0; i < num_pg; i++) {
259                 /*
260                  * selector
261                  */
262
263                 if (!mpp->selector) {
264                         p += get_word(p, &mpp->selector);
265
266                         if (!mpp->selector)
267                                 goto out;
268
269                         /*
270                          * selector args
271                          */
272                         p += get_word(p, &word);
273
274                         if (!word)
275                                 goto out;
276
277                         num_pg_args = atoi(word);
278
279                         if (merge_words(&mpp->selector, word, 1)) {
280                                 goto out1;
281                         }
282                         FREE(word);
283                 } else {
284                         p += get_word(p, NULL);
285                         p += get_word(p, NULL);
286                 }
287
288                 for (j = 0; j < num_pg_args; j++)
289                         p += get_word(p, NULL);
290
291                 /*
292                  * paths
293                  */
294                 pgp = alloc_pathgroup();
295
296                 if (!pgp)
297                         goto out;
298
299                 if (store_pathgroup(mpp->pg, pgp))
300                         goto out;
301
302                 p += get_word(p, &word);
303
304                 if (!word)
305                         goto out;
306
307                 num_paths = atoi(word);
308                 FREE(word);
309
310                 p += get_word(p, &word);
311
312                 if (!word)
313                         goto out;
314
315                 num_paths_args = atoi(word);
316                 FREE(word);
317
318                 for (j = 0; j < num_paths; j++) {
319                         char devname[FILE_NAME_SIZE];
320
321                         pp = NULL;
322                         p += get_word(p, &word);
323
324                         if (!word)
325                                 goto out;
326
327                         if (devt2devname(devname, FILE_NAME_SIZE, word)) {
328                                 condlog(2, "%s: cannot find block device",
329                                         word);
330                                 devname[0] = '\0';
331                         }
332
333                         if (pathvec) {
334                                 if (strlen(devname))
335                                         pp = find_path_by_dev(pathvec, devname);
336                                 else
337                                         pp = find_path_by_devt(pathvec, word);
338                         }
339
340                         if (!pp) {
341                                 pp = alloc_path();
342
343                                 if (!pp)
344                                         goto out1;
345
346                                 strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1);
347                                 strncpy(pp->dev, devname, FILE_NAME_SIZE - 1);
348                                 if (strlen(mpp->wwid)) {
349                                         strncpy(pp->wwid, mpp->wwid,
350                                                 WWID_SIZE - 1);
351                                 }
352                                 /* Only call this in multipath client mode */
353                                 if (!is_daemon && store_path(pathvec, pp))
354                                         goto out1;
355                         } else {
356                                 if (!strlen(pp->wwid) &&
357                                     strlen(mpp->wwid))
358                                         strncpy(pp->wwid, mpp->wwid,
359                                                 WWID_SIZE - 1);
360                         }
361                         FREE(word);
362
363                         if (store_path(pgp->paths, pp))
364                                 goto out;
365
366                         /*
367                          * Update wwid for multipaths which are not setup
368                          * in the get_dm_mpvec() code path
369                          */
370                         if (!strlen(mpp->wwid))
371                                 strncpy(mpp->wwid, pp->wwid,
372                                         WWID_SIZE - 1);
373
374                         /*
375                          * Update wwid for paths which may not have been
376                          * active at the time the getuid callout was run
377                          */
378                         else if (!strlen(pp->wwid))
379                                 strncpy(pp->wwid, mpp->wwid,
380                                         WWID_SIZE - 1);
381
382                         /*
383                          * Do not allow in-use patch to change wwid
384                          */
385                         else if (strcmp(pp->wwid, mpp->wwid) != 0) {
386                                 condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t);
387                                 strncpy(pp->wwid, mpp->wwid, WWID_SIZE);
388                         }
389
390                         pgp->id ^= (long)pp;
391                         pp->pgindex = i + 1;
392
393                         for (k = 0; k < num_paths_args; k++)
394                                 if (k == 0) {
395                                         if (!strncmp(mpp->selector,
396                                                      "round-robin", 11)) {
397                                                 p += get_word(p, &word);
398                                                 def_minio = atoi(word);
399
400                                                 if (mpp->rr_weight == RR_WEIGHT_PRIO
401                                                     && pp->priority > 0)
402                                                         def_minio /= pp->priority;
403
404                                                 FREE(word);
405                                         } else {
406                                                 p += get_word(p, NULL);
407                                                 def_minio = 0;
408                                         }
409
410                                         if (def_minio != mpp->minio)
411                                                 mpp->minio = def_minio;
412                                 }
413                                 else
414                                         p += get_word(p, NULL);
415
416                 }
417         }
418         return 0;
419 out1:
420         FREE(word);
421 out:
422         free_pgvec(mpp->pg, KEEP_PATHS);
423         mpp->pg = NULL;
424         return 1;
425 }
426
427 int disassemble_status(char *params, struct multipath *mpp)
428 {
429         char * word;
430         char * p;
431         int i, j, k;
432         int num_feature_args;
433         int num_hwhandler_args;
434         int num_pg;
435         int num_pg_args;
436         int num_paths;
437         int def_minio = 0;
438         struct path * pp;
439         struct pathgroup * pgp;
440
441         p = params;
442
443         condlog(3, "%s: disassemble status [%s]", mpp->alias, params);
444
445         /*
446          * features
447          */
448         p += get_word(p, &word);
449
450         if (!word)
451                 return 1;
452
453         num_feature_args = atoi(word);
454         FREE(word);
455
456         for (i = 0; i < num_feature_args; i++) {
457                 if (i == 1) {
458                         p += get_word(p, &word);
459
460                         if (!word)
461                                 return 1;
462
463                         mpp->queuedio = atoi(word);
464                         FREE(word);
465                         continue;
466                 }
467                 /* unknown */
468                 p += get_word(p, NULL);
469         }
470         /*
471          * hwhandler
472          */
473         p += get_word(p, &word);
474
475         if (!word)
476                 return 1;
477
478         num_hwhandler_args = atoi(word);
479         FREE(word);
480
481         for (i = 0; i < num_hwhandler_args; i++)
482                 p += get_word(p, NULL);
483
484         /*
485          * nb of path groups
486          */
487         p += get_word(p, &word);
488
489         if (!word)
490                 return 1;
491
492         num_pg = atoi(word);
493         FREE(word);
494
495         if (num_pg == 0)
496                 return 0;
497
498         /*
499          * next pg to try
500          */
501         p += get_word(p, NULL);
502
503         if (VECTOR_SIZE(mpp->pg) < num_pg)
504                 return 1;
505
506         for (i = 0; i < num_pg; i++) {
507                 pgp = VECTOR_SLOT(mpp->pg, i);
508                 /*
509                  * PG status
510                  */
511                 p += get_word(p, &word);
512
513                 if (!word)
514                         return 1;
515
516                 switch (*word) {
517                 case 'D':
518                         pgp->status = PGSTATE_DISABLED;
519                         break;
520                 case 'A':
521                         pgp->status = PGSTATE_ACTIVE;
522                         break;
523                 case 'E':
524                         pgp->status = PGSTATE_ENABLED;
525                         break;
526                 default:
527                         pgp->status = PGSTATE_UNDEF;
528                         break;
529                 }
530                 FREE(word);
531
532                 /*
533                  * PG Status (discarded, would be '0' anyway)
534                  */
535                 p += get_word(p, NULL);
536
537                 p += get_word(p, &word);
538
539                 if (!word)
540                         return 1;
541
542                 num_paths = atoi(word);
543                 FREE(word);
544
545                 p += get_word(p, &word);
546
547                 if (!word)
548                         return 1;
549
550                 num_pg_args = atoi(word);
551                 FREE(word);
552
553                 if (VECTOR_SIZE(pgp->paths) < num_paths)
554                         return 1;
555
556                 for (j = 0; j < num_paths; j++) {
557                         pp = VECTOR_SLOT(pgp->paths, j);
558                         /*
559                          * path
560                          */
561                         p += get_word(p, NULL);
562
563                         /*
564                          * path status
565                          */
566                         p += get_word(p, &word);
567
568                         if (!word)
569                                 return 1;
570
571                         switch (*word) {
572                         case 'F':
573                                 pp->dmstate = PSTATE_FAILED;
574                                 break;
575                         case 'A':
576                                 pp->dmstate = PSTATE_ACTIVE;
577                                 break;
578                         default:
579                                 break;
580                         }
581                         FREE(word);
582                         /*
583                          * fail count
584                          */
585                         p += get_word(p, &word);
586
587                         if (!word)
588                                 return 1;
589
590                         pp->failcount = atoi(word);
591                         FREE(word);
592
593                         /*
594                          * selector args
595                          */
596                         for (k = 0; k < num_pg_args; k++) {
597                                 if (!strncmp(mpp->selector,
598                                              "least-pending", 13)) {
599                                         p += get_word(p, &word);
600                                         if (sscanf(word,"%d:*d",
601                                                    &def_minio) == 1 &&
602                                             def_minio != mpp->minio)
603                                                         mpp->minio = def_minio;
604                                 } else
605                                         p += get_word(p, NULL);
606                         }
607                 }
608         }
609         return 0;
610 }