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