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