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 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 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 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 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 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 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 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 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 * 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 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 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 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