[multipath] map flushing to remove layered DM-partitioned too
[platform/upstream/multipath-tools.git] / libmultipath / devmapper.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <libdevmapper.h>
5 #include <ctype.h>
6 #include <linux/kdev_t.h>
7 #include <unistd.h>
8
9 #include "vector.h"
10 #include "structs.h"
11 #include "debug.h"
12 #include "memory.h"
13 #include "devmapper.h"
14
15 #define MAX_WAIT 5
16 #define LOOPS_PER_SEC 5
17
18 extern int
19 dm_prereq (char * str, int x, int y, int z)
20 {
21         int r = 1;
22         struct dm_task *dmt;
23         struct dm_versions *target;
24         struct dm_versions *last_target;
25
26         if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
27                 return 1;
28
29         dm_task_no_open_count(dmt);
30
31         if (!dm_task_run(dmt))
32                 goto out;
33
34         target = dm_task_get_versions(dmt);
35
36         /* Fetch targets and print 'em */
37         do {
38                 last_target = target;
39
40                 if (!strncmp(str, target->name, strlen(str)) &&
41                     /* dummy prereq on multipath version */
42                     target->version[0] >= x &&
43                     target->version[1] >= y &&
44                     target->version[2] >= z
45                    )
46                         r = 0;
47
48                 target = (void *) target + target->next;
49         } while (last_target != target);
50
51         out:
52         dm_task_destroy(dmt);
53         return r;
54 }
55
56 extern int
57 dm_simplecmd (int task, const char *name) {
58         int r = 0;
59         struct dm_task *dmt;
60
61         if (!(dmt = dm_task_create (task)))
62                 return 0;
63
64         if (!dm_task_set_name (dmt, name))
65                 goto out;
66
67         dm_task_no_open_count(dmt);
68
69         r = dm_task_run (dmt);
70
71         out:
72         dm_task_destroy (dmt);
73         return r;
74 }
75
76 extern int
77 dm_addmap (int task, const char *name, const char *target,
78            const char *params, unsigned long size) {
79         int r = 0;
80         struct dm_task *dmt;
81
82         if (!(dmt = dm_task_create (task)))
83                 return 0;
84
85         if (!dm_task_set_name (dmt, name))
86                 goto addout;
87
88         if (!dm_task_add_target (dmt, 0, size, target, params))
89                 goto addout;
90
91         dm_task_no_open_count(dmt);
92
93         r = dm_task_run (dmt);
94
95         addout:
96         dm_task_destroy (dmt);
97         return r;
98 }
99
100 extern int
101 dm_map_present (char * str)
102 {
103         int r = 0;
104         struct dm_task *dmt;
105         struct dm_info info;
106
107         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
108                 return 0;
109
110         if (!dm_task_set_name(dmt, str))
111                 goto out;
112
113         dm_task_no_open_count(dmt);
114
115         if (!dm_task_run(dmt))
116                 goto out;
117
118         if (!dm_task_get_info(dmt, &info))
119                 goto out;
120
121         if (info.exists)
122                 r = 1;
123 out:
124         dm_task_destroy(dmt);
125         return r;
126 }
127
128 extern int
129 dm_get_map(char * name, unsigned long * size, char * outparams)
130 {
131         int r = 1;
132         struct dm_task *dmt;
133         void *next = NULL;
134         uint64_t start, length;
135         char *target_type = NULL;
136         char *params = NULL;
137
138         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
139                 return 1;
140
141         if (!dm_task_set_name(dmt, name))
142                 goto out;
143
144         dm_task_no_open_count(dmt);
145
146         if (!dm_task_run(dmt))
147                 goto out;
148
149         /* Fetch 1st target */
150         next = dm_get_next_target(dmt, next, &start, &length,
151                                   &target_type, &params);
152
153         if (size)
154                 *size = length;
155
156         if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
157                 r = 0;
158 out:
159         dm_task_destroy(dmt);
160         return r;
161 }
162
163 extern int
164 dm_get_status(char * name, char * outstatus)
165 {
166         int r = 1;
167         struct dm_task *dmt;
168         void *next = NULL;
169         uint64_t start, length;
170         char *target_type;
171         char *status;
172
173         if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
174                 return 1;
175
176         if (!dm_task_set_name(dmt, name))
177                 goto out;
178
179         dm_task_no_open_count(dmt);
180
181         if (!dm_task_run(dmt))
182                 goto out;
183
184         /* Fetch 1st target */
185         next = dm_get_next_target(dmt, next, &start, &length,
186                                   &target_type, &status);
187
188         if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
189                 r = 0;
190 out:
191         dm_task_destroy(dmt);
192         return r;
193 }
194
195 extern int
196 dm_type(char * name, char * type)
197 {
198         int r = 0;
199         struct dm_task *dmt;
200         void *next = NULL;
201         uint64_t start, length;
202         char *target_type = NULL;
203         char *params;
204
205         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
206                 return 0;
207
208         if (!dm_task_set_name(dmt, name))
209                 goto out;
210
211         dm_task_no_open_count(dmt);
212
213         if (!dm_task_run(dmt))
214                 goto out;
215
216         /* Fetch 1st target */
217         next = dm_get_next_target(dmt, next, &start, &length,
218                                   &target_type, &params);
219
220         if (0 == strcmp(target_type, type))
221                 r = 1;
222
223 out:
224         dm_task_destroy(dmt);
225         return r;
226 }
227
228 static int
229 dm_dev_t (char * mapname, char * dev_t, int len)
230 {
231         int r = 1;
232         struct dm_task *dmt;
233         struct dm_info info;
234
235         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
236                 return 0;
237
238         if (!dm_task_set_name(dmt, mapname))
239                 goto out;
240
241         if (!dm_task_run(dmt))
242                 goto out;
243
244         if (!dm_task_get_info(dmt, &info))
245                 goto out;
246
247         r = info.open_count;
248         if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len)
249                     goto out;
250
251         r = 0;
252 out:
253         dm_task_destroy(dmt);
254         return r;
255 }
256         
257 int
258 dm_get_opencount (char * mapname)
259 {
260         int r = -1;
261         struct dm_task *dmt;
262         struct dm_info info;
263
264         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
265                 return 0;
266
267         if (!dm_task_set_name(dmt, mapname))
268                 goto out;
269
270         if (!dm_task_run(dmt))
271                 goto out;
272
273         if (!dm_task_get_info(dmt, &info))
274                 goto out;
275
276         r = info.open_count;
277 out:
278         dm_task_destroy(dmt);
279         return r;
280 }
281         
282 int
283 dm_get_minor (char * mapname)
284 {
285         int r = -1;
286         struct dm_task *dmt;
287         struct dm_info info;
288
289         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
290                 return 0;
291
292         if (!dm_task_set_name(dmt, mapname))
293                 goto out;
294
295         if (!dm_task_run(dmt))
296                 goto out;
297
298         if (!dm_task_get_info(dmt, &info))
299                 goto out;
300
301         r = info.minor;
302 out:
303         dm_task_destroy(dmt);
304         return r;
305 }
306         
307 extern int
308 dm_flush_map (char * mapname, char * type)
309 {
310         int r;
311
312         if (!dm_map_present(mapname))
313                 return 0;
314
315         if (!dm_type(mapname, type))
316                 return 1;
317
318         if (dm_remove_partmaps(mapname))
319                 return 1;
320
321         if (dm_get_opencount(mapname))
322                 return 1;
323
324         r = dm_simplecmd(DM_DEVICE_REMOVE, mapname);
325
326         if (r) {
327                 condlog(4, "multipath map %s removed", mapname);
328                 return 0;
329         }
330         return 1;
331 }
332
333 extern int
334 dm_flush_maps (char * type)
335 {
336         int r = 0;
337         struct dm_task *dmt;
338         struct dm_names *names;
339         unsigned next = 0;
340
341         if (!(dmt = dm_task_create (DM_DEVICE_LIST)))
342                 return 0;
343
344         dm_task_no_open_count(dmt);
345
346         if (!dm_task_run (dmt))
347                 goto out;
348
349         if (!(names = dm_task_get_names (dmt)))
350                 goto out;
351
352         if (!names->dev)
353                 goto out;
354
355         do {
356                 r += dm_flush_map(names->name, type);
357                 next = names->next;
358                 names = (void *) names + next;
359         } while (next);
360
361         out:
362         dm_task_destroy (dmt);
363         return r;
364 }
365
366 int
367 dm_fail_path(char * mapname, char * path)
368 {
369         int r = 1;
370         struct dm_task *dmt;
371         char str[32];
372
373         if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
374                 return 1;
375
376         if (!dm_task_set_name(dmt, mapname))
377                 goto out;
378
379         if (!dm_task_set_sector(dmt, 0))
380                 goto out;
381
382         if (snprintf(str, 32, "fail_path %s\n", path) > 32)
383                 goto out;
384
385         if (!dm_task_set_message(dmt, str))
386                 goto out;
387
388         dm_task_no_open_count(dmt);
389
390         if (!dm_task_run(dmt))
391                 goto out;
392
393         r = 0;
394 out:
395         dm_task_destroy(dmt);
396         return r;
397 }
398
399 int
400 dm_reinstate(char * mapname, char * path)
401 {
402         int r = 1;
403         struct dm_task *dmt;
404         char str[32];
405
406         if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
407                 return 1;
408
409         if (!dm_task_set_name(dmt, mapname))
410                 goto out;
411
412         if (!dm_task_set_sector(dmt, 0))
413                 goto out;
414
415         if (snprintf(str, 32, "reinstate_path %s\n", path) > 32)
416                 goto out;
417
418         if (!dm_task_set_message(dmt, str))
419                 goto out;
420
421         dm_task_no_open_count(dmt);
422
423         if (!dm_task_run(dmt))
424                 goto out;
425
426         r = 0;
427 out:
428         dm_task_destroy(dmt);
429         return r;
430 }
431
432 static int
433 dm_groupmsg (char * msg, char * mapname, int index)
434 {
435         int r = 0;
436         struct dm_task *dmt;
437         char str[24];
438
439         if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
440                 return 0;
441
442         if (!dm_task_set_name(dmt, mapname))
443                 goto out;
444
445         if (!dm_task_set_sector(dmt, 0))
446                 goto out;
447
448         snprintf(str, 24, "%s_group %i\n", msg, index);
449         condlog(3, "message %s 0 %s", mapname, str);
450
451         if (!dm_task_set_message(dmt, str))
452                 goto out;
453
454         dm_task_no_open_count(dmt);
455
456         if (!dm_task_run(dmt))
457                 goto out;
458
459         r = 1;
460
461         out:
462         dm_task_destroy(dmt);
463
464         return r;
465 }
466
467 int
468 dm_switchgroup(char * mapname, int index)
469 {
470         return dm_groupmsg("switch", mapname,index);
471 }
472
473 int
474 dm_enablegroup(char * mapname, int index)
475 {
476         return dm_groupmsg("enable", mapname,index);
477 }
478
479 int
480 dm_disablegroup(char * mapname, int index)
481 {
482         return dm_groupmsg("disable", mapname,index);
483 }
484
485 int
486 dm_get_maps (vector mp, char * type)
487 {
488         struct multipath * mpp;
489         int r = 1;
490         struct dm_task *dmt;
491         struct dm_names *names;
492         unsigned next = 0;
493
494         if (!type || !mp)
495                 return 1;
496
497         if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
498                 return 1;
499
500         dm_task_no_open_count(dmt);
501
502         if (!dm_task_run(dmt))
503                 goto out;
504
505         if (!(names = dm_task_get_names(dmt)))
506                 goto out;
507
508         if (!names->dev) {
509                 r = 0; /* this is perfectly valid */
510                 goto out;
511         }
512
513         do {
514                 if (dm_type(names->name, type)) {
515                         mpp = alloc_multipath();
516
517                         if (!mpp)
518                                 goto out;
519
520                         if (dm_get_map(names->name, &mpp->size, mpp->params))
521                                 goto out1;
522
523                         if (dm_get_status(names->name, mpp->status))
524                                 goto out1;
525
526                         mpp->alias = MALLOC(strlen(names->name) + 1);
527
528                         if (!mpp->alias)
529                                 goto out1;
530
531                         strncat(mpp->alias, names->name, strlen(names->name));
532
533                         if (!vector_alloc_slot(mp))
534                                 goto out1;
535                         
536                         vector_set_slot(mp, mpp);
537                         mpp = NULL;
538                 }
539                 next = names->next;
540                 names = (void *) names + next;
541         } while (next);
542
543         r = 0;
544         goto out;
545 out1:
546         free_multipath(mpp, KEEP_PATHS);
547 out:
548         dm_task_destroy (dmt);
549         return r;
550 }
551
552 int
553 dm_geteventnr (char *name)
554 {
555         struct dm_task *dmt;
556         struct dm_info info;
557
558         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
559                 return 0;
560
561         if (!dm_task_set_name(dmt, name))
562                 goto out;
563
564         dm_task_no_open_count(dmt);
565
566         if (!dm_task_run(dmt))
567                 goto out;
568
569         if (!dm_task_get_info(dmt, &info)) {
570                 info.event_nr = 0;
571                 goto out;
572         }
573
574         if (!info.exists) {
575                 info.event_nr = 0;
576                 goto out;
577         }
578
579 out:
580         dm_task_destroy(dmt);
581
582         return info.event_nr;
583 }
584
585 char *
586 dm_mapname(int major, int minor, char *type)
587 {
588         struct dm_task *dmt;
589         void *next = NULL;
590         uint64_t start, length;
591         char *target_type = NULL;
592         char *params;
593         int r;
594         int loop = MAX_WAIT * LOOPS_PER_SEC;
595
596         if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
597                 return NULL;
598
599         if (!dm_task_set_major(dmt, major) ||
600             !dm_task_set_minor(dmt, minor))
601                 goto bad;
602
603         dm_task_no_open_count(dmt);
604
605         /*
606          * device map might not be ready when we get here from
607          * uevent trigger
608          */
609         while (--loop) {
610                 r = dm_task_run(dmt);
611
612                 if (r)
613                         break;
614
615                 usleep(1000 * 1000 / LOOPS_PER_SEC);
616         }
617
618         if (!r)
619                 goto bad;
620
621         if (!type)
622                 goto good;
623
624         do {
625                 next = dm_get_next_target(dmt, next, &start, &length,
626                                           &target_type, &params);
627                 if (target_type && strcmp(target_type, type))
628                         goto bad;
629         } while (next);
630
631 good:
632         dm_task_destroy(dmt);
633         return strdup(dm_task_get_name(dmt));
634 bad:
635         dm_task_destroy(dmt);
636         return NULL;
637 }
638
639 int
640 dm_remove_partmaps (char * mapname)
641 {
642         struct dm_task *dmt;
643         struct dm_names *names;
644         unsigned next = 0;
645         char params[PARAMS_SIZE];
646         unsigned long size;
647         char dev_t[32];
648         int r = 1;
649
650         if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
651                 return 1;
652
653         dm_task_no_open_count(dmt);
654
655         if (!dm_task_run(dmt))
656                 goto out;
657
658         if (!(names = dm_task_get_names(dmt)))
659                 goto out;
660
661         if (!names->dev) {
662                 r = 0; /* this is perfectly valid */
663                 goto out;
664         }
665
666         if (dm_dev_t(mapname, &dev_t[0], 32))
667                 goto out;
668
669         do {
670                 if (
671                     /*
672                      * if devmap target is "linear"
673                      */
674                     dm_type(names->name, "linear") &&
675
676                     /*
677                      * and the multipath mapname and the part mapname start
678                      * the same
679                      */
680                     !strncmp(names->name, mapname, strlen(mapname)) &&
681
682                     /*
683                      * and the opencount is 0 for us to allow removal
684                      */
685                     !dm_get_opencount(names->name) &&
686
687                     /*
688                      * and we can fetch the map table from the kernel
689                      */
690                     !dm_get_map(names->name, &size, &params[0]) &&
691
692                     /*
693                      * and the table maps over the multipath map
694                      */
695                     strstr(params, dev_t)
696                    ) {
697                                 /*
698                                  * then it's a kpartx generated partition.
699                                  * remove it.
700                                  */
701                                 condlog(4, "partition map %s removed",
702                                         names->name);
703                                 dm_simplecmd(DM_DEVICE_REMOVE, names->name);
704                    }
705
706                 next = names->next;
707                 names = (void *) names + next;
708         } while (next);
709
710         r = 0;
711 out:
712         dm_task_destroy (dmt);
713         return r;
714 }