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
55enum SWRunType {
56	SRT_UNKNOWN		= 1,
57	SRT_OPERATING_SYSTEM	= 2,
58	SRT_DEVICE_DRIVER	= 3,
59	SRT_APPLICATION		= 4
60
61};
62
63enum 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 */
80struct 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};
95TAILQ_HEAD(swrun_tbl, swrun_entry);
96
97/* the head of the list with hrSWRunTable's entries */
98static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl);
99
100/* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */
101static uint64_t swrun_tick;
102
103/* maximum number of ticks between updates of SWRun and SWRunPerf table */
104uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100;
105
106/* the value of the MIB object with the same name */
107static 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 */
114static struct swrun_entry *
115swrun_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 */
133static void
134swrun_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 */
150static struct swrun_entry *
151swrun_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 */
164static enum SWRunStatus
165swrun_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 */
203static void
204kinfo_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 */
284static void
285kld_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 */
324static void
325swrun_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 */
359static void
360swrun_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 */
394static void
395refresh_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 */
427static void
428fetch_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 */
471static int
472invalidate_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("atempt 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 */
553void
554init_swrun_tbl(void)
555{
556
557	refresh_swrun_tbl();
558	HRDBG("done");
559}
560
561/**
562 * Finalize the hrSWRunTable.
563 */
564void
565fini_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 */
580int
581op_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 */
705int
706op_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 */
743int
744op_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