/* GKrellM | Copyright (C) 1999-2010 Bill Wilson | | Author: Bill Wilson billw@gkrellm.net | Latest versions might be found at: http://gkrellm.net | | darwin.c code is Copyright (c) Ben Hines | | | GKrellM is free software: you can redistribute it and/or modify it | under the terms of the GNU General Public License as published by | the Free Software Foundation, either version 3 of the License, or | (at your option) any later version. | | GKrellM is distributed in the hope that it will be useful, but WITHOUT | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | License for more details. | | You should have received a copy of the GNU General Public License | along with this program. If not, see http://www.gnu.org/licenses/ | | | Additional permission under GNU GPL version 3 section 7 | | If you modify this program, or any covered work, by linking or | combining it with the OpenSSL project's OpenSSL library (or a | modified version of that library), containing parts covered by | the terms of the OpenSSL or SSLeay licenses, you are granted | additional permission to convey the resulting work. | Corresponding Source for a non-source form of such a combination | shall include the source code for the parts of OpenSSL used as well | as that of the covered work. */ #ifdef HAVE_KVM_H #include #endif #include #include #include #ifdef HAVE_KVM_H kvm_t *kvmd = NULL; char errbuf[_POSIX2_LINE_MAX]; #endif void gkrellm_sys_main_init(void) { #ifdef HAVE_KVM_H /* We just ignore error, here. Even if GKrellM doesn't have | kmem privilege, it runs with available information. */ kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); if (setgid(getgid()) != 0) { fprintf(stderr, "Can't drop setgid privileges."); exit(1); } #endif } void gkrellm_sys_main_cleanup(void) { } /* ===================================================================== */ /* CPU monitor interface */ static guint n_cpus; void gkrellm_sys_cpu_read_data(void) { processor_cpu_load_info_data_t *pinfo; mach_msg_type_number_t info_count; int i = 0; if (host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO, &n_cpus, (processor_info_array_t*)&pinfo, &info_count)) { return; } for (i = 0; i < n_cpus; i++) { gkrellm_cpu_assign_data(i, pinfo[i].cpu_ticks [CPU_STATE_USER], pinfo[i].cpu_ticks [CPU_STATE_NICE], pinfo[i].cpu_ticks [CPU_STATE_SYSTEM], pinfo[i].cpu_ticks [CPU_STATE_IDLE]); } vm_deallocate (mach_task_self (), (vm_address_t) pinfo, info_count); } gboolean gkrellm_sys_cpu_init(void) { processor_cpu_load_info_data_t *pinfo; mach_msg_type_number_t info_count; n_cpus = 0; if (host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO, &n_cpus, (processor_info_array_t*)&pinfo, &info_count)) { return FALSE; } gkrellm_cpu_set_number_of_cpus(n_cpus); return TRUE; } /* ===================================================================== */ /* Proc monitor interface */ #include #include #define PID_MAX 30000 #ifdef HAVE_KVM_H #include #endif #include #include #include static int oid_v_forks[CTL_MAXNAME + 2]; static int oid_v_vforks[CTL_MAXNAME + 2]; static int oid_v_rforks[CTL_MAXNAME + 2]; static size_t oid_v_forks_len = sizeof(oid_v_forks); static size_t oid_v_vforks_len = sizeof(oid_v_vforks); static size_t oid_v_rforks_len = sizeof(oid_v_rforks); static int have_v_forks = 0; gboolean gkrellm_sys_proc_init(void) { static int oid_name2oid[2] = { 0, 3 }; static char *name = "vm.stats.vm.v_forks"; static char *vname = "vm.stats.vm.v_vforks"; static char *rname = "vm.stats.vm.v_rforks"; /* check if vm.stats.vm.v_forks is available */ if (sysctl(oid_name2oid, 2, oid_v_forks, &oid_v_forks_len, (void *)name, strlen(name)) < 0) return TRUE; if (sysctl(oid_name2oid, 2, oid_v_vforks, &oid_v_vforks_len, (void *)vname, strlen(vname)) < 0) return TRUE; if (sysctl(oid_name2oid, 2, oid_v_rforks, &oid_v_rforks_len, (void *)rname, strlen(rname)) < 0) return TRUE; oid_v_forks_len /= sizeof(int); oid_v_vforks_len /= sizeof(int); oid_v_rforks_len /= sizeof(int); ++have_v_forks; return TRUE; } void gkrellm_sys_proc_read_data(void) { static int oid_proc[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; double avenrun; static u_int n_processes, n_forks = 0, curpid = -1; u_int n_vforks, n_rforks; gint r_forks, r_vforks, r_rforks; size_t len; #ifdef HAVE_KVM_H gint nextpid, nforked; static struct nlist nl[] = { #define N_NEXTPID 0 { "_nextpid" }, { "" } }; #endif if (getloadavg(&avenrun, 1) <= 0) avenrun = 0; if (have_v_forks) { /* We don't want to just use sysctlbyname(). Because, * we call it so often. */ len = sizeof(n_forks); r_forks = sysctl(oid_v_forks, oid_v_forks_len, &n_forks, &len, NULL, 0); len = sizeof(n_vforks); r_vforks = sysctl(oid_v_vforks, oid_v_vforks_len, &n_vforks, &len, NULL, 0); len = sizeof(n_rforks); r_rforks = sysctl(oid_v_rforks, oid_v_rforks_len, &n_rforks, &len, NULL, 0); if (r_forks >= 0 && r_vforks >= 0 && r_rforks >= 0) n_forks = n_forks + n_vforks + n_rforks; } #ifdef HAVE_KVM_H else { /* workaround: Can I get total number of processes? */ if (kvmd != NULL) { if (nl[0].n_type == 0) kvm_nlist(kvmd, nl); if (nl[0].n_type != 0 && kvm_read(kvmd, nl[N_NEXTPID].n_value, (char *)&nextpid, sizeof(nextpid)) == sizeof(nextpid)) { if (curpid < 0) curpid = nextpid; if ((nforked = nextpid - curpid) < 0) n_forks += PID_MAX - 100; n_forks += nforked; curpid = nextpid; n_forks = n_forks; } } } #endif if (sysctl(oid_proc, 3, NULL, &len, NULL, 0) >= 0) n_processes = len / sizeof(struct kinfo_proc); gkrellm_proc_assign_data(n_processes, 0, n_forks, avenrun); } void gkrellm_sys_proc_read_users(void) { gint n_users; struct stat sb, s; gchar ttybuf[MAXPATHLEN]; FILE *ut; struct utmp utmp; static time_t utmp_mtime; if (stat(_PATH_UTMP, &s) != 0 || s.st_mtime == utmp_mtime) return; if ((ut = fopen(_PATH_UTMP, "r")) != NULL) { n_users = 0; while (fread(&utmp, sizeof(utmp), 1, ut)) { if (utmp.ut_name[0] == '\0') continue; (void)snprintf(ttybuf, sizeof(ttybuf), "%s/%s", _PATH_DEV, utmp.ut_line); /* corrupted record */ if (stat(ttybuf, &sb)) continue; ++n_users; } (void)fclose(ut); gkrellm_proc_assign_users(n_users); } utmp_mtime = s.st_mtime; } /* ===================================================================== */ /* Disk monitor interface */ #include #include #include io_iterator_t drivelist = 0; /* needs release */ mach_port_t masterPort = 0; static GList *disk_list; /* list of names */ gchar * gkrellm_sys_disk_name_from_device(gint device_number, gint unit_number, gint *order) { return NULL; /* Not implemented */ } gint gkrellm_sys_disk_order_from_name(gchar *name) { /* implement this if you want disk charts to show up in a particular | order in gkrellm. */ return -1; /* Not implemented, disks show up in same order as disk_list */ } void gkrellm_sys_disk_read_data(void) { io_registry_entry_t drive = 0; /* needs release */ UInt64 totalReadBytes = 0; UInt64 totalReadCount = 0; UInt64 totalWriteBytes = 0; UInt64 totalWriteCount = 0; kern_return_t status = 0; GList *list; list = disk_list; while ( (drive = IOIteratorNext(drivelist)) ) { CFNumberRef number = 0; /* don't release */ CFDictionaryRef properties = 0; /* needs release */ CFDictionaryRef statistics = 0; /* don't release */ UInt64 value = 0; /* Obtain the properties for this drive object */ status = IORegistryEntryCreateCFProperties (drive, (CFMutableDictionaryRef *) &properties, kCFAllocatorDefault, kNilOptions); if (properties) { /* Obtain the statistics from the drive properties */ statistics = (CFDictionaryRef) CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)); if (statistics) { /* Obtain the number of bytes read from the drive statistics */ number = (CFNumberRef) CFDictionaryGetValue (statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)); if (number) { status = CFNumberGetValue(number, kCFNumberSInt64Type, &value); totalReadBytes += value; } /* Obtain the number of reads from the drive statistics */ number = (CFNumberRef) CFDictionaryGetValue (statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)); if (number) { status = CFNumberGetValue(number, kCFNumberSInt64Type, &value); totalReadCount += value; } /* Obtain the number of writes from the drive statistics */ number = (CFNumberRef) CFDictionaryGetValue (statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)); if (number) { status = CFNumberGetValue(number, kCFNumberSInt64Type, &value); totalWriteCount += value; } /* Obtain the number of bytes written from the drive statistics */ number = (CFNumberRef) CFDictionaryGetValue (statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)); if (number) { status = CFNumberGetValue(number, kCFNumberSInt64Type, &value); totalWriteBytes += value; } /* Release resources */ CFRelease(properties); properties = 0; } } IOObjectRelease(drive); drive = 0; if (list) { gkrellm_disk_assign_data_by_name((gchar *) list->data, totalReadCount, totalWriteCount, FALSE); list = list->next; } } IOIteratorReset(drivelist); } gboolean gkrellm_sys_disk_init(void) { io_registry_entry_t drive = 0; /* needs release */ io_registry_entry_t child = 0; /* needs release */ /* get ports and services for drive stats */ /* Obtain the I/O Kit communication handle */ if (IOMasterPort(MACH_PORT_NULL, &masterPort)) return FALSE; /* Obtain the list of all drive objects */ if (IOServiceGetMatchingServices(masterPort, IOServiceMatching("IOBlockStorageDriver"), &drivelist)) return FALSE; while ( (drive = IOIteratorNext(drivelist)) ) { gchar * name = malloc(128); /* io_name_t is char[128] */ kern_return_t status = 0; int ptr = 0; if(!name) return FALSE; /* Obtain the properties for this drive object */ status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &child ); if(!status) status = IORegistryEntryGetName(child, name ); /* Convert spaces to underscores, for prefs safety */ if(!status) { for(ptr = 0; ptr < strlen(name); ptr++) { if(name[ptr] == ' ') name[ptr] = '_'; } disk_list = g_list_append(disk_list, name); } IOObjectRelease(drive); drive = 0; } IOIteratorReset(drivelist); return (disk_list != NULL) ? TRUE : FALSE; } #include "../inet.h" #include #include #include #include #ifdef INET6 #include #endif /* INET6 */ #include #include #include #include #include #include #include #include #define TCPSTATES #include #include #include #include #include #define warn(x...) fprintf(stderr,x) void gkrellm_sys_inet_read_tcp_data(void) { ActiveTCP tcp; const char *mibvar="net.inet.tcp.pcblist"; char *buf; struct tcpcb *tp = NULL; struct inpcb *inp; struct xinpgen *xig, *oxig; struct xsocket *so; size_t len=0; if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { if (errno != ENOENT) warn("sysctl: %s", mibvar); return; } if ((buf = malloc(len)) == 0) { warn("malloc %lu bytes", (u_long)len); return; } if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { warn("sysctl: %s", mibvar); free(buf); return; } /* * Bail-out to avoid logic error in the loop below when * there is in fact no more control block to process */ if (len <= sizeof(struct xinpgen)) { free(buf); return; } oxig = xig = (struct xinpgen *)buf; for (xig = (struct xinpgen *)((char *)xig + xig->xig_len); xig->xig_len > sizeof(struct xinpgen); xig = (struct xinpgen *)((char *)xig + xig->xig_len)) { tp = &((struct xtcpcb *)xig)->xt_tp; inp = &((struct xtcpcb *)xig)->xt_inp; so = &((struct xtcpcb *)xig)->xt_socket; if (so->xso_protocol != IPPROTO_TCP) continue; /* Ignore PCBs which were freed during copyout. */ if (inp->inp_gencnt > oxig->xig_gen) continue; if ((inp->inp_vflag & INP_IPV4) == 0 #ifdef INET6 && (inp->inp_vflag & INP_IPV6) == 0 #endif /* INET6 */ ) continue; /* * Local address is not an indication of listening socket or * server sockey but just rather the socket has been bound. * That why many UDP sockets were not displayed in the original code. */ if (tp->t_state <= TCPS_LISTEN){ continue; } if (inp->inp_vflag & INP_IPV4) { tcp.local_port=ntohs(inp->inp_lport); tcp.remote_addr.s_addr=(uint32_t)inp->inp_faddr.s_addr; tcp.remote_port=ntohs(inp->inp_fport); tcp.family=AF_INET; gkrellm_inet_log_tcp_port_data(&tcp); } #ifdef INET6 else if (inp->inp_vflag & INP_IPV6) { tcp.local_port=ntohs(inp->inp_lport); memcpy(&(tcp.remote_addr6),&(inp->in6p_faddr),sizeof(struct in6_addr)); tcp.remote_port=ntohs(inp->inp_fport); tcp.family=AF_INET6; gkrellm_inet_log_tcp_port_data(&tcp); } /* else nothing printed now */ #endif /* INET6 */ } free(buf); } gboolean gkrellm_sys_inet_init(void) { return TRUE; } /* ===================================================================== */ /* Memory/Swap monitor interface */ #include #include #include #include #include #include #include #include static guint64 swapin, swapout, swap_total, swap_used; void gkrellm_sys_mem_read_data(void) { static gint psize, pshift, first_time_done = 0; vm_statistics_data_t vm_info; mach_msg_type_number_t info_count; kern_return_t error; static DIR *dirp; struct dirent *dp; guint64 total, used, free, shared, buffers, cached; info_count = HOST_VM_INFO_COUNT; error = host_statistics (mach_host_self (), HOST_VM_INFO, (host_info_t)&vm_info, &info_count); if (error != KERN_SUCCESS) { mach_error("host_info", error); return; } if (pshift == 0) { for (psize = getpagesize(); psize > 1; psize >>= 1) pshift++; } used = (guint64)(vm_info.active_count) << pshift; free = (guint64)vm_info.free_count << pshift; total = (guint64)(vm_info.active_count + vm_info.inactive_count + vm_info.free_count + vm_info.wire_count) << pshift; /* Don't know how to get cached or buffers. */ buffers = (guint64) (vm_info.wire_count) << pshift; cached = (guint64) (vm_info.inactive_count) << pshift; /* shared 0 for now, shared is a PITA */ shared = 0; gkrellm_mem_assign_data(total, used, free, shared, buffers, cached); /* Swap is available at same time as mem, so grab values here. */ swapin = vm_info.pageins; swapout = vm_info.pageouts; swap_used = vm_info.pageouts << pshift; /* Figure out total swap. This adds up the size of the swapfiles */ if (!first_time_done) { dirp = opendir ("/private/var/vm"); if (!dirp) return; ++first_time_done; } swap_total = 0; while ((dp = readdir (dirp)) != NULL) { struct stat sb; char fname [MAXNAMLEN]; if (strncmp (dp->d_name, "swapfile", 8)) continue; strcpy (fname, "/private/var/vm/"); strcat (fname, dp->d_name); if (stat (fname, &sb) < 0) continue; swap_total += sb.st_size; } /* Save overhead, leave it open. where can we close it? */ rewinddir(dirp); /* closedir (dirp); */ } void gkrellm_sys_swap_read_data(void) { gkrellm_swap_assign_data(swap_total, swap_used, swapin, swapout); } gboolean gkrellm_sys_mem_init(void) { return TRUE; } /* ===================================================================== */ /* Battery monitor interface - not implemented */ void gkrellm_sys_battery_read_data(void) { // gkrellm_battery_assign_data(0, available, on_line, charging, // percent, time_left); } gboolean gkrellm_sys_battery_init(void) { return FALSE; } /* ===================================================================== */ /* Sensor monitor interface - not implemented */ gboolean gkrellm_sys_sensors_get_temperature(gchar *path, gint id, gint iodev, gint interface, gfloat *temp) { return FALSE; } gboolean gkrellm_sys_sensors_get_fan(gchar *path, gint id, gint iodev, gint interface, gfloat *fan) { return FALSE; } gboolean gkrellm_sys_sensors_get_voltage(gchar *path, gint id, gint iodev, gint interface, gfloat *volt) { return FALSE; } gboolean gkrellm_sys_sensors_init(void) { return FALSE; }