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