#define IOKIT 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <err.h>
#include <fcntl.h>
#include <errno.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <sys/param.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <ifaddrs.h>
#include <sadc.h>
extern int errno;
FILE *data_fp = (FILE *)0;
#define REVISION_HISTORY_DATE 20030718
struct record_hdr restart_record = { SAR_RESTART, REVISION_HISTORY_DATE, 0, 0 };
struct record_hdr timestamp_record = { SAR_TIMESTAMP, 1, 0, 0 };
struct record_hdr vmstat_record = {SAR_VMSTAT, 1, 1, 0 };
struct record_hdr cpu_record = {SAR_CPU, 1, 1, 0 };
struct record_hdr drivestats_record = {SAR_DRIVESTATS, 1, 0, 0 };
struct record_hdr drivepath_record = {SAR_DRIVEPATH, 1, 1, 0 };
struct record_hdr netstats_record = {SAR_NETSTATS, 1, 0, 0};
int t_interval = 0;
int n_samples = 1;
char *ofile = NULL;
int ofd;
static mach_port_t myHost;
static mach_port_t masterPort;
struct drivepath *dp_table = NULL;
int dp_count = 0;
struct netstats *ns_table = NULL;
int ns_count = 0;
static uid_t realuid;
int network_mode = 0;
static void exit_usage();
static void open_datafile(char *);
static void write_record_hdr(struct record_hdr *);
static void write_record_data(char *, int);
static void get_all_stats();
static void get_vmstat_sample();
static void get_drivestat_sample();
static int get_ndrives();
static int record_device(io_registry_entry_t, struct drivestats *, int ndrives);
static int check_device_path (char *name, char *path, int ndrives);
static void get_netstat_sample(int pppflag);
int
main(argc, argv)
int argc;
char *argv[];
{
char *p;
char ch;
if (geteuid() != 0)
{
fprintf(stderr, "sadc: must be setuid root or root");
exit(1);
}
realuid = getuid();
seteuid(realuid);
setvbuf(stdout, (char *)NULL, _IONBF, 0);
while ((ch=getopt(argc, argv, "m:")) != EOF) {
switch(ch) {
case 'm':
if (!strncmp(optarg, "PPP", 3))
network_mode |= NET_PPP_MODE;
break;
default:
exit_usage();
break;
}
}
argc -= optind;
if (argc > 0)
{
if (isdigit(*argv[optind]))
{
errno=0;
t_interval = strtol(argv[optind], &p, 0);
if (errno || (*p !='\0') || t_interval <= 0)
{
exit_usage();
}
optind++;
if ((argc < 2) || (!isdigit(*argv[optind]))) {
exit_usage();
}
errno=0;
n_samples = strtol(argv[optind], &p, 0);
if (errno || (*p != '\0') || n_samples <= 0)
{
exit_usage();
}
optind++;
if (argc == 3)
{
ofile = argv[optind];
}
}
else
{
ofile = argv[optind];
}
}
(void)open_datafile(ofile);
myHost = mach_host_self();
IOMasterPort(bootstrap_port, &masterPort);
restart_record.rec_timestamp = time((time_t *)0);
write_record_hdr(&restart_record);
get_all_stats();
sleep(t_interval);
if (n_samples > 0)
{
timestamp_record.rec_data = time((time_t *)0);
#if 0
struct tm *tm;
tm = gmtime(&(timestamp_record.rec_data));
fprintf(stderr, "timestamp=%ld\n", timestamp_record.rec_data);
fprintf(stderr, "GMTIME offset from UTC in seconds = %ld\n", tm->tm_gmtoff);
fprintf(stderr, "GMTIME secnds=%d, min=%d, hour=%d\n", tm->tm_sec, tm->tm_min, tm->tm_hour);
fprintf(stderr, "asctime = %s\n", asctime(tm));
tm=localtime(&(timestamp_record.rec_data));
fprintf(stderr, "LOCTIME offset from UTC in seconds = %ld\n",tm->tm_gmtoff);
fprintf(stderr, "LOCTIME secnds=%d, min=%d, hour=%d\n", tm->tm_sec, tm->tm_min, tm->tm_hour);
fprintf(stderr, "asctime = %s\n", asctime(tm));
#endif
write_record_hdr(×tamp_record);
get_all_stats();
}
while (n_samples)
{
sleep(t_interval);
timestamp_record.rec_timestamp = time((time_t *)0);
write_record_hdr(×tamp_record);
get_all_stats();
n_samples--;
}
exit(EXIT_SUCCESS);
}
static void
exit_usage()
{
fprintf(stderr, "/usr/lib/sa/sadc [-m {PPP}] [t n] [ofile]\n");
exit(EXIT_FAILURE);
}
static void
open_datafile(char *path)
{
if (path == NULL)
{
data_fp = stdout;
return;
}
else
data_fp = fopen(path, "w+");
if (!data_fp)
{
fprintf(stderr, "sadc: failed to open data file [%s]\n", path?path:"stdout");
exit_usage();
}
}
static void
write_record_hdr(hdr)
struct record_hdr *hdr;
{
errno = 0;
if (fwrite(hdr, sizeof(struct record_hdr), 1, data_fp) != 1)
{
fprintf(stderr, "sadc: write_record_hdr failed, errno=%d\n", errno);
exit(EXIT_FAILURE);
}
fflush(data_fp);
return;
}
static void
write_record_data(data, size)
char *data;
int size;
{
errno = 0;
if (fwrite(data, size, 1, data_fp) != 1)
{
fprintf(stderr, "sadc: write_record_data failed, errno=%d\n", errno);
exit(EXIT_FAILURE);
}
fflush(data_fp);
return;
}
static void
get_vmstat_sample()
{
struct vm_statistics stat;
kern_return_t error;
mach_msg_type_number_t count;
count = HOST_VM_INFO_COUNT;
error = host_statistics(myHost, HOST_VM_INFO, (host_info_t)&stat, &count);
if (error != KERN_SUCCESS) {
fprintf(stderr, "sadc: Error in vm host_statistics(): %s\n",
mach_error_string(error));
exit(2);
}
vmstat_record.rec_count = 1;
vmstat_record.rec_size = sizeof(vm_statistics_data_t);
write_record_hdr(&vmstat_record);
write_record_data((char *)&stat, sizeof(vm_statistics_data_t));
}
static void
get_cpu_sample()
{
host_cpu_load_info_data_t cpuload;
kern_return_t error;
mach_msg_type_number_t count;
count = HOST_CPU_LOAD_INFO_COUNT;
error = host_statistics(myHost, HOST_CPU_LOAD_INFO,(host_info_t)&cpuload, &count);
if (error != KERN_SUCCESS) {
fprintf(stderr, "sadc: Error in cpu host_statistics(): %s",
mach_error_string(error));
exit(2);
}
cpu_record.rec_count = 1;
cpu_record.rec_size = sizeof(host_cpu_load_info_data_t);
write_record_hdr(&cpu_record);
write_record_data((char *)&cpuload, sizeof(host_cpu_load_info_data_t));
}
static void
get_drivestat_sample()
{
io_registry_entry_t drive;
io_iterator_t drivelist;
CFMutableDictionaryRef match;
int ndrives;
int i = 0;
long bufsize = 0;
char *buf;
struct drivestats *dbuf;
kern_return_t status;
int error;
if ((ndrives = get_ndrives()) <= 0)
return;
bufsize = ndrives * sizeof(struct drivestats);
buf = (char *) malloc (bufsize);
dbuf = (struct drivestats *)buf;
if (buf)
bzero((char *)buf, bufsize);
else
return;
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
if (status != KERN_SUCCESS)
goto RETURN;
error = 1;
i = 0;
while ((drive = IOIteratorNext(drivelist)))
{
if (i < ndrives)
{
if (record_device(drive, &dbuf[i], ndrives))
{
error = 0;
i++;
}
}
else
{
IOObjectRelease(drive);
break;
}
IOObjectRelease(drive);
}
IOObjectRelease(drivelist);
if (! error)
{
drivestats_record.rec_count = i;
drivestats_record.rec_size = sizeof (struct drivestats);
write_record_hdr(&drivestats_record);
write_record_data((char *)buf, (i * sizeof(struct drivestats)));
}
RETURN:
if (buf)
free(buf);
return;
}
static int
record_device(io_registry_entry_t drive, struct drivestats* drivestat, int ndrives)
{
io_registry_entry_t parent;
CFDictionaryRef properties, statistics;
CFStringRef name;
CFNumberRef number;
UInt64 value;
kern_return_t status;
int retval = 0;
int drive_id;
io_string_t path;
char BSDName[MAXDRIVENAME + 1];
status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent);
if (status != KERN_SUCCESS)
{
return(retval);
}
if (IOObjectConformsTo(parent, "IOBlockStorageDriver"))
{
bzero(path, sizeof(io_string_t));
if (IORegistryEntryGetPath(drive, kIODeviceTreePlane, path) != KERN_SUCCESS)
{
if(IORegistryEntryGetPath(drive, kIOServicePlane, path) != KERN_SUCCESS)
goto RETURN;
}
retval++;
status = IORegistryEntryCreateCFProperties(drive,
(CFMutableDictionaryRef *)&properties,
kCFAllocatorDefault,
kNilOptions);
if (status != KERN_SUCCESS)
{
goto RETURN;
}
bzero(BSDName, MAXDRIVENAME+1);
name = (CFStringRef)CFDictionaryGetValue(properties,
CFSTR(kIOBSDNameKey));
if (name) {
CFStringGetCString(name, BSDName,
MAXDRIVENAME, CFStringGetSystemEncoding());
retval++;
}
number = (CFNumberRef)CFDictionaryGetValue(properties,
CFSTR(kIOMediaPreferredBlockSizeKey));
if (number != 0) {
CFNumberGetValue(number,
kCFNumberSInt64Type, &value);
drivestat->blocksize = value;
retval++;
}
CFRelease(properties);
}
else
goto RETURN;
if (retval != 3)
{
retval = FALSE;
goto RETURN;
}
drive_id = check_device_path (BSDName, path, ndrives);
if (drive_id == -1)
{
retval = FALSE;
goto RETURN;
}
else
drivestat->drivepath_id = drive_id;
status = IORegistryEntryCreateCFProperties(parent,
(CFMutableDictionaryRef *)&properties,
kCFAllocatorDefault,
kNilOptions);
if (status != KERN_SUCCESS)
{
goto RETURN;
}
statistics
= (CFDictionaryRef)CFDictionaryGetValue(properties,
CFSTR(kIOBlockStorageDriverStatisticsKey));
if (statistics != 0)
{
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsReadsKey));
if (number != 0) {
CFNumberGetValue(number,
kCFNumberSInt64Type, &value);
drivestat->Reads = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->BytesRead = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsWritesKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->Writes = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->BytesWritten = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->LatentReadTime = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->LatentWriteTime = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsReadErrorsKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->ReadErrors = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsWriteErrorsKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->WriteErrors = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsReadRetriesKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->ReadRetries = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsWriteRetriesKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->WriteRetries = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->TotalReadTime = value;
}
number =
(CFNumberRef)CFDictionaryGetValue(statistics,
CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey));
if (number != 0) {
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
drivestat->TotalWriteTime = value;
}
CFRelease(properties);
}
RETURN:
IOObjectRelease(parent);
return(retval);
}
static int
get_ndrives(void)
{
io_iterator_t drivelist;
io_registry_entry_t drive;
io_registry_entry_t parent;
CFMutableDictionaryRef match;
int error, ndrives;
kern_return_t status;
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
if (status != KERN_SUCCESS)
return(0);
error = 1;
ndrives = 0;
while ((drive = IOIteratorNext(drivelist)))
{
status = IORegistryEntryGetParentEntry(drive,
kIOServicePlane, &parent);
if (status != KERN_SUCCESS)
{
IOObjectRelease(drive);
continue;
}
if (IOObjectConformsTo(parent, "IOBlockStorageDriver"))
{
error = 0;
ndrives++;
}
IOObjectRelease(parent);
IOObjectRelease(drive);
}
IOObjectRelease(drivelist);
return(ndrives);
}
void
get_all_stats()
{
get_drivestat_sample();
get_netstat_sample(network_mode);
get_vmstat_sample();
get_cpu_sample();
}
static int
check_device_path (char *name, char *path, int ndrives)
{
int i;
int index;
int n;
if (dp_table == NULL)
{
dp_table = (struct drivepath *)malloc (ndrives * sizeof(struct drivepath));
if (dp_table == NULL)
return(-1);
else
{
bzero(dp_table, (ndrives * sizeof(struct drivepath)));
dp_count = ndrives;
drivepath_record.rec_size = sizeof(struct drivepath);
}
}
for (i=0; i < dp_count; i++)
{
if (dp_table[i].state == DPSTATE_UNINITIALIZED)
{
index = i;
goto NEW_ENTRY;
}
else if (!strcmp (dp_table[i].ioreg_path, path))
{
if (!strcmp(dp_table[i].BSDName, name))
{
return(i);
}
else
{
bzero((char *)dp_table[i].BSDName, MAXDRIVENAME+1);
dp_table[i].drivepath_id = i;
dp_table[i].state = DPSTATE_CHANGED;
strcpy(dp_table[i].BSDName, name);
write_record_hdr(&drivepath_record);
write_record_data((char *)&dp_table[i], sizeof(struct drivepath));
return(i);
}
}
}
n = dp_count * 2;
dp_table = (struct drivepath *)realloc(dp_table, n * sizeof(struct drivepath));
bzero(&dp_table[dp_count], dp_count * sizeof(struct drivepath));
index = dp_count;
dp_count = n;
NEW_ENTRY:
dp_table[index].drivepath_id = index;
dp_table[index].state = DPSTATE_NEW;
strcpy(dp_table[index].BSDName, name);
strcpy(dp_table[index].ioreg_path, path);
write_record_hdr(&drivepath_record);
write_record_data((char *)&dp_table[index], sizeof(struct drivepath));
return(index);
}
static void
get_netstat_sample(int mode)
{
int n;
int ns_index = 0;
char tname[MAX_TNAME_SIZE + 1];
char name[MAX_TNAME_UNIT_SIZE + 1];
struct ifaddrs *ifa_list, *ifa;
ns_count = 100;
ns_table = (struct netstats *) malloc(ns_count * sizeof (struct netstats));
if (ns_table == NULL)
{
fprintf(stderr, "sadc: malloc netstat table failed\n");
return;
}
bzero(ns_table, ns_count * sizeof(struct netstats));
if (getifaddrs(&ifa_list) == -1)
return;
for (ifa = ifa_list; ifa; ifa = ifa->ifa_next)
{
struct if_data *if_data = (struct if_data *)ifa->ifa_data;
if (AF_LINK != ifa->ifa_addr->sa_family)
continue;
if (ifa->ifa_data == 0)
continue;
tname[MAX_TNAME_SIZE] = '\0';
if (!(network_mode & NET_PPP_MODE))
{
if(!strncmp(ifa->ifa_name, "ppp", 3))
continue;
}
snprintf(name, MAX_TNAME_UNIT_SIZE, "%s", ifa->ifa_name);
name[MAX_TNAME_UNIT_SIZE] = '\0';
if (ns_index == ns_count)
{
n = ns_count * 2;
ns_table = (struct netstats *)realloc(ns_table, n * sizeof(struct netstats));
bzero(&ns_table[ns_count], ns_count * sizeof(struct netstats));
ns_count = n;
}
ns_table[ns_index].gen_counter = 0;
strncpy(ns_table[ns_index].tname_unit, name, MAX_TNAME_UNIT_SIZE);
ns_table[ns_index].tname_unit[MAX_TNAME_UNIT_SIZE] = '\0';
ns_table[ns_index].net_ipackets = if_data->ifi_ipackets;
ns_table[ns_index].net_ierrors = if_data->ifi_ierrors;
ns_table[ns_index].net_opackets = if_data->ifi_opackets;
ns_table[ns_index].net_oerrors = if_data->ifi_oerrors;
ns_table[ns_index].net_collisions = if_data->ifi_collisions;
ns_table[ns_index].net_ibytes = if_data->ifi_ibytes;
ns_table[ns_index].net_obytes = if_data->ifi_obytes;
ns_table[ns_index].net_imcasts = if_data->ifi_imcasts;
ns_table[ns_index].net_omcasts = if_data->ifi_omcasts;
ns_table[ns_index].net_drops = if_data->ifi_iqdrops;
ns_index++;
}
netstats_record.rec_count = ns_index;
netstats_record.rec_size = sizeof(struct netstats);
write_record_hdr(&netstats_record);
write_record_data((char *)ns_table, (ns_index * sizeof(struct netstats)));
return;
}