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