/* * swrun_darwin.c: * hrSWRunTable data access: * Darwin */ /* * Copyright (C) 2007 Apple, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #include #include #include #include "swrun_private.h" #include #include #include #include #include /* for sysctl() and struct kinfo_proc */ #define __APPLE_API_EVOLVING 1 #include /* or else CoreFoundation.h barfs */ #undef __APPLE_API_EVOLVING #include #include #include #include #include #include #include /** sigh... can't find Processes.h */ #ifndef kProcessDictionaryIncludeAllInformationMask #define kProcessDictionaryIncludeAllInformationMask (long)0xFFFFFFFF #endif #ifndef procNotFound #define procNotFound -600 #endif /* --------------------------------------------------------------------- */ static int _kern_argmax; static int _set_command_name(netsnmp_swrun_entry *entry); /** avoid kernel bug in 10.2. 8192 oughta be enough anyways, right? */ #define MAX_KERN_ARGMAX 8192 /* --------------------------------------------------------------------- */ void netsnmp_arch_swrun_init(void) { int mib[2] = { CTL_KERN, KERN_ARGMAX }; size_t size, mib_size = sizeof(mib)/sizeof(mib[0]); DEBUGMSGTL(("swrun:load:arch","init\n")); size = sizeof(_kern_argmax); if (sysctl(mib, mib_size, &_kern_argmax, &size, NULL, 0) == -1) { snmp_log(LOG_ERR, "Error in ARGMAX sysctl(): %s", strerror(errno)); _kern_argmax = MAX_KERN_ARGMAX; } else if (_kern_argmax > MAX_KERN_ARGMAX) { DEBUGMSGTL(("swrun:load:arch", "artificially limiting ARGMAX to %d (from %d)\n", MAX_KERN_ARGMAX, _kern_argmax)); _kern_argmax = MAX_KERN_ARGMAX; } } /* --------------------------------------------------------------------- */ #define SWRUNINDENT " " int netsnmp_arch_swrun_container_load( netsnmp_container *container, u_int flags) { int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; size_t buf_size, mib_size = sizeof(mib)/sizeof(mib[0]); struct kinfo_proc *processes = NULL; struct proc_taskallinfo taskinfo; netsnmp_swrun_entry *entry; int rc, num_entries, i; DEBUGMSGTL(("swrun:load:arch"," load\n")); /* * get size to allocate. This introduces a bit of a race condition, * as the size could change between this call and the next... */ rc = sysctl(mib, mib_size, NULL, &buf_size, NULL, 0); if (rc < 0) { snmp_log(LOG_ERR, "KERN_PROC_ALL size sysctl failed: %d\n", rc); return -1; } processes = (struct kinfo_proc*) malloc(buf_size); if (NULL == processes) { snmp_log(LOG_ERR, "malloc failed\n"); return -1; } rc = sysctl(mib, mib_size, processes, &buf_size, NULL, 0); if (rc < 0) { snmp_log(LOG_ERR, "KERN_PROC_ALL sysctl failed: %d\n", rc); free(processes); return -1; } num_entries = buf_size / sizeof(struct kinfo_proc); for (i = 0; i < num_entries; i++) { /* * skip empty names. * p_stat = (SIDL|SRUN|SSLEEP|SSTOP|SZOMB) */ if (('\0' == processes[i].kp_proc.p_comm[0]) || (0 == processes[i].kp_proc.p_pid)) { DEBUGMSGTL(("swrun:load:arch", " skipping p_comm '%s', pid %5d, p_pstat %d\n", processes[i].kp_proc.p_comm ? processes[i].kp_proc.p_comm : "NULL", processes[i].kp_proc.p_pid, processes[i].kp_proc.p_stat)); continue; } DEBUGMSGTL(("swrun:load:arch"," %s pid %5d\n", processes[i].kp_proc.p_comm, processes[i].kp_proc.p_pid)); entry = netsnmp_swrun_entry_create(processes[i].kp_proc.p_pid); if (NULL == entry) continue; /* error already logged by function */ rc = CONTAINER_INSERT(container, entry); /* * p_comm is a partial name, but it is all we have at this point. */ entry->hrSWRunName_len = snprintf(entry->hrSWRunName, sizeof(entry->hrSWRunName)-1, "%s", processes[i].kp_proc.p_comm); /** sysctl for name, path, params */ rc = _set_command_name(entry); /* * map p_stat to RunStatus. Odd that there is no 'running' status. */ switch(processes[i].kp_proc.p_stat) { case SRUN: entry->hrSWRunStatus = HRSWRUNSTATUS_RUNNABLE; break; case SSLEEP: case SSTOP: entry->hrSWRunStatus = HRSWRUNSTATUS_NOTRUNNABLE; break; case SIDL: case SZOMB: default: entry->hrSWRunStatus = HRSWRUNSTATUS_INVALID; break; } /* * check for system processes */ if (P_SYSTEM & processes[i].kp_proc.p_flag) { entry->hrSWRunType = HRSWRUNTYPE_OPERATINGSYSTEM; DEBUGMSGTL(("swrun:load:arch", SWRUNINDENT "SYSTEM\n")); } else entry->hrSWRunType = HRSWRUNTYPE_APPLICATION; /* * get mem size, run time */ rc = proc_pidinfo( processes[i].kp_proc.p_pid, PROC_PIDTASKALLINFO, 0, &taskinfo, sizeof(taskinfo)); if (sizeof(taskinfo) != rc) { DEBUGMSGTL(("swrun:load:arch", " proc_pidinfo returned %d\n", rc)); } else { uint64_t task_mem = taskinfo.ptinfo.pti_resident_size / 1024; union { u_quad_t uq; /* u_int64_t */ UnsignedWide uw; /* struct u_int32_t hi/lo */ } at, ns; at.uq = taskinfo.ptinfo.pti_total_user + taskinfo.ptinfo.pti_total_system; ns = at; ns.uq = ns.uq / 10000000LL; /* nano to deci */ if (task_mem > INT32_MAX) { DEBUGMSGTL(("swrun:load:arch", SWRUNINDENT "mem overflow\n")); task_mem = INT32_MAX; } if (ns.uq > INT32_MAX) { DEBUGMSGTL(("swrun:load:arch", SWRUNINDENT "time overflow\n")); ns.uq = INT32_MAX; } entry->hrSWRunPerfMem = task_mem; entry->hrSWRunPerfCPU = ns.uq; } } free(processes); DEBUGMSGTL(("swrun:load:arch"," loaded %d entries\n", (int)CONTAINER_SIZE(container))); return 0; } /* --------------------------------------------------------------------- * The following code was snagged from Darwin code, and the original * file had the following licences: */ /* * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #ifdef JAGUAR /* xxx configure test? */ static int _set_command_name_jaguar(netsnmp_swrun_entry *entry) { int mib[3] = {CTL_KERN, KERN_PROCARGS, 0}; size_t procargssize, mib_size = sizeof(mib)/sizeof(mib[0]); char *arg_end, *exec_path; int *ip; int len; char *command_beg, *command, *command_end; char arg_buf[MAX_KERN_ARGMAX]; /* max to avoid kernel bug */ DEBUGMSGTL(("swrun:load:arch:_cn"," pid %d\n", entry->hrSWRunIndex)); mib[2] = entry->hrSWRunIndex; memset(arg_buf, 0x0, sizeof(arg_buf)); procargssize = _kern_argmax; if (sysctl(mib, mib_size, arg_buf, &procargssize, NULL, 0) == -1) { snmp_log(LOG_ERR, "Error in PROCARGS sysctl() for %s: %s\n", entry->hrSWRunName, strerror(errno)); entry->hrSWRunPath_len = 0; return -1; } /* Set ip just above the end of arg_buf. */ arg_end = &arg_buf[procargssize]; ip = (int *)arg_end; /* * Skip the last 2 words, since the last is a 0 word, and * the second to last may be as well, if there are no * arguments. */ ip -= 3; /* Iterate down the arguments until a 0 word is found. */ for (; *ip != 0; ip--) { if (ip == (int *)arg_buf) { DEBUGMSGTL(("swrun:load:arch:_cn"," unexpected toparg\n")); return -1; } } /* The saved exec_path is just above the 0 word. */ ip++; exec_path = (char *)ip; DEBUGMSGTL(("swrun:load:arch:_cn"," exec_path %s\n", exec_path)); len = strlen(exec_path); strlcpy(entry->hrSWRunPath, exec_path, sizeof(entry->hrSWRunPath)); if (len > sizeof(entry->hrSWRunPath)-1) { DEBUGMSGTL(("swrun:load:arch:_cn"," truncating long run path\n")); entry->hrSWRunPath[sizeof(entry->hrSWRunPath)-2] = '$'; entry->hrSWRunPath_len = sizeof(entry->hrSWRunPath)-1; DEBUGMSGTL(("swrun:load:arch:_cn"," exec_path %s\n", entry->hrSWRunPath)); } else entry->hrSWRunPath_len = len; /* * Get the beginning of the first argument. It is word-aligned, * so skip padding '\0' bytes. */ command_beg = exec_path + strlen(exec_path); DEBUGMSGTL(("swrun:load:arch:_cn"," command_beg '%s'\n", command_beg)); for (; *command_beg == '\0'; command_beg++) { if (command_beg >= arg_end) return -1; } DEBUGMSGTL(("swrun:load:arch:_cn"," command_beg '%s'\n", command_beg)); /* Get the basename of command. */ command = command_end = command_beg + strlen(command_beg) + 1; for (command--; command >= command_beg; command--) { if (*command == '/') break; } command++; DEBUGMSGTL(("swrun:load:arch:_cn"," command '%s'\n", command)); /* Allocate space for the command and copy. */ DEBUGMSGTL(("swrun:load:arch:_cn", SWRUNINDENT "kernel name %s\n", command)); if (strncmp(command, entry->hrSWRunName, sizeof(entry->hrSWRunName)-1)) { strlcpy(entry->hrSWRunName, command, sizeof(entry->hrSWRunName)); entry->hrSWRunName_len = strlen(entry->hrSWRunName); DEBUGMSGTL(("swrun:load:arch:_cn", "**" SWRUNINDENT "updated name to %s\n", entry->hrSWRunName)); return 0; } /** no error, no change */ return 1; } #else static int _set_command_name(netsnmp_swrun_entry *entry) { int mib[3] = {CTL_KERN, 0, 0}; size_t procargssize, mib_size = sizeof(mib)/sizeof(mib[0]); char *cp; int len, nargs; char *command_beg, *command, *command_end, *exec_path, *argN; char arg_buf[MAX_KERN_ARGMAX]; /* max to avoid kernel bug */ /* * arguments */ mib[1] = KERN_PROCARGS2; mib[2] = entry->hrSWRunIndex; memset(arg_buf, 0x0, sizeof(arg_buf)); procargssize = _kern_argmax; if (sysctl(mib, mib_size, arg_buf, &procargssize, NULL, 0) == -1) { snmp_log(LOG_ERR, "Error in PROCARGS2 sysctl() for %s: %s\n", entry->hrSWRunName, strerror(errno)); entry->hrSWRunPath_len = 0; entry->hrSWRunParameters_len = 0; return -1; } else { memcpy(&nargs,arg_buf, sizeof(nargs)); } exec_path = arg_buf + sizeof(nargs); len = strlen(exec_path); strlcpy(entry->hrSWRunPath, exec_path, sizeof(entry->hrSWRunPath)); if (len > sizeof(entry->hrSWRunPath)-1) { DEBUGMSGTL(("swrun:load:arch:_cn"," truncating long run path\n")); entry->hrSWRunPath[sizeof(entry->hrSWRunPath)-2] = '$'; entry->hrSWRunPath_len = sizeof(entry->hrSWRunPath)-1; } else entry->hrSWRunPath_len = len; /** Skip the saved exec_path. */ #if 0 cp = exec_path + len; #else for (cp = exec_path; cp < &arg_buf[procargssize]; cp++) { if (*cp == '\0') break; /* End of exec_path reached. */ } if (cp != exec_path + len) { DEBUGMSGTL(("swrun:load:arch:_cn", " OFF BY %d\n", (int)((exec_path + len) - cp))); netsnmp_assert( cp == exec_path + len ); } #endif if (cp == &arg_buf[procargssize]) { DEBUGMSGTL(("swrun:load:arch:_cn"," unexpected end of buffer\n")); return -1; } /** Skip trailing '\0' characters. */ for (; cp < &arg_buf[procargssize]; cp++) { if (*cp != '\0') break; /* Beginning of first argument reached. */ } if (cp == &arg_buf[procargssize]) { DEBUGMSGTL(("swrun:load:arch:_cn"," unexpected end of buffer\n")); return -1; } command_beg = cp; /* * Make sure that the command is '\0'-terminated. This protects * against malicious programs; under normal operation this never * ends up being a problem.. */ for (; cp < &arg_buf[procargssize]; cp++) { if (*cp == '\0') break; /* End of first argument reached. */ } if (cp == &arg_buf[procargssize]) { DEBUGMSGTL(("swrun:load:arch:_cn"," unexpected end of buffer\n")); return -1; } command_end = command = cp; --nargs; /* * save arguments */ while( nargs && cp < &arg_buf[procargssize] ) { /** Skip trailing '\0' characters from prev arg. */ for (; (cp < &arg_buf[procargssize]) && (*cp == 0); cp++) ; /* noop */ if (cp == &arg_buf[procargssize]) continue; /* effectively a break */ /** save argN start */ argN = cp; --nargs; if (0 == nargs) continue; /* effectively a break */ /** Skip to end of arg */ for (; (cp < &arg_buf[procargssize]) && (*cp != 0); cp++) ; /* noop */ if (cp == &arg_buf[procargssize]) continue; /* effectively a break */ /* * check for overrun into env */ if ((*argN != '-') && strchr(argN,'=')) { DEBUGMSGTL(("swrun:load:arch:_cn", " *** OVERRUN INTO ENV %d\n",nargs)); continue; } /* * save arg */ if(entry->hrSWRunParameters_len < sizeof(entry->hrSWRunParameters)-1) { strlcat(&entry->hrSWRunParameters[entry->hrSWRunParameters_len], argN, sizeof(entry->hrSWRunParameters)-entry->hrSWRunParameters_len-1); entry->hrSWRunParameters_len = strlen(entry->hrSWRunParameters); if ((entry->hrSWRunParameters_len+2 < sizeof(entry->hrSWRunParameters)-1) && (0 != nargs)) { /* add space between params */ entry->hrSWRunParameters[entry->hrSWRunParameters_len++] = ' '; entry->hrSWRunParameters[entry->hrSWRunParameters_len] = 0; } else { DEBUGMSGTL(("swrun:load:arch:_cn"," truncating long arg list\n")); entry->hrSWRunParameters[entry->hrSWRunParameters_len++] = '$'; entry->hrSWRunParameters[entry->hrSWRunParameters_len] = '0'; } } } if (' ' == entry->hrSWRunParameters[entry->hrSWRunParameters_len]) entry->hrSWRunParameters[entry->hrSWRunParameters_len--] = 0; /* Get the basename of command. */ for (command--; command >= command_beg; command--) { if (*command == '/') break; } command++; /* Allocate space for the command and copy. */ if (strncmp(command, entry->hrSWRunName, sizeof(entry->hrSWRunName)-1)) { strlcpy(entry->hrSWRunName, command, sizeof(entry->hrSWRunName)); entry->hrSWRunName_len = strlen(entry->hrSWRunName); DEBUGMSGTL(("swrun:load:arch:_cn", " **updated name to %s\n", entry->hrSWRunName)); } return 0; } #endif