/* * 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 #include #include #include #include #include #include #include #include #include #include __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); }