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 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 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 * 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 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 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 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 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 * 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 * 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