[checkers] API rewrite
[platform/upstream/multipath-tools.git] / libmultipath / devmapper.c
1 /*
2  * snippets copied from device-mapper dmsetup.c
3  * Copyright (c) 2004, 2005 Christophe Varoqui
4  * Copyright (c) 2005 Kiyoshi Ueda, NEC
5  * Copyright (c) 2005 Patrick Caulfield, Redhat
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <libdevmapper.h>
11 #include <ctype.h>
12 #include <linux/kdev_t.h>
13 #include <unistd.h>
14
15 #include <checkers.h>
16
17 #include "vector.h"
18 #include "structs.h"
19 #include "debug.h"
20 #include "memory.h"
21 #include "devmapper.h"
22
23 #define MAX_WAIT 5
24 #define LOOPS_PER_SEC 5
25
26 static void
27 dm_dummy_log (int level, const char *file, int line, const char *f, ...)
28 {
29         return;
30 }
31
32 void
33 dm_restore_log (void)
34 {
35         dm_log_init(NULL);
36 }
37
38 void
39 dm_shut_log (void)
40 {
41         dm_log_init(&dm_dummy_log);
42 }
43
44 extern int
45 dm_prereq (char * str, int x, int y, int z)
46 {
47         int r = 2;
48         struct dm_task *dmt;
49         struct dm_versions *target;
50         struct dm_versions *last_target;
51
52         if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
53                 return 3;
54
55         dm_task_no_open_count(dmt);
56
57         if (!dm_task_run(dmt)) {
58                 condlog(0, "Can not communicate with kernel DM");
59                 goto out;
60         }
61
62         target = dm_task_get_versions(dmt);
63
64         do {
65                 last_target = target;
66
67                 if (!strncmp(str, target->name, strlen(str))) {
68                         r--;
69                         
70                         if (target->version[0] >= x &&
71                             target->version[1] >= y &&
72                             target->version[2] >= z)
73                                 r--;
74
75                         break;
76                 }
77
78                 target = (void *) target + target->next;
79         } while (last_target != target);
80
81         if (r == 2)
82                 condlog(0, "DM multipath kernel driver not loaded");
83         else if (r == 1)
84                 condlog(0, "DM multipath kernel driver version too old");
85
86 out:
87         dm_task_destroy(dmt);
88         return r;
89 }
90
91 extern int
92 dm_simplecmd (int task, const char *name) {
93         int r = 0;
94         struct dm_task *dmt;
95
96         if (!(dmt = dm_task_create (task)))
97                 return 0;
98
99         if (!dm_task_set_name (dmt, name))
100                 goto out;
101
102         dm_task_no_open_count(dmt);
103
104         r = dm_task_run (dmt);
105
106         out:
107         dm_task_destroy (dmt);
108         return r;
109 }
110
111 extern int
112 dm_addmap (int task, const char *name, const char *target,
113            const char *params, unsigned long long size, const char *uuid) {
114         int r = 0;
115         struct dm_task *dmt;
116
117         if (!(dmt = dm_task_create (task)))
118                 return 0;
119
120         if (!dm_task_set_name (dmt, name))
121                 goto addout;
122
123         if (!dm_task_add_target (dmt, 0, size, target, params))
124                 goto addout;
125
126         if (uuid && !dm_task_set_uuid(dmt, uuid))
127                 goto addout;
128
129         dm_task_no_open_count(dmt);
130
131         r = dm_task_run (dmt);
132
133         addout:
134         dm_task_destroy (dmt);
135         return r;
136 }
137
138 extern int
139 dm_map_present (char * str)
140 {
141         int r = 0;
142         struct dm_task *dmt;
143         struct dm_info info;
144
145         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
146                 return 0;
147
148         if (!dm_task_set_name(dmt, str))
149                 goto out;
150
151         dm_task_no_open_count(dmt);
152
153         if (!dm_task_run(dmt))
154                 goto out;
155
156         if (!dm_task_get_info(dmt, &info))
157                 goto out;
158
159         if (info.exists)
160                 r = 1;
161 out:
162         dm_task_destroy(dmt);
163         return r;
164 }
165
166 extern int
167 dm_get_map(char * name, unsigned long long * size, char * outparams)
168 {
169         int r = 1;
170         struct dm_task *dmt;
171         void *next = NULL;
172         uint64_t start, length;
173         char *target_type = NULL;
174         char *params = NULL;
175
176         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
177                 return 1;
178
179         if (!dm_task_set_name(dmt, name))
180                 goto out;
181
182         dm_task_no_open_count(dmt);
183
184         if (!dm_task_run(dmt))
185                 goto out;
186
187         /* Fetch 1st target */
188         next = dm_get_next_target(dmt, next, &start, &length,
189                                   &target_type, &params);
190
191         if (size)
192                 *size = length;
193
194         if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
195                 r = 0;
196 out:
197         dm_task_destroy(dmt);
198         return r;
199 }
200
201 extern int
202 dm_get_uuid(char *name, char *uuid)
203 {
204         struct dm_task *dmt;
205         const char *uuidtmp;
206
207         dmt = dm_task_create(DM_DEVICE_INFO);
208         if (!dmt)
209                 return 1;
210
211         if (!dm_task_set_name (dmt, name))
212                 goto uuidout;
213
214         if (!dm_task_run(dmt))
215                 goto uuidout;
216
217         uuidtmp = dm_task_get_uuid(dmt);
218         if (uuidtmp)
219                 strcpy(uuid, uuidtmp);
220         else
221                 uuid[0] = '\0';
222
223 uuidout:
224         dm_task_destroy(dmt);
225
226         return 0;
227 }
228
229 extern int
230 dm_get_status(char * name, char * outstatus)
231 {
232         int r = 1;
233         struct dm_task *dmt;
234         void *next = NULL;
235         uint64_t start, length;
236         char *target_type;
237         char *status;
238
239         if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
240                 return 1;
241
242         if (!dm_task_set_name(dmt, name))
243                 goto out;
244
245         dm_task_no_open_count(dmt);
246
247         if (!dm_task_run(dmt))
248                 goto out;
249
250         /* Fetch 1st target */
251         next = dm_get_next_target(dmt, next, &start, &length,
252                                   &target_type, &status);
253
254         if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
255                 r = 0;
256 out:
257         if (r)
258                 condlog(0, "%s: error getting map status string", name);
259
260         dm_task_destroy(dmt);
261         return r;
262 }
263
264 /*
265  * returns:
266  *    1 : match
267  *    0 : no match
268  *   -1 : empty map
269  */
270 extern int
271 dm_type(char * name, char * type)
272 {
273         int r = 0;
274         struct dm_task *dmt;
275         void *next = NULL;
276         uint64_t start, length;
277         char *target_type = NULL;
278         char *params;
279
280         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
281                 return 0;
282
283         if (!dm_task_set_name(dmt, name))
284                 goto out;
285
286         dm_task_no_open_count(dmt);
287
288         if (!dm_task_run(dmt))
289                 goto out;
290
291         /* Fetch 1st target */
292         next = dm_get_next_target(dmt, next, &start, &length,
293                                   &target_type, &params);
294
295         if (!target_type)
296                 r = -1;
297         else if (!strcmp(target_type, type))
298                 r = 1;
299
300 out:
301         dm_task_destroy(dmt);
302         return r;
303 }
304
305 static int
306 dm_dev_t (char * mapname, char * dev_t, int len)
307 {
308         int r = 1;
309         struct dm_task *dmt;
310         struct dm_info info;
311
312         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
313                 return 0;
314
315         if (!dm_task_set_name(dmt, mapname))
316                 goto out;
317
318         if (!dm_task_run(dmt))
319                 goto out;
320
321         if (!dm_task_get_info(dmt, &info))
322                 goto out;
323
324         r = info.open_count;
325         if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len)
326                     goto out;
327
328         r = 0;
329 out:
330         dm_task_destroy(dmt);
331         return r;
332 }
333         
334 int
335 dm_get_opencount (char * mapname)
336 {
337         int r = -1;
338         struct dm_task *dmt;
339         struct dm_info info;
340
341         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
342                 return 0;
343
344         if (!dm_task_set_name(dmt, mapname))
345                 goto out;
346
347         if (!dm_task_run(dmt))
348                 goto out;
349
350         if (!dm_task_get_info(dmt, &info))
351                 goto out;
352
353         r = info.open_count;
354 out:
355         dm_task_destroy(dmt);
356         return r;
357 }
358         
359 int
360 dm_get_minor (char * mapname)
361 {
362         int r = -1;
363         struct dm_task *dmt;
364         struct dm_info info;
365
366         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
367                 return 0;
368
369         if (!dm_task_set_name(dmt, mapname))
370                 goto out;
371
372         if (!dm_task_run(dmt))
373                 goto out;
374
375         if (!dm_task_get_info(dmt, &info))
376                 goto out;
377
378         r = info.minor;
379 out:
380         dm_task_destroy(dmt);
381         return r;
382 }
383         
384 extern int
385 dm_flush_map (char * mapname, char * type)
386 {
387         int r;
388
389         if (!dm_map_present(mapname))
390                 return 0;
391
392         if (dm_type(mapname, type) <= 0)
393                 return 1;
394
395         if (dm_remove_partmaps(mapname))
396                 return 1;
397
398         if (dm_get_opencount(mapname)) {
399                 condlog(2, "%s: map in use", mapname);
400                 return 1;
401         }       
402
403         r = dm_simplecmd(DM_DEVICE_REMOVE, mapname);
404
405         if (r) {
406                 condlog(4, "multipath map %s removed", mapname);
407                 return 0;
408         }
409         return 1;
410 }
411
412 extern int
413 dm_flush_maps (char * type)
414 {
415         int r = 0;
416         struct dm_task *dmt;
417         struct dm_names *names;
418         unsigned next = 0;
419
420         if (!(dmt = dm_task_create (DM_DEVICE_LIST)))
421                 return 0;
422
423         dm_task_no_open_count(dmt);
424
425         if (!dm_task_run (dmt))
426                 goto out;
427
428         if (!(names = dm_task_get_names (dmt)))
429                 goto out;
430
431         if (!names->dev)
432                 goto out;
433
434         do {
435                 r += dm_flush_map(names->name, type);
436                 next = names->next;
437                 names = (void *) names + next;
438         } while (next);
439
440         out:
441         dm_task_destroy (dmt);
442         return r;
443 }
444
445 int
446 dm_message(char * mapname, char * message)
447 {
448         int r = 1;
449         struct dm_task *dmt;
450
451         if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
452                 return 1;
453
454         if (!dm_task_set_name(dmt, mapname))
455                 goto out;
456
457         if (!dm_task_set_sector(dmt, 0))
458                 goto out;
459
460         if (!dm_task_set_message(dmt, message))
461                 goto out;
462
463         dm_task_no_open_count(dmt);
464
465         if (!dm_task_run(dmt))
466                 goto out;
467
468         r = 0;
469 out:
470         if (r)
471                 condlog(0, "DM message failed [%s]", message);
472
473         dm_task_destroy(dmt);
474         return r;
475 }
476
477 int
478 dm_fail_path(char * mapname, char * path)
479 {
480         char message[32];
481
482         if (snprintf(message, 32, "fail_path %s\n", path) > 32)
483                 return 1;
484
485         return dm_message(mapname, message);
486 }
487
488 int
489 dm_reinstate_path(char * mapname, char * path)
490 {
491         char message[32];
492
493         if (snprintf(message, 32, "reinstate_path %s\n", path) > 32)
494                 return 1;
495
496         return dm_message(mapname, message);
497 }
498
499 int
500 dm_queue_if_no_path(char *mapname, int enable)
501 {
502         char *message;
503
504         if (enable)
505                 message = "queue_if_no_path\n";
506         else
507                 message = "fail_if_no_path\n";
508
509         return dm_message(mapname, message);
510 }
511
512 static int
513 dm_groupmsg (char * msg, char * mapname, int index)
514 {
515         char message[32];
516
517         if (snprintf(message, 32, "%s_group %i\n", msg, index) > 32)
518                 return 1;
519
520         return dm_message(mapname, message);
521 }
522
523 int
524 dm_switchgroup(char * mapname, int index)
525 {
526         return dm_groupmsg("switch", mapname, index);
527 }
528
529 int
530 dm_enablegroup(char * mapname, int index)
531 {
532         return dm_groupmsg("enable", mapname, index);
533 }
534
535 int
536 dm_disablegroup(char * mapname, int index)
537 {
538         return dm_groupmsg("disable", mapname, index);
539 }
540
541 int
542 dm_get_maps (vector mp, char * type)
543 {
544         struct multipath * mpp;
545         int r = 1;
546         int info;
547         struct dm_task *dmt;
548         struct dm_names *names;
549         unsigned next = 0;
550
551         if (!type || !mp)
552                 return 1;
553
554         if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
555                 return 1;
556
557         dm_task_no_open_count(dmt);
558
559         if (!dm_task_run(dmt))
560                 goto out;
561
562         if (!(names = dm_task_get_names(dmt)))
563                 goto out;
564
565         if (!names->dev) {
566                 r = 0; /* this is perfectly valid */
567                 goto out;
568         }
569
570         do {
571                 info = dm_type(names->name, type);
572
573                 if (info <= 0)
574                         goto next;
575
576                 mpp = alloc_multipath();
577
578                 if (!mpp)
579                         goto out;
580
581                 mpp->alias = STRDUP(names->name);
582
583                 if (!mpp->alias)
584                         goto out1;
585
586                 if (info > 0) {
587                         if (dm_get_map(names->name, &mpp->size, mpp->params))
588                                 goto out1;
589
590                         if (dm_get_status(names->name, mpp->status))
591                                 goto out1;
592
593                         dm_get_uuid(names->name, mpp->wwid);
594                 }
595
596                 if (!vector_alloc_slot(mp))
597                         goto out1;
598
599                 vector_set_slot(mp, mpp);
600                 mpp = NULL;
601 next:
602                 next = names->next;
603                 names = (void *) names + next;
604         } while (next);
605
606         r = 0;
607         goto out;
608 out1:
609         free_multipath(mpp, KEEP_PATHS);
610 out:
611         dm_task_destroy (dmt);
612         return r;
613 }
614
615 extern int
616 dm_get_name(char *uuid, char *type, char *name)
617 {
618         vector vec;
619         struct multipath *mpp;
620         int i;
621
622         vec = vector_alloc();
623
624         if (!vec)
625                 return 0;
626
627         if (dm_get_maps(vec, type)) {
628                 vector_free(vec);
629                 return 0;
630         }
631
632         vector_foreach_slot(vec, mpp, i) {
633                 if (!strcmp(uuid, mpp->wwid)) {
634                         vector_free(vec);
635                         strcpy(name, mpp->alias);
636                         return 1;
637                 }
638         }
639
640         vector_free(vec);
641         return 0;
642 }
643
644 int
645 dm_geteventnr (char *name)
646 {
647         struct dm_task *dmt;
648         struct dm_info info;
649
650         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
651                 return 0;
652
653         if (!dm_task_set_name(dmt, name))
654                 goto out;
655
656         dm_task_no_open_count(dmt);
657
658         if (!dm_task_run(dmt))
659                 goto out;
660
661         if (!dm_task_get_info(dmt, &info)) {
662                 info.event_nr = 0;
663                 goto out;
664         }
665
666         if (!info.exists) {
667                 info.event_nr = 0;
668                 goto out;
669         }
670
671 out:
672         dm_task_destroy(dmt);
673
674         return info.event_nr;
675 }
676
677 char *
678 dm_mapname(int major, int minor)
679 {
680         char * response = NULL;
681         const char *map;
682         struct dm_task *dmt;
683         int r;
684         int loop = MAX_WAIT * LOOPS_PER_SEC;
685
686         if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
687                 return NULL;
688
689         if (!dm_task_set_major(dmt, major) ||
690             !dm_task_set_minor(dmt, minor))
691                 goto bad;
692
693         dm_task_no_open_count(dmt);
694
695         /*
696          * device map might not be ready when we get here from
697          * daemon uev_trigger -> uev_add_map
698          */
699         while (--loop) {
700                 dm_shut_log();
701                 r = dm_task_run(dmt);
702                 dm_restore_log();
703
704                 if (r)
705                         break;
706
707                 usleep(1000 * 1000 / LOOPS_PER_SEC);
708         }
709
710         if (!r) {
711                 condlog(0, "%i:%i: timeout fetching map name", major, minor);
712                 goto bad;
713         }
714
715         map = dm_task_get_name(dmt);
716         if (map && strlen(map))
717                 response = STRDUP((char *)dm_task_get_name(dmt));
718
719         dm_task_destroy(dmt);
720         return response;
721 bad:
722         dm_task_destroy(dmt);
723         condlog(0, "%i:%i: error fetching map name", major, minor);
724         return NULL;
725 }
726
727 int
728 dm_remove_partmaps (char * mapname)
729 {
730         struct dm_task *dmt;
731         struct dm_names *names;
732         unsigned next = 0;
733         char params[PARAMS_SIZE];
734         unsigned long long size;
735         char dev_t[32];
736         int r = 1;
737
738         if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
739                 return 1;
740
741         dm_task_no_open_count(dmt);
742
743         if (!dm_task_run(dmt))
744                 goto out;
745
746         if (!(names = dm_task_get_names(dmt)))
747                 goto out;
748
749         if (!names->dev) {
750                 r = 0; /* this is perfectly valid */
751                 goto out;
752         }
753
754         if (dm_dev_t(mapname, &dev_t[0], 32))
755                 goto out;
756
757         do {
758                 if (
759                     /*
760                      * if devmap target is "linear"
761                      */
762                     (dm_type(names->name, "linear") > 0) &&
763
764                     /*
765                      * and the multipath mapname and the part mapname start
766                      * the same
767                      */
768                     !strncmp(names->name, mapname, strlen(mapname)) &&
769
770                     /*
771                      * and the opencount is 0 for us to allow removal
772                      */
773                     !dm_get_opencount(names->name) &&
774
775                     /*
776                      * and we can fetch the map table from the kernel
777                      */
778                     !dm_get_map(names->name, &size, &params[0]) &&
779
780                     /*
781                      * and the table maps over the multipath map
782                      */
783                     strstr(params, dev_t)
784                    ) {
785                                 /*
786                                  * then it's a kpartx generated partition.
787                                  * remove it.
788                                  */
789                                 condlog(4, "partition map %s removed",
790                                         names->name);
791                                 dm_simplecmd(DM_DEVICE_REMOVE, names->name);
792                    }
793
794                 next = names->next;
795                 names = (void *) names + next;
796         } while (next);
797
798         r = 0;
799 out:
800         dm_task_destroy (dmt);
801         return r;
802 }
803
804 static struct dm_info *
805 alloc_dminfo (void)
806 {
807         return MALLOC(sizeof(struct dm_info));
808 }
809
810 int
811 dm_get_info (char * mapname, struct dm_info ** dmi)
812 {
813         int r = 1;
814         struct dm_task *dmt = NULL;
815         
816         if (!mapname)
817                 return 1;
818
819         if (!*dmi)
820                 *dmi = alloc_dminfo();
821
822         if (!*dmi)
823                 return 1;
824
825         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
826                 goto out;
827
828         if (!dm_task_set_name(dmt, mapname))
829                 goto out;
830
831         dm_task_no_open_count(dmt);
832
833         if (!dm_task_run(dmt))
834                 goto out;
835
836         if (!dm_task_get_info(dmt, *dmi))
837                 goto out;
838
839         r = 0;
840 out:
841         if (r)
842                 memset(*dmi, 0, sizeof(struct dm_info));
843
844         if (dmt)
845                 dm_task_destroy(dmt);
846
847         return r;
848 }
849
850 int
851 dm_rename_partmaps (char * old, char * new)
852 {
853         struct dm_task *dmt;
854         struct dm_names *names;
855         unsigned next = 0;
856         char buff[PARAMS_SIZE];
857         unsigned long long size;
858         char dev_t[32];
859         int r = 1;
860
861         if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
862                 return 1;
863
864         dm_task_no_open_count(dmt);
865
866         if (!dm_task_run(dmt))
867                 goto out;
868
869         if (!(names = dm_task_get_names(dmt)))
870                 goto out;
871
872         if (!names->dev) {
873                 r = 0; /* this is perfectly valid */
874                 goto out;
875         }
876
877         if (dm_dev_t(old, &dev_t[0], 32))
878                 goto out;
879
880         do {
881                 if (
882                     /*
883                      * if devmap target is "linear"
884                      */
885                     (dm_type(names->name, "linear") > 0) &&
886
887                     /*
888                      * and the multipath mapname and the part mapname start
889                      * the same
890                      */
891                     !strncmp(names->name, old, strlen(old)) &&
892
893                     /*
894                      * and we can fetch the map table from the kernel
895                      */
896                     !dm_get_map(names->name, &size, &buff[0]) &&
897
898                     /*
899                      * and the table maps over the multipath map
900                      */
901                     strstr(buff, dev_t)
902                    ) {
903                                 /*
904                                  * then it's a kpartx generated partition.
905                                  * Rename it.
906                                  */
907                                 snprintf(buff, PARAMS_SIZE, "%s%s",
908                                          new, names->name + strlen(old));
909                                 dm_rename(names->name, buff);
910                                 condlog(4, "partition map %s renamed",
911                                         names->name);
912                    }
913
914                 next = names->next;
915                 names = (void *) names + next;
916         } while (next);
917
918         r = 0;
919 out:
920         dm_task_destroy (dmt);
921         return r;
922 }
923
924 int
925 dm_rename (char * old, char * new)
926 {
927         int r = 0;
928         struct dm_task *dmt;
929
930         if (dm_rename_partmaps(old, new))
931                 return r;
932
933         if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
934                 return r;
935
936         if (!dm_task_set_name(dmt, old))
937                 goto out;
938
939         if (!dm_task_set_newname(dmt, new))
940                 goto out;
941         
942         dm_task_no_open_count(dmt);
943
944         if (!dm_task_run(dmt))
945                 goto out;
946
947         r = 1;
948 out:
949         dm_task_destroy(dmt);
950         return r;
951 }