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