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