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