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