2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include "libzfs_jni_diskmgt.h"
2N/A#include "libzfs_jni_util.h"
2N/A#include <strings.h>
2N/A#include <libzfs.h>
2N/A#include <sys/mnttab.h>
2N/A
2N/A/*
2N/A * Function prototypes
2N/A */
2N/A
2N/Astatic char *get_device_name(dm_descriptor_t device, int *error);
2N/Astatic dmgt_disk_t *get_disk(dm_descriptor_t disk, int *error);
2N/Astatic char **get_disk_aliases(dm_descriptor_t disk, char *name, int *error);
2N/Astatic int get_disk_online(dm_descriptor_t disk, int *error);
2N/Astatic void remove_slice_from_list(dmgt_slice_t **slices, int index);
2N/Astatic dmgt_slice_t **get_disk_slices(dm_descriptor_t media,
2N/A const char *name, uint32_t blocksize, int *error);
2N/Astatic dmgt_slice_t **get_disk_usable_slices(dm_descriptor_t media,
2N/A const char *name, uint32_t blocksize, int *in_use, int *error);
2N/Astatic void get_disk_size(dm_descriptor_t media, char *name,
2N/A uint64_t *size, uint32_t *blocksize, int *error);
2N/Astatic void get_slice_use(dm_descriptor_t slice, char *name,
2N/A char **used_name, char **used_by, int *error);
2N/Astatic dmgt_slice_t *get_slice(
2N/A dm_descriptor_t slice, uint32_t blocksize, int *error);
2N/Astatic void handle_error(const char *format, ...);
2N/Astatic int slice_in_use(dmgt_slice_t *slice, int *error);
2N/Astatic int slice_too_small(dmgt_slice_t *slice);
2N/A
2N/A/*
2N/A * Static data
2N/A */
2N/A
2N/Astatic void (*error_func)(const char *, va_list);
2N/A
2N/A/*
2N/A * Static functions
2N/A */
2N/A
2N/Astatic char *
2N/Aget_device_name(dm_descriptor_t device, int *error)
2N/A{
2N/A char *dup;
2N/A char *name;
2N/A
2N/A *error = 0;
2N/A name = dm_get_name(device, error);
2N/A if (*error) {
2N/A handle_error("could not determine name of device");
2N/A } else {
2N/A dup = strdup(name);
2N/A if (dup == NULL) {
2N/A handle_error("out of memory");
2N/A *error = -1;
2N/A }
2N/A
2N/A dm_free_name(name);
2N/A }
2N/A
2N/A return (dup);
2N/A}
2N/A
2N/A/*
2N/A * Gets a dmgt_disk_t for the given disk dm_descriptor_t.
2N/A *
2N/A * Results:
2N/A *
2N/A * 1. Success: error is set to 0 and a dmgt_disk_t is returned
2N/A *
2N/A * 2. Failure: error is set to -1 and NULL is returned
2N/A */
2N/Astatic dmgt_disk_t *
2N/Aget_disk(dm_descriptor_t disk, int *error)
2N/A{
2N/A dmgt_disk_t *dp;
2N/A *error = 0;
2N/A
2N/A dp = (dmgt_disk_t *)calloc(1, sizeof (dmgt_disk_t));
2N/A if (dp == NULL) {
2N/A handle_error("out of memory");
2N/A *error = -1;
2N/A } else {
2N/A
2N/A /* Get name */
2N/A dp->name = get_device_name(disk, error);
2N/A if (!*error) {
2N/A
2N/A /* Get aliases */
2N/A dp->aliases = get_disk_aliases(disk, dp->name, error);
2N/A if (!*error) {
2N/A
2N/A /* Get media */
2N/A dm_descriptor_t *media =
2N/A dm_get_associated_descriptors(disk,
2N/A DM_MEDIA, error);
2N/A if (*error != 0 || media == NULL ||
2N/A *media == NULL) {
2N/A handle_error(
2N/A "could not get media from disk %s",
2N/A dp->name);
2N/A *error = -1;
2N/A } else {
2N/A /* Get size */
2N/A get_disk_size(media[0], dp->name,
2N/A &(dp->size), &(dp->blocksize),
2N/A error);
2N/A if (!*error) {
2N/A /* Get free slices */
2N/A dp->slices =
2N/A get_disk_usable_slices(
2N/A media[0], dp->name,
2N/A dp->blocksize,
2N/A &(dp->in_use), error);
2N/A }
2N/A dm_free_descriptors(media);
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (*error) {
2N/A /* Normalize error */
2N/A *error = -1;
2N/A
2N/A if (dp != NULL) {
2N/A dmgt_free_disk(dp);
2N/A dp = NULL;
2N/A }
2N/A }
2N/A
2N/A return (dp);
2N/A}
2N/A
2N/Astatic char **
2N/Aget_disk_aliases(dm_descriptor_t disk, char *name, int *error)
2N/A{
2N/A char **names = NULL;
2N/A dm_descriptor_t *aliases;
2N/A
2N/A *error = 0;
2N/A aliases = dm_get_associated_descriptors(disk, DM_ALIAS, error);
2N/A if (*error || aliases == NULL) {
2N/A *error = -1;
2N/A handle_error("could not get aliases for disk %s", name);
2N/A } else {
2N/A
2N/A int j;
2N/A
2N/A /* Count aliases */
2N/A for (j = 0; aliases[j] != NULL; j++);
2N/A
2N/A names = (char **)calloc(j + 1, sizeof (char *));
2N/A if (names == NULL) {
2N/A *error = -1;
2N/A handle_error("out of memory");
2N/A } else {
2N/A
2N/A /* For each alias... */
2N/A for (j = 0; *error == 0 && aliases[j] != NULL; j++) {
2N/A
2N/A dm_descriptor_t alias = aliases[j];
2N/A char *aname = dm_get_name(alias, error);
2N/A if (*error) {
2N/A handle_error("could not get alias %d "
2N/A "for disk %s", (j + 1), name);
2N/A } else {
2N/A names[j] = strdup(aname);
2N/A if (names[j] == NULL) {
2N/A *error = -1;
2N/A handle_error("out of memory");
2N/A }
2N/A
2N/A dm_free_name(aname);
2N/A }
2N/A }
2N/A }
2N/A
2N/A dm_free_descriptors(aliases);
2N/A }
2N/A
2N/A if (*error && names != NULL) {
2N/A /* Free previously-allocated names */
2N/A zjni_free_array((void **)names, free);
2N/A }
2N/A
2N/A return (names);
2N/A}
2N/A
2N/Astatic int
2N/Aget_disk_online(dm_descriptor_t disk, int *error)
2N/A{
2N/A uint32_t status = 0;
2N/A
2N/A nvlist_t *attrs;
2N/A *error = 0;
2N/A attrs = dm_get_attributes(disk, error);
2N/A if (*error) {
2N/A handle_error("could not get disk attributes for disk");
2N/A } else {
2N/A
2N/A /* Try to get the status */
2N/A nvpair_t *match = zjni_nvlist_walk_nvpair(
2N/A attrs, DM_STATUS, DATA_TYPE_UINT32, NULL);
2N/A
2N/A if (match == NULL || nvpair_value_uint32(match, &status)) {
2N/A
2N/A handle_error("could not get status of disk");
2N/A *error = 1;
2N/A }
2N/A
2N/A nvlist_free(attrs);
2N/A }
2N/A
2N/A return (status != 0);
2N/A}
2N/A
2N/A/*
2N/A * Gets the slices for the given disk.
2N/A *
2N/A * Results:
2N/A *
2N/A * 1. Success: error is set to 0 and slices are returned
2N/A *
2N/A * 2. Failure: error is set to -1 and NULL is returned
2N/A */
2N/Astatic dmgt_slice_t **
2N/Aget_disk_slices(dm_descriptor_t media, const char *name, uint32_t blocksize,
2N/A int *error)
2N/A{
2N/A dm_descriptor_t *slices;
2N/A dmgt_slice_t **sap = NULL;
2N/A
2N/A *error = 0;
2N/A slices = dm_get_associated_descriptors(media, DM_SLICE, error);
2N/A if (*error != 0) {
2N/A handle_error("could not get slices of disk %s", name);
2N/A } else {
2N/A int j;
2N/A int nslices = 0;
2N/A
2N/A /* For each slice... */
2N/A for (j = 0; *error == 0 &&
2N/A slices != NULL && slices[j] != NULL; j++) {
2N/A
2N/A /* Get slice */
2N/A dmgt_slice_t *slice =
2N/A get_slice(slices[j], blocksize, error);
2N/A if (!*error) {
2N/A
2N/A dmgt_slice_t **mem =
2N/A (dmgt_slice_t **)realloc(sap,
2N/A (nslices + 2) * sizeof (dmgt_slice_t *));
2N/A
2N/A if (mem == NULL) {
2N/A handle_error("out of memory");
2N/A *error = -1;
2N/A } else {
2N/A
2N/A sap = mem;
2N/A
2N/A /* NULL-terminated array */
2N/A sap[nslices] = slice;
2N/A sap[nslices + 1] = NULL;
2N/A
2N/A nslices++;
2N/A }
2N/A }
2N/A }
2N/A
2N/A dm_free_descriptors(slices);
2N/A }
2N/A
2N/A if (*error) {
2N/A /* Normalize error */
2N/A *error = -1;
2N/A
2N/A if (sap != NULL) {
2N/A zjni_free_array((void **)sap,
2N/A (zjni_free_f)dmgt_free_slice);
2N/A sap = NULL;
2N/A }
2N/A }
2N/A
2N/A return (sap);
2N/A}
2N/A
2N/Astatic void
2N/Aremove_slice_from_list(dmgt_slice_t **slices, int index)
2N/A{
2N/A int i;
2N/A for (i = index; slices[i] != NULL; i++) {
2N/A slices[i] = slices[i + 1];
2N/A }
2N/A}
2N/A
2N/Astatic int
2N/Aslices_overlap(dmgt_slice_t *slice1, dmgt_slice_t *slice2)
2N/A{
2N/A
2N/A uint64_t start1 = slice1->start;
2N/A uint64_t end1 = start1 + slice1->size - 1;
2N/A uint64_t start2 = slice2->start;
2N/A uint64_t end2 = start2 + slice2->size - 1;
2N/A
2N/A int overlap = (start2 <= end1 && start1 <= end2);
2N/A
2N/A#ifdef DEBUG
2N/A if (overlap) {
2N/A (void) fprintf(stderr, "can't use %s: overlaps with %s\n",
2N/A slice2->name, slice1->name);
2N/A (void) fprintf(stderr, " 1: start: %llu - %llu\n",
2N/A (unsigned long long)start1, (unsigned long long)end1);
2N/A (void) fprintf(stderr, " 2: start: %llu - %llu\n",
2N/A (unsigned long long)start2, (unsigned long long)end2);
2N/A }
2N/A#endif
2N/A
2N/A return (overlap);
2N/A}
2N/A
2N/A/*
2N/A * Gets the slices for the given disk.
2N/A *
2N/A * Results:
2N/A *
2N/A * 1. Success: error is set to 0 and slices are returned
2N/A *
2N/A * 2. Failure: error is set to -1 and NULL is returned
2N/A */
2N/Astatic dmgt_slice_t **
2N/Aget_disk_usable_slices(dm_descriptor_t media, const char *name,
2N/A uint32_t blocksize, int *in_use, int *error)
2N/A{
2N/A dmgt_slice_t **slices = get_disk_slices(media, name, blocksize, error);
2N/A if (*error) {
2N/A slices = NULL;
2N/A }
2N/A
2N/A *in_use = 0;
2N/A
2N/A if (slices != NULL) {
2N/A int i, nslices;
2N/A
2N/A for (nslices = 0; slices[nslices] != NULL; nslices++);
2N/A
2N/A /* Prune slices based on use */
2N/A for (i = nslices - 1; i >= 0; i--) {
2N/A dmgt_slice_t *slice = slices[i];
2N/A int s_in_use;
2N/A
2N/A /*
2N/A * Slice at this index could be NULL if
2N/A * removed in earlier iteration
2N/A */
2N/A if (slice == NULL) {
2N/A continue;
2N/A }
2N/A
2N/A s_in_use = slice_in_use(slice, error);
2N/A if (*error) {
2N/A break;
2N/A }
2N/A
2N/A if (s_in_use) {
2N/A int j;
2N/A remove_slice_from_list(slices, i);
2N/A
2N/A /* Disk is in use */
2N/A *in_use = 1;
2N/A
2N/A /*
2N/A * Remove any slice that overlaps with this
2N/A * in-use slice
2N/A */
2N/A for (j = nslices - 1; j >= 0; j--) {
2N/A dmgt_slice_t *slice2 = slices[j];
2N/A
2N/A if (slice2 != NULL &&
2N/A slices_overlap(slice, slice2)) {
2N/A remove_slice_from_list(slices,
2N/A j);
2N/A dmgt_free_slice(slice2);
2N/A }
2N/A }
2N/A
2N/A dmgt_free_slice(slice);
2N/A } else if (slice_too_small(slice)) {
2N/A remove_slice_from_list(slices, i);
2N/A dmgt_free_slice(slice);
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (*error) {
2N/A /* Normalize error */
2N/A *error = -1;
2N/A
2N/A if (slices != NULL) {
2N/A zjni_free_array((void **)slices,
2N/A (zjni_free_f)dmgt_free_slice);
2N/A slices = NULL;
2N/A }
2N/A }
2N/A
2N/A return (slices);
2N/A}
2N/A
2N/Astatic void
2N/Aget_disk_size(dm_descriptor_t media, char *name, uint64_t *size,
2N/A uint32_t *blocksize, int *error)
2N/A{
2N/A nvlist_t *attrs;
2N/A
2N/A *size = 0;
2N/A *error = 0;
2N/A
2N/A attrs = dm_get_attributes(media, error);
2N/A
2N/A if (*error) {
2N/A handle_error("could not get media attributes from disk: %s",
2N/A name);
2N/A } else {
2N/A /* Try to get the number of accessible blocks */
2N/A nvpair_t *match = zjni_nvlist_walk_nvpair(
2N/A attrs, DM_NACCESSIBLE, DATA_TYPE_UINT64, NULL);
2N/A if (match == NULL || nvpair_value_uint64(match, size)) {
2N/A
2N/A /* Disk is probably not labeled, get raw size instead */
2N/A match = zjni_nvlist_walk_nvpair(
2N/A attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
2N/A if (match == NULL || nvpair_value_uint64(match, size)) {
2N/A handle_error("could not get size of disk: %s",
2N/A name);
2N/A *error = 1;
2N/A }
2N/A }
2N/A
2N/A if (*error == 0) {
2N/A match = zjni_nvlist_walk_nvpair(
2N/A attrs, DM_BLOCKSIZE, DATA_TYPE_UINT32, NULL);
2N/A if (match == NULL ||
2N/A nvpair_value_uint32(match, blocksize)) {
2N/A handle_error("could not get "
2N/A "block size of disk: %s", name);
2N/A *error = 1;
2N/A } else {
2N/A *size *= *blocksize;
2N/A }
2N/A }
2N/A
2N/A nvlist_free(attrs);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Aget_slice_use(dm_descriptor_t slice, char *name, char **used_name,
2N/A char **used_by, int *error)
2N/A{
2N/A /* Get slice use statistics */
2N/A nvlist_t *stats = dm_get_stats(slice, DM_SLICE_STAT_USE, error);
2N/A if (*error != 0) {
2N/A handle_error("could not get stats of slice %s", name);
2N/A } else {
2N/A
2N/A *used_name = NULL;
2N/A *used_by = NULL;
2N/A
2N/A if (stats != NULL) {
2N/A char *tmp;
2N/A nvpair_t *match;
2N/A
2N/A /* Get the type of usage for this slice */
2N/A match = zjni_nvlist_walk_nvpair(
2N/A stats, DM_USED_BY, DATA_TYPE_STRING, NULL);
2N/A
2N/A if (match != NULL &&
2N/A nvpair_value_string(match, &tmp) == 0) {
2N/A
2N/A *used_name = strdup(tmp);
2N/A if (*used_name == NULL) {
2N/A *error = -1;
2N/A handle_error("out of memory");
2N/A } else {
2N/A
2N/A /* Get the object using this slice */
2N/A match =
2N/A zjni_nvlist_walk_nvpair(stats,
2N/A DM_USED_NAME, DATA_TYPE_STRING,
2N/A NULL);
2N/A
2N/A if (match != NULL &&
2N/A nvpair_value_string(match, &tmp) ==
2N/A 0) {
2N/A *used_by = strdup(tmp);
2N/A if (*used_by == NULL) {
2N/A *error = -1;
2N/A handle_error(
2N/A "out of memory");
2N/A }
2N/A }
2N/A }
2N/A }
2N/A nvlist_free(stats);
2N/A }
2N/A }
2N/A}
2N/A
2N/Astatic dmgt_slice_t *
2N/Aget_slice(dm_descriptor_t slice, uint32_t blocksize, int *error)
2N/A{
2N/A dmgt_slice_t *sp;
2N/A *error = 0;
2N/A sp = (dmgt_slice_t *)calloc(1, sizeof (dmgt_slice_t));
2N/A if (sp == NULL) {
2N/A *error = -1;
2N/A handle_error("out of memory");
2N/A } else {
2N/A
2N/A /* Get name */
2N/A sp->name = get_device_name(slice, error);
2N/A if (!*error) {
2N/A
2N/A nvlist_t *attrs = dm_get_attributes(slice, error);
2N/A if (*error) {
2N/A handle_error("could not get "
2N/A "attributes from slice: %s", sp->name);
2N/A } else {
2N/A /* Get the size in blocks */
2N/A nvpair_t *match = zjni_nvlist_walk_nvpair(
2N/A attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
2N/A uint64_t size_blocks;
2N/A
2N/A sp->size = 0;
2N/A
2N/A if (match == NULL ||
2N/A nvpair_value_uint64(match, &size_blocks)) {
2N/A handle_error("could not get "
2N/A "size of slice: %s", sp->name);
2N/A *error = 1;
2N/A } else {
2N/A uint64_t start_blocks;
2N/A
2N/A /* Convert to bytes */
2N/A sp->size = blocksize * size_blocks;
2N/A
2N/A /* Get the starting block */
2N/A match = zjni_nvlist_walk_nvpair(
2N/A attrs, DM_START, DATA_TYPE_UINT64,
2N/A NULL);
2N/A
2N/A if (match == NULL ||
2N/A nvpair_value_uint64(match,
2N/A &start_blocks)) {
2N/A handle_error(
2N/A "could not get "
2N/A "start block of slice: %s",
2N/A sp->name);
2N/A *error = 1;
2N/A } else {
2N/A /* Convert to bytes */
2N/A sp->start =
2N/A blocksize * start_blocks;
2N/A
2N/A /* Set slice use */
2N/A get_slice_use(slice, sp->name,
2N/A &(sp->used_name),
2N/A &(sp->used_by), error);
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (*error && sp != NULL) {
2N/A dmgt_free_slice(sp);
2N/A }
2N/A
2N/A return (sp);
2N/A}
2N/A
2N/Astatic void
2N/Ahandle_error(const char *format, ...)
2N/A{
2N/A va_list ap;
2N/A va_start(ap, format);
2N/A
2N/A if (error_func != NULL) {
2N/A error_func(format, ap);
2N/A }
2N/A
2N/A va_end(ap);
2N/A}
2N/A
2N/A/* Should go away once 6285992 is fixed */
2N/Astatic int
2N/Aslice_too_small(dmgt_slice_t *slice)
2N/A{
2N/A /* Check size */
2N/A if (slice->size < SPA_MINDEVSIZE) {
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "can't use %s: slice too small: %llu\n",
2N/A slice->name, (unsigned long long)slice->size);
2N/A#endif
2N/A return (1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aslice_in_use(dmgt_slice_t *slice, int *error)
2N/A{
2N/A char *msg = NULL;
2N/A int in_use;
2N/A
2N/A /* Determine whether this slice could be passed to "zpool -f" */
2N/A in_use = dm_inuse(slice->name, &msg, DM_WHO_ZPOOL_FORCE, error);
2N/A if (*error) {
2N/A handle_error("%s: could not determine usage", slice->name);
2N/A }
2N/A
2N/A#ifdef DEBUG
2N/A if (in_use) {
2N/A (void) fprintf(stderr,
2N/A "can't use %s: used name: %s: used by: %s\n message: %s\n",
2N/A slice->name, slice->used_name, slice->used_by, msg);
2N/A }
2N/A#endif
2N/A
2N/A if (msg != NULL) {
2N/A free(msg);
2N/A }
2N/A
2N/A return (in_use);
2N/A}
2N/A
2N/A/*
2N/A * Extern functions
2N/A */
2N/A
2N/A/*
2N/A * Iterates through each available disk on the system. For each free
2N/A * dmgt_disk_t *, runs the given function with the dmgt_disk_t * as
2N/A * the first arg and the given void * as the second arg.
2N/A */
2N/Aint
2N/Admgt_avail_disk_iter(dmgt_disk_iter_f func, void *data)
2N/A{
2N/A int error = 0;
2N/A int filter[] = { DM_DT_FIXED, -1 };
2N/A
2N/A /* Search for fixed disks */
2N/A dm_descriptor_t *disks = dm_get_descriptors(DM_DRIVE, filter, &error);
2N/A
2N/A if (error) {
2N/A handle_error("unable to communicate with libdiskmgt");
2N/A } else {
2N/A int i;
2N/A
2N/A /* For each disk... */
2N/A for (i = 0; disks != NULL && disks[i] != NULL; i++) {
2N/A dm_descriptor_t disk = (dm_descriptor_t)disks[i];
2N/A int online;
2N/A
2N/A /* Reset error flag for each disk */
2N/A error = 0;
2N/A
2N/A /* Is this disk online? */
2N/A online = get_disk_online(disk, &error);
2N/A if (!error && online) {
2N/A
2N/A /* Get a dmgt_disk_t for this dm_descriptor_t */
2N/A dmgt_disk_t *dp = get_disk(disk, &error);
2N/A if (!error) {
2N/A
2N/A /*
2N/A * If this disk or any of its
2N/A * slices is usable...
2N/A */
2N/A if (!dp->in_use ||
2N/A zjni_count_elements(
2N/A (void **)dp->slices) != 0) {
2N/A
2N/A /* Run the given function */
2N/A if (func(dp, data)) {
2N/A error = -1;
2N/A }
2N/A dmgt_free_disk(dp);
2N/A#ifdef DEBUG
2N/A } else {
2N/A (void) fprintf(stderr, "disk "
2N/A "has no available slices: "
2N/A "%s\n", dp->name);
2N/A#endif
2N/A }
2N/A
2N/A }
2N/A }
2N/A }
2N/A dm_free_descriptors(disks);
2N/A }
2N/A return (error);
2N/A}
2N/A
2N/Avoid
2N/Admgt_free_disk(dmgt_disk_t *disk)
2N/A{
2N/A if (disk != NULL) {
2N/A free(disk->name);
2N/A zjni_free_array((void **)disk->aliases, free);
2N/A zjni_free_array((void **)disk->slices,
2N/A (zjni_free_f)dmgt_free_slice);
2N/A free(disk);
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Admgt_free_slice(dmgt_slice_t *slice)
2N/A{
2N/A if (slice != NULL) {
2N/A free(slice->name);
2N/A free(slice->used_name);
2N/A free(slice->used_by);
2N/A free(slice);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * For clients that need to capture error output.
2N/A */
2N/Avoid
2N/Admgt_set_error_handler(void (*func)(const char *, va_list))
2N/A{
2N/A error_func = func;
2N/A}