1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2021 Oxide Computer Company
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <ctype.h>
33 #include <unistd.h>
34 #include <memory.h>
35 #include <strings.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <poll.h>
40 #include "kstat.h"
41
42 /*LINTLIBRARY*/
43
44 static void
kstat_zalloc(void ** ptr,size_t size,int free_first)45 kstat_zalloc(void **ptr, size_t size, int free_first)
46 {
47 if (free_first)
48 free(*ptr);
49 *ptr = calloc(size, 1);
50 }
51
52 static void
kstat_chain_free(kstat_ctl_t * kc)53 kstat_chain_free(kstat_ctl_t *kc)
54 {
55 kstat_t *ksp, *nksp;
56
57 ksp = kc->kc_chain;
58 while (ksp) {
59 nksp = ksp->ks_next;
60 free(ksp->ks_data);
61 free(ksp);
62 ksp = nksp;
63 }
64 kc->kc_chain = NULL;
65 kc->kc_chain_id = 0;
66 }
67
68 kstat_ctl_t *
kstat_open(void)69 kstat_open(void)
70 {
71 kstat_ctl_t *kc;
72 int kd;
73
74 kd = open("/dev/kstat", O_RDONLY | O_CLOEXEC);
75 if (kd == -1)
76 return (NULL);
77 kstat_zalloc((void **)&kc, sizeof (kstat_ctl_t), 0);
78 if (kc == NULL)
79 return (NULL);
80
81 kc->kc_kd = kd;
82 kc->kc_chain = NULL;
83 kc->kc_chain_id = 0;
84 if (kstat_chain_update(kc) == -1) {
85 int saved_err = errno;
86 (void) kstat_close(kc);
87 errno = saved_err;
88 return (NULL);
89 }
90 return (kc);
91 }
92
93 int
kstat_close(kstat_ctl_t * kc)94 kstat_close(kstat_ctl_t *kc)
95 {
96 int rc;
97
98 kstat_chain_free(kc);
99 rc = close(kc->kc_kd);
100 free(kc);
101 return (rc);
102 }
103
104 kid_t
kstat_read(kstat_ctl_t * kc,kstat_t * ksp,void * data)105 kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data)
106 {
107 kid_t kcid;
108
109 if (ksp->ks_data == NULL && ksp->ks_data_size > 0) {
110 kstat_zalloc(&ksp->ks_data, ksp->ks_data_size, 0);
111 if (ksp->ks_data == NULL)
112 return (-1);
113 }
114 while ((kcid = (kid_t)ioctl(kc->kc_kd, KSTAT_IOC_READ, ksp)) == -1) {
115 if (errno == EAGAIN) {
116 (void) poll(NULL, 0, 100); /* back off a moment */
117 continue; /* and try again */
118 }
119 /*
120 * Mating dance for variable-size kstats.
121 * You start with a buffer of a certain size,
122 * which you hope will hold all the data.
123 * If your buffer is too small, the kstat driver
124 * returns ENOMEM and sets ksp->ks_data_size to
125 * the current size of the kstat's data. You then
126 * resize your buffer and try again. In practice,
127 * this almost always converges in two passes.
128 */
129 if (errno == ENOMEM && (ksp->ks_flags &
130 (KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_LONGSTRINGS))) {
131 kstat_zalloc(&ksp->ks_data, ksp->ks_data_size, 1);
132 if (ksp->ks_data != NULL)
133 continue;
134 }
135 return (-1);
136 }
137 if (data != NULL) {
138 (void) memcpy(data, ksp->ks_data, ksp->ks_data_size);
139 if (ksp->ks_type == KSTAT_TYPE_NAMED &&
140 ksp->ks_data_size !=
141 ksp->ks_ndata * sizeof (kstat_named_t)) {
142 /*
143 * Has KSTAT_DATA_STRING fields. Fix the pointers.
144 */
145 uint_t i;
146 kstat_named_t *knp = data;
147
148 for (i = 0; i < ksp->ks_ndata; i++, knp++) {
149 if (knp->data_type != KSTAT_DATA_STRING)
150 continue;
151 if (KSTAT_NAMED_STR_PTR(knp) == NULL)
152 continue;
153 /*
154 * The offsets of the strings within the
155 * buffers are the same, so add the offset of
156 * the string to the beginning of 'data' to fix
157 * the pointer so that strings in 'data' don't
158 * point at memory in 'ksp->ks_data'.
159 */
160 KSTAT_NAMED_STR_PTR(knp) = (char *)data +
161 (KSTAT_NAMED_STR_PTR(knp) -
162 (char *)ksp->ks_data);
163 }
164 }
165 }
166 return (kcid);
167 }
168
169 kid_t
kstat_write(kstat_ctl_t * kc,kstat_t * ksp,void * data)170 kstat_write(kstat_ctl_t *kc, kstat_t *ksp, void *data)
171 {
172 kid_t kcid;
173
174 if (ksp->ks_data == NULL && ksp->ks_data_size > 0) {
175 kstat_zalloc(&ksp->ks_data, ksp->ks_data_size, 0);
176 if (ksp->ks_data == NULL)
177 return (-1);
178 }
179 if (data != NULL) {
180 (void) memcpy(ksp->ks_data, data, ksp->ks_data_size);
181 if (ksp->ks_type == KSTAT_TYPE_NAMED) {
182 kstat_named_t *oknp = data;
183 kstat_named_t *nknp = KSTAT_NAMED_PTR(ksp);
184 uint_t i;
185
186 for (i = 0; i < ksp->ks_ndata; i++, oknp++, nknp++) {
187 if (nknp->data_type != KSTAT_DATA_STRING)
188 continue;
189 if (KSTAT_NAMED_STR_PTR(nknp) == NULL)
190 continue;
191 /*
192 * The buffer passed in as 'data' has string
193 * pointers that point within 'data'. Fix the
194 * pointers so they point into the same offset
195 * within the newly allocated buffer.
196 */
197 KSTAT_NAMED_STR_PTR(nknp) =
198 (char *)ksp->ks_data +
199 (KSTAT_NAMED_STR_PTR(oknp) - (char *)data);
200 }
201 }
202
203 }
204 while ((kcid = (kid_t)ioctl(kc->kc_kd, KSTAT_IOC_WRITE, ksp)) == -1) {
205 if (errno == EAGAIN) {
206 (void) poll(NULL, 0, 100); /* back off a moment */
207 continue; /* and try again */
208 }
209 break;
210 }
211 return (kcid);
212 }
213
214 /*
215 * If the current KCID is the same as kc->kc_chain_id, return 0;
216 * if different, update the chain and return the new KCID.
217 * This operation is non-destructive for unchanged kstats.
218 */
219 kid_t
kstat_chain_update(kstat_ctl_t * kc)220 kstat_chain_update(kstat_ctl_t *kc)
221 {
222 kstat_t k0, *headers, *oksp, *nksp, **okspp, *next;
223 int i;
224 kid_t kcid;
225
226 kcid = (kid_t)ioctl(kc->kc_kd, KSTAT_IOC_CHAIN_ID, NULL);
227 if (kcid == -1)
228 return (-1);
229 if (kcid == kc->kc_chain_id)
230 return (0);
231
232 /*
233 * kstat 0's data is the kstat chain, so we can get the chain
234 * by doing a kstat_read() of this kstat. The only fields the
235 * kstat driver needs are ks_kid (this identifies the kstat),
236 * ks_data (the pointer to our buffer), and ks_data_size (the
237 * size of our buffer). By zeroing the struct we set ks_data = NULL
238 * and ks_data_size = 0, so that kstat_read() will automatically
239 * determine the size and allocate space for us. We also fill in the
240 * name, so that truss can print something meaningful.
241 */
242 bzero(&k0, sizeof (k0));
243 (void) strcpy(k0.ks_name, "kstat_headers");
244
245 kcid = kstat_read(kc, &k0, NULL);
246 if (kcid == -1) {
247 free(k0.ks_data);
248 /* errno set by kstat_read() */
249 return (-1);
250 }
251 headers = k0.ks_data;
252
253 /*
254 * Chain the new headers together
255 */
256 for (i = 1; i < k0.ks_ndata; i++)
257 headers[i - 1].ks_next = &headers[i];
258
259 headers[k0.ks_ndata - 1].ks_next = NULL;
260
261 /*
262 * Remove all deleted kstats from the chain.
263 */
264 nksp = headers;
265 okspp = &kc->kc_chain;
266 oksp = kc->kc_chain;
267 while (oksp != NULL) {
268 next = oksp->ks_next;
269 if (nksp != NULL && oksp->ks_kid == nksp->ks_kid) {
270 okspp = &oksp->ks_next;
271 nksp = nksp->ks_next;
272 } else {
273 *okspp = oksp->ks_next;
274 free(oksp->ks_data);
275 free(oksp);
276 }
277 oksp = next;
278 }
279
280 /*
281 * Add all new kstats to the chain.
282 */
283 while (nksp != NULL) {
284 kstat_zalloc((void **)okspp, sizeof (kstat_t), 0);
285 if ((oksp = *okspp) == NULL) {
286 free(headers);
287 return (-1);
288 }
289 *oksp = *nksp;
290 okspp = &oksp->ks_next;
291 oksp->ks_next = NULL;
292 oksp->ks_data = NULL;
293 nksp = nksp->ks_next;
294 }
295
296 free(headers);
297 kc->kc_chain_id = kcid;
298 return (kcid);
299 }
300
301 kstat_t *
kstat_lookup(kstat_ctl_t * kc,const char * ks_module,int ks_instance,const char * ks_name)302 kstat_lookup(kstat_ctl_t *kc, const char *ks_module, int ks_instance,
303 const char *ks_name)
304 {
305 kstat_t *ksp;
306
307 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
308 if ((ks_module == NULL ||
309 strcmp(ksp->ks_module, ks_module) == 0) &&
310 (ks_instance == -1 || ksp->ks_instance == ks_instance) &&
311 (ks_name == NULL || strcmp(ksp->ks_name, ks_name) == 0))
312 return (ksp);
313 }
314
315 errno = ENOENT;
316 return (NULL);
317 }
318
319 void *
kstat_data_lookup(kstat_t * ksp,const char * name)320 kstat_data_lookup(kstat_t *ksp, const char *name)
321 {
322 int i, size;
323 char *namep, *datap;
324
325 switch (ksp->ks_type) {
326
327 case KSTAT_TYPE_NAMED:
328 size = sizeof (kstat_named_t);
329 namep = KSTAT_NAMED_PTR(ksp)->name;
330 break;
331
332 case KSTAT_TYPE_TIMER:
333 size = sizeof (kstat_timer_t);
334 namep = KSTAT_TIMER_PTR(ksp)->name;
335 break;
336
337 default:
338 errno = EINVAL;
339 return (NULL);
340 }
341
342 datap = ksp->ks_data;
343 for (i = 0; i < ksp->ks_ndata; i++) {
344 if (strcmp(name, namep) == 0)
345 return (datap);
346 namep += size;
347 datap += size;
348 }
349 errno = ENOENT;
350 return (NULL);
351 }
352