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