xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c (revision 71ccf09269546d398fa847168fc74c22d6338a62)
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 
32 /*
33  * Host Resources MIB scalars implementation for SNMPd.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/sysctl.h>
38 
39 #include <pwd.h>
40 #include <stdlib.h>
41 #include <stdint.h>
42 #include <string.h>
43 #include <syslog.h>
44 #define	_ULOG_POSIX_NAMES
45 #include <ulog.h>
46 
47 #include "hostres_snmp.h"
48 #include "hostres_oid.h"
49 #include "hostres_tree.h"
50 
51 /* boot timestamp in centi-seconds */
52 static uint64_t kernel_boot;
53 
54 /* physical memory size in Kb */
55 static uint64_t phys_mem_size;
56 
57 /* boot line (malloced) */
58 static u_char *boot_line;
59 
60 /* maximum number of processes */
61 static uint32_t max_proc;
62 
63 /**
64  * Free all static data
65  */
66 void
67 fini_scalars(void)
68 {
69 
70 	free(boot_line);
71 }
72 
73 /**
74  * Get system uptime in hundredths of seconds since the epoch
75  * Returns 0 in case of an error
76  */
77 static int
78 OS_getSystemUptime(uint32_t *ut)
79 {
80 	struct timeval right_now;
81 	uint64_t now;
82 
83 	if (kernel_boot == 0) {
84 		/* first time, do the sysctl */
85 		struct timeval kernel_boot_timestamp;
86 		int mib[2] = { CTL_KERN, KERN_BOOTTIME };
87 		size_t len = sizeof(kernel_boot_timestamp);
88 
89 		if (sysctl(mib, 2, &kernel_boot_timestamp,
90 		    &len, NULL, 0) == -1) {
91 			syslog(LOG_ERR, "sysctl KERN_BOOTTIME failed: %m");
92 			return (SNMP_ERR_GENERR);
93 		}
94 
95 		HRDBG("boot timestamp from kernel: {%lld, %ld}",
96 		    (long long)kernel_boot_timestamp.tv_sec,
97 		    (long)kernel_boot_timestamp.tv_usec);
98 
99 		kernel_boot = ((uint64_t)kernel_boot_timestamp.tv_sec * 100) +
100 		    (kernel_boot_timestamp.tv_usec / 10000);
101 	}
102 
103 	if (gettimeofday(&right_now, NULL) < 0) {
104 		syslog(LOG_ERR, "gettimeofday failed: %m");
105 		return (SNMP_ERR_GENERR);
106 	}
107 	now = ((uint64_t)right_now.tv_sec * 100) + (right_now.tv_usec / 10000);
108 
109 	if (now - kernel_boot > UINT32_MAX)
110 		*ut = UINT32_MAX;
111 	else
112 		*ut = now - kernel_boot;
113 
114 	return (SNMP_ERR_NOERROR);
115 }
116 
117 /**
118  * Get system local date and time in a foramt suitable for DateAndTime TC:
119  *           field  octets  contents                  range
120  *           -----  ------  --------                  -----
121  *             1      1-2   year*                     0..65536
122  *             2       3    month                     1..12
123  *             3       4    day                       1..31
124  *             4       5    hour                      0..23
125  *             5       6    minutes                   0..59
126  *             6       7    seconds                   0..60
127  *                          (use 60 for leap-second)
128  *             7       8    deci-seconds              0..9
129  *             8       9    direction from UTC        '+' / '-'
130  *             9      10    hours from UTC*           0..13
131  *            10      11    minutes from UTC          0..59
132  *
133  *           * Notes:
134  *           - the value of year is in network-byte order
135  *           - daylight saving time in New Zealand is +13
136  *
137  *           For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be
138  *           displayed as:
139  *
140  *                            1992-5-26,13:30:15.0,-4:0
141  *
142  * Returns -1 in case of an error or the length of the string (8 or 11)
143  * Actually returns always 11 on freebsd
144  */
145 static int
146 OS_getSystemDate(struct snmp_value *value)
147 {
148 	u_char s_date_time[11];
149 	struct tm tloc_tm;
150 	time_t tloc_time_t;
151 	struct timeval right_now;
152 	int string_len;
153 
154 	if (gettimeofday(&right_now, NULL) < 0) {
155 		syslog(LOG_ERR, "gettimeofday failed: %m");
156 		return (SNMP_ERR_GENERR);
157 	}
158 
159 	tloc_time_t = right_now.tv_sec;
160 
161 	if (localtime_r(&tloc_time_t, &tloc_tm) == NULL) {
162 		syslog(LOG_ERR, "localtime_r() failed: %m ");
163 		return (SNMP_ERR_GENERR);
164 	}
165 
166 	string_len = make_date_time(s_date_time, &tloc_tm,
167 	    right_now.tv_usec / 100000);
168 
169 	return (string_get(value, s_date_time, string_len));
170 }
171 
172 /**
173  * Get kernel boot path. For FreeBSD it seems that no arguments are
174  * present. Returns NULL if an error occured. The returned data is a
175  * pointer to a global strorage.
176  */
177 int
178 OS_getSystemInitialLoadParameters(u_char **params)
179 {
180 
181 	if (boot_line == NULL) {
182 		int mib[2] = { CTL_KERN, KERN_BOOTFILE };
183 		char *buf;
184 		size_t buf_len = 0;
185 
186 		/* get the needed buffer len */
187 		if (sysctl(mib, 2, NULL, &buf_len, NULL, 0) != 0) {
188 			syslog(LOG_ERR,
189 			    "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m");
190 			return (SNMP_ERR_GENERR);
191 		}
192 
193 		if ((buf = malloc(buf_len)) == NULL) {
194 			syslog(LOG_ERR, "malloc failed");
195 			return (SNMP_ERR_GENERR);
196 		}
197                 if (sysctl(mib, 2, buf, &buf_len, NULL, 0)) {
198 			syslog(LOG_ERR,
199 			    "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m");
200 			free(buf);
201 			return (SNMP_ERR_GENERR);
202 		}
203 
204 		boot_line = buf;
205 		HRDBG("kernel boot file: %s", boot_line);
206 	}
207 
208 	*params = boot_line;
209 	return (SNMP_ERR_NOERROR);
210 }
211 
212 /**
213  * Get number of current users which are logged in
214  */
215 static int
216 OS_getSystemNumUsers(uint32_t *nu)
217 {
218 	struct utmpx *utmp;
219 
220 	setutxent();
221 	*nu = 0;
222 	while ((utmp = getutxent()) != NULL) {
223 		if (utmp->ut_type == USER_PROCESS)
224 			(*nu)++;
225 	}
226 	endutxent();
227 
228 	return (SNMP_ERR_NOERROR);
229 }
230 
231 /**
232  * Get number of current processes existing into the system
233  */
234 static int
235 OS_getSystemProcesses(uint32_t *proc_count)
236 {
237 	int pc;
238 
239 	if (hr_kd == NULL)
240 		return (SNMP_ERR_GENERR);
241 
242 	if (kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &pc) == NULL) {
243 		syslog(LOG_ERR, "kvm_getprocs failed: %m");
244 		return (SNMP_ERR_GENERR);
245 	}
246 
247 	*proc_count = pc;
248 	return (SNMP_ERR_NOERROR);
249 }
250 
251 /**
252  * Get maximum number of processes allowed on this system
253  */
254 static int
255 OS_getSystemMaxProcesses(uint32_t *mproc)
256 {
257 
258 	if (max_proc == 0) {
259 		int mib[2] = { CTL_KERN, KERN_MAXPROC };
260 		int mp;
261 		size_t len = sizeof(mp);
262 
263 		if (sysctl(mib, 2, &mp, &len, NULL, 0) == -1) {
264 			syslog(LOG_ERR, "sysctl KERN_MAXPROC failed: %m");
265 			return (SNMP_ERR_GENERR);
266 		}
267 		max_proc = mp;
268 	}
269 
270 	*mproc = max_proc;
271 	return (SNMP_ERR_NOERROR);
272 }
273 
274 /*
275  * Get the physical memeory size in Kbytes.
276  * Returns SNMP error code.
277  */
278 static int
279 OS_getMemorySize(uint32_t *ms)
280 {
281 
282 	if (phys_mem_size == 0) {
283 		int mib[2] = { CTL_HW, HW_PHYSMEM };
284 		u_long physmem;
285 		size_t len = sizeof(physmem);
286 
287 		if (sysctl(mib, 2, &physmem, &len, NULL, 0) == -1) {
288 			syslog(LOG_ERR,
289 			    "sysctl({ CTL_HW, HW_PHYSMEM }) failed: %m");
290 			return (SNMP_ERR_GENERR);
291 		}
292 
293 		phys_mem_size = physmem / 1024;
294 	}
295 
296 	if (phys_mem_size > UINT32_MAX)
297 		*ms = UINT32_MAX;
298 	else
299 		*ms = phys_mem_size;
300         return (SNMP_ERR_NOERROR);
301 }
302 
303 /*
304  * Try to use the s_date_time parameter as a DateAndTime TC to fill in
305  * the second parameter.
306  * Returns 0 on succes and -1 for an error.
307  * Bug: time zone info is not used
308  */
309 static struct timeval *
310 OS_checkSystemDateInput(const u_char *str, u_int len)
311 {
312 	struct tm tm_to_set;
313 	time_t t;
314 	struct timeval *tv;
315 
316 	if (len != 8 && len != 11)
317 		return (NULL);
318 
319 	if (str[2] == 0 || str[2] > 12 ||
320 	    str[3] == 0 || str[3] > 31 ||
321 	    str[4] > 23 || str[5] > 59 || str[6] > 60 || str[7] > 9)
322 		return (NULL);
323 
324 	tm_to_set.tm_year = ((str[0] << 8) + str[1]) - 1900;
325 	tm_to_set.tm_mon = str[2] - 1;
326 	tm_to_set.tm_mday = str[3];
327 	tm_to_set.tm_hour = str[4];
328 	tm_to_set.tm_min = str[5];
329 	tm_to_set.tm_sec = str[6];
330 	tm_to_set.tm_isdst = 0;
331 
332 	/* now make UTC from it */
333 	if ((t = timegm(&tm_to_set)) == (time_t)-1)
334 		return (NULL);
335 
336 	/* now apply timezone if specified */
337 	if (len == 11) {
338 		if (str[9] > 13 || str[10] > 59)
339 			return (NULL);
340 		if (str[8] == '+')
341 			t += 3600 * str[9] + 60 * str[10];
342 		else
343 			t -= 3600 * str[9] + 60 * str[10];
344 	}
345 
346 	if ((tv = malloc(sizeof(*tv))) == NULL)
347 		return (NULL);
348 
349 	tv->tv_sec = t;
350 	tv->tv_usec = (int32_t)str[7] * 100000;
351 
352 	return (tv);
353 }
354 
355 /*
356  * Set system date and time. Timezone is not changed
357  */
358 static int
359 OS_setSystemDate(const struct timeval *timeval_to_set)
360 {
361 	if (settimeofday(timeval_to_set, NULL) == -1) {
362 		syslog(LOG_ERR, "settimeofday failed: %m");
363 		return (SNMP_ERR_GENERR);
364         }
365 	return (SNMP_ERR_NOERROR);
366 }
367 
368 /*
369  * prototype of this function was genrated by gensnmptree tool in header file
370  * hostres_tree.h
371  * Returns SNMP_ERR_NOERROR on success
372  */
373 int
374 op_hrSystem(struct snmp_context *ctx, struct snmp_value *value,
375     u_int sub, u_int iidx __unused, enum snmp_op curr_op)
376 {
377 	int err;
378 	u_char *str;
379 
380 	switch (curr_op) {
381 
382           case SNMP_OP_GET:
383 		switch (value->var.subs[sub - 1]) {
384 
385 		case LEAF_hrSystemUptime:
386 			return (OS_getSystemUptime(&value->v.uint32));
387 
388 		case LEAF_hrSystemDate:
389 			return (OS_getSystemDate(value));
390 
391 		case LEAF_hrSystemInitialLoadDevice:
392 			value->v.uint32 = 0; /* FIXME */
393 			return (SNMP_ERR_NOERROR);
394 
395 		case LEAF_hrSystemInitialLoadParameters:
396 			if ((err = OS_getSystemInitialLoadParameters(&str)) !=
397 			    SNMP_ERR_NOERROR)
398 				return (err);
399 			return (string_get(value, str, -1));
400 
401 		case LEAF_hrSystemNumUsers:
402 			return (OS_getSystemNumUsers(&value->v.uint32));
403 
404 		case LEAF_hrSystemProcesses:
405 			return (OS_getSystemProcesses(&value->v.uint32));
406 
407 		case LEAF_hrSystemMaxProcesses:
408 			return (OS_getSystemMaxProcesses(&value->v.uint32));
409 		}
410 		abort();
411 
412 	  case SNMP_OP_SET:
413 		switch (value->var.subs[sub - 1]) {
414 
415 		case LEAF_hrSystemDate:
416 			if ((ctx->scratch->ptr1 =
417 			    OS_checkSystemDateInput(value->v.octetstring.octets,
418 			    value->v.octetstring.len)) == NULL)
419 				return (SNMP_ERR_WRONG_VALUE);
420 
421 			return (SNMP_ERR_NOERROR);
422 
423 		case LEAF_hrSystemInitialLoadDevice:
424 		case LEAF_hrSystemInitialLoadParameters:
425 			return (SNMP_ERR_NOT_WRITEABLE);
426 
427 		}
428 		abort();
429 
430 	  case SNMP_OP_ROLLBACK:
431 		switch (value->var.subs[sub - 1]) {
432 
433 		case LEAF_hrSystemDate:
434 			free(ctx->scratch->ptr1);
435 			return (SNMP_ERR_NOERROR);
436 
437 		case LEAF_hrSystemInitialLoadDevice:
438 		case LEAF_hrSystemInitialLoadParameters:
439 			abort();
440 		}
441 		abort();
442 
443 	  case SNMP_OP_COMMIT:
444 		switch (value->var.subs[sub - 1]) {
445 
446 		case LEAF_hrSystemDate:
447 			(void)OS_setSystemDate(ctx->scratch->ptr1);
448 			free(ctx->scratch->ptr1);
449 			return (SNMP_ERR_NOERROR);
450 
451 		case LEAF_hrSystemInitialLoadDevice:
452 		case LEAF_hrSystemInitialLoadParameters:
453 			abort();
454 		}
455 		abort();
456 
457 	  case SNMP_OP_GETNEXT:
458 		abort();
459 	}
460 	abort();
461 }
462 
463 /*
464  * prototype of this function was genrated by gensnmptree tool
465  * in the header file hostres_tree.h
466  * Returns SNMP_ERR_NOERROR on success
467  */
468 int
469 op_hrStorage(struct snmp_context *ctx __unused, struct snmp_value *value,
470     u_int sub, u_int iidx __unused, enum snmp_op curr_op)
471 {
472 
473 	/* only GET is possible */
474 	switch (curr_op) {
475 
476 	case SNMP_OP_GET:
477 		switch (value->var.subs[sub - 1]) {
478 
479 		case LEAF_hrMemorySize:
480 			return (OS_getMemorySize(&value->v.uint32));
481 		}
482 		abort();
483 
484 	case SNMP_OP_SET:
485 		return (SNMP_ERR_NOT_WRITEABLE);
486 
487 	case SNMP_OP_ROLLBACK:
488 	case SNMP_OP_COMMIT:
489 	case SNMP_OP_GETNEXT:
490 		abort();
491 	}
492 	abort();
493 }
494