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 /* 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
fini_scalars(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
OS_getSystemUptime(uint32_t * ut)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, 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 */
144 static int
OS_getSystemDate(struct snmp_value * value)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 occurred. The returned data is a
174 * pointer to a global storage.
175 */
176 int
OS_getSystemInitialLoadParameters(u_char ** params)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
OS_getSystemNumUsers(uint32_t * nu)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
OS_getSystemProcesses(uint32_t * proc_count)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_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 */
253 static int
OS_getSystemMaxProcesses(uint32_t * mproc)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
OS_getMemorySize(uint32_t * ms)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 *
OS_checkSystemDateInput(const u_char * str,u_int len)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
OS_setSystemDate(const struct timeval * timeval_to_set)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
op_hrSystem(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)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
op_hrStorage(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)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