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