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 <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 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 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, 2, &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 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 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 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 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 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 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 * 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 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 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 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