xref: /illumos-gate/usr/src/lib/libipd/common/libipd.c (revision e86372a01d2d16a5dd4a64e144ed978ba17fe7dd)
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