xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
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  * 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 
53 enum SWRunType {
54 	SRT_UNKNOWN		= 1,
55 	SRT_OPERATING_SYSTEM	= 2,
56 	SRT_DEVICE_DRIVER	= 3,
57 	SRT_APPLICATION		= 4
58 
59 };
60 
61 enum 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  */
78 struct 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 };
93 TAILQ_HEAD(swrun_tbl, swrun_entry);
94 
95 /* the head of the list with hrSWRunTable's entries */
96 static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl);
97 
98 /* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */
99 static uint64_t swrun_tick;
100 
101 /* maximum number of ticks between updates of SWRun and SWRunPerf table */
102 uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100;
103 
104 /* the value of the MIB object with the same name */
105 static 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  */
112 static struct swrun_entry *
swrun_entry_create(int32_t idx)113 swrun_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  */
131 static void
swrun_entry_delete(struct swrun_entry * entry)132 swrun_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  */
148 static struct swrun_entry *
swrun_entry_find_by_index(int32_t idx)149 swrun_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  */
162 static enum SWRunStatus
swrun_OS_get_proc_status(const struct kinfo_proc * kp)163 swrun_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  */
201 static void
kinfo_proc_to_swrun_entry(const struct kinfo_proc * kp,struct swrun_entry * entry)202 kinfo_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  */
282 static void
kld_file_stat_to_swrun(const struct kld_file_stat * kfs,struct swrun_entry * entry)283 kld_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  */
322 static void
swrun_OS_get_procs(void)323 swrun_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  */
357 static void
swrun_OS_get_kinfo(void)358 swrun_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  */
392 static void
refresh_swrun_tbl(void)393 refresh_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  */
425 static void
fetch_swrun_entry(struct swrun_entry * entry)426 fetch_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  */
469 static int
invalidate_swrun_entry(struct swrun_entry * entry,int commit)470 invalidate_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  */
551 void
init_swrun_tbl(void)552 init_swrun_tbl(void)
553 {
554 
555 	refresh_swrun_tbl();
556 	HRDBG("done");
557 }
558 
559 /**
560  * Finalize the hrSWRunTable.
561  */
562 void
fini_swrun_tbl(void)563 fini_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  */
578 int
op_hrSWRunTable(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)579 op_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  */
703 int
op_hrSWRun(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)704 op_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  */
741 int
op_hrSWRunPerfTable(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)742 op_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