hostres_scalars.c revision 311720
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: stable/10/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c 311720 2017-01-09 01:07:16Z ngie $
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/* boot timestamp in centi-seconds */
51static uint64_t kernel_boot;
52
53/* physical memory size in Kb */
54static uint64_t phys_mem_size;
55
56/* boot line (malloced) */
57static u_char *boot_line;
58
59/* maximum number of processes */
60static uint32_t max_proc;
61
62/**
63 * Free all static data
64 */
65void
66fini_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 */
76static int
77OS_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, nitems(mib), &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 */
144static int
145OS_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 occurred. The returned data is a
174 * pointer to a global storage.
175 */
176int
177OS_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 */
214static int
215OS_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 */
233static int
234OS_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_PROC, 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 */
253static int
254OS_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 */
277static int
278OS_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 */
308static struct timeval *
309OS_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 */
357static int
358OS_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 */
372int
373op_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 */
467int
468op_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