diff options
Diffstat (limited to 'usr/src/lib/libipd/common/libipd.c')
-rw-r--r-- | usr/src/lib/libipd/common/libipd.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/usr/src/lib/libipd/common/libipd.c b/usr/src/lib/libipd/common/libipd.c new file mode 100644 index 0000000000..c580fdf61e --- /dev/null +++ b/usr/src/lib/libipd/common/libipd.c @@ -0,0 +1,303 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> + +#include <libipd.h> +#include <sys/ipd.h> + +__thread ipd_errno_t ipd_errno = 0; +__thread char ipd_errmsg[512]; + +struct ipd_stat { + uint_t is_nzones; + zoneid_t *is_zoneids; + struct ipd_config *is_configs; +}; + +static ipd_errno_t +xlate_errno(int e) +{ + switch (e) { + case 0: + return (EIPD_NOERROR); + case ENOMEM: + case EAGAIN: + return (EIPD_NOMEM); + case ERANGE: + return (EIPD_RANGE); + case EPERM: + return (EIPD_PERM); + case EFAULT: + return (EIPD_FAULT); + case ENOTTY: + return (EIPD_INTERNAL); + default: + return (EIPD_UNKNOWN); + } +} + +const char * +ipd_strerror(ipd_errno_t e) +{ + switch (e) { + case EIPD_NOERROR: + return ("no error"); + case EIPD_NOMEM: + return ("out of memory"); + case EIPD_ZC_NOENT: + return ("zone does not exist or is not using ipd"); + case EIPD_RANGE: + return ("argument out of range"); + case EIPD_PERM: + return ("permission denied"); + case EIPD_FAULT: + return ("bad pointer"); + case EIPD_INTERNAL: + return ("internal library error"); + case EIPD_UNKNOWN: + default: + return ("unknown error"); + } +} + +static int +ipd_set_errno(ipd_errno_t e, const char *fmt, ...) +{ + va_list ap; + + ipd_errno = e; + if (fmt != NULL) { + va_start(ap, fmt); + (void) vsnprintf(ipd_errmsg, sizeof (ipd_errmsg), fmt, ap); + va_end(ap); + } else { + (void) strlcpy(ipd_errmsg, + ipd_strerror(e), sizeof (ipd_errmsg)); + } + + return (-1); +} + +int +ipd_open(const char *path) +{ + int fd; + + if (path == NULL) + path = IPD_DEV_PATH; + + fd = open(path, O_RDWR); + if (fd < 0) { + return (ipd_set_errno(xlate_errno(errno), + "unable to open %s: %s", path, strerror(errno))); + } + + return (fd); +} + +int +ipd_close(int fd) +{ + (void) close(fd); + return (0); +} + +int +ipd_status_read(int fd, ipd_stathdl_t *ispp) +{ + struct ipd_stat *isp = NULL; + ipd_ioc_list_t ipil; + uint_t rzones; + uint_t i; + + bzero(&ipil, sizeof (ipil)); + if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) { + return (ipd_set_errno(xlate_errno(errno), + "unable to retrieve ipd zone list: %s", strerror(errno))); + } + + for (;;) { + if ((rzones = ipil.ipil_nzones) == 0) + break; + + ipil.ipil_info = + malloc(sizeof (ipd_ioc_info_t) * ipil.ipil_nzones); + if (ipil.ipil_info == NULL) + return (ipd_set_errno(EIPD_NOMEM, NULL)); + + if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) { + free(ipil.ipil_info); + return (ipd_set_errno(xlate_errno(errno), + "unable to retrieve ipd zone list: %s", + strerror(errno))); + } + + if (ipil.ipil_nzones <= rzones) + break; + + free(ipil.ipil_info); + } + + if ((isp = malloc(sizeof (struct ipd_stat))) == NULL) { + free(ipil.ipil_info); + return (ipd_set_errno(EIPD_NOMEM, NULL)); + } + + isp->is_nzones = ipil.ipil_nzones; + + if (isp->is_nzones == 0) { + isp->is_zoneids = NULL; + isp->is_configs = NULL; + *ispp = isp; + return (0); + } + + isp->is_zoneids = malloc(sizeof (zoneid_t) * ipil.ipil_nzones); + if (isp->is_zoneids == NULL) { + free(ipil.ipil_info); + free(isp); + return (ipd_set_errno(EIPD_NOMEM, NULL)); + } + isp->is_configs = malloc(sizeof (struct ipd_config) * ipil.ipil_nzones); + if (isp->is_configs == NULL) { + free(ipil.ipil_info); + free(isp->is_zoneids); + free(isp); + return (ipd_set_errno(EIPD_NOMEM, NULL)); + } + + for (i = 0; i < isp->is_nzones; i++) { + isp->is_zoneids[i] = ipil.ipil_info[i].ipii_zoneid; + + isp->is_configs[i].ic_corrupt = ipil.ipil_info[i].ipii_corrupt; + isp->is_configs[i].ic_drop = ipil.ipil_info[i].ipii_drop; + isp->is_configs[i].ic_delay = ipil.ipil_info[i].ipii_delay; + + isp->is_configs[i].ic_mask = + ((!!isp->is_configs[i].ic_corrupt) * IPDM_CORRUPT) | + ((!!isp->is_configs[i].ic_drop) * IPDM_DROP) | + ((!!isp->is_configs[i].ic_delay) * IPDM_DELAY); + } + + *ispp = isp; + return (0); +} + +void +ipd_status_foreach_zone(const ipd_stathdl_t hdl, ipd_status_cb_f f, void *arg) +{ + const struct ipd_stat *isp = hdl; + uint_t i; + + for (i = 0; i < isp->is_nzones; i++) + f(isp->is_zoneids[i], &isp->is_configs[i], arg); +} + +int +ipd_status_get_config(const ipd_stathdl_t hdl, zoneid_t z, ipd_config_t **icpp) +{ + const struct ipd_stat *isp = hdl; + uint_t i; + + for (i = 0; i < isp->is_nzones; i++) { + if (isp->is_zoneids[i] == z) { + *icpp = &isp->is_configs[i]; + return (0); + } + } + + return (ipd_set_errno(EIPD_ZC_NOENT, + "zone %d does not exist or has no ipd configuration", z)); +} + +void +ipd_status_free(ipd_stathdl_t hdl) +{ + struct ipd_stat *isp = hdl; + + if (isp != NULL) { + free(isp->is_zoneids); + free(isp->is_configs); + } + free(isp); +} + +int +ipd_ctl(int fd, zoneid_t z, const ipd_config_t *icp) +{ + ipd_ioc_perturb_t ipip; + + bzero(&ipip, sizeof (ipd_ioc_perturb_t)); + ipip.ipip_zoneid = z; + + if (icp->ic_mask & IPDM_CORRUPT) { + if (icp->ic_corrupt == 0) + ipip.ipip_arg |= IPD_CORRUPT; + } + if (icp->ic_mask & IPDM_DELAY) { + if (icp->ic_delay == 0) + ipip.ipip_arg |= IPD_DELAY; + } + if (icp->ic_mask & IPDM_DROP) { + if (icp->ic_drop == 0) + ipip.ipip_arg |= IPD_DROP; + } + + if (ipip.ipip_arg != 0 && ioctl(fd, IPDIOC_REMOVE, &ipip) != 0) { + return (ipd_set_errno(xlate_errno(errno), + "unable to remove cleared ipd settings: %s", + strerror(errno))); + } + + if ((icp->ic_mask & IPDM_CORRUPT) && icp->ic_corrupt != 0) { + ipip.ipip_zoneid = z; + ipip.ipip_arg = icp->ic_corrupt; + if (ioctl(fd, IPDIOC_CORRUPT, &ipip) != 0) { + return (ipd_set_errno(xlate_errno(errno), + "unable to set corruption rate to %d: %s", + ipip.ipip_arg, strerror(errno))); + } + } + if ((icp->ic_mask & IPDM_DELAY) && icp->ic_delay != 0) { + ipip.ipip_zoneid = z; + ipip.ipip_arg = icp->ic_delay; + if (ioctl(fd, IPDIOC_DELAY, &ipip) != 0) { + return (ipd_set_errno(xlate_errno(errno), + "unable to set delay time to %d: %s", + ipip.ipip_arg, strerror(errno))); + } + } + if ((icp->ic_mask & IPDM_DROP) && icp->ic_drop != 0) { + ipip.ipip_zoneid = z; + ipip.ipip_arg = icp->ic_drop; + if (ioctl(fd, IPDIOC_DROP, &ipip) != 0) { + return (ipd_set_errno(xlate_errno(errno), + "unable to set drop probability to %d: %s", + ipip.ipip_arg, strerror(errno))); + } + } + + return (0); +} |