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 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <memory.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <poll.h>
41 #include "kstat.h"
42
43 /*LINTLIBRARY*/
44
45 static void
kstat_zalloc(void ** ptr,size_t size,int free_first)46 kstat_zalloc(void **ptr, size_t size, int free_first)
47 {
48 if (free_first)
49 free(*ptr);
50 *ptr = calloc(size, 1);
51 }
52
53 static void
kstat_chain_free(kstat_ctl_t * kc)54 kstat_chain_free(kstat_ctl_t *kc)
55 {
56 kstat_t *ksp, *nksp;
57
58 ksp = kc->kc_chain;
59 while (ksp) {
60 nksp = ksp->ks_next;
61 free(ksp->ks_data);
62 free(ksp);
63 ksp = nksp;
64 }
65 kc->kc_chain = NULL;
66 kc->kc_chain_id = 0;
67 }
68
69 kstat_ctl_t *
kstat_open(void)70 kstat_open(void)
71 {
72 kstat_ctl_t *kc;
73 int kd;
74
75 kd = open("/dev/kstat", O_RDONLY);
76 if (kd == -1)
77 return (NULL);
78 kstat_zalloc((void **)&kc, sizeof (kstat_ctl_t), 0);
79 if (kc == NULL)
80 return (NULL);
81
82 kc->kc_kd = kd;
83 kc->kc_chain = NULL;
84 kc->kc_chain_id = 0;
85 if (kstat_chain_update(kc) == -1) {
86 int saved_err = errno;
87 (void) kstat_close(kc);
88 errno = saved_err;
89 return (NULL);
90 }
91 return (kc);
92 }
93
94 int
kstat_close(kstat_ctl_t * kc)95 kstat_close(kstat_ctl_t *kc)
96 {
97 int rc;
98
99 kstat_chain_free(kc);
100 rc = close(kc->kc_kd);
101 free(kc);
102 return (rc);
103 }
104
105 kid_t
kstat_read(kstat_ctl_t * kc,kstat_t * ksp,void * data)106 kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data)
107 {
108 kid_t kcid;
109
110 if (ksp->ks_data == NULL && ksp->ks_data_size > 0) {
111 kstat_zalloc(&ksp->ks_data, ksp->ks_data_size, 0);
112 if (ksp->ks_data == NULL)
113 return (-1);
114 }
115 while ((kcid = (kid_t)ioctl(kc->kc_kd, KSTAT_IOC_READ, ksp)) == -1) {
116 if (errno == EAGAIN) {
117 (void) poll(NULL, 0, 100); /* back off a moment */
118 continue; /* and try again */
119 }
120 /*
121 * Mating dance for variable-size kstats.
122 * You start with a buffer of a certain size,
123 * which you hope will hold all the data.
124 * If your buffer is too small, the kstat driver
125 * returns ENOMEM and sets ksp->ks_data_size to
126 * the current size of the kstat's data. You then
127 * resize your buffer and try again. In practice,
128 * this almost always converges in two passes.
129 */
130 if (errno == ENOMEM && (ksp->ks_flags & KSTAT_FLAG_VAR_SIZE)) {
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,char * ks_module,int ks_instance,char * ks_name)302 kstat_lookup(kstat_ctl_t *kc, char *ks_module, int ks_instance, char *ks_name)
303 {
304 kstat_t *ksp;
305
306 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
307 if ((ks_module == NULL ||
308 strcmp(ksp->ks_module, ks_module) == 0) &&
309 (ks_instance == -1 || ksp->ks_instance == ks_instance) &&
310 (ks_name == NULL || strcmp(ksp->ks_name, ks_name) == 0))
311 return (ksp);
312 }
313
314 errno = ENOENT;
315 return (NULL);
316 }
317
318 void *
kstat_data_lookup(kstat_t * ksp,char * name)319 kstat_data_lookup(kstat_t *ksp, char *name)
320 {
321 int i, size;
322 char *namep, *datap;
323
324 switch (ksp->ks_type) {
325
326 case KSTAT_TYPE_NAMED:
327 size = sizeof (kstat_named_t);
328 namep = KSTAT_NAMED_PTR(ksp)->name;
329 break;
330
331 case KSTAT_TYPE_TIMER:
332 size = sizeof (kstat_timer_t);
333 namep = KSTAT_TIMER_PTR(ksp)->name;
334 break;
335
336 default:
337 errno = EINVAL;
338 return (NULL);
339 }
340
341 datap = ksp->ks_data;
342 for (i = 0; i < ksp->ks_ndata; i++) {
343 if (strcmp(name, namep) == 0)
344 return (datap);
345 namep += size;
346 datap += size;
347 }
348 errno = ENOENT;
349 return (NULL);
350 }
351