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
fini_scalars(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
OS_getSystemUptime(uint32_t * ut)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
OS_getSystemDate(struct snmp_value * value)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
OS_getSystemInitialLoadParameters(u_char ** params)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
OS_getSystemNumUsers(uint32_t * nu)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
OS_getSystemProcesses(uint32_t * proc_count)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
OS_getSystemMaxProcesses(uint32_t * mproc)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
OS_getMemorySize(uint32_t * ms)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 *
OS_checkSystemDateInput(const u_char * str,u_int len)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
OS_setSystemDate(const struct timeval * timeval_to_set)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
op_hrSystem(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)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
op_hrStorage(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)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