xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c (revision 3d11b6c8f01e1fca5936a11d6996448467851a94)
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