4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include <libdevinfo.h>
29 #include <sys/sunddi.h>
30 #include <sys/types.h>
36 #include <sys/debug.h>
41 #include "libdiskmgt.h"
42 #include "disks_private.h"
43 #include "partition.h"
45 #define ANY_ZPOOL_USE(who) \
46 (((who) == DM_WHO_ZPOOL_FORCE) || \
47 ((who) == DM_WHO_ZPOOL) || \
48 ((who) == DM_WHO_ZPOOL_SPARE))
50 extern char *getfullblkname();
52 extern dm_desc_type_t drive_assoc_types
[];
53 extern dm_desc_type_t bus_assoc_types
[];
54 extern dm_desc_type_t controller_assoc_types
[];
55 extern dm_desc_type_t media_assoc_types
[];
56 extern dm_desc_type_t slice_assoc_types
[];
57 extern dm_desc_type_t partition_assoc_types
[];
58 extern dm_desc_type_t path_assoc_types
[];
59 extern dm_desc_type_t alias_assoc_types
[];
62 static dm_descriptor_t
*ptr_array_to_desc_array(descriptor_t
**ptrs
, int *errp
);
63 static descriptor_t
**desc_array_to_ptr_array(dm_descriptor_t
*da
, int *errp
);
64 static int build_usage_string(char *dname
, char *by
, char *data
, char **use
,
65 int *found
, int *errp
);
68 dm_free_descriptor(dm_descriptor_t desc
)
75 dp
= (descriptor_t
*)(uintptr_t)desc
;
78 cache_free_descriptor(dp
);
83 dm_free_descriptors(dm_descriptor_t
*desc_list
)
88 if (desc_list
== NULL
) {
91 dp
= desc_array_to_ptr_array(desc_list
, &error
);
98 cache_free_descriptors(dp
);
104 dm_free_name(char *name
)
110 dm_get_associated_descriptors(dm_descriptor_t desc
, dm_desc_type_t type
,
113 descriptor_t
**descs
= NULL
;
117 dp
= (descriptor_t
*)(uintptr_t)desc
;
121 if (!cache_is_valid_desc(dp
)) {
127 /* verify that the descriptor is still valid */
128 if (dp
->p
.generic
== NULL
) {
136 descs
= drive_get_assoc_descriptors(dp
, type
, errp
);
139 descs
= bus_get_assoc_descriptors(dp
, type
, errp
);
142 descs
= controller_get_assoc_descriptors(dp
, type
, errp
);
145 descs
= media_get_assoc_descriptors(dp
, type
, errp
);
148 descs
= slice_get_assoc_descriptors(dp
, type
, errp
);
151 descs
= partition_get_assoc_descriptors(dp
, type
, errp
);
154 descs
= path_get_assoc_descriptors(dp
, type
, errp
);
157 descs
= alias_get_assoc_descriptors(dp
, type
, errp
);
166 return (ptr_array_to_desc_array(descs
, errp
));
170 dm_get_associated_types(dm_desc_type_t type
)
174 return (drive_assoc_types
);
176 return (bus_assoc_types
);
178 return (controller_assoc_types
);
180 return (media_assoc_types
);
182 return (slice_assoc_types
);
184 return (partition_assoc_types
);
186 return (path_assoc_types
);
188 return (alias_assoc_types
);
195 dm_get_attributes(dm_descriptor_t desc
, int *errp
)
198 nvlist_t
*attrs
= NULL
;
201 dp
= (descriptor_t
*)(uintptr_t)desc
;
205 if (!cache_is_valid_desc(dp
)) {
211 /* verify that the descriptor is still valid */
212 if (dp
->p
.generic
== NULL
) {
220 attrs
= drive_get_attributes(dp
, errp
);
223 attrs
= bus_get_attributes(dp
, errp
);
226 attrs
= controller_get_attributes(dp
, errp
);
229 attrs
= media_get_attributes(dp
, errp
);
232 attrs
= slice_get_attributes(dp
, errp
);
235 attrs
= partition_get_attributes(dp
, errp
);
238 attrs
= path_get_attributes(dp
, errp
);
241 attrs
= alias_get_attributes(dp
, errp
);
254 dm_get_descriptor_by_name(dm_desc_type_t desc_type
, char *name
, int *errp
)
256 dm_descriptor_t desc
= NULL
;
263 desc
= (uintptr_t)drive_get_descriptor_by_name(name
, errp
);
266 desc
= (uintptr_t)bus_get_descriptor_by_name(name
, errp
);
269 desc
= (uintptr_t)controller_get_descriptor_by_name(name
,
273 desc
= (uintptr_t)media_get_descriptor_by_name(name
, errp
);
276 desc
= (uintptr_t)slice_get_descriptor_by_name(name
, errp
);
279 desc
= (uintptr_t)partition_get_descriptor_by_name(name
,
283 desc
= (uintptr_t)path_get_descriptor_by_name(name
, errp
);
286 desc
= (uintptr_t)alias_get_descriptor_by_name(name
, errp
);
299 dm_get_descriptors(dm_desc_type_t type
, int filter
[], int *errp
)
301 descriptor_t
**descs
= NULL
;
308 descs
= drive_get_descriptors(filter
, errp
);
311 descs
= bus_get_descriptors(filter
, errp
);
314 descs
= controller_get_descriptors(filter
, errp
);
317 descs
= media_get_descriptors(filter
, errp
);
320 descs
= slice_get_descriptors(filter
, errp
);
323 descs
= partition_get_descriptors(filter
, errp
);
326 descs
= path_get_descriptors(filter
, errp
);
329 descs
= alias_get_descriptors(filter
, errp
);
338 return (ptr_array_to_desc_array(descs
, errp
));
342 dm_get_name(dm_descriptor_t desc
, int *errp
)
348 dp
= (descriptor_t
*)(uintptr_t)desc
;
352 if (!cache_is_valid_desc(dp
)) {
358 /* verify that the descriptor is still valid */
359 if (dp
->p
.generic
== NULL
) {
367 nm
= (drive_get_name(dp
));
370 nm
= (bus_get_name(dp
));
373 nm
= (controller_get_name(dp
));
376 nm
= (media_get_name(dp
));
379 nm
= (slice_get_name(dp
));
382 nm
= (partition_get_name(dp
));
385 nm
= (path_get_name(dp
));
388 nm
= (alias_get_name(dp
));
407 dm_get_stats(dm_descriptor_t desc
, int stat_type
, int *errp
)
410 nvlist_t
*stats
= NULL
;
413 dp
= (descriptor_t
*)(uintptr_t)desc
;
417 if (!cache_is_valid_desc(dp
)) {
423 /* verify that the descriptor is still valid */
424 if (dp
->p
.generic
== NULL
) {
432 stats
= drive_get_stats(dp
, stat_type
, errp
);
435 stats
= bus_get_stats(dp
, stat_type
, errp
);
438 stats
= controller_get_stats(dp
, stat_type
, errp
);
441 stats
= media_get_stats(dp
, stat_type
, errp
);
444 if (stat_type
== DM_SLICE_STAT_USE
) {
446 * If NOINUSE_CHECK is set, we do not perform
447 * the in use checking if the user has set stat_type
455 stats
= slice_get_stats(dp
, stat_type
, errp
);
458 stats
= partition_get_stats(dp
, stat_type
, errp
);
461 stats
= path_get_stats(dp
, stat_type
, errp
);
464 stats
= alias_get_stats(dp
, stat_type
, errp
);
477 dm_get_type(dm_descriptor_t desc
)
481 dp
= (descriptor_t
*)(uintptr_t)desc
;
485 if (!cache_is_valid_desc(dp
)) {
495 * Returns, via slices paramater, a dm_descriptor_t list of
496 * slices for the named disk drive.
499 dm_get_slices(char *drive
, dm_descriptor_t
**slices
, int *errp
)
501 dm_descriptor_t alias
;
502 dm_descriptor_t
*media
;
503 dm_descriptor_t
*disk
;
512 alias
= dm_get_descriptor_by_name(DM_ALIAS
, drive
, errp
);
515 * Errors must be handled by the caller. The dm_descriptor_t *
516 * values will be NULL if an error occured in these calls.
520 disk
= dm_get_associated_descriptors(alias
, DM_DRIVE
, errp
);
521 dm_free_descriptor(alias
);
523 media
= dm_get_associated_descriptors(*disk
,
525 dm_free_descriptors(disk
);
527 *slices
= dm_get_associated_descriptors(*media
,
529 dm_free_descriptors(media
);
535 * Convenience function to get slice stats
538 dm_get_slice_stats(char *slice
, nvlist_t
**dev_stats
, int *errp
)
540 dm_descriptor_t devp
;
550 * Errors must be handled by the caller. The dm_descriptor_t *
551 * values will be NULL if an error occured in these calls.
553 devp
= dm_get_descriptor_by_name(DM_SLICE
, slice
, errp
);
555 *dev_stats
= dm_get_stats(devp
, DM_SLICE_STAT_USE
,
557 dm_free_descriptor(devp
);
562 * Checks for overlapping slices. If the given device is a slice, and it
563 * overlaps with any non-backup slice on the disk, return true with a detailed
564 * description similar to dm_inuse().
567 dm_isoverlapping(char *slicename
, char **overlaps_with
, int *errp
)
569 dm_descriptor_t slice
= NULL
;
570 dm_descriptor_t
*media
= NULL
;
571 dm_descriptor_t
*slices
= NULL
;
574 uint64_t start_block
= 0;
575 uint64_t end_block
= 0;
576 uint64_t media_size
= 0;
578 nvlist_t
*media_attrs
= NULL
;
579 nvlist_t
*slice_attrs
= NULL
;
582 slice
= dm_get_descriptor_by_name(DM_SLICE
, slicename
, errp
);
587 * Get the list of slices be fetching the associated media, and then all
590 media
= dm_get_associated_descriptors(slice
, DM_MEDIA
, errp
);
591 if (media
== NULL
|| *media
== NULL
|| *errp
!= 0)
594 slices
= dm_get_associated_descriptors(*media
, DM_SLICE
, errp
);
595 if (slices
== NULL
|| *slices
== NULL
|| *errp
!= 0)
598 media_attrs
= dm_get_attributes(*media
, errp
);
599 if (media_attrs
== NULL
|| *errp
)
602 *errp
= nvlist_lookup_uint64(media_attrs
, DM_NACCESSIBLE
, &media_size
);
606 slice_attrs
= dm_get_attributes(slice
, errp
);
607 if (slice_attrs
== NULL
|| *errp
!= 0)
610 *errp
= nvlist_lookup_uint64(slice_attrs
, DM_START
, &start_block
);
614 *errp
= nvlist_lookup_uint64(slice_attrs
, DM_SIZE
, &size
);
618 *errp
= nvlist_lookup_uint32(slice_attrs
, DM_INDEX
, &in_snum
);
622 end_block
= (start_block
+ size
) - 1;
624 for (i
= 0; slices
[i
]; i
++) {
625 uint64_t other_start
;
630 nvlist_t
*other_attrs
= dm_get_attributes(slices
[i
], errp
);
632 if (other_attrs
== NULL
)
638 *errp
= nvlist_lookup_uint64(other_attrs
, DM_START
,
641 nvlist_free(other_attrs
);
645 *errp
= nvlist_lookup_uint64(other_attrs
, DM_SIZE
,
649 nvlist_free(other_attrs
);
654 other_end
= (other_size
+ other_start
) - 1;
656 *errp
= nvlist_lookup_uint32(other_attrs
, DM_INDEX
,
660 nvlist_free(other_attrs
);
666 * Check to see if there are > 2 overlapping regions
667 * on this media in the same region as this slice.
668 * This is done by assuming the following:
669 * Slice 2 is the backup slice if it is the size
671 * If slice 2 is the overlap and slice 2 is the size of
672 * the whole disk, continue. If another slice is found
673 * that overlaps with our slice, return it.
674 * There is the potential that there is more than one slice
675 * that our slice overlaps with, however, we only return
676 * the first overlapping slice we find.
679 if (start_block
>= other_start
&& start_block
<= other_end
) {
680 if ((snum
== 2 && (other_size
== media_size
)) ||
684 char *str
= dm_get_name(slices
[i
], errp
);
686 nvlist_free(other_attrs
);
690 *overlaps_with
= strdup(str
);
692 nvlist_free(other_attrs
);
696 } else if (other_start
>= start_block
&&
697 other_start
<= end_block
) {
698 if ((snum
== 2 && (other_size
== media_size
)) ||
702 char *str
= dm_get_name(slices
[i
], errp
);
704 nvlist_free(other_attrs
);
708 *overlaps_with
= strdup(str
);
710 nvlist_free(other_attrs
);
715 nvlist_free(other_attrs
);
719 nvlist_free(media_attrs
);
720 nvlist_free(slice_attrs
);
723 dm_free_descriptors(slices
);
725 dm_free_descriptors(media
);
727 dm_free_descriptor(slice
);
733 * Get the full list of swap entries. Returns -1 on error, or >= 0 to
734 * indicate the number of entries in the list. Callers are responsible
735 * for calling dm_free_swapentries() to deallocate memory. If this
736 * returns 0, the swaptbl_t still needs to be freed.
739 dm_get_swapentries(swaptbl_t
**stp
, int *errp
)
747 /* get number of swap entries */
748 if ((count
= swapctl(SC_GETNSWP
, NULL
)) < 0) {
758 tbl
= calloc(1, sizeof (int) + count
* sizeof (swapent_t
));
764 ptr
= calloc(1, count
* MAXPATHLEN
);
771 /* set up pointers to the pathnames */
773 for (i
= 0; i
< count
; i
++) {
774 tbl
->swt_ent
[i
].ste_path
= ptr
;
778 /* get list of swap paths */
779 count
= swapctl(SC_LIST
, tbl
);
793 dm_free_swapentries(swaptbl_t
*stp
)
797 free(stp
->swt_ent
[0].ste_path
);
802 * Check a slice to see if it's being used by swap.
805 dm_inuse_swap(const char *dev_name
, int *errp
)
809 swaptbl_t
*tbl
= NULL
;
813 count
= dm_get_swapentries(&tbl
, errp
);
814 if (count
< 0 || *errp
) {
816 dm_free_swapentries(tbl
);
820 /* if there are no swap entries, we're done */
829 if (strcmp(dev_name
, tbl
->swt_ent
[count
].ste_path
) == 0) {
835 dm_free_swapentries(tbl
);
840 * Returns 'in use' details, if found, about a specific dev_name,
841 * based on the caller(who). It is important to note that it is possible
842 * for there to be more than one 'in use' statistic regarding a dev_name.
843 * The **msg parameter returns a list of 'in use' details. This message
844 * is formatted via gettext().
847 dm_inuse(char *dev_name
, char **msg
, dm_who_type_t who
, int *errp
)
849 nvlist_t
*dev_stats
= NULL
;
851 nvpair_t
*nvwhat
= NULL
;
852 nvpair_t
*nvdesc
= NULL
;
861 * If the user doesn't want to do in use checking, return.
867 dname
= getfullblkname(dev_name
);
869 * If we cannot find the block name, we cannot check the device
870 * for in use statistics. So, return found, which is == 0.
872 if (dname
== NULL
|| *dname
== '\0') {
877 * Slice stats for swap devices are only returned if mounted
878 * (e.g. /tmp). Other devices or files being used for swap
879 * are ignored, so we add a special check here to use swapctl(2)
880 * to perform in-use checking.
882 if (ANY_ZPOOL_USE(who
) && (err
= dm_inuse_swap(dname
, errp
))) {
884 /* on error, dm_inuse_swap sets errp */
890 /* simulate a mounted swap device */
891 (void) build_usage_string(dname
, DM_USE_MOUNT
, "swap", msg
,
894 /* if this fails, dm_get_usage_string changed */
901 dm_get_slice_stats(dname
, &dev_stats
, errp
);
902 if (dev_stats
== NULL
) {
904 * If there is an error, but it isn't a no device found error
905 * return the error as recorded. Otherwise, with a full
906 * block name, we might not be able to get the slice
907 * associated, and will get an ENODEV error.
917 nvwhat
= nvlist_next_nvpair(dev_stats
, nvdesc
);
918 nvdesc
= nvlist_next_nvpair(dev_stats
, nvwhat
);
921 * End of the list found.
923 if (nvwhat
== NULL
|| nvdesc
== NULL
) {
927 * Otherwise, we check to see if this client(who) cares
928 * about this in use scenario
931 ASSERT(strcmp(nvpair_name(nvwhat
), DM_USED_BY
) == 0);
932 ASSERT(strcmp(nvpair_name(nvdesc
), DM_USED_NAME
) == 0);
934 * If we error getting the string value continue on
935 * to the next pair(if there is one)
937 if (nvpair_value_string(nvwhat
, &by
)) {
940 if (nvpair_value_string(nvdesc
, &data
)) {
947 * mkfs is not in use for these cases.
948 * All others are in use.
950 if (strcmp(by
, DM_USE_LU
) == 0 ||
951 strcmp(by
, DM_USE_FS
) == 0 ||
952 strcmp(by
, DM_USE_EXPORTED_ZPOOL
) == 0) {
955 if (build_usage_string(dname
,
956 by
, data
, msg
, &found
, errp
) != 0) {
964 * Not in use for this.
966 if (strcmp(by
, DM_USE_DUMP
) == 0 ||
967 strcmp(by
, DM_USE_FS
) == 0 ||
968 strcmp(by
, DM_USE_EXPORTED_ZPOOL
) == 0) {
971 if (strcmp(by
, DM_USE_LU
) == 0 &&
972 strcmp(data
, "-") == 0) {
975 if (strcmp(by
, DM_USE_VFSTAB
) == 0 &&
976 strcmp(data
, "") == 0) {
979 if (build_usage_string(dname
,
980 by
, data
, msg
, &found
, errp
) != 0) {
988 * Not in use for this.
990 if ((strcmp(by
, DM_USE_MOUNT
) == 0 &&
991 strcmp(data
, "swap") == 0) ||
992 strcmp(by
, DM_USE_DUMP
) == 0 ||
993 strcmp(by
, DM_USE_FS
) == 0 ||
994 strcmp(by
, DM_USE_EXPORTED_ZPOOL
) == 0) {
997 if (build_usage_string(dname
,
998 by
, data
, msg
, &found
, errp
)) {
1006 if (strcmp(by
, DM_USE_FS
) == 0 ||
1007 strcmp(by
, DM_USE_EXPORTED_ZPOOL
) == 0)
1009 if (build_usage_string(dname
,
1010 by
, data
, msg
, &found
, errp
) != 0) {
1017 case DM_WHO_ZPOOL_FORCE
:
1018 if (strcmp(by
, DM_USE_FS
) == 0 ||
1019 strcmp(by
, DM_USE_EXPORTED_ZPOOL
) == 0)
1023 if (build_usage_string(dname
,
1024 by
, data
, msg
, &found
, errp
) != 0) {
1030 case DM_WHO_ZPOOL_SPARE
:
1031 if (strcmp(by
, DM_USE_SPARE_ZPOOL
) != 0) {
1032 if (build_usage_string(dname
, by
,
1033 data
, msg
, &found
, errp
) != 0) {
1042 * nothing found in use for this client
1043 * of libdiskmgt. Default is 'not in use'.
1051 nvlist_free(dev_stats
);
1057 dm_get_usage_string(char *what
, char *how
, char **usage_string
)
1061 if (usage_string
== NULL
|| what
== NULL
) {
1064 *usage_string
= NULL
;
1066 if (strcmp(what
, DM_USE_MOUNT
) == 0) {
1067 if (strcmp(how
, "swap") == 0) {
1068 *usage_string
= dgettext(TEXT_DOMAIN
,
1069 "%s is currently used by swap. Please see swap(1M)."
1072 *usage_string
= dgettext(TEXT_DOMAIN
,
1073 "%s is currently mounted on %s."
1074 " Please see umount(1M).\n");
1076 } else if (strcmp(what
, DM_USE_VFSTAB
) == 0) {
1077 *usage_string
= dgettext(TEXT_DOMAIN
,
1078 "%s is normally mounted on %s according to /etc/vfstab. "
1079 "Please remove this entry to use this device.\n");
1080 } else if (strcmp(what
, DM_USE_FS
) == 0) {
1081 *usage_string
= dgettext(TEXT_DOMAIN
,
1082 "%s contains a %s filesystem.\n");
1083 } else if (strcmp(what
, DM_USE_VXVM
) == 0) {
1084 *usage_string
= dgettext(TEXT_DOMAIN
,
1085 "%s is part of VxVM volume %s.\n");
1086 } else if (strcmp(what
, DM_USE_LU
) == 0) {
1087 *usage_string
= dgettext(TEXT_DOMAIN
,
1088 "%s is in use for live upgrade %s. Please see ludelete(1M)."
1090 } else if (strcmp(what
, DM_USE_DUMP
) == 0) {
1091 *usage_string
= dgettext(TEXT_DOMAIN
,
1092 "%s is in use by %s. Please see dumpadm(1M)."
1094 } else if (strcmp(what
, DM_USE_EXPORTED_ZPOOL
) == 0) {
1095 *usage_string
= dgettext(TEXT_DOMAIN
,
1096 "%s is part of exported or potentially active ZFS pool %s. "
1097 "Please see zpool(1M).\n");
1098 } else if (strcmp(what
, DM_USE_ACTIVE_ZPOOL
) == 0) {
1099 *usage_string
= dgettext(TEXT_DOMAIN
,
1100 "%s is part of active ZFS pool %s. Please see zpool(1M)."
1102 } else if (strcmp(what
, DM_USE_SPARE_ZPOOL
) == 0) {
1103 *usage_string
= dgettext(TEXT_DOMAIN
,
1104 "%s is reserved as a hot spare for ZFS pool %s. Please "
1105 "see zpool(1M).\n");
1106 } else if (strcmp(what
, DM_USE_L2CACHE_ZPOOL
) == 0) {
1107 *usage_string
= dgettext(TEXT_DOMAIN
,
1108 "%s is in use as a cache device for ZFS pool %s. "
1109 "Please see zpool(1M).\n");
1113 libdiskmgt_add_str(nvlist_t
*attrs
, char *name
, char *val
, int *errp
)
1116 *errp
= nvlist_add_string(attrs
, name
, val
);
1121 libdiskmgt_empty_desc_array(int *errp
)
1123 descriptor_t
**empty
;
1125 empty
= (descriptor_t
**)calloc(1, sizeof (descriptor_t
*));
1126 if (empty
== NULL
) {
1137 libdiskmgt_init_debug()
1141 if ((valp
= getenv(DM_DEBUG
)) != NULL
) {
1142 dm_debug
= atoi(valp
);
1147 libdiskmgt_str_eq(char *nm1
, char *nm2
)
1151 (void) fprintf(stderr
, "WARNING: str_eq nm1 NULL\n");
1165 (void) fprintf(stderr
, "WARNING: str_eq nm2 NULL\n");
1170 if (strcmp(nm1
, nm2
) == 0) {
1178 static descriptor_t
**
1179 desc_array_to_ptr_array(dm_descriptor_t
*descs
, int *errp
)
1182 return ((descriptor_t
**)descs
);
1184 /* convert the 64 bit descriptors to 32 bit ptrs */
1189 for (cnt
= 0; descs
[cnt
]; cnt
++)
1192 da
= (descriptor_t
**)calloc(cnt
+ 1, sizeof (descriptor_t
*));
1198 for (i
= 0; descs
[i
]; i
++) {
1199 da
[i
] = (descriptor_t
*)(uintptr_t)descs
[i
];
1209 static dm_descriptor_t
*
1210 ptr_array_to_desc_array(descriptor_t
**ptrs
, int *errp
)
1213 return ((dm_descriptor_t
*)ptrs
);
1215 /* convert the 32 bit ptrs to the 64 bit descriptors */
1218 dm_descriptor_t
*da
;
1220 if (*errp
!= 0 || ptrs
== NULL
) {
1224 for (cnt
= 0; ptrs
[cnt
]; cnt
++)
1227 da
= (dm_descriptor_t
*)calloc(cnt
+ 1, sizeof (dm_descriptor_t
));
1233 for (i
= 0; ptrs
[i
]; i
++) {
1234 da
[i
] = (uintptr_t)ptrs
[i
];
1243 * Build the usage string for the in use data. Return the build string in
1244 * the msg parameter. This function takes care of reallocing all the memory
1245 * for this usage string. Usage string is returned already formatted for
1249 build_usage_string(char *dname
, char *by
, char *data
, char **msg
,
1250 int *found
, int *errp
)
1259 dm_get_usage_string(by
, data
, &use
);
1265 len0
= strlen(*msg
);
1269 len1
= snprintf(NULL
, 0, use
, dname
, data
);
1272 * If multiple in use details they
1273 * are listed 1 per line for ease of
1274 * reading. dm_find_usage_string
1275 * formats these appropriately.
1277 if ((p
= realloc(*msg
, len0
+ len1
+ 1)) == NULL
) {
1285 (void) snprintf(*msg
+ len0
, len1
+ 1, use
, dname
, data
);