1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2012 Joyent, Inc. All rights reserved. 14 * Use is subject to license terms. 15 */ 16 17 #include <sys/types.h> 18 #include <stdio.h> 19 #include <fcntl.h> 20 #include <errno.h> 21 #include <string.h> 22 #include <strings.h> 23 #include <stdlib.h> 24 #include <unistd.h> 25 #include <stdarg.h> 26 27 #include <libipd.h> 28 #include <sys/ipd.h> 29 30 __thread ipd_errno_t ipd_errno = 0; 31 __thread char ipd_errmsg[512]; 32 33 struct ipd_stat { 34 uint_t is_nzones; 35 zoneid_t *is_zoneids; 36 struct ipd_config *is_configs; 37 }; 38 39 static ipd_errno_t 40 xlate_errno(int e) 41 { 42 switch (e) { 43 case 0: 44 return (EIPD_NOERROR); 45 case ENOMEM: 46 case EAGAIN: 47 return (EIPD_NOMEM); 48 case ERANGE: 49 return (EIPD_RANGE); 50 case EPERM: 51 return (EIPD_PERM); 52 case EFAULT: 53 return (EIPD_FAULT); 54 case ENOTTY: 55 return (EIPD_INTERNAL); 56 default: 57 return (EIPD_UNKNOWN); 58 } 59 } 60 61 const char * 62 ipd_strerror(ipd_errno_t e) 63 { 64 switch (e) { 65 case EIPD_NOERROR: 66 return ("no error"); 67 case EIPD_NOMEM: 68 return ("out of memory"); 69 case EIPD_ZC_NOENT: 70 return ("zone does not exist or is not using ipd"); 71 case EIPD_RANGE: 72 return ("argument out of range"); 73 case EIPD_PERM: 74 return ("permission denied"); 75 case EIPD_FAULT: 76 return ("bad pointer"); 77 case EIPD_INTERNAL: 78 return ("internal library error"); 79 case EIPD_UNKNOWN: 80 default: 81 return ("unknown error"); 82 } 83 } 84 85 static int 86 ipd_set_errno(ipd_errno_t e, const char *fmt, ...) 87 { 88 va_list ap; 89 90 ipd_errno = e; 91 if (fmt != NULL) { 92 va_start(ap, fmt); 93 (void) vsnprintf(ipd_errmsg, sizeof (ipd_errmsg), fmt, ap); 94 va_end(ap); 95 } else { 96 (void) strlcpy(ipd_errmsg, 97 ipd_strerror(e), sizeof (ipd_errmsg)); 98 } 99 100 return (-1); 101 } 102 103 int 104 ipd_open(const char *path) 105 { 106 int fd; 107 108 if (path == NULL) 109 path = IPD_DEV_PATH; 110 111 fd = open(path, O_RDWR); 112 if (fd < 0) { 113 return (ipd_set_errno(xlate_errno(errno), 114 "unable to open %s: %s", path, strerror(errno))); 115 } 116 117 return (fd); 118 } 119 120 int 121 ipd_close(int fd) 122 { 123 (void) close(fd); 124 return (0); 125 } 126 127 int 128 ipd_status_read(int fd, ipd_stathdl_t *ispp) 129 { 130 struct ipd_stat *isp = NULL; 131 ipd_ioc_list_t ipil; 132 uint_t rzones; 133 uint_t i; 134 135 bzero(&ipil, sizeof (ipil)); 136 if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) { 137 return (ipd_set_errno(xlate_errno(errno), 138 "unable to retrieve ipd zone list: %s", strerror(errno))); 139 } 140 141 for (;;) { 142 if ((rzones = ipil.ipil_nzones) == 0) 143 break; 144 145 ipil.ipil_info = 146 malloc(sizeof (ipd_ioc_info_t) * ipil.ipil_nzones); 147 if (ipil.ipil_info == NULL) 148 return (ipd_set_errno(EIPD_NOMEM, NULL)); 149 150 if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) { 151 free(ipil.ipil_info); 152 return (ipd_set_errno(xlate_errno(errno), 153 "unable to retrieve ipd zone list: %s", 154 strerror(errno))); 155 } 156 157 if (ipil.ipil_nzones <= rzones) 158 break; 159 160 free(ipil.ipil_info); 161 } 162 163 if ((isp = malloc(sizeof (struct ipd_stat))) == NULL) { 164 free(ipil.ipil_info); 165 return (ipd_set_errno(EIPD_NOMEM, NULL)); 166 } 167 168 isp->is_nzones = ipil.ipil_nzones; 169 170 if (isp->is_nzones == 0) { 171 isp->is_zoneids = NULL; 172 isp->is_configs = NULL; 173 *ispp = isp; 174 return (0); 175 } 176 177 isp->is_zoneids = malloc(sizeof (zoneid_t) * ipil.ipil_nzones); 178 if (isp->is_zoneids == NULL) { 179 free(ipil.ipil_info); 180 free(isp); 181 return (ipd_set_errno(EIPD_NOMEM, NULL)); 182 } 183 isp->is_configs = malloc(sizeof (struct ipd_config) * ipil.ipil_nzones); 184 if (isp->is_configs == NULL) { 185 free(ipil.ipil_info); 186 free(isp->is_zoneids); 187 free(isp); 188 return (ipd_set_errno(EIPD_NOMEM, NULL)); 189 } 190 191 for (i = 0; i < isp->is_nzones; i++) { 192 isp->is_zoneids[i] = ipil.ipil_info[i].ipii_zoneid; 193 194 isp->is_configs[i].ic_corrupt = ipil.ipil_info[i].ipii_corrupt; 195 isp->is_configs[i].ic_drop = ipil.ipil_info[i].ipii_drop; 196 isp->is_configs[i].ic_delay = ipil.ipil_info[i].ipii_delay; 197 198 isp->is_configs[i].ic_mask = 199 ((!!isp->is_configs[i].ic_corrupt) * IPDM_CORRUPT) | 200 ((!!isp->is_configs[i].ic_drop) * IPDM_DROP) | 201 ((!!isp->is_configs[i].ic_delay) * IPDM_DELAY); 202 } 203 204 *ispp = isp; 205 return (0); 206 } 207 208 void 209 ipd_status_foreach_zone(const ipd_stathdl_t hdl, ipd_status_cb_f f, void *arg) 210 { 211 const struct ipd_stat *isp = hdl; 212 uint_t i; 213 214 for (i = 0; i < isp->is_nzones; i++) 215 f(isp->is_zoneids[i], &isp->is_configs[i], arg); 216 } 217 218 int 219 ipd_status_get_config(const ipd_stathdl_t hdl, zoneid_t z, ipd_config_t **icpp) 220 { 221 const struct ipd_stat *isp = hdl; 222 uint_t i; 223 224 for (i = 0; i < isp->is_nzones; i++) { 225 if (isp->is_zoneids[i] == z) { 226 *icpp = &isp->is_configs[i]; 227 return (0); 228 } 229 } 230 231 return (ipd_set_errno(EIPD_ZC_NOENT, 232 "zone %d does not exist or has no ipd configuration", z)); 233 } 234 235 void 236 ipd_status_free(ipd_stathdl_t hdl) 237 { 238 struct ipd_stat *isp = hdl; 239 240 if (isp != NULL) { 241 free(isp->is_zoneids); 242 free(isp->is_configs); 243 } 244 free(isp); 245 } 246 247 int 248 ipd_ctl(int fd, zoneid_t z, const ipd_config_t *icp) 249 { 250 ipd_ioc_perturb_t ipip; 251 252 bzero(&ipip, sizeof (ipd_ioc_perturb_t)); 253 ipip.ipip_zoneid = z; 254 255 if (icp->ic_mask & IPDM_CORRUPT) { 256 if (icp->ic_corrupt == 0) 257 ipip.ipip_arg |= IPD_CORRUPT; 258 } 259 if (icp->ic_mask & IPDM_DELAY) { 260 if (icp->ic_delay == 0) 261 ipip.ipip_arg |= IPD_DELAY; 262 } 263 if (icp->ic_mask & IPDM_DROP) { 264 if (icp->ic_drop == 0) 265 ipip.ipip_arg |= IPD_DROP; 266 } 267 268 if (ipip.ipip_arg != 0 && ioctl(fd, IPDIOC_REMOVE, &ipip) != 0) { 269 return (ipd_set_errno(xlate_errno(errno), 270 "unable to remove cleared ipd settings: %s", 271 strerror(errno))); 272 } 273 274 if ((icp->ic_mask & IPDM_CORRUPT) && icp->ic_corrupt != 0) { 275 ipip.ipip_zoneid = z; 276 ipip.ipip_arg = icp->ic_corrupt; 277 if (ioctl(fd, IPDIOC_CORRUPT, &ipip) != 0) { 278 return (ipd_set_errno(xlate_errno(errno), 279 "unable to set corruption rate to %d: %s", 280 ipip.ipip_arg, strerror(errno))); 281 } 282 } 283 if ((icp->ic_mask & IPDM_DELAY) && icp->ic_delay != 0) { 284 ipip.ipip_zoneid = z; 285 ipip.ipip_arg = icp->ic_delay; 286 if (ioctl(fd, IPDIOC_DELAY, &ipip) != 0) { 287 return (ipd_set_errno(xlate_errno(errno), 288 "unable to set delay time to %d: %s", 289 ipip.ipip_arg, strerror(errno))); 290 } 291 } 292 if ((icp->ic_mask & IPDM_DROP) && icp->ic_drop != 0) { 293 ipip.ipip_zoneid = z; 294 ipip.ipip_arg = icp->ic_drop; 295 if (ioctl(fd, IPDIOC_DROP, &ipip) != 0) { 296 return (ipd_set_errno(xlate_errno(errno), 297 "unable to set drop probability to %d: %s", 298 ipip.ipip_arg, strerror(errno))); 299 } 300 } 301 302 return (0); 303 } 304