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