// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see .
// XIdleTime:
// Copyright (C) 2011 Universidade Federal de Campina Grande
// Initial version: Magnus Henoch
// Contributors: Danny Kukawka, Eivind Magnus Hvidevold
// LGPL Version of xidletime: https://github.com/rodrigods/xidletime
// There is a reason that having a file called "cpp.h" that includes config.h
// and some of the C++ header files is bad. That reason is because there are
// #defines that alter the behiour of the standard C and C++ headers. In
// this case we need to use the "small files" environment on some unix
// systems. That can't be done if we include "cpp.h"
#include "version.h" // version numbers from autoconf
#include "cpp.h"
#include "config.h"
#if !defined(_WIN32) || defined(__CYGWIN32__)
// Access to binary files in /proc filesystem doesn't work in the 64bit
// files environment on some systems. None of the functions here need
// 64bit file functions, so we'll undefine _FILE_OFFSET_BITS and _LARGE_FILES.
#undef _FILE_OFFSET_BITS
#undef _LARGE_FILES
#undef _LARGEFILE_SOURCE
#undef _LARGEFILE64_SOURCE
#include
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#if HAVE_SYS_TYPES_H
#include
#endif
#if HAVE_SYS_MOUNT_H
#include
#endif
#if HAVE_SYS_VFS_H
#include
#endif
#if HAVE_SYS_VMMETER_H
#include
#endif
#include
#if HAVE_SYS_SWAP_H
#ifdef ANDROID
#include
#else
#include
#endif
#endif
#if HAVE_SYS_SYSCTL_H
#include
#endif
#if HAVE_SYS_SYSTEMINFO_H
#include
#endif
#if HAVE_SYS_UTSNAME_H
#include
#endif
#if HAVE_UNISTD_H
#include
#endif
#if HAVE_UTMP_H
#include
#endif
#if HAVE_NETINET_IN_H
#include
#endif
#if HAVE_SYS_SENSORS_H
#include
#endif
#ifdef __EMX__
#define INCL_DOSMISC
#include
#include "win/opt_x86.h"
#endif
#include "error_numbers.h"
#include "common_defs.h"
#include "filesys.h"
#include "str_util.h"
#include "str_replace.h"
#include "util.h"
#include "client_state.h"
#include "client_types.h"
#include "client_msgs.h"
#include "hostinfo_network.h"
#include "hostinfo.h"
using std::string;
#ifdef __APPLE__
#include
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#ifdef __cplusplus
} // extern "C"
#include
#endif
mach_port_t gEventHandle = NULL;
#endif // __APPLE__
#ifdef _HPUX_SOURCE
#include
#endif
// Tru64 UNIX.
// 2005-12-26 SMS.
#ifdef __osf__
#include
#include
#include
#endif
#ifdef __HAIKU__
#include
#endif
// Some OS define _SC_PAGE_SIZE instead of _SC_PAGESIZE
#if defined(_SC_PAGE_SIZE) && !defined(_SC_PAGESIZE)
#define _SC_PAGESIZE _SC_PAGE_SIZE
#endif
#if HAVE_DPMS
#include
#include
#endif
#if HAVE_XSS
#include
#include
#endif
// The following is intended to be true both on Linux
// and Debian GNU/kFreeBSD (see trac #521)
//
#define LINUX_LIKE_SYSTEM (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(__HAIKU__)
// Returns the offset between LOCAL STANDARD TIME and UTC.
// LOCAL_STANDARD_TIME = UTC_TIME + get_timezone().
//
int get_timezone() {
tzset();
// TODO: take daylight savings time into account
#if HAVE_STRUCT_TM_TM_ZONE
time_t cur_time;
struct tm *time_data;
cur_time = time(NULL);
time_data = localtime( &cur_time );
// tm_gmtoff is already adjusted for daylight savings time
return time_data->tm_gmtoff;
#elif LINUX_LIKE_SYSTEM
return -1*(__timezone);
#elif defined(__CYGWIN32__)
return -1*(_timezone);
#elif defined(unix)
return -1*timezone;
#elif HAVE_TZNAME
return -1*timezone;
#else
#error timezone
#endif
return 0;
}
// Returns true if the host is currently running off battery power
// If you can't figure out, return false
//
bool HOST_INFO::host_is_running_on_batteries() {
#if defined(__APPLE__)
CFDictionaryRef pSource = NULL;
CFStringRef psState;
int i;
bool retval = false;
CFTypeRef blob = IOPSCopyPowerSourcesInfo();
CFArrayRef list = IOPSCopyPowerSourcesList(blob);
for (i=0; i ==buf necessary */
if ((strstr(buf, "model\t\t: ") == buf) && model<0) {
model = atoi(buf+strlen("model\t\t: "));
}
/* ia64 */
if (strstr(buf, "model : ") && model<0) {
model = atoi(buf+strlen("model : "));
}
#endif
if (strstr(buf, "stepping\t: ") && stepping<0) {
stepping = atoi(buf+strlen("stepping\t: "));
}
#ifdef __hppa__
bool icache_found=false,dcache_found=false;
if (!icache_found && strstr(buf, "I-cache\t\t: ")) {
icache_found = true;
sscanf(buf, "I-cache\t\t: %d", &n);
host.m_cache += n*1024;
}
if (!dcache_found && strstr(buf, "D-cache\t\t: ")) {
dcache_found = true;
sscanf(buf, "D-cache\t\t: %d", &n);
host.m_cache += n*1024;
}
#elif __powerpc__
if (!cache_found && strstr(buf, "L2 cache\t: ")) {
cache_found = true;
sscanf(buf, "L2 cache\t: %d", &n);
host.m_cache = n*1024;
}
#else
if (!cache_found && (strstr(buf, "cache size\t: ") == buf)) {
cache_found = true;
sscanf(buf, "cache size\t: %d", &n);
host.m_cache = n*1024;
}
#endif
if (!features_found) {
// Some versions of the linux kernel call them flags,
// others call them features, so look for both.
//
if ((strstr(buf, "flags\t\t: ") == buf)) {
strlcpy(features, strchr(buf, ':') + 2, sizeof(features));
} else if ((strstr(buf, "features\t\t: ") == buf)) {
strlcpy(features, strchr(buf, ':') + 2, sizeof(features));
} else if ((strstr(buf, "features : ") == buf)) { /* ia64 */
strlcpy(features, strchr(buf, ':') + 2, sizeof(features));
} else if ((strstr(buf, "Features\t: ") == buf)) { /* arm */
strlcpy(features, strchr(buf, ':') + 2, sizeof(features));
}
if (strlen(features)) {
features_found = true;
}
}
}
safe_strcpy(model_buf, host.p_model);
if (family>=0 || model>=0 || stepping>0) {
strcat(model_buf, " [");
if (family>=0) {
sprintf(buf, "Family %d ", family);
strcat(model_buf, buf);
}
if (model>=0) {
sprintf(buf, "Model %d ", model);
strcat(model_buf, buf);
}
if (stepping>=0) {
sprintf(buf, "Stepping %d", stepping);
strcat(model_buf, buf);
}
strcat(model_buf, "]");
}
if (strlen(features)) {
safe_strcpy(host.p_features, features);
}
safe_strcpy(host.p_model, model_buf);
fclose(f);
}
#endif // LINUX_LIKE_SYSTEM
#ifdef __FreeBSD__
#if defined(__i386__) || defined(__amd64__)
#include
#include
#include
void use_cpuid(HOST_INFO& host) {
u_int p[4];
int hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, has3DNowExt;
char capabilities[256];
hasMMX = hasSSE = hasSSE2 = hasSSE3 = has3DNow = has3DNowExt = 0;
do_cpuid(0x0, p);
if (p[0] >= 0x1) {
do_cpuid(0x1, p);
hasMMX = (p[3] & (1 << 23 )) >> 23; // 0x0800000
hasSSE = (p[3] & (1 << 25 )) >> 25; // 0x2000000
hasSSE2 = (p[3] & (1 << 26 )) >> 26; // 0x4000000
hasSSE3 = (p[2] & (1 << 0 )) >> 0;
}
do_cpuid(0x80000000, p);
if (p[0]>=0x80000001) {
do_cpuid(0x80000001, p);
hasMMX |= (p[3] & (1 << 23 )) >> 23; // 0x0800000
has3DNow = (p[3] & (1 << 31 )) >> 31; //0x80000000
has3DNowExt = (p[3] & (1 << 30 )) >> 30;
}
capabilities[0] = '\0';
if (hasSSE) strcat(capabilities, "sse ");
if (hasSSE2) strcat(capabilities, "sse2 ");
if (hasSSE3) strcat(capabilities, "pni ");
if (has3DNow) strcat(capabilities, "3dnow ");
if (has3DNowExt) strcat(capabilities, "3dnowext ");
if (hasMMX) strcat(capabilities, "mmx ");
strip_whitespace(capabilities);
char buf[1024];
snprintf(buf, sizeof(buf), "%s [] [%s]",
host.p_model, capabilities
);
strlcat(host.p_model, buf, sizeof(host.p_model));
}
#endif
#endif
#ifdef __APPLE__
static void get_cpu_info_maxosx(HOST_INFO& host) {
int p_model_size = sizeof(host.p_model);
size_t len;
#if defined(__i386__) || defined(__x86_64__)
char brand_string[256];
int family, stepping, model;
len = sizeof(host.p_vendor);
sysctlbyname("machdep.cpu.vendor", host.p_vendor, &len, NULL, 0);
len = sizeof(brand_string);
sysctlbyname("machdep.cpu.brand_string", brand_string, &len, NULL, 0);
len = sizeof(family);
sysctlbyname("machdep.cpu.family", &family, &len, NULL, 0);
len = sizeof(model);
sysctlbyname("machdep.cpu.model", &model, &len, NULL, 0);
len = sizeof(stepping);
sysctlbyname("machdep.cpu.stepping", &stepping, &len, NULL, 0);
len = sizeof(host.p_features);
sysctlbyname("machdep.cpu.features", host.p_features, &len, NULL, 0);
snprintf(
host.p_model, sizeof(host.p_model),
"%s [x86 Family %d Model %d Stepping %d]",
brand_string, family, model, stepping
);
#else // PowerPC
char model[256];
int response = 0;
int retval;
len = sizeof(response);
retval = sysctlbyname("hw.optional.altivec", &response, &len, NULL, 0);
if (response && (!retval)) {
safe_strcpy(host.p_features, "AltiVec");
}
len = sizeof(model);
sysctlbyname("hw.model", model, &len, NULL, 0);
safe_strcpy(host.p_vendor, "Power Macintosh");
snprintf(host.p_model, p_model_size, "%s [%s Model %s] [%s]", host.p_vendor, host.p_vendor, model, host.p_features);
#endif
host.p_model[p_model_size-1] = 0;
char *in = host.p_model + 1;
char *out = in;
// Strip out runs of multiple spaces
do {
if ((!isspace(*(in-1))) || (!isspace(*in))) {
*out++ = *in;
}
} while (*in++);
// This returns an Apple hardware model designation such as "MacPro3,1".
// One source for converting this to a common model name is:
//
len = sizeof(host.product_name);
sysctlbyname("hw.model", host.product_name, &len, NULL, 0);
}
#endif
#ifdef __HAIKU__
static void get_cpu_info_haiku(HOST_INFO& host) {
/* This function has been adapted from Haiku's sysinfo.c
* which spits out a bunch of formatted CPU info to
* the terminal, it was easier to copy some of the logic
* here.
*/
system_info sys_info;
int32 cpu = 0; // always use first CPU for now
cpuid_info cpuInfo;
int32 maxStandardFunction;
int32 maxExtendedFunction = 0;
char brand_string[256];
if (get_system_info(&sys_info) != B_OK) {
msg_printf(NULL, MSG_INTERNAL_ERROR, "Error getting Haiku system information!\n");
return;
}
if (get_cpuid(&cpuInfo, 0, cpu) != B_OK) {
// this CPU doesn't support cpuid
return;
}
snprintf(host.p_vendor, sizeof(host.p_vendor), "%.12s",
cpuInfo.eax_0.vendor_id);
maxStandardFunction = cpuInfo.eax_0.max_eax;
if (maxStandardFunction >= 500)
maxStandardFunction = 0; /* old Pentium sample chips has
cpu signature here */
/* Extended cpuid */
get_cpuid(&cpuInfo, 0x80000000, cpu);
// extended cpuid is only supported if max_eax is greater
// than the service id
if (cpuInfo.eax_0.max_eax > 0x80000000)
maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff;
if (maxExtendedFunction >=4 ) {
char buffer[49];
char *name = buffer;
int32 i;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 3; i++) {
cpuid_info nameInfo;
get_cpuid(&nameInfo, 0x80000002 + i, cpu);
memcpy(name, &nameInfo.regs.eax, 4);
memcpy(name + 4, &nameInfo.regs.ebx, 4);
memcpy(name + 8, &nameInfo.regs.ecx, 4);
memcpy(name + 12, &nameInfo.regs.edx, 4);
name += 16;
}
// cut off leading spaces (names are right aligned)
name = buffer;
while (name[0] == ' ')
name++;
// the BIOS may not have set the processor name
if (name[0]) {
strlcpy(brand_string, name, sizeof(brand_string));
} else {
// Intel CPUs don't seem to have the genuine vendor field
snprintf(brand_string, sizeof(brand_string), "%.12s",
cpuInfo.eax_0.vendor_id);
}
}
get_cpuid(&cpuInfo, 1, cpu);
int family, stepping, model;
family = cpuInfo.eax_1.family + (cpuInfo.eax_1.family == 0xf ?
cpuInfo.eax_1.extended_family : 0);
// model calculation is different for AMD and INTEL
if ((sys_info.cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86) {
model = cpuInfo.eax_1.model + (cpuInfo.eax_1.model == 0xf ?
cpuInfo.eax_1.extended_model << 4 : 0);
} else if ((sys_info.cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) {
model = cpuInfo.eax_1.model + ((cpuInfo.eax_1.family == 0xf ||
cpuInfo.eax_1.family == 0x6) ? cpuInfo.eax_1.extended_model << 4 : 0);
}
stepping = cpuInfo.eax_1.stepping;
snprintf(host.p_model, sizeof(host.p_model),
"%s [Family %u Model %u Stepping %u]", brand_string, family, model,
stepping);
static const char *kFeatures[32] = {
"fpu", "vme", "de", "pse",
"tsc", "msr", "pae", "mce",
"cx8", "apic", NULL, "sep",
"mtrr", "pge", "mca", "cmov",
"pat", "pse36", "psnum", "clflush",
NULL, "ds", "acpi", "mmx",
"fxsr", "sse", "sse2", "ss",
"htt", "tm", "ia64", "pbe",
};
int32 found = 0;
int32 i;
char buf[12];
for (i = 0; i < 32; i++) {
if ((cpuInfo.eax_1.features & (1UL << i)) && kFeatures[i] != NULL) {
snprintf(buf, sizeof(buf), "%s%s", found == 0 ? "" : " ",
kFeatures[i]);
strlcat(host.p_features, buf, sizeof(host.p_features));
found++;
}
}
if (maxStandardFunction >= 1) {
/* Extended features */
static const char *kFeatures2[32] = {
"sse3", NULL, "dtes64", "monitor", "ds-cpl", "vmx", "smx" "est",
"tm2", "ssse3", "cnxt-id", NULL, NULL, "cx16", "xtpr", "pdcm",
NULL, NULL, "dca", "sse4.1", "sse4.2", "x2apic", "movbe", "popcnt",
NULL, NULL, "xsave", "osxsave", NULL, NULL, NULL, NULL
};
for (i = 0; i < 32; i++) {
if ((cpuInfo.eax_1.extended_features & (1UL << i)) &&
kFeatures2[i] != NULL) {
snprintf(buf, sizeof(buf), "%s%s", found == 0 ? "" : " ",
kFeatures2[i]);
strlcat(host.p_features, buf, sizeof(host.p_features));
found++;
}
}
}
//TODO: there are additional AMD features that probably need to be queried
}
#endif
// Note: this may also work on other UNIX-like systems in addition to Macintosh
#ifdef __APPLE__
#include
#include
#include
// detect the network usage totals for the host.
//
int get_network_usage_totals(
unsigned int& total_received, unsigned int& total_sent
) {
static size_t sysctlBufferSize = 0;
static uint8_t *sysctlBuffer = NULL;
int mib[] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
struct if_msghdr *ifmsg;
size_t currentSize = 0;
total_received = 0;
total_sent = 0;
if (sysctl(mib, 6, NULL, ¤tSize, NULL, 0) != 0) return errno;
if (!sysctlBuffer || (currentSize > sysctlBufferSize)) {
if (sysctlBuffer) free(sysctlBuffer);
sysctlBufferSize = 0;
sysctlBuffer = (uint8_t*)malloc(currentSize);
if (!sysctlBuffer) return ERR_MALLOC;
sysctlBufferSize = currentSize;
}
// Read in new data
if (sysctl(mib, 6, sysctlBuffer, ¤tSize, NULL, 0) != 0) return errno;
// Walk through the reply
uint8_t *currentData = sysctlBuffer;
uint8_t *currentDataEnd = sysctlBuffer + currentSize;
while (currentData < currentDataEnd) {
// Expecting interface data
ifmsg = (struct if_msghdr *)currentData;
if (ifmsg->ifm_type != RTM_IFINFO) {
currentData += ifmsg->ifm_msglen;
continue;
}
// Must not be loopback
if (ifmsg->ifm_flags & IFF_LOOPBACK) {
currentData += ifmsg->ifm_msglen;
continue;
}
// Only look at link layer items
struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifmsg + 1);
if (sdl->sdl_family != AF_LINK) {
currentData += ifmsg->ifm_msglen;
continue;
}
#if 0 // Use this code if we want only Ethernet interface 0
if (!strcmp(sdl->sdl_data, "en0")) {
total_received = ifmsg->ifm_data.ifi_ibytes;
total_sent = ifmsg->ifm_data.ifi_obytes;
return 0;
}
#else // Use this code if we want total of all non-loopback interfaces
total_received += ifmsg->ifm_data.ifi_ibytes;
total_sent += ifmsg->ifm_data.ifi_obytes;
#endif
}
return 0;
}
#if defined(__i386__) || defined(__x86_64__)
// Code to get maximum CPU temperature (Apple Intel only)
// Adapted from Apple System Management Control (SMC) Tool under the GPL
#define KERNEL_INDEX_SMC 2
#define SMC_CMD_READ_BYTES 5
#define SMC_CMD_READ_KEYINFO 9
typedef struct {
char major;
char minor;
char build;
char reserved[1];
UInt16 release;
} SMCKeyData_vers_t;
typedef struct {
UInt16 version;
UInt16 length;
UInt32 cpuPLimit;
UInt32 gpuPLimit;
UInt32 memPLimit;
} SMCKeyData_pLimitData_t;
typedef struct {
UInt32 dataSize;
UInt32 dataType;
char dataAttributes;
} SMCKeyData_keyInfo_t;
typedef char SMCBytes_t[32];
typedef struct {
UInt32 key;
SMCKeyData_vers_t vers;
SMCKeyData_pLimitData_t pLimitData;
SMCKeyData_keyInfo_t keyInfo;
char result;
char status;
char data8;
UInt32 data32;
SMCBytes_t bytes;
} SMCKeyData_t;
static io_connect_t conn;
kern_return_t SMCOpen() {
kern_return_t result;
mach_port_t masterPort;
io_iterator_t iterator;
io_object_t device;
result = IOMasterPort(MACH_PORT_NULL, &masterPort);
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
if (result != kIOReturnSuccess) {
return result;
}
device = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if (device == 0) {
return result;
}
result = IOServiceOpen(device, mach_task_self(), 0, &conn);
IOObjectRelease(device);
if (result != kIOReturnSuccess) {
return result;
}
return kIOReturnSuccess;
}
kern_return_t SMCClose() {
if (conn) {
return IOServiceClose(conn);
}
return kIOReturnSuccess;
}
kern_return_t SMCReadKey(UInt32 key, SMCBytes_t val) {
kern_return_t result;
SMCKeyData_t inputStructure;
SMCKeyData_t outputStructure;
size_t structureOutputSize = 0;
memset(&inputStructure, 0, sizeof(inputStructure));
memset(&outputStructure, 0, sizeof(outputStructure));
memset(val, 0, sizeof(SMCBytes_t));
inputStructure.key = key;
inputStructure.data8 = SMC_CMD_READ_KEYINFO;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
result = IOConnectCallStructMethod(conn,
KERNEL_INDEX_SMC,
&inputStructure,
sizeof(inputStructure),
&outputStructure,
&structureOutputSize
);
#else
result = IOConnectMethodStructureIStructureO(conn,
KERNEL_INDEX_SMC,
sizeof(inputStructure),
&structureOutputSize,
&inputStructure,
&outputStructure
);
#endif
if (result != kIOReturnSuccess) {
return result;
}
inputStructure.keyInfo.dataSize = outputStructure.keyInfo.dataSize;
inputStructure.data8 = SMC_CMD_READ_BYTES;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
result = IOConnectCallStructMethod(conn,
KERNEL_INDEX_SMC,
&inputStructure,
sizeof(inputStructure),
&outputStructure,
&structureOutputSize
);
#else
result = IOConnectMethodStructureIStructureO(conn,
KERNEL_INDEX_SMC,
sizeof(inputStructure),
&structureOutputSize,
&inputStructure,
&outputStructure
);
#endif
if (result != kIOReturnSuccess) {
return result;
}
memcpy(val, outputStructure.bytes, sizeof(outputStructure.bytes));
return kIOReturnSuccess;
}
// Check up to 10 die temperatures (TC0D, TC1D, etc.) and
// 10 heatsink temperatures (TCAH, TCBH, etc.)
// Returns the highest current CPU temperature as degrees Celsius.
// Returns zero if it fails (or on a PowerPC Mac).
int get_max_cpu_temperature() {
kern_return_t result;
int maxTemp = 0, thisTemp, i;
union tempKey {
UInt32 word;
char bytes[4];
};
tempKey key;
SMCBytes_t val;
static bool skip[20];
// open connection to SMC kext if this is the first time
if (!conn) {
result = SMCOpen();
if (result != kIOReturnSuccess) {
return 0;
}
}
for (i=0; i<20; ++i) {
if (skip[i]) continue;
if (i < 10) {
key.word = 'TC0D';
key.bytes[1] += i; // TC0D, TC1D, TC2D, etc.
} else {
key.word = 'TCAH';
key.bytes[1] += (i - 10); // TCAH, TCBH, TCCH, etc.
}
result = SMCReadKey(key.word, val);
if (result != kIOReturnSuccess) {
skip[i] = true;
continue;
}
if (val[0] < 1) {
skip[i] = true;
continue;
}
thisTemp = val[0];
if (val[1] & 0x80) ++thisTemp;
if (thisTemp > maxTemp) {
maxTemp = thisTemp;
}
}
return maxTemp;
}
#else // PowerPC
int get_max_cpu_temperature() {
return 0;
}
#endif
// Is this a dual GPU MacBook with automatic GPU switching?
bool isDualGPUMacBook() {
io_service_t service = IO_OBJECT_NULL;
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleGraphicsControl"));
return (service != IO_OBJECT_NULL);
}
#endif // __APPLE__
// see if Virtualbox is installed
//
int HOST_INFO::get_virtualbox_version() {
char path[MAXPATHLEN];
char cmd [MAXPATHLEN+35];
char *newlinePtr;
FILE* fd;
#if LINUX_LIKE_SYSTEM
safe_strcpy(path, "/usr/lib/virtualbox/VBoxManage");
#elif defined( __APPLE__)
FSRef theFSRef;
OSStatus status = noErr;
// First try to locate the VirtualBox application by Bundle ID and Creator Code
status = LSFindApplicationForInfo(
'VBOX', CFSTR("org.virtualbox.app.VirtualBox"), NULL, &theFSRef, NULL
);
if (status == noErr) {
status = FSRefMakePath(&theFSRef, (unsigned char *)path, sizeof(path));
}
// If that failed, try its default location
if (status != noErr) {
strcpy(path, "/Applications/VirtualBox.app");
}
#endif
if (boinc_file_exists(path)) {
#if LINUX_LIKE_SYSTEM
if (access(path, X_OK)) {
return 0;
}
safe_strcpy(cmd, path);
safe_strcat(cmd, " --version");
#elif defined( __APPLE__)
safe_strcpy(cmd, "defaults read ");
safe_strcat(cmd, path);
safe_strcat(cmd, "/Contents/Info CFBundleShortVersionString");
#endif
fd = popen(cmd, "r");
if (fd) {
if (fgets(virtualbox_version, sizeof(virtualbox_version), fd)) {
newlinePtr = strchr(virtualbox_version, '\n');
if (newlinePtr) *newlinePtr = '\0';
newlinePtr = strchr(virtualbox_version, '\r');
if (newlinePtr) *newlinePtr = '\0';
}
pclose(fd);
}
}
return 0;
}
// Rules:
// - Keep code in the right place
// - only one level of #if
//
int HOST_INFO::get_host_info() {
int retval = get_filesystem_info(d_total, d_free);
if (retval) {
msg_printf(0, MSG_INTERNAL_ERROR,
"get_filesystem_info() failed: %s", boincerror(retval)
);
}
get_virtualbox_version();
///////////// p_vendor, p_model, p_features /////////////////
#if LINUX_LIKE_SYSTEM
parse_cpuinfo_linux(*this);
#elif defined( __APPLE__)
int mib[2];
size_t len;
get_cpu_info_maxosx(*this);
#elif defined(__EMX__)
CPU_INFO_t cpuInfo;
strlcpy( p_vendor, cpuInfo.vendor.company, sizeof(p_vendor));
strlcpy( p_model, cpuInfo.name.fromID, sizeof(p_model));
#elif defined(__HAIKU__)
get_cpu_info_haiku(*this);
#elif HAVE_SYS_SYSCTL_H
int mib[2];
size_t len;
// Get machine
#ifdef IRIX
mib[0] = 0;
mib[1] = 1;
#else
mib[0] = CTL_HW;
mib[1] = HW_MACHINE;
#endif
len = sizeof(p_vendor);
sysctl(mib, 2, &p_vendor, &len, NULL, 0);
// Get model
#ifdef IRIX
mib[0] = 0;
mib[1] = 1;
#else
mib[0] = CTL_HW;
mib[1] = HW_MODEL;
#endif
len = sizeof(p_model);
sysctl(mib, 2, &p_model, &len, NULL, 0);
#elif defined(__osf__)
// Tru64 UNIX.
// 2005-12-26 SMS.
long cpu_type;
char *cpu_type_name;
strcpy(p_vendor, "HP (DEC)");
getsysinfo( GSI_PROC_TYPE, (caddr_t) &cpu_type, sizeof( cpu_type));
CPU_TYPE_TO_TEXT( (cpu_type& 0xffffffff), cpu_type_name);
strncpy( p_model, "Alpha ", sizeof( p_model));
strncat( p_model, cpu_type_name, (sizeof( p_model)- strlen( p_model)- 1));
p_model[sizeof(p_model)-1]=0;
#elif HAVE_SYS_SYSTEMINFO_H
sysinfo(SI_PLATFORM, p_vendor, sizeof(p_vendor));
sysinfo(SI_ISALIST, p_model, sizeof(p_model));
for (unsigned int i=0; i p_ncpus){
p_ncpus = cpus_sys_path;
}
#elif defined(_SC_NPROCESSORS_ONLN) && !defined(__EMX__) && !defined(__APPLE__)
p_ncpus = sysconf(_SC_NPROCESSORS_ONLN);
#elif defined(HAVE_SYS_SYSCTL_H) && defined(CTL_HW) && defined(HW_NCPU)
// Get number of CPUs
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
len = sizeof(p_ncpus);
sysctl(mib, 2, &p_ncpus, &len, NULL, 0);
#elif defined(_HPUX_SOURCE)
struct pst_dynamic psd;
pstat_getdynamic ( &psd, sizeof ( psd ), (size_t)1, 0 );
p_ncpus = psd.psd_proc_cnt;
#else
#error Need to specify a method to get number of processors
#endif
///////////// m_nbytes, m_swap /////////////////
#ifdef __EMX__
{
ULONG ulMem;
CPU_INFO_t cpuInfo;
DosQuerySysInfo( QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &ulMem, sizeof(ulMem));
m_nbytes = ulMem;
// YD this is not the swap free space, but should be enough
DosQuerySysInfo( QSV_TOTAVAILMEM, QSV_TOTAVAILMEM, &ulMem, sizeof(ulMem));
m_swap = ulMem;
}
#elif LINUX_LIKE_SYSTEM
parse_meminfo_linux(*this);
#elif defined(_SC_USEABLE_MEMORY)
// UnixWare
m_nbytes = (double)sysconf(_SC_PAGESIZE) * (double)sysconf(_SC_USEABLE_MEMORY);
#elif defined(_SC_PHYS_PAGES)
m_nbytes = (double)sysconf(_SC_PAGESIZE) * (double)sysconf(_SC_PHYS_PAGES);
if (m_nbytes < 0) {
msg_printf(NULL, MSG_INTERNAL_ERROR,
"RAM size not measured correctly: page size %ld, #pages %ld",
sysconf(_SC_PAGESIZE), sysconf(_SC_PHYS_PAGES)
);
}
#elif defined(__APPLE__)
// On Mac OS X, sysctl with selectors CTL_HW, HW_PHYSMEM returns only a
// 4-byte value, even if passed an 8-byte buffer, and limits the returned
// value to 2GB when the actual RAM size is > 2GB. The Gestalt selector
// gestaltPhysicalRAMSizeInMegabytes is available starting with OS 10.3.0.
SInt32 mem_size;
if (Gestalt(gestaltPhysicalRAMSizeInMegabytes, &mem_size)) {
msg_printf(NULL, MSG_INTERNAL_ERROR,
"Couldn't determine physical RAM size"
);
}
m_nbytes = (1024. * 1024.) * (double)mem_size;
#elif defined(_HPUX_SOURCE)
struct pst_static pst;
pstat_getstatic(&pst, sizeof(pst), (size_t)1, 0);
m_nbytes = (long double)pst.physical_memory * (long double)pst.page_size;
#elif defined(__osf__)
// Tru64 UNIX.
// 2005-12-26 SMS.
int mem_size;
getsysinfo( GSI_PHYSMEM, (caddr_t) &mem_size, sizeof( mem_size));
m_nbytes = 1024.* (double)mem_size;
#elif defined(HW_PHYSMEM)
// for OpenBSD & NetBSD & FreeBSD
int mem_size;
mib[0] = CTL_HW;
mib[1] = HW_PHYSMEM;
len = sizeof(mem_size);
sysctl(mib, 2, &mem_size, &len, NULL, 0);
m_nbytes = mem_size;
#else
#error Need to specify a method to get memory size
#endif
#if HAVE_SYS_SWAP_H && defined(SC_GETNSWP)
// Solaris, ...
char buf[256];
swaptbl_t* s;
int i, n;
n = swapctl(SC_GETNSWP, 0);
s = (swaptbl_t*)malloc(n*sizeof(swapent_t) + sizeof(struct swaptable));
for (i=0; iswt_ent[i].ste_path = buf;
}
s->swt_n = n;
n = swapctl(SC_LIST, s);
m_swap = 0.0;
for (i=0; iswt_ent[i].ste_length;
}
#elif HAVE_SYS_SWAP_H && defined(SWAP_NSWAP)
// NetBSD (the above line should probably be more comprehensive
struct swapent * s;
int i, n;
n = swapctl(SWAP_NSWAP, NULL, 0);
s = (struct swapent*)malloc(n * sizeof(struct swapent));
swapctl(SWAP_STATS, s, n);
m_swap = 0.0;
for (i = 0; i < n; i ++) {
if (s[i].se_flags & SWF_ENABLE) {
m_swap += 512. * (double)s[i].se_nblks;
}
}
#elif defined(__APPLE__)
// The sysctl(vm.vmmeter) function doesn't work on OS X. However, swap
// space is limited only by free disk space, so we get that info instead.
// This is larger than free disk space reported by get_filesystem_info()
// because it includes space available only to the kernel / super-user.
//
// http://developer.apple.com/documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html says:
// Unlike most UNIX-based operating systems, Mac OS X does not use a
// preallocated swap partition for virtual memory. Instead, it uses all
// of the available space on the machineÕs boot partition.
struct statfs fs_info;
statfs(".", &fs_info);
m_swap = (double)fs_info.f_bsize * (double)fs_info.f_bfree;
#elif defined(__HAIKU__)
#warning HAIKU: missing swapfile size info
#elif defined(HAVE_VMMETER_H) && defined(HAVE_SYS_SYSCTL_H) && defined(CTL_VM) && defined(VM_METER)
// MacOSX, I think...
//
// The sysctl(vm.vmmeter) function doesn't work on OS X, so the following
// code fails to get the total swap space. See note above for APPLE case.
// I've left this code here in case it is used by a different platform,
// though I believe the first argument should be CTL_VM instead of CTL_USER.
struct vmtotal vm_info;
mib[0] = CTL_USER; // Should this be CTL_VM ?
mib[1] = VM_METER;
len = sizeof(vm_info);
if (!sysctl(mib, 2, &vm_info, &len, NULL, 0)) {
m_swap = 1024. * getpagesize() * (double) vm_info.t_vm;
}
#elif defined(_HPUX_SOURCE)
struct pst_vminfo vminfo;
pstat_getvminfo(&vminfo, sizeof(vminfo), (size_t)1, 0);
m_swap = (vminfo.psv_swapspc_max * pst.page_size);
#else
//#error Need to specify a method to obtain swap space
#endif
get_local_network_info();
timezone = get_timezone();
///////////// os_name, os_version /////////////////
#if HAVE_SYS_UTSNAME_H
struct utsname u;
uname(&u);
#ifdef ANDROID
safe_strcpy(os_name, "Android");
#else
safe_strcpy(os_name, u.sysname);
#endif //ANDROID
#if defined(__EMX__) // OS2: version is in u.version
safe_strcpy(os_version, u.version);
#elif defined(__HAIKU__)
snprintf(os_version, sizeof(os_version), "%s, %s", u.release, u.version);
#else
safe_strcpy(os_version, u.release);
#endif
#ifdef _HPUX_SOURCE
safe_strcpy(p_model, u.machine);
safe_strcpy(p_vendor, "Hewlett-Packard");
#endif
#elif HAVE_SYS_SYSCTL_H && defined(CTL_KERN) && defined(KERN_OSTYPE) && defined(KERN_OSRELEASE)
mib[0] = CTL_KERN;
mib[1] = KERN_OSTYPE;
len = sizeof(os_name);
sysctl(mib, 2, &os_name, &len, NULL, 0);
mib[0] = CTL_KERN;
mib[1] = KERN_OSRELEASE;
len = sizeof(os_version);
sysctl(mib, 2, &os_version, &len, NULL, 0);
#elif HAVE_SYS_SYSTEMINFO_H
sysinfo(SI_SYSNAME, os_name, sizeof(os_name));
sysinfo(SI_RELEASE, os_version, sizeof(os_version));
#else
#error Need to specify a method to obtain OS name/version
#endif
if (!strlen(host_cpid)) {
generate_host_cpid();
}
return 0;
}
// returns true iff device was last accessed before t
// or if an error occurred looking at the device.
//
inline bool device_idle(time_t t, const char *device) {
struct stat sbuf;
return stat(device, &sbuf) || (sbuf.st_atime < t);
}
static const struct dir_dev {
const char *dir;
const char *dev;
} tty_patterns[] = {
#ifdef unix
{ "/dev","tty" },
{ "/dev","pty" },
{ "/dev/pts","" },
#endif
// add other ifdefs here as necessary.
{ NULL, NULL },
};
vector get_tty_list() {
// Create a list of all terminal devices on the system.
char devname[1024];
char fullname[1024];
int done,i=0;
vector tty_list;
do {
DIRREF dev=dir_open(tty_patterns[i].dir);
if (dev) {
do {
// get next file
done=dir_scan(devname,dev,1024);
// does it match our tty pattern? If so, add it to the tty list.
if (!done && (strstr(devname,tty_patterns[i].dev) == devname)) {
// don't add anything starting with .
if (devname[0] != '.') {
sprintf(fullname,"%s/%s",tty_patterns[i].dir,devname);
tty_list.push_back(fullname);
}
}
} while (!done);
dir_close(dev);
}
i++;
} while (tty_patterns[i].dir != NULL);
return tty_list;
}
inline bool all_tty_idle(time_t t) {
static vector tty_list;
struct stat sbuf;
unsigned int i;
if (tty_list.size()==0) tty_list=get_tty_list();
for (i=0; i= t) {
return false;
}
}
}
return true;
}
#if HAVE_UTMP_H
inline bool user_idle(time_t t, struct utmp* u) {
char tty[5 + sizeof u->ut_line + 1] = "/dev/";
unsigned int i;
for (i=0; i < sizeof(u->ut_line); i++) {
// clean up tty if garbled
if (isalnum((int) u->ut_line[i]) || (u->ut_line[i]=='/')) {
tty[i+5] = u->ut_line[i];
} else {
tty[i+5] = '\0';
}
}
return device_idle(t, tty);
}
#if !HAVE_SETUTENT || !HAVE_GETUTENT
static FILE *ufp = NULL;
static struct utmp ut;
// get next user login record
// (this is defined on everything except BSD)
//
struct utmp *getutent() {
if (ufp == NULL) {
#if defined(UTMP_LOCATION)
if ((ufp = fopen(UTMP_LOCATION, "r")) == NULL) {
#elif defined(UTMP_FILE)
if ((ufp = fopen(UTMP_FILE, "r")) == NULL) {
#elif defined(_PATH_UTMP)
if ((ufp = fopen(_PATH_UTMP, "r")) == NULL) {
#else
if ((ufp = fopen("/etc/utmp", "r")) == NULL) {
#endif
return((struct utmp *)NULL);
}
}
do {
if (fread((char *)&ut, sizeof(ut), 1, ufp) != 1) {
return((struct utmp *)NULL);
}
} while (ut.ut_name[0] == 0);
return(&ut);
}
void setutent() {
if (ufp != NULL) rewind(ufp);
}
#endif
// scan list of logged-in users, and see if they're all idle
//
inline bool all_logins_idle(time_t t) {
struct utmp* u;
setutent();
while ((u = getutent()) != NULL) {
if (!user_idle(t, u)) {
return false;
}
}
return true;
}
#endif // HAVE_UTMP_H
#ifdef __APPLE__
// NXIdleTime() is an undocumented Apple API to return user idle time, which
// was implemented from before OS 10.0 through OS 10.5. In OS 10.4, Apple
// added the CGEventSourceSecondsSinceLastEventType() API as a replacement for
// NXIdleTime(). However, BOINC could not use this newer API when configured
// as a pre-login launchd daemon unless that daemon was running as root,
// because it could not connect to the Window Server. So BOINC continued to
// use NXIdleTime().
//
// In OS 10.6, Apple removed the NXIdleTime() API. BOINC can instead use the
// IOHIDGetParameter() API in OS 10.6. When BOINC is a pre-login launchd
// daemon running as user boinc_master, this API works properly under OS 10.6
// but fails under OS 10.5 and earlier.
//
// In OS 10.7, IOHIDGetParameter() fails to recognize activity from remote
// logins via Apple Remote Desktop or Screen Sharing (VNC), but the
// CGEventSourceSecondsSinceLastEventType() API does work with ARD and VNC,
// except when BOINC is a pre-login launchd daemon running as user boinc_master.
//
// Also, CGEventSourceSecondsSinceLastEventType() does not detect user activity
// when the user who launched the client is switched out by fast user switching.
//
// So we use weak-linking of NxIdleTime() to prevent a run-time crash from the
// dynamic linker and use it if it exists.
// If NXIdleTime does not exist, we call both IOHIDGetParameter() and
// CGEventSourceSecondsSinceLastEventType(). If both return without error,
// we use the lower of the two returned values.
//
bool HOST_INFO::users_idle(
bool check_all_logins, double idle_time_to_run, double *actual_idle_time
) {
static bool error_posted = false;
static long OSVersionInfo = 0;
OSStatus err = noErr;
double idleTime = 0;
double idleTimeFromCG = 0;
io_service_t service;
kern_return_t kernResult = kIOReturnError;
UInt64 params;
IOByteCount rcnt = sizeof(UInt64);
void *IOKitlib = NULL;
static bool triedToLoadNXIdleTime = false;
static nxIdleTimeProc myNxIdleTimeProc = NULL;
if (error_posted) goto bail;
if (!triedToLoadNXIdleTime) {
triedToLoadNXIdleTime = true;
err = Gestalt(gestaltSystemVersion, &OSVersionInfo);
if (err) OSVersionInfo = 0;
IOKitlib = dlopen ("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW );
if (IOKitlib) {
myNxIdleTimeProc = (nxIdleTimeProc)dlsym(IOKitlib, "NXIdleTime");
}
}
if (myNxIdleTimeProc) { // Use NXIdleTime API in OS 10.5 and earlier
if (gEventHandle) {
idleTime = myNxIdleTimeProc(gEventHandle);
} else {
// Initialize Mac OS X idle time measurement / idle detection
// Do this here because NXOpenEventStatus() may not be available
// immediately on system startup when running as a deaemon.
gEventHandle = NXOpenEventStatus();
if (!gEventHandle) {
if (TickCount() > (120*60)) { // If system has been up for more than 2 minutes
msg_printf(NULL, MSG_INFO,
"User idle detection is disabled: initialization failed."
);
error_posted = true;
goto bail;
}
}
}
} else { // NXIdleTime API does not exist in OS 10.6 and later
if (gEventHandle) {
kernResult = IOHIDGetParameter( gEventHandle, CFSTR(EVSIOIDLE), sizeof(UInt64), ¶ms, &rcnt );
if ( kernResult != kIOReturnSuccess ) {
msg_printf(NULL, MSG_INFO,
"User idle time measurement failed because IOHIDGetParameter failed."
);
error_posted = true;
goto bail;
}
idleTime = ((double)params) / 1000.0 / 1000.0 / 1000.0;
if (!gstate.executing_as_daemon) {
idleTimeFromCG = CGEventSourceSecondsSinceLastEventType
(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType);
if (idleTimeFromCG < idleTime) {
idleTime = idleTimeFromCG;
}
}
} else {
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
if (service) {
kernResult = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &gEventHandle);
}
if ( (!service) || (kernResult != KERN_SUCCESS) ) {
// When the system first starts up, allow time for HIDSystem to be available if needed
if (TickCount() > (120*60)) { // If system has been up for more than 2 minutes
msg_printf(NULL, MSG_INFO,
"Could not connect to HIDSystem: user idle detection is disabled."
);
error_posted = true;
goto bail;
}
}
} // End (gEventHandle == NULL)
} // End NXIdleTime API does not exist
bail:
if (actual_idle_time) {
*actual_idle_time = idleTime;
}
return (idleTime > (60 * idle_time_to_run));
}
#else // ! __APPLE__
#if LINUX_LIKE_SYSTEM
bool interrupts_idle(time_t t) {
// This method doesn't really work reliably on USB keyboards and mice.
static FILE *ifp = NULL;
static long irq_count[256];
static time_t last_irq = time(NULL);
char line[256];
int i = 0;
long ccount = 0;
if (ifp == NULL) {
if ((ifp = fopen("/proc/interrupts", "r")) == NULL) {
return true;
}
}
rewind(ifp);
while (fgets(line, sizeof(line), ifp)) {
// Check for mouse, keyboard and PS/2 devices.
if (strcasestr(line, "mouse") != NULL ||
strcasestr(line, "keyboard") != NULL ||
strcasestr(line, "i8042") != NULL) {
// If any IRQ count changed, update last_irq.
if (sscanf(line, "%d: %ld", &i, &ccount) == 2
&& irq_count[i] != ccount
) {
last_irq = time(NULL);
irq_count[i] = ccount;
}
}
}
return last_irq < t;
}
#if HAVE_XSS
// Ask the X server for user idle time (using XScreenSaver API)
// Returns true if the idle_treshold is smaller than the
// idle time of the user (means: true = user is idle)
bool xss_idle(long idle_treshold) {
static XScreenSaverInfo* xssInfo = NULL;
static Display* disp = NULL;
long idle_time = 0;
if(disp != NULL) {
XScreenSaverQueryInfo(disp, DefaultRootWindow(disp), xssInfo);
idle_time = xssInfo->idle;
#if HAVE_DPMS
// XIdleTime Detection
// See header for location and copywrites.
//
int dummy;
CARD16 standby, suspend, off;
CARD16 state;
BOOL onoff;
if (DPMSQueryExtension(disp, &dummy, &dummy)) {
if (DPMSCapable(disp)) {
DPMSGetTimeouts(disp, &standby, &suspend, &off);
DPMSInfo(disp, &state, &onoff);
if (onoff) {
switch (state) {
case DPMSModeStandby:
/* this check is a littlebit paranoid, but be sure */
if (idle_time < (unsigned) (standby * 1000)) {
idle_time += (standby * 1000);
}
break;
case DPMSModeSuspend:
if (idle_time < (unsigned) ((suspend + standby) * 1000)) {
idle_time += ((suspend + standby) * 1000);
}
break;
case DPMSModeOff:
if (idle_time < (unsigned) ((off + suspend + standby) * 1000)) {
idle_time += ((off + suspend + standby) * 1000);
}
break;
case DPMSModeOn:
default:
break;
}
}
}
}
#endif
// convert from milliseconds to seconds
idle_time = idle_time / 1000;
} else {
disp = XOpenDisplay(NULL);
// XOpenDisplay may return NULL if there is no running X
// or DISPLAY points to wrong/invalid display
if(disp != NULL) {
xssInfo = XScreenSaverAllocInfo();
}
}
return idle_treshold < idle_time;
}
#endif // HAVE_XSS
#endif // LINUX_LIKE_SYSTEM
bool HOST_INFO::users_idle(bool check_all_logins, double idle_time_to_run) {
time_t idle_time = time(0) - (long) (60 * idle_time_to_run);
#if HAVE_UTMP_H
if (check_all_logins) {
if (!all_logins_idle(idle_time)) {
return false;
}
}
#endif
if (!all_tty_idle(idle_time)) {
return false;
}
#if LINUX_LIKE_SYSTEM
// Check /proc/interrupts to detect keyboard or mouse activity.
// this ignores USB keyboards/mice. They don't use the keyboard
// and mouse interrupts.
if (!interrupts_idle(idle_time)) {
return false;
}
// Lets at least check the dev entries which should be correct for
// USB mice. The tty check will catch keyboards if they are entering
// data into a tty.
if (!device_idle(idle_time, "/dev/input/mice")) return false;
#if HAVE_XSS
if (!xss_idle((long)(idle_time_to_run * 60))) {
return false;
}
#endif
#else
// We should find out which of the following are actually relevant
// on which systems (if any)
//
if (!device_idle(idle_time, "/dev/mouse")) return false;
// solaris, linux
if (!device_idle(idle_time, "/dev/input/mice")) return false;
if (!device_idle(idle_time, "/dev/kbd")) return false;
// solaris
#endif
return true;
}
#endif // ! __APPLE__