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