1 /* 2 * Copyright (c) 2005-2006 The FreeBSD Project 3 * All rights reserved. 4 * 5 * Author: Victor Cruceru <soc-victor@freebsd.org> 6 * 7 * Redistribution of this software and documentation and use in source and 8 * binary forms, with or without modification, are permitted provided that 9 * the following conditions are met: 10 * 11 * 1. Redistributions of source code or documentation must retain the above 12 * copyright notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 * 31 * Host Resources MIB for SNMPd. Implementation for hrSWRunTable 32 */ 33 34 #include <sys/param.h> 35 #include <sys/proc.h> 36 #include <sys/sysctl.h> 37 #include <sys/user.h> 38 #include <sys/linker.h> 39 40 #include <assert.h> 41 #include <signal.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <syslog.h> 45 46 #include "hostres_snmp.h" 47 #include "hostres_oid.h" 48 #include "hostres_tree.h" 49 50 51 /* 52 * Ugly thing: PID_MAX, NO_PID defined only in kernel 53 */ 54 #define NO_PID 100000 55 56 enum SWRunType { 57 SRT_UNKNOWN = 1, 58 SRT_OPERATING_SYSTEM = 2, 59 SRT_DEVICE_DRIVER = 3, 60 SRT_APPLICATION = 4 61 62 }; 63 64 enum SWRunStatus { 65 SRS_RUNNING = 1, 66 SRS_RUNNABLE = 2, 67 SRS_NOT_RUNNABLE = 3, 68 SRS_INVALID = 4 69 }; 70 71 /* 72 * This structure is used to hold a SNMP table entry 73 * for both hrSWRunTable and hrSWRunPerfTable because 74 * hrSWRunPerfTable AUGMENTS hrSWRunTable 75 */ 76 struct swrun_entry { 77 int32_t index; 78 u_char name[64 + 1]; 79 struct asn_oid id; 80 u_char path[128 + 1]; 81 u_char parameters[128 + 1]; 82 int32_t type; /* enum SWRunType */ 83 int32_t status; /* enum SWRunStatus */ 84 int32_t perfCPU; 85 int32_t perfMemory; 86 #define HR_SWRUN_FOUND 0x001 87 uint32_t flags; 88 uint64_t r_tick; /* tick when entry refreshed */ 89 TAILQ_ENTRY(swrun_entry) link; 90 }; 91 TAILQ_HEAD(swrun_tbl, swrun_entry); 92 93 /* the head of the list with hrSWRunTable's entries */ 94 static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl); 95 96 /* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */ 97 static uint64_t swrun_tick; 98 99 /* maximum number of ticks between updates of SWRun and SWRunPerf table */ 100 uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100; 101 102 /* the value of the MIB object with the same name */ 103 static int32_t SWOSIndex; 104 105 /** 106 * Malloc a new entry and add it to the list 107 * associated to this table. The item identified by 108 * the index parameter must not exist in this list. 109 */ 110 static struct swrun_entry * 111 swrun_entry_create(int32_t idx) 112 { 113 struct swrun_entry *entry; 114 115 if ((entry = malloc(sizeof(*entry))) == NULL) { 116 syslog(LOG_WARNING, "%s: %m", __func__); 117 return (NULL); 118 } 119 memset(entry, 0, sizeof(*entry)); 120 entry->index = idx; 121 122 INSERT_OBJECT_INT(entry, &swrun_tbl); 123 return (entry); 124 } 125 126 /** 127 * Unlink the entry from the list and then free its heap memory 128 */ 129 static void 130 swrun_entry_delete(struct swrun_entry *entry) 131 { 132 133 assert(entry != NULL); 134 135 TAILQ_REMOVE(&swrun_tbl, entry, link); 136 137 free(entry); 138 } 139 140 /** 141 * Search one item by its index, return NULL if none found 142 */ 143 static struct swrun_entry * 144 swrun_entry_find_by_index(int32_t idx) 145 { 146 struct swrun_entry *entry; 147 148 TAILQ_FOREACH(entry, &swrun_tbl, link) 149 if (entry->index == idx) 150 return (entry); 151 return (NULL); 152 } 153 154 /** 155 * Translate the kernel's process status to the SNMP one. 156 */ 157 static enum SWRunStatus 158 swrun_OS_get_proc_status(const struct kinfo_proc *kp) 159 { 160 161 assert(kp != NULL); 162 if(kp == NULL) { 163 return (SRS_INVALID); 164 } 165 166 /* 167 * I'm using the old style flags - they look cleaner to me, 168 * at least for the purpose of this SNMP table 169 */ 170 switch (kp->ki_stat) { 171 172 case SSTOP: 173 return (SRS_NOT_RUNNABLE); 174 175 case SWAIT: 176 case SLOCK: 177 case SSLEEP: 178 return (SRS_RUNNABLE); 179 180 case SZOMB: 181 return (SRS_INVALID); 182 183 case SIDL: 184 case SRUN: 185 return (SRS_RUNNING); 186 187 default: 188 syslog(LOG_ERR,"Unknown process state: %d", kp->ki_stat); 189 return (SRS_INVALID); 190 } 191 } 192 193 /** 194 * Make an SNMP table entry from a kernel one. 195 */ 196 static void 197 kinfo_proc_to_swrun_entry(const struct kinfo_proc *kp, 198 struct swrun_entry *entry) 199 { 200 char **argv = NULL; 201 uint64_t cpu_time = 0; 202 203 strlcpy((char*)entry->name, kp->ki_comm, sizeof(entry->name)); 204 205 entry->id = oid_zeroDotZero; /* unknown id - FIXME */ 206 207 entry->path[0] = '\0'; 208 entry->parameters[0] = '\0'; 209 210 assert(hr_kd != NULL); 211 212 argv = kvm_getargv(hr_kd, kp, sizeof(entry->parameters) - 1); 213 if(argv != NULL){ 214 memset(entry->parameters, '\0', sizeof(entry->parameters)); 215 216 /* 217 * FIXME 218 * Path seems to not be available. 219 * Try to hack the info in argv[0]; 220 * this argv is under control of the program so this info 221 * is not realiable 222 */ 223 if(*argv != NULL && (*argv)[0] == '/') { 224 memset(entry->path, '\0', sizeof(entry->path)); 225 strlcpy((char*)entry->path, *argv, sizeof(entry->path)); 226 } 227 228 argv++; /* skip the first one which was used for path */ 229 230 while (argv != NULL && *argv != NULL ) { 231 if (entry->parameters[0] != 0) { 232 /* 233 * add a space between parameters, 234 * except before the first one 235 */ 236 strlcat((char *)entry->parameters, 237 " ", sizeof(entry->parameters)); 238 } 239 strlcat((char *)entry->parameters, *argv, 240 sizeof(entry->parameters)); 241 argv++; 242 } 243 } 244 245 entry->type = (int32_t)(IS_KERNPROC(kp) ? SRT_OPERATING_SYSTEM : 246 SRT_APPLICATION); 247 248 entry->status = (int32_t)swrun_OS_get_proc_status(kp); 249 cpu_time = kp->ki_runtime / 100000; /* centi-seconds */ 250 251 /* may overflow the snmp type */ 252 entry->perfCPU = (cpu_time > (uint64_t)INT_MAX ? INT_MAX : cpu_time); 253 entry->perfMemory = kp->ki_size / 1024; /* in kilo-bytes */ 254 entry->r_tick = get_ticks(); 255 } 256 257 /** 258 * Create a table entry for a KLD 259 */ 260 static void 261 kld_file_stat_to_swrun(const struct kld_file_stat *kfs, 262 struct swrun_entry *entry) 263 { 264 265 assert(kfs != NULL); 266 assert(entry != NULL); 267 268 strlcpy((char *)entry->name, kfs->name, sizeof(entry->name)); 269 270 /* FIXME: can we find the location where the module was loaded from? */ 271 entry->path[0] = '\0'; 272 273 /* no parameters for kernel files (.ko) of for the kernel */ 274 entry->parameters[0] = '\0'; 275 276 entry->id = oid_zeroDotZero; /* unknown id - FIXME */ 277 278 if (strcmp(kfs->name, "kernel") == 0) { 279 entry->type = (int32_t)SRT_OPERATING_SYSTEM; 280 SWOSIndex = entry->index; 281 } else { 282 entry->type = (int32_t)SRT_DEVICE_DRIVER; /* well, not really */ 283 } 284 entry->status = (int32_t)SRS_RUNNING; 285 entry->perfCPU = 0; /* Info not available */ 286 entry->perfMemory = kfs->size / 1024; /* in kilo-bytes */ 287 entry->r_tick = get_ticks(); 288 } 289 290 /** 291 * Get all visible proceses including the kernel visible threads 292 */ 293 static void 294 swrun_OS_get_procs(void) 295 { 296 struct kinfo_proc *plist, *kp; 297 int i; 298 int nproc; 299 struct swrun_entry *entry; 300 301 plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc); 302 if (plist == NULL || nproc < 0) { 303 syslog(LOG_ERR, "kvm_getprocs() failed: %m"); 304 return; 305 } 306 for (i = 0, kp = plist; i < nproc; i++, kp++) { 307 /* 308 * The SNMP table's index must begin from 1 (as specified by 309 * this table definition), the PIDs are starting from 0 310 * so we are translating the PIDs to +1 311 */ 312 entry = swrun_entry_find_by_index((int32_t)kp->ki_pid + 1); 313 if (entry == NULL) { 314 /* new entry - get memory for it */ 315 entry = swrun_entry_create((int32_t)kp->ki_pid + 1); 316 if (entry == NULL) 317 continue; 318 } 319 entry->flags |= HR_SWRUN_FOUND; /* mark it as found */ 320 321 kinfo_proc_to_swrun_entry(kp, entry); 322 } 323 } 324 325 /* 326 * Get kernel items: first the kernel itself, then the loaded modules. 327 */ 328 static void 329 swrun_OS_get_kinfo(void) 330 { 331 int fileid; 332 struct swrun_entry *entry; 333 struct kld_file_stat stat; 334 335 for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { 336 stat.version = sizeof(struct kld_file_stat); 337 if (kldstat(fileid, &stat) < 0) { 338 syslog(LOG_ERR, "kldstat() failed: %m"); 339 continue; 340 } 341 342 /* 343 * kernel and kernel files (*.ko) will be indexed starting with 344 * NO_PID + 1; NO_PID is PID_MAX + 1 thus it will be no risk to 345 * overlap with real PIDs which are in range of 1 .. NO_PID 346 */ 347 entry = swrun_entry_find_by_index(NO_PID + 1 + stat.id); 348 if (entry == NULL) { 349 /* new entry - get memory for it */ 350 entry = swrun_entry_create(NO_PID + 1 + stat.id); 351 if (entry == NULL) 352 continue; 353 } 354 entry->flags |= HR_SWRUN_FOUND; /* mark it as found */ 355 356 kld_file_stat_to_swrun(&stat, entry); 357 } 358 } 359 360 /** 361 * Refresh the hrSWRun and hrSWRunPert tables. 362 */ 363 static void 364 refresh_swrun_tbl(void) 365 { 366 367 struct swrun_entry *entry, *entry_tmp; 368 369 if (this_tick - swrun_tick < swrun_tbl_refresh) { 370 HRDBG("no refresh needed "); 371 return; 372 } 373 374 /* mark each entry as missing */ 375 TAILQ_FOREACH(entry, &swrun_tbl, link) 376 entry->flags &= ~HR_SWRUN_FOUND; 377 378 swrun_OS_get_procs(); 379 swrun_OS_get_kinfo(); 380 381 /* 382 * Purge items that disappeared 383 */ 384 TAILQ_FOREACH_SAFE(entry, &swrun_tbl, link, entry_tmp) 385 if (!(entry->flags & HR_SWRUN_FOUND)) 386 swrun_entry_delete(entry); 387 388 swrun_tick = this_tick; 389 390 HRDBG("refresh DONE"); 391 } 392 393 /** 394 * Update the information in this entry 395 */ 396 static void 397 fetch_swrun_entry(struct swrun_entry *entry) 398 { 399 struct kinfo_proc *plist; 400 int nproc; 401 struct kld_file_stat stat; 402 403 assert(entry != NULL); 404 405 if (entry->index >= NO_PID + 1) { 406 /* 407 * kernel and kernel files (*.ko) will be indexed 408 * starting with NO_PID + 1; NO_PID is PID_MAX + 1 409 * thus it will be no risk to overlap with real PIDs 410 * which are in range of 1 .. NO_PID 411 */ 412 stat.version = sizeof(stat); 413 if (kldstat(entry->index - NO_PID - 1, &stat) == -1) { 414 /* 415 * not found, it's gone. Mark it as invalid for now, it 416 * will be removed from the list at next global refersh 417 */ 418 HRDBG("missing item with kid=%d", 419 entry->index - NO_PID - 1); 420 entry->status = (int32_t)SRS_INVALID; 421 } else 422 kld_file_stat_to_swrun(&stat, entry); 423 424 } else { 425 /* this is a process */ 426 assert(hr_kd != NULL); 427 plist = kvm_getprocs(hr_kd, KERN_PROC_PID, 428 entry->index - 1, &nproc); 429 if (plist == NULL || nproc != 1) { 430 HRDBG("missing item with PID=%d", entry->index - 1); 431 entry->status = (int32_t)SRS_INVALID; 432 } else 433 kinfo_proc_to_swrun_entry(plist, entry); 434 } 435 } 436 437 /** 438 * Invalidate entry. For KLDs we try to unload it, for processes we SIGKILL it. 439 */ 440 static int 441 invalidate_swrun_entry(struct swrun_entry *entry, int commit) 442 { 443 struct kinfo_proc *plist; 444 int nproc; 445 struct kld_file_stat stat; 446 447 assert(entry != NULL); 448 449 if (entry->index >= NO_PID + 1) { 450 /* this is a kernel item */ 451 HRDBG("atempt to unload KLD %d", 452 entry->index - NO_PID - 1); 453 454 if (entry->index == SWOSIndex) { 455 /* can't invalidate the kernel itself */ 456 return (SNMP_ERR_NOT_WRITEABLE); 457 } 458 459 stat.version = sizeof(stat); 460 if (kldstat(entry->index - NO_PID - 1, &stat) == -1) { 461 /* 462 * not found, it's gone. Mark it as invalid for now, it 463 * will be removed from the list at next global 464 * refresh 465 */ 466 HRDBG("missing item with kid=%d", 467 entry->index - NO_PID - 1); 468 entry->status = (int32_t)SRS_INVALID; 469 return (SNMP_ERR_NOERROR); 470 } 471 /* 472 * There is no way to try to unload a module. There seems 473 * also no way to find out whether it is busy without unloading 474 * it. We can assume that it is busy, if the reference count 475 * is larger than 2, but if it is 1 nothing helps. 476 */ 477 if (!commit) { 478 if (stat.refs > 1) 479 return (SNMP_ERR_NOT_WRITEABLE); 480 return (SNMP_ERR_NOERROR); 481 } 482 if (kldunload(stat.id) == -1) { 483 syslog(LOG_ERR,"kldunload for %d/%s failed: %m", 484 stat.id, stat.name); 485 if (errno == EBUSY) 486 return (SNMP_ERR_NOT_WRITEABLE); 487 else 488 return (SNMP_ERR_RES_UNAVAIL); 489 } 490 } else { 491 /* this is a process */ 492 assert(hr_kd != NULL); 493 494 plist = kvm_getprocs(hr_kd, KERN_PROC_PID, 495 entry->index - 1, &nproc); 496 if (plist == NULL || nproc != 1) { 497 HRDBG("missing item with PID=%d", entry->index - 1); 498 entry->status = (int32_t)SRS_INVALID; 499 return (SNMP_ERR_NOERROR); 500 } 501 if (IS_KERNPROC(plist)) { 502 /* you don't want to do this */ 503 return (SNMP_ERR_NOT_WRITEABLE); 504 } 505 if (kill(entry->index - 1, commit ? SIGKILL : 0) < 0) { 506 syslog(LOG_ERR,"kill (%d, SIGKILL) failed: %m", 507 entry->index - 1); 508 if (errno == ESRCH) { 509 /* race: just gone */ 510 entry->status = (int32_t)SRS_INVALID; 511 return (SNMP_ERR_NOERROR); 512 } 513 return (SNMP_ERR_GENERR); 514 } 515 } 516 return (SNMP_ERR_NOERROR); 517 } 518 519 /** 520 * Popuplate the hrSWRunTable. 521 */ 522 void 523 init_swrun_tbl(void) 524 { 525 526 refresh_swrun_tbl(); 527 HRDBG("done"); 528 } 529 530 /** 531 * Finalize the hrSWRunTable. 532 */ 533 void 534 fini_swrun_tbl(void) 535 { 536 struct swrun_entry *n1; 537 538 while ((n1 = TAILQ_FIRST(&swrun_tbl)) != NULL) { 539 TAILQ_REMOVE(&swrun_tbl, n1, link); 540 free(n1); 541 } 542 } 543 544 /* 545 * This is the implementation for a generated (by a SNMP tool) 546 * function prototype, see hostres_tree.h 547 * It hanldes the SNMP operations for hrSWRunTable 548 */ 549 int 550 op_hrSWRunTable(struct snmp_context *ctx __unused, struct snmp_value *value, 551 u_int sub, u_int iidx __unused, enum snmp_op curr_op) 552 { 553 struct swrun_entry *entry; 554 int ret; 555 556 refresh_swrun_tbl(); 557 558 switch (curr_op) { 559 560 case SNMP_OP_GETNEXT: 561 if ((entry = NEXT_OBJECT_INT(&swrun_tbl, 562 &value->var, sub)) == NULL) 563 return (SNMP_ERR_NOSUCHNAME); 564 value->var.len = sub + 1; 565 value->var.subs[sub] = entry->index; 566 goto get; 567 568 case SNMP_OP_GET: 569 if ((entry = FIND_OBJECT_INT(&swrun_tbl, 570 &value->var, sub)) == NULL) 571 return (SNMP_ERR_NOSUCHNAME); 572 goto get; 573 574 case SNMP_OP_SET: 575 if ((entry = FIND_OBJECT_INT(&swrun_tbl, 576 &value->var, sub)) == NULL) 577 return (SNMP_ERR_NO_CREATION); 578 579 if (entry->r_tick < this_tick) 580 fetch_swrun_entry(entry); 581 582 switch (value->var.subs[sub - 1]) { 583 584 case LEAF_hrSWRunStatus: 585 if (value->v.integer != (int32_t)SRS_INVALID) 586 return (SNMP_ERR_WRONG_VALUE); 587 588 if (entry->status == (int32_t)SRS_INVALID) 589 return (SNMP_ERR_NOERROR); 590 591 /* 592 * Here we have a problem with the entire SNMP 593 * model: if we kill now, we cannot rollback. 594 * If we kill in the commit code, we cannot 595 * return an error. Because things may change between 596 * SET and COMMIT this is impossible to handle 597 * correctly. 598 */ 599 return (invalidate_swrun_entry(entry, 0)); 600 } 601 return (SNMP_ERR_NOT_WRITEABLE); 602 603 case SNMP_OP_ROLLBACK: 604 return (SNMP_ERR_NOERROR); 605 606 case SNMP_OP_COMMIT: 607 if ((entry = FIND_OBJECT_INT(&swrun_tbl, 608 &value->var, sub)) == NULL) 609 return (SNMP_ERR_NOERROR); 610 611 switch (value->var.subs[sub - 1]) { 612 613 case LEAF_hrSWRunStatus: 614 if (value->v.integer == (int32_t)SRS_INVALID && 615 entry->status != (int32_t)SRS_INVALID) 616 (void)invalidate_swrun_entry(entry, 1); 617 return (SNMP_ERR_NOERROR); 618 } 619 abort(); 620 } 621 abort(); 622 623 get: 624 ret = SNMP_ERR_NOERROR; 625 switch (value->var.subs[sub - 1]) { 626 627 case LEAF_hrSWRunIndex: 628 value->v.integer = entry->index; 629 break; 630 631 case LEAF_hrSWRunName: 632 ret = string_get(value, entry->name, -1); 633 break; 634 635 case LEAF_hrSWRunID: 636 value->v.oid = entry->id; 637 break; 638 639 case LEAF_hrSWRunPath: 640 ret = string_get(value, entry->path, -1); 641 break; 642 643 case LEAF_hrSWRunParameters: 644 ret = string_get(value, entry->parameters, -1); 645 break; 646 647 case LEAF_hrSWRunType: 648 value->v.integer = entry->type; 649 break; 650 651 case LEAF_hrSWRunStatus: 652 value->v.integer = entry->status; 653 break; 654 655 default: 656 abort(); 657 } 658 return (ret); 659 } 660 661 /** 662 * Scalar(s) in the SWRun group 663 */ 664 int 665 op_hrSWRun(struct snmp_context *ctx __unused, struct snmp_value *value, 666 u_int sub, u_int iidx __unused, enum snmp_op curr_op) 667 { 668 669 /* only SNMP GET is possible */ 670 switch (curr_op) { 671 672 case SNMP_OP_GET: 673 goto get; 674 675 case SNMP_OP_SET: 676 return (SNMP_ERR_NOT_WRITEABLE); 677 678 case SNMP_OP_ROLLBACK: 679 case SNMP_OP_COMMIT: 680 case SNMP_OP_GETNEXT: 681 abort(); 682 } 683 abort(); 684 685 get: 686 switch (value->var.subs[sub - 1]) { 687 688 case LEAF_hrSWOSIndex: 689 value->v.uint32 = SWOSIndex; 690 return (SNMP_ERR_NOERROR); 691 692 default: 693 abort(); 694 } 695 } 696 697 /* 698 * This is the implementation for a generated (by a SNMP tool) 699 * function prototype, see hostres_tree.h 700 * It handles the SNMP operations for hrSWRunPerfTable 701 */ 702 int 703 op_hrSWRunPerfTable(struct snmp_context *ctx __unused, 704 struct snmp_value *value, u_int sub, u_int iidx __unused, 705 enum snmp_op curr_op ) 706 { 707 struct swrun_entry *entry; 708 709 refresh_swrun_tbl(); 710 711 switch (curr_op) { 712 713 case SNMP_OP_GETNEXT: 714 if ((entry = NEXT_OBJECT_INT(&swrun_tbl, 715 &value->var, sub)) == NULL) 716 return (SNMP_ERR_NOSUCHNAME); 717 value->var.len = sub + 1; 718 value->var.subs[sub] = entry->index; 719 goto get; 720 721 case SNMP_OP_GET: 722 if ((entry = FIND_OBJECT_INT(&swrun_tbl, 723 &value->var, sub)) == NULL) 724 return (SNMP_ERR_NOSUCHNAME); 725 goto get; 726 727 case SNMP_OP_SET: 728 if ((entry = FIND_OBJECT_INT(&swrun_tbl, 729 &value->var, sub)) == NULL) 730 return (SNMP_ERR_NO_CREATION); 731 return (SNMP_ERR_NOT_WRITEABLE); 732 733 case SNMP_OP_ROLLBACK: 734 case SNMP_OP_COMMIT: 735 abort(); 736 } 737 abort(); 738 739 get: 740 switch (value->var.subs[sub - 1]) { 741 742 case LEAF_hrSWRunPerfCPU: 743 value->v.integer = entry->perfCPU; 744 return (SNMP_ERR_NOERROR); 745 746 case LEAF_hrSWRunPerfMem: 747 value->v.integer = entry->perfMemory; 748 return (SNMP_ERR_NOERROR); 749 } 750 abort(); 751 } 752